@cortexkit/opencode-magic-context 0.47.0 → 0.49.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 (425) hide show
  1. package/dist/agents/magic-context-prompt.d.ts +1 -1
  2. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  3. package/dist/{announcement-y35wzg2r.js → announcement-fp14dw0n.js} +3 -2
  4. package/dist/auto-search-worker.js +27 -30
  5. package/dist/config/index.d.ts.map +1 -1
  6. package/dist/config/mural-model-deprecation.d.ts +15 -0
  7. package/dist/config/mural-model-deprecation.d.ts.map +1 -0
  8. package/dist/config/project-security.d.ts +0 -3
  9. package/dist/config/project-security.d.ts.map +1 -1
  10. package/dist/config/schema/magic-context.d.ts +10 -6
  11. package/dist/config/schema/magic-context.d.ts.map +1 -1
  12. package/dist/{conflict-warning-hook-z44ynfz7.js → conflict-warning-hook-kzx4dmk1.js} +12 -10
  13. package/dist/{context-wq4ffwyb.js → context-nj5f293n.js} +2765 -1925
  14. package/dist/{derive-budgets-ke12jnnh.js → derive-budgets-qz5rrr08.js} +4 -3
  15. package/dist/embedding-activity-g1dhes6g.js +10 -0
  16. package/dist/embedding-backfill-aetxv1vt.js +21 -0
  17. package/dist/embedding-cache-mgyr73ef.js +16 -0
  18. package/dist/embedding-worker.js +11 -9
  19. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  20. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +1 -0
  21. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  22. package/dist/features/magic-context/dreamer/task-config.d.ts +3 -3
  23. package/dist/features/magic-context/dreamer/task-config.d.ts.map +1 -1
  24. package/dist/features/magic-context/dreamer/task-executor.d.ts +1 -1
  25. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  26. package/dist/features/magic-context/dreamer/task-registry.d.ts +1 -0
  27. package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
  28. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  29. package/dist/features/magic-context/git-commits/search-git-commits.d.ts +2 -0
  30. package/dist/features/magic-context/git-commits/search-git-commits.d.ts.map +1 -1
  31. package/dist/features/magic-context/message-index-async.d.ts +16 -3
  32. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  33. package/dist/features/magic-context/message-index-bounded.d.ts +17 -0
  34. package/dist/features/magic-context/message-index-bounded.d.ts.map +1 -0
  35. package/dist/features/magic-context/message-index.d.ts +7 -1
  36. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  37. package/dist/features/magic-context/mural/render-trigger.d.ts +7 -4
  38. package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
  39. package/dist/features/magic-context/pi-content-decisions.d.ts +7 -1
  40. package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -1
  41. package/dist/features/magic-context/search-rerank.d.ts +35 -0
  42. package/dist/features/magic-context/search-rerank.d.ts.map +1 -0
  43. package/dist/features/magic-context/search.d.ts +40 -0
  44. package/dist/features/magic-context/search.d.ts.map +1 -1
  45. package/dist/features/magic-context/session-activity.d.ts.map +1 -1
  46. package/dist/features/magic-context/smart-notes/runner.d.ts +1 -0
  47. package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
  48. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  49. package/dist/features/magic-context/storage-meta-persisted.d.ts +13 -2
  50. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  51. package/dist/features/magic-context/storage-meta-session.d.ts +7 -1
  52. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  53. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  54. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  55. package/dist/features/magic-context/storage-session-tables.d.ts +2 -0
  56. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  57. package/dist/features/magic-context/storage-tag-identity-revision.d.ts +50 -0
  58. package/dist/features/magic-context/storage-tag-identity-revision.d.ts.map +1 -0
  59. package/dist/features/magic-context/storage-tags.d.ts +153 -4
  60. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  61. package/dist/features/magic-context/storage.d.ts +1 -1
  62. package/dist/features/magic-context/storage.d.ts.map +1 -1
  63. package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
  64. package/dist/features/magic-context/tagger.d.ts +2 -0
  65. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  66. package/dist/features/magic-context/transform-decision-log.d.ts +4 -1
  67. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  68. package/dist/harness-e4avh192.js +10 -0
  69. package/dist/hooks/magic-context/active-anthropic-turn.d.ts +2 -0
  70. package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -1
  71. package/dist/hooks/magic-context/auto-search-deadline.d.ts.map +1 -1
  72. package/dist/hooks/magic-context/auto-search-replay.fixture.d.ts +24 -0
  73. package/dist/hooks/magic-context/auto-search-replay.fixture.d.ts.map +1 -0
  74. package/dist/hooks/magic-context/auto-search-runner.d.ts +1 -1
  75. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  76. package/dist/hooks/magic-context/auto-search-worker-client.d.ts +34 -3
  77. package/dist/hooks/magic-context/auto-search-worker-client.d.ts.map +1 -1
  78. package/dist/hooks/magic-context/command-handler.d.ts +3 -0
  79. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  80. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  82. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  83. package/dist/hooks/magic-context/emergency-fail-closed.d.ts +28 -0
  84. package/dist/hooks/magic-context/emergency-fail-closed.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  86. package/dist/hooks/magic-context/historian-host-runner.d.ts +8 -0
  87. package/dist/hooks/magic-context/historian-host-runner.d.ts.map +1 -1
  88. package/dist/hooks/magic-context/hook.d.ts +3 -2
  89. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  90. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  91. package/dist/hooks/magic-context/latest-thinking-recovery.d.ts +7 -0
  92. package/dist/hooks/magic-context/latest-thinking-recovery.d.ts.map +1 -1
  93. package/dist/hooks/magic-context/lkg-persist.d.ts +4 -3
  94. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
  95. package/dist/hooks/magic-context/lkg-replay.d.ts +13 -8
  96. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  97. package/dist/hooks/magic-context/lkg-slot.d.ts +139 -2
  98. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  99. package/dist/hooks/magic-context/message-search-accepting-silent-worker.fixture.d.ts +2 -0
  100. package/dist/hooks/magic-context/message-search-accepting-silent-worker.fixture.d.ts.map +1 -0
  101. package/dist/hooks/magic-context/oc-stall-r2-warmup-worker.fixture.d.ts +2 -0
  102. package/dist/hooks/magic-context/oc-stall-r2-warmup-worker.fixture.d.ts.map +1 -0
  103. package/dist/hooks/magic-context/oc-stall-search-review.fixture.d.ts +2 -0
  104. package/dist/hooks/magic-context/oc-stall-search-review.fixture.d.ts.map +1 -0
  105. package/dist/hooks/magic-context/opencode1-to-model-messages.fixture.d.ts +63 -0
  106. package/dist/hooks/magic-context/opencode1-to-model-messages.fixture.d.ts.map +1 -0
  107. package/dist/hooks/magic-context/pass-outcome.d.ts +1 -0
  108. package/dist/hooks/magic-context/pass-outcome.d.ts.map +1 -1
  109. package/dist/hooks/magic-context/provider-visible-parts.d.ts +45 -0
  110. package/dist/hooks/magic-context/provider-visible-parts.d.ts.map +1 -0
  111. package/dist/hooks/magic-context/raw-ordinal-warmup-silent-worker.fixture.d.ts +2 -0
  112. package/dist/hooks/magic-context/raw-ordinal-warmup-silent-worker.fixture.d.ts.map +1 -0
  113. package/dist/hooks/magic-context/raw-ordinal-warmup-worker.d.ts +2 -0
  114. package/dist/hooks/magic-context/raw-ordinal-warmup-worker.d.ts.map +1 -0
  115. package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts +87 -0
  116. package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts.map +1 -0
  117. package/dist/hooks/magic-context/read-session-chunk.d.ts +19 -1
  118. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  119. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  120. package/dist/hooks/magic-context/read-session-formatting.d.ts +5 -0
  121. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  122. package/dist/hooks/magic-context/read-session-raw.d.ts +118 -1
  123. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  124. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  125. package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -1
  126. package/dist/hooks/magic-context/rust-mode-transform.d.ts +36 -0
  127. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  128. package/dist/hooks/magic-context/sentinel.d.ts +41 -1
  129. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  130. package/dist/hooks/magic-context/shared-compartment-boundaries.d.ts +8 -1
  131. package/dist/hooks/magic-context/shared-compartment-boundaries.d.ts.map +1 -1
  132. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts +8 -0
  133. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts.map +1 -0
  134. package/dist/hooks/magic-context/strip-content.d.ts +14 -2
  135. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  136. package/dist/hooks/magic-context/system-prompt-hash.d.ts +1 -0
  137. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  138. package/dist/hooks/magic-context/tag-messages.d.ts +8 -0
  139. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  140. package/dist/hooks/magic-context/tag-owner-warmup.d.ts +6 -0
  141. package/dist/hooks/magic-context/tag-owner-warmup.d.ts.map +1 -0
  142. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
  143. package/dist/hooks/magic-context/token-count-exact.d.ts +35 -0
  144. package/dist/hooks/magic-context/token-count-exact.d.ts.map +1 -0
  145. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +13 -0
  146. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  147. package/dist/hooks/magic-context/transform.d.ts +9 -2
  148. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  149. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  150. package/dist/{index-q6hef7fh.js → index-0sahzkwr.js} +6 -19
  151. package/dist/{index-k5bdx1bx.js → index-0w56v4fm.js} +3 -5
  152. package/dist/{index-5gq3b2ya.js → index-1bt88epp.js} +227 -205
  153. package/dist/{index-cwf6s3ay.js → index-1g30axtz.js} +3279 -2907
  154. package/dist/{index-5a2r0p9f.js → index-1r0k9js3.js} +20 -13
  155. package/dist/index-2bvekkfy.js +11 -0
  156. package/dist/{index-6d9e1m08.js → index-2jcbj7r1.js} +13301 -13225
  157. package/dist/index-2jgwttpt.js +3269 -0
  158. package/dist/index-4dhv7mqz.js +131 -0
  159. package/dist/{index-sj29b3z2.js → index-4mjhqadz.js} +3 -3
  160. package/dist/{index-0c82743p.js → index-57mrqed8.js} +17 -15
  161. package/dist/{index-b35pxds2.js → index-58e3y90n.js} +1 -18
  162. package/dist/index-7jzrrxap.js +931 -0
  163. package/dist/index-8ajstryz.js +18 -0
  164. package/dist/{index-sfpzqvdz.js → index-8cd2j99z.js} +2 -3
  165. package/dist/{index-6wkcswft.js → index-9qjqq45k.js} +29 -19
  166. package/dist/{index-adwfkpex.js → index-9rt53q79.js} +23 -23
  167. package/dist/index-b7mv1jx3.js +1060 -0
  168. package/dist/index-d9vfe7xq.js +16 -0
  169. package/dist/{index-gg8j07zz.js → index-ezmszwbd.js} +8 -6
  170. package/dist/index-fb3zxn06.js +13 -0
  171. package/dist/index-fr0wbhdn.js +43 -0
  172. package/dist/{index-mc22ayvf.js → index-ghc9f0n7.js} +11 -9
  173. package/dist/index-gzcmtmar.js +171 -0
  174. package/dist/{index-w2t4ah5j.js → index-h2py7207.js} +6762 -7400
  175. package/dist/index-h2wc01qk.js +120 -0
  176. package/dist/index-h5zfkjyk.js +1277 -0
  177. package/dist/{index-92bp57hg.js → index-ha87j7a8.js} +1343 -149
  178. package/dist/index-hj2q50k0.js +856 -0
  179. package/dist/{index-wx8a6bw3.js → index-kb9nn0a0.js} +270 -53
  180. package/dist/index-kmpvemph.js +1314 -0
  181. package/dist/index-kp5qe690.js +3030 -0
  182. package/dist/{index-m5fe6jch.js → index-kq57tdf0.js} +152 -2453
  183. package/dist/{v2/server-fq7kq32k.js → index-mfkzp7qj.js} +1 -16
  184. package/dist/{v2/server-njt3np1k.js → index-nm6a8xpd.js} +220 -961
  185. package/dist/{index-hybnrwq0.js → index-nsjyysj0.js} +137 -130
  186. package/dist/index-nydv6g8r.js +3569 -0
  187. package/dist/index-pavzsdc6.js +17 -0
  188. package/dist/index-peshmj6h.js +152 -0
  189. package/dist/index-pxwp2hjk.js +571 -0
  190. package/dist/{v2/server-yr3zj7e7.js → index-qt1qqasc.js} +6 -7
  191. package/dist/{v2/server-51dv1pr5.js → index-qx53vezw.js} +301 -27
  192. package/dist/{index-rrrsepyw.js → index-s4j7t92z.js} +90 -85
  193. package/dist/{index-9pk9exrb.js → index-stbr4ce6.js} +18 -18
  194. package/dist/{index-fzbvakh0.js → index-tqf7yg27.js} +3 -3
  195. package/dist/index-vkbqdnkj.js +2307 -0
  196. package/dist/{index-8da2zjhz.js → index-z1tjp257.js} +9 -7
  197. package/dist/index.d.ts.map +1 -1
  198. package/dist/index.js +608 -349
  199. package/dist/logger-ewak5q1s.js +20 -0
  200. package/dist/message-index-bounded-h0mrd4pr.js +124 -0
  201. package/dist/{v2/message-time-backfill-1f9tzvcs.js → message-time-backfill-8x06r9kx.js} +8 -6
  202. package/dist/migration-worker.js +25 -18
  203. package/dist/{model-resolution-xhfmyyvy.js → model-resolution-nx97z3x7.js} +1 -1
  204. package/dist/plugin/dream-timer.d.ts +1 -1
  205. package/dist/plugin/dream-timer.d.ts.map +1 -1
  206. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -0
  207. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  208. package/dist/plugin/host-stall-profiler-report.d.ts +1 -66
  209. package/dist/plugin/host-stall-profiler-report.d.ts.map +1 -1
  210. package/dist/plugin/host-stall-profiler.d.ts +2 -95
  211. package/dist/plugin/host-stall-profiler.d.ts.map +1 -1
  212. package/dist/plugin/messages-transform.d.ts +0 -13
  213. package/dist/plugin/messages-transform.d.ts.map +1 -1
  214. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  215. package/dist/plugin/tool-registry.d.ts.map +1 -1
  216. package/dist/project-embedding-registry-rtt0djyb.js +88 -0
  217. package/dist/raw-ordinal-warmup-worker.js +60 -0
  218. package/dist/{read-session-chunk-4jey7hpt.js → read-session-chunk-ty89v75e.js} +17 -9
  219. package/dist/read-session-raw-8sjbc9h0.js +60 -0
  220. package/dist/recomp-orchestrator-4f1yyd1q.js +57 -0
  221. package/dist/{v2/safe-notification-target-eb1n1vfb.js → safe-notification-target-q0cyrx3x.js} +4 -2
  222. package/dist/search-pz4f4wge.js +45 -0
  223. package/dist/{v2/send-session-notification-c0wtscea.js → send-session-notification-38cd5dqt.js} +10 -7
  224. package/dist/shared/auto-search-lifecycle.d.ts +14 -0
  225. package/dist/shared/auto-search-lifecycle.d.ts.map +1 -1
  226. package/dist/shared/conflict-detector.d.ts.map +1 -1
  227. package/dist/shared/host-stall-profiler-bun.d.ts +46 -0
  228. package/dist/shared/host-stall-profiler-bun.d.ts.map +1 -0
  229. package/dist/shared/host-stall-profiler-common.d.ts +58 -0
  230. package/dist/shared/host-stall-profiler-common.d.ts.map +1 -0
  231. package/dist/shared/host-stall-profiler-node.d.ts +36 -0
  232. package/dist/shared/host-stall-profiler-node.d.ts.map +1 -0
  233. package/dist/shared/host-stall-profiler-report.d.ts +68 -0
  234. package/dist/shared/host-stall-profiler-report.d.ts.map +1 -0
  235. package/dist/shared/host-stall-profiler.d.ts +5 -0
  236. package/dist/shared/host-stall-profiler.d.ts.map +1 -0
  237. package/dist/shared/logger.d.ts.map +1 -1
  238. package/dist/shared/model-resolution.d.ts +2 -3
  239. package/dist/shared/model-resolution.d.ts.map +1 -1
  240. package/dist/shared/models-dev-temperature.d.ts +18 -0
  241. package/dist/shared/models-dev-temperature.d.ts.map +1 -0
  242. package/dist/shared/project-directory-key.d.ts +16 -0
  243. package/dist/shared/project-directory-key.d.ts.map +1 -1
  244. package/dist/shared/sqlite.d.ts +30 -0
  245. package/dist/shared/sqlite.d.ts.map +1 -1
  246. package/dist/shared/stable-json.d.ts.map +1 -1
  247. package/dist/shared/subagent-temperature.d.ts +46 -0
  248. package/dist/shared/subagent-temperature.d.ts.map +1 -0
  249. package/dist/shared/user-facing-codes.d.ts +0 -5
  250. package/dist/shared/user-facing-codes.d.ts.map +1 -1
  251. package/dist/sqlite-context-pragmas-mv9jqwjh.js +6 -0
  252. package/dist/sqlite-mhjkerzq.js +46 -0
  253. package/dist/storage-memory-07fx57k6.js +71 -0
  254. package/dist/storage-tags-k0pda32s.js +133 -0
  255. package/dist/{task-config-v81k8jvk.js → task-config-cgpg0nvh.js} +2 -2
  256. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  257. package/dist/tools/ctx-note/constants.d.ts +4 -0
  258. package/dist/tools/ctx-note/constants.d.ts.map +1 -1
  259. package/dist/tools/ctx-note/tools.d.ts +1 -0
  260. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  261. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  262. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  263. package/dist/tools/ctx-search/types.d.ts +1 -0
  264. package/dist/tools/ctx-search/types.d.ts.map +1 -1
  265. package/dist/tools/parameter-descriptions.d.ts +2 -2
  266. package/dist/tools/parameter-descriptions.d.ts.map +1 -1
  267. package/dist/{transform-postprocess-phase-z5hzpsxv.js → transform-postprocess-phase-tck4g20s.js} +35 -20
  268. package/dist/transformers-node-wasm.js +18415 -18415
  269. package/dist/v2/auto-search-worker.js +27 -30
  270. package/dist/v2/{context-14v1hr8b.js → context-crbt6p1y.js} +4708 -4218
  271. package/dist/v2/{derive-budgets-nc9avxz8.js → derive-budgets-fgej9j46.js} +4 -3
  272. package/dist/v2/embedding-activity-g1dhes6g.js +10 -0
  273. package/dist/v2/embedding-backfill-ph1qfyn3.js +21 -0
  274. package/dist/v2/embedding-cache-mgyr73ef.js +16 -0
  275. package/dist/v2/embedding-worker.js +12 -9
  276. package/dist/v2/fold/host-media.d.ts +4 -0
  277. package/dist/v2/fold/host-media.d.ts.map +1 -1
  278. package/dist/v2/fold/memory-cache.d.ts +70 -0
  279. package/dist/v2/fold/memory-cache.d.ts.map +1 -0
  280. package/dist/v2/fold/native-codec.d.ts +10 -0
  281. package/dist/v2/fold/native-codec.d.ts.map +1 -0
  282. package/dist/v2/fold/native-replay.d.ts +58 -0
  283. package/dist/v2/fold/native-replay.d.ts.map +1 -0
  284. package/dist/v2/fold/policy.d.ts +24 -0
  285. package/dist/v2/fold/policy.d.ts.map +1 -0
  286. package/dist/v2/harness-e4avh192.js +10 -0
  287. package/dist/v2/hidden-child-native.d.ts.map +1 -1
  288. package/dist/v2/hidden-completion.d.ts +7 -0
  289. package/dist/v2/hidden-completion.d.ts.map +1 -1
  290. package/dist/v2/hooks/context.d.ts +2 -2
  291. package/dist/v2/hooks/context.d.ts.map +1 -1
  292. package/dist/v2/hooks/dream-manual.d.ts +1 -1
  293. package/dist/v2/hooks/dream-manual.d.ts.map +1 -1
  294. package/dist/v2/hooks/dream-timer.d.ts.map +1 -1
  295. package/dist/v2/hooks/dream-trigger.d.ts +5 -3
  296. package/dist/v2/hooks/dream-trigger.d.ts.map +1 -1
  297. package/dist/v2/hooks/generate.d.ts +9 -2
  298. package/dist/v2/hooks/generate.d.ts.map +1 -1
  299. package/dist/v2/hooks/payload.d.ts.map +1 -1
  300. package/dist/v2/hooks/tools.d.ts.map +1 -1
  301. package/dist/v2/hooks/types.d.ts +6 -0
  302. package/dist/v2/hooks/types.d.ts.map +1 -1
  303. package/dist/v2/logger-pj02s7sr.js +19 -0
  304. package/dist/v2/message-index-bounded-xwqdywxg.js +123 -0
  305. package/dist/{message-time-backfill-r5epkzj3.js → v2/message-time-backfill-cje68egj.js} +7 -7
  306. package/dist/v2/migration-worker.js +24 -17
  307. package/dist/v2/{model-resolution-xhfmyyvy.js → model-resolution-nx97z3x7.js} +1 -1
  308. package/dist/v2/project-embedding-registry-d6m7tyxr.js +86 -0
  309. package/dist/v2/raw-ordinal-warmup-worker.js +60 -0
  310. package/dist/v2/{read-session-chunk-gyyv7w2r.js → read-session-chunk-8p5mdryx.js} +16 -8
  311. package/dist/v2/read-session-raw-j8dvp7f4.js +59 -0
  312. package/dist/v2/recomp-orchestrator-ckfgt6wq.js +57 -0
  313. package/dist/{safe-notification-target-43r8jsxw.js → v2/safe-notification-target-y3p1hce9.js} +3 -3
  314. package/dist/v2/search-ym8rehhz.js +45 -0
  315. package/dist/{send-session-notification-0m68ms3n.js → v2/send-session-notification-5m0yapqx.js} +9 -8
  316. package/dist/v2/{server-rz514f14.js → server-19zjmn02.js} +2044 -2060
  317. package/dist/v2/server-1tk82krw.js +796 -0
  318. package/dist/v2/server-2bvekkfy.js +11 -0
  319. package/dist/v2/{server-ajzeteqw.js → server-2ra6pwf3.js} +1 -18
  320. package/dist/v2/server-2s6ayq3t.js +1048 -0
  321. package/dist/v2/{server-fcdehk1s.js → server-2s9qz6n7.js} +4 -4
  322. package/dist/v2/server-3j4d00kf.js +171 -0
  323. package/dist/v2/{server-14vq4kpm.js → server-3t0q2cqx.js} +3 -3
  324. package/dist/v2/server-4dhv7mqz.js +131 -0
  325. package/dist/v2/server-4ez8mntn.js +3213 -0
  326. package/dist/v2/{server-6r257mf5.js → server-676xw7at.js} +3 -3
  327. package/dist/v2/{server-vs5gpnd4.js → server-6byqfbqf.js} +165 -145
  328. package/dist/v2/server-7ekna9zs.js +120 -0
  329. package/dist/v2/server-7h861yms.js +1272 -0
  330. package/dist/v2/server-7jzrrxap.js +931 -0
  331. package/dist/v2/server-8ajstryz.js +18 -0
  332. package/dist/v2/{server-sfpzqvdz.js → server-8cd2j99z.js} +2 -3
  333. package/dist/v2/{server-cpepxy2q.js → server-8k2agx29.js} +14 -12
  334. package/dist/v2/server-c5rrgrcd.js +3545 -0
  335. package/dist/v2/server-d9vfe7xq.js +16 -0
  336. package/dist/v2/server-fb3zxn06.js +13 -0
  337. package/dist/v2/server-fr0wbhdn.js +43 -0
  338. package/dist/v2/{server-y10vbq6y.js → server-hb6xhh9q.js} +11 -1053
  339. package/dist/v2/server-jvxzm1jy.js +3023 -0
  340. package/dist/v2/{server-wx8a6bw3.js → server-kb9nn0a0.js} +270 -53
  341. package/dist/{index-5eqk6z3s.js → v2/server-kzns2hbz.js} +301 -27
  342. package/dist/{index-rrbbspwh.js → v2/server-mfkzp7qj.js} +1 -16
  343. package/dist/v2/server-pavzsdc6.js +17 -0
  344. package/dist/v2/{server-vd4e7174.js → server-pj4pjv02.js} +21 -17
  345. package/dist/v2/{server-e0c1b7a7.js → server-q4ph9bwn.js} +1340 -148
  346. package/dist/{index-yr3zj7e7.js → v2/server-qt1qqasc.js} +6 -7
  347. package/dist/v2/{server-qdjnb2z1.js → server-r5p3b1wy.js} +11 -9
  348. package/dist/v2/{server-d0sraw7m.js → server-sa8jvrfc.js} +291 -286
  349. package/dist/v2/server-ss95ky04.js +1048 -0
  350. package/dist/v2/{server-1qbnzc80.js → server-t4k4qexw.js} +142 -150
  351. package/dist/{index-x3kwrmqk.js → v2/server-v4mewb78.js} +35 -792
  352. package/dist/v2/server-w32kmdez.js +154 -0
  353. package/dist/v2/{server-nf82q6pt.js → server-x4hscabv.js} +5547 -5449
  354. package/dist/v2/server-xwz4dqh9.js +569 -0
  355. package/dist/v2/{server-efhwzx05.js → server-ycw9qqcy.js} +23 -23
  356. package/dist/v2/{server-50hvkg3v.js → server-zm570tr2.js} +160 -158
  357. package/dist/v2/server-zp53y8js.js +1311 -0
  358. package/dist/v2/server.js +16 -14
  359. package/dist/v2/sqlite-context-pragmas-mv9jqwjh.js +6 -0
  360. package/dist/v2/sqlite-mhjkerzq.js +46 -0
  361. package/dist/v2/storage-memory-07fx57k6.js +71 -0
  362. package/dist/v2/storage-tags-8vw924zn.js +132 -0
  363. package/dist/v2/store-reader.d.ts +24 -2
  364. package/dist/v2/store-reader.d.ts.map +1 -1
  365. package/dist/v2/{task-config-tbndqyny.js → task-config-x0hq3qfd.js} +2 -2
  366. package/dist/v2/transformers-node-wasm.js +18415 -18415
  367. package/dist/v2/tui/index.d.ts.map +1 -1
  368. package/dist/v2/tui/types.d.ts +1 -10
  369. package/dist/v2/tui/types.d.ts.map +1 -1
  370. package/dist/v2/{wrapup-orchestrator-nbz854vc.js → wrapup-orchestrator-a7efxaax.js} +51 -30
  371. package/dist/wrapup-orchestrator-barbc6h9.js +44 -0
  372. package/package.json +4 -3
  373. package/src/agents/magic-context-prompt.ts +27 -2
  374. package/src/agents/smart-notes-enabled.test.ts +294 -0
  375. package/src/agents/smart-notes-on.golden.json +70 -0
  376. package/src/config/index.test.ts +67 -1
  377. package/src/config/index.ts +2 -0
  378. package/src/config/latch-permanence-guard.test.ts +4 -0
  379. package/src/config/mural-model-deprecation.ts +24 -0
  380. package/src/config/project-security.hostile-matrix.test.ts +4 -44
  381. package/src/config/project-security.test.ts +9 -14
  382. package/src/config/project-security.ts +13 -55
  383. package/src/config/schema/magic-context.test.ts +2 -2
  384. package/src/config/schema/magic-context.ts +16 -10
  385. package/src/features/magic-context/dreamer/task-registry.ts +6 -0
  386. package/src/shared/auto-search-lifecycle.ts +43 -0
  387. package/src/shared/conflict-detector.test.ts +56 -0
  388. package/src/shared/conflict-detector.ts +16 -5
  389. package/src/shared/host-stall-profiler-bun.ts +358 -0
  390. package/src/shared/host-stall-profiler-common.ts +159 -0
  391. package/src/shared/host-stall-profiler-node.test.ts +234 -0
  392. package/src/shared/host-stall-profiler-node.ts +274 -0
  393. package/src/shared/host-stall-profiler-report.ts +248 -0
  394. package/src/shared/host-stall-profiler.ts +57 -0
  395. package/src/shared/logger.test.ts +38 -0
  396. package/src/shared/logger.ts +10 -2
  397. package/src/shared/model-resolution.test.ts +54 -17
  398. package/src/shared/model-resolution.ts +6 -17
  399. package/src/shared/models-dev-temperature.ts +107 -0
  400. package/src/shared/project-directory-key.test.ts +59 -0
  401. package/src/shared/project-directory-key.ts +20 -0
  402. package/src/shared/sqlite.test.ts +52 -0
  403. package/src/shared/sqlite.ts +339 -23
  404. package/src/shared/stable-json-linear.test.ts +109 -0
  405. package/src/shared/stable-json.ts +37 -7
  406. package/src/shared/subagent-temperature.test.ts +270 -0
  407. package/src/shared/subagent-temperature.ts +110 -0
  408. package/src/shared/user-facing-codes.ts +2 -6
  409. package/src/shared/write-transaction-attribution.test.ts +2 -2
  410. package/src/v2/tui/host-contract.test.ts +41 -67
  411. package/src/v2/tui/index.ts +6 -214
  412. package/src/v2/tui/types.ts +5 -17
  413. package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts +0 -45
  414. package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts.map +0 -1
  415. package/dist/features/magic-context/checkout-claim.d.ts +0 -155
  416. package/dist/features/magic-context/checkout-claim.d.ts.map +0 -1
  417. package/dist/index-bb36vgr3.js +0 -6608
  418. package/dist/index-fnn5pjtt.js +0 -6554
  419. package/dist/recomp-orchestrator-rervg6q4.js +0 -42
  420. package/dist/v2/recomp-orchestrator-42vjba7p.js +0 -42
  421. package/dist/v2/server-3trjk1hx.js +0 -6360
  422. package/dist/v2/server-nm9n3aqq.js +0 -6461
  423. package/dist/wrapup-orchestrator-b2dztrar.js +0 -29
  424. /package/dist/{index-5prbx026.js → index-2pp04pmg.js} +0 -0
  425. /package/dist/v2/{index-5prbx026.js → index-2pp04pmg.js} +0 -0
@@ -13,6 +13,7 @@ import {
13
13
  __setCanonicalDirectoryRealpathForTests,
14
14
  canonicalProjectDirectory,
15
15
  projectDirectoryKey,
16
+ sameProjectDirectory,
16
17
  } from "./project-directory-key";
17
18
  import { projectHash, rpcPortDirsForLookup } from "./rpc-utils";
18
19
  import { createTestTempDirFromPath } from "./test-temp-dir";
@@ -109,3 +110,61 @@ describe("one discovery directory per project directory", () => {
109
110
  expect(dirs[0]).toBe(join("/storage", "rpc", projectHash("C:\\Users\\A\\Proj\\")));
110
111
  });
111
112
  });
113
+
114
+ /**
115
+ * Background-session code compares a session's recorded directory with the
116
+ * plugin's own. OpenCode 2 on Windows records `D:/coding/app` while the plugin
117
+ * knows `D:\coding\app`; both must count as one directory, and a different
118
+ * directory must never be taken for this one.
119
+ */
120
+ describe("sameProjectDirectory", () => {
121
+ for (const [name, left, right] of WINDOWS_PAIRS) {
122
+ test(`Windows ${name} is the same directory`, () => {
123
+ expect(sameProjectDirectory(left, right)).toBe(true);
124
+ expect(sameProjectDirectory(right, left)).toBe(true);
125
+ });
126
+ }
127
+
128
+ test("Windows forward slashes against backslashes, as OpenCode 2 stores them", () => {
129
+ expect(
130
+ sameProjectDirectory(
131
+ "D:/coding/siac/wt/migracion-asincrona",
132
+ "D:\\coding\\siac\\wt\\migracion-asincrona",
133
+ ),
134
+ ).toBe(true);
135
+ });
136
+
137
+ test("Windows `.` and `..` segments and a trailing forward slash", () => {
138
+ expect(
139
+ sameProjectDirectory("C:\\Users\\dev\\.\\work\\tmp\\..\\app", "C:/Users/dev/work/app/"),
140
+ ).toBe(true);
141
+ });
142
+
143
+ test("POSIX trailing slash and `..` segments", () => {
144
+ expect(sameProjectDirectory("/work/app/", "/work/app")).toBe(true);
145
+ expect(sameProjectDirectory("/work/tmp/../app", "/work/app")).toBe(true);
146
+ });
147
+
148
+ test("different directories are refused", () => {
149
+ expect(
150
+ sameProjectDirectory(
151
+ "D:/coding/siac/wt/notificaciones-pui",
152
+ "D:\\coding\\siac\\wt\\migracion-asincrona",
153
+ ),
154
+ ).toBe(false);
155
+ // A sibling whose name extends this one, a parent, and another drive.
156
+ expect(sameProjectDirectory("C:/Users/dev/app-worktree", "C:\\Users\\dev\\app")).toBe(
157
+ false,
158
+ );
159
+ expect(sameProjectDirectory("C:/Users/dev", "C:\\Users\\dev\\app")).toBe(false);
160
+ expect(sameProjectDirectory("D:/Users/dev/app", "C:\\Users\\dev\\app")).toBe(false);
161
+ // POSIX paths are case-sensitive.
162
+ expect(sameProjectDirectory("/work/App", "/work/app")).toBe(false);
163
+ });
164
+
165
+ test("an empty directory matches nothing, not even the process's working directory", () => {
166
+ expect(sameProjectDirectory("", process.cwd())).toBe(false);
167
+ expect(sameProjectDirectory(process.cwd(), "")).toBe(false);
168
+ expect(sameProjectDirectory("", "")).toBe(false);
169
+ });
170
+ });
@@ -20,6 +20,26 @@ export function projectDirectoryKey(directory: string): string {
20
20
  return path.resolve(directory);
21
21
  }
22
22
 
23
+ /**
24
+ * Whether two spellings name the same directory, judged by spelling alone.
25
+ *
26
+ * Hosts and the plugin do not agree on how to write a directory: OpenCode 2 on
27
+ * Windows stores a session's directory as `D:/work/app` while the plugin's own
28
+ * location is `D:\work\app`, and a drive letter can arrive in either case.
29
+ * Both sides go through `projectDirectoryKey`, so separators, Windows case,
30
+ * `\\?\` prefixes, trailing separators and `.`/`..` segments cannot make one
31
+ * directory look like two. POSIX paths keep their case. Symlinks are not
32
+ * resolved here; callers that must follow them use `canonicalProjectDirectory`.
33
+ *
34
+ * An empty spelling never matches anything: `path.resolve("")` is the process's
35
+ * working directory, and a session with no recorded directory must not be
36
+ * taken for one in the directory the process happens to run in.
37
+ */
38
+ export function sameProjectDirectory(a: string, b: string): boolean {
39
+ if (a.length === 0 || b.length === 0) return false;
40
+ return projectDirectoryKey(a) === projectDirectoryKey(b);
41
+ }
42
+
23
43
  let realpathForCanonicalDirectory: (directory: string) => string = (directory) =>
24
44
  realpathSync.native(directory);
25
45
 
@@ -1,12 +1,64 @@
1
1
  import { describe, expect, it } from "bun:test";
2
+ import { spawnSync } from "node:child_process";
2
3
  import {
3
4
  Database,
5
+ detectSqliteRuntime,
4
6
  getSqliteMemoryStats,
5
7
  loadSqliteModule,
6
8
  SqliteRuntimeUnavailableError,
7
9
  } from "./sqlite";
8
10
 
9
11
  describe("SQLite runtime selector", () => {
12
+ it("suppresses only the node:sqlite experimental warning in a real Node process", () => {
13
+ // Node 24.21 no longer emits this warning, so inject it during loading
14
+ // to keep the suppression assertion load-bearing on newer runtimes.
15
+ const selectorUrl = new URL("./sqlite.ts", import.meta.url).href;
16
+ const script = `
17
+ import { loadSqliteModule } from ${JSON.stringify(selectorUrl)};
18
+ const originalEmitWarning = process.emitWarning;
19
+ let listenerSawX = false;
20
+ process.on("warning", (warning) => {
21
+ if (warning.message === "x") listenerSawX = true;
22
+ });
23
+ await loadSqliteModule("Node.js", async (specifier) => {
24
+ if (specifier !== "node:sqlite") throw new Error("unexpected backend: " + specifier);
25
+ process.emitWarning("SQLite is an experimental feature and might change at any time", "ExperimentalWarning");
26
+ process.emitWarning("x", "ExperimentalWarning");
27
+ return {};
28
+ });
29
+ if (process.emitWarning !== originalEmitWarning) throw new Error("emitWarning was not restored");
30
+ process.emitWarning("after", "ExperimentalWarning");
31
+ await new Promise((resolve) => setImmediate(resolve));
32
+ if (!listenerSawX) throw new Error("the original warning listener was not called");
33
+ `;
34
+ const child = spawnSync("node", ["--trace-warnings", "--input-type=module", "-e", script], {
35
+ encoding: "utf8",
36
+ windowsHide: true,
37
+ });
38
+
39
+ expect(child.error).toBeUndefined();
40
+ expect(child.status).toBe(0);
41
+ expect(child.stderr).not.toContain("SQLite is an experimental feature");
42
+ expect(child.stderr).toContain("ExperimentalWarning: x");
43
+ expect(child.stderr).toContain("ExperimentalWarning: after");
44
+ expect(child.stderr).toMatch(/ExperimentalWarning: x[\s\S]*?\n\s+at /);
45
+ });
46
+
47
+ it("leaves Bun and OpenCode warning emission untouched", async () => {
48
+ expect(detectSqliteRuntime()).toBe("Bun");
49
+ const originalEmitWarning = process.emitWarning;
50
+ let requestedSpecifier = "";
51
+
52
+ await loadSqliteModule("Bun", async (specifier) => {
53
+ requestedSpecifier = specifier;
54
+ expect(process.emitWarning).toBe(originalEmitWarning);
55
+ return {};
56
+ });
57
+
58
+ expect(requestedSpecifier).toBe("bun:sqlite");
59
+ expect(process.emitWarning).toBe(originalEmitWarning);
60
+ });
61
+
10
62
  it("reports live connection PRAGMAs and removes closed handles", () => {
11
63
  const before = getSqliteMemoryStats().connectionCount;
12
64
  const db = new Database(":memory:");
@@ -41,6 +41,7 @@
41
41
 
42
42
  import { AsyncLocalStorage } from "node:async_hooks";
43
43
  import { statSync } from "node:fs";
44
+
44
45
  // Type import only — runtime is loaded dynamically below. @types/better-sqlite3
45
46
  // has the richest definitions and is a structural superset of the API surface
46
47
  // we use, so calls typed against BetterSqlite3 work under bun:sqlite and
@@ -169,13 +170,51 @@ export class SqliteRuntimeUnavailableError extends Error {
169
170
  }
170
171
  }
171
172
 
173
+ // Node requests this warning during node:sqlite's module evaluation, although
174
+ // warning delivery happens later. Intercept only the request while importing;
175
+ // replacing warning listeners would not suppress the default stderr output.
176
+ function isNodeSqliteExperimentalWarning(args: unknown[]): boolean {
177
+ const [warning, options] = args;
178
+ const message = warning instanceof Error ? warning.message : warning;
179
+ const type =
180
+ typeof options === "string"
181
+ ? options
182
+ : options && typeof options === "object"
183
+ ? (options as { type?: unknown }).type
184
+ : undefined;
185
+ return (
186
+ type === "ExperimentalWarning" &&
187
+ typeof message === "string" &&
188
+ message.startsWith("SQLite is an experimental feature")
189
+ );
190
+ }
191
+
192
+ async function importSqliteModuleForRuntime(
193
+ runtime: SqliteRuntime,
194
+ specifier: string,
195
+ importer: (specifier: string) => Promise<SqliteModule>,
196
+ ): Promise<SqliteModule> {
197
+ if (runtime !== "Node.js") return importer(specifier);
198
+
199
+ const originalEmitWarning = process.emitWarning;
200
+ process.emitWarning = function (this: unknown, ...args: unknown[]): void {
201
+ if (isNodeSqliteExperimentalWarning(args)) return;
202
+ Reflect.apply(originalEmitWarning, this, args);
203
+ } as typeof process.emitWarning;
204
+ try {
205
+ return await importer(specifier);
206
+ } finally {
207
+ process.emitWarning = originalEmitWarning;
208
+ }
209
+ }
210
+
172
211
  export async function loadSqliteModule(
173
212
  runtime: SqliteRuntime = detectSqliteRuntime(),
174
213
  importer: (specifier: string) => Promise<SqliteModule> = importSqliteModule,
175
214
  ): Promise<SqliteModule> {
176
215
  const specifier = runtime === "Bun" ? bunSpec : nodeSpec;
177
216
  try {
178
- return await importer(specifier);
217
+ return await importSqliteModuleForRuntime(runtime, specifier, importer);
179
218
  } catch (error) {
180
219
  if (isModuleNotFoundError(error, specifier)) {
181
220
  throw new SqliteRuntimeUnavailableError(runtime, specifier, error);
@@ -234,6 +273,110 @@ export interface SqliteMemoryStats {
234
273
  const trackedSqliteConnections = new Map<number, TrackedSqliteConnection>();
235
274
  let nextSqliteConnectionSequence = 1;
236
275
 
276
+ /**
277
+ * Columns of Magic Context's `tags` table (context.db) that never decide which
278
+ * message a tag belongs to, its number, its type, or its reasoning token estimate.
279
+ */
280
+ const TAG_COLUMNS_OUTSIDE_IDENTITY = new Set([
281
+ "status",
282
+ "drop_mode",
283
+ "byte_size",
284
+ "input_byte_size",
285
+ "reasoning_byte_size",
286
+ "token_count",
287
+ "input_token_count",
288
+ "caveman_depth",
289
+ "entry_fingerprint",
290
+ "tool_name",
291
+ "harness",
292
+ ]);
293
+
294
+ let tagIdentityWriteGeneration = 0;
295
+
296
+ /**
297
+ * Whether a write statement may change an existing `tags` row's identity
298
+ * (session, number, type, owner message, reasoning token count) or remove a
299
+ * row. Plain appends without an explicit `id`, and updates that assign only
300
+ * columns in {@link TAG_COLUMNS_OUTSIDE_IDENTITY}, cannot; everything else that
301
+ * names the table is treated as if it could, including DDL and multi-statement
302
+ * scripts. Erring towards true only costs a cache rebuild.
303
+ */
304
+ export function mayChangeTagIdentity(sql: string): boolean {
305
+ if (!/\btags\b/i.test(sql) || /^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql)) return false;
306
+ if (/^\s*WITH\b/i.test(sql) && !/\b(?:INSERT|UPDATE|DELETE|REPLACE)\b/i.test(sql)) return false;
307
+ // One statement only: a script can hide a DELETE behind a harmless first statement.
308
+ if (/;\s*\S/.test(sql) || /\bREPLACE\b/i.test(sql) || /\bON\s+CONFLICT\b/i.test(sql))
309
+ return true;
310
+ const insert = /^\s*INSERT\s+INTO\s+(?:main\.)?["`]?tags["`]?\s*\(([^)]*)\)/i.exec(sql);
311
+ if (insert) {
312
+ const columns = (insert[1] ?? "").split(",").map((column) =>
313
+ column
314
+ .trim()
315
+ .replace(/["`[\]]/g, "")
316
+ .toLowerCase(),
317
+ );
318
+ return columns.includes("id") || columns.includes("rowid");
319
+ }
320
+ const update = /^\s*UPDATE\s+(?:main\.)?["`]?tags["`]?\s+SET\s+([\s\S]*?)\bWHERE\b/i.exec(sql);
321
+ if (update && !/\bSELECT\b/i.test(sql)) {
322
+ // Provably identity-free only when every top-level assignment is a plain
323
+ // `column = ...` naming an allowed column. A tuple assignment such as
324
+ // `(tag_number, reasoning_token_count) = (...)`, or anything this cannot
325
+ // read, counts as an identity write.
326
+ const assignments = topLevelCommaSegments(update[1] ?? "");
327
+ if (!assignments) return true;
328
+ return !assignments.every((assignment) => {
329
+ const column = /^\s*["`]?(\w+)["`]?\s*=(?!=)/.exec(assignment)?.[1];
330
+ return column !== undefined && TAG_COLUMNS_OUTSIDE_IDENTITY.has(column.toLowerCase());
331
+ });
332
+ }
333
+ return true;
334
+ }
335
+
336
+ /**
337
+ * Split SQL text at commas outside parentheses and quotes. Returns null when
338
+ * the parentheses or quotes do not balance.
339
+ */
340
+ function topLevelCommaSegments(text: string): string[] | null {
341
+ const segments: string[] = [];
342
+ let depth = 0;
343
+ let quote: string | null = null;
344
+ let start = 0;
345
+ for (let index = 0; index < text.length; index += 1) {
346
+ const char = text[index];
347
+ if (quote) {
348
+ // A doubled quote inside a quoted run is an escaped quote.
349
+ if (char === quote) {
350
+ if (text[index + 1] === quote) index += 1;
351
+ else quote = null;
352
+ }
353
+ continue;
354
+ }
355
+ if (char === "'" || char === '"' || char === "`") quote = char;
356
+ else if (char === "(") depth += 1;
357
+ else if (char === ")") {
358
+ depth -= 1;
359
+ if (depth < 0) return null;
360
+ } else if (char === "," && depth === 0) {
361
+ segments.push(text.slice(start, index));
362
+ start = index + 1;
363
+ }
364
+ }
365
+ if (quote || depth !== 0) return null;
366
+ segments.push(text.slice(start));
367
+ return segments;
368
+ }
369
+
370
+ /**
371
+ * Counts executions, on any connection this process opened, of statements for
372
+ * which {@link mayChangeTagIdentity} is true. A cache built from `tags` stays
373
+ * valid for this process's own writes while the count is unchanged; commits by
374
+ * other connections show up in `PRAGMA data_version` instead.
375
+ */
376
+ export function getTagIdentityWriteGeneration(): number {
377
+ return tagIdentityWriteGeneration;
378
+ }
379
+
237
380
  /** Route native Bun and Node transaction entry through the same acquisition retry.
238
381
  * Native Bun transaction wrappers execute BEGIN internally, bypassing an exec override,
239
382
  * so both runtimes use this small synchronous wrapper instead. Writable handles
@@ -257,8 +400,15 @@ function installTransactionRouting(db: BetterSqlite3.Database, readonly: boolean
257
400
  }
258
401
  if (WRITER_TRANSACTION_END.test(sql) && openWriterTransactions.has(db))
259
402
  return endWriterTransaction(db, () => nativeExec(sql), sql);
260
- if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql))
261
- return withAutocommitTimeout(db, () => nativeExec(sql));
403
+ if (
404
+ !/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql)
405
+ ) {
406
+ try {
407
+ return withAutocommitTimeout(db, () => nativeExec(sql));
408
+ } finally {
409
+ if (mayChangeTagIdentity(sql)) tagIdentityWriteGeneration += 1;
410
+ }
411
+ }
262
412
  return nativeExec(sql);
263
413
  },
264
414
  });
@@ -271,15 +421,22 @@ function installTransactionRouting(db: BetterSqlite3.Database, readonly: boolean
271
421
  // SELECT/EXPLAIN cannot acquire a writer. Treat CTEs conservatively:
272
422
  // WITH can introduce INSERT/UPDATE/DELETE as well as a read query.
273
423
  if (!readonly && !/^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql)) {
424
+ const changesTagIdentity = mayChangeTagIdentity(sql);
274
425
  for (const method of ["run", "get", "all"] as const) {
275
426
  const execute = statement[method].bind(statement);
276
427
  Object.defineProperty(statement, method, {
277
428
  configurable: true,
278
429
  // biome-ignore lint/suspicious/noExplicitAny: retain SQLite's native binding overloads.
279
- value: (...args: any[]) =>
280
- withAutocommitTimeout(db, () => execute(...args)),
430
+ value: (...args: any[]) => {
431
+ try {
432
+ return withAutocommitTimeout(db, () => execute(...args));
433
+ } finally {
434
+ if (changesTagIdentity) tagIdentityWriteGeneration += 1;
435
+ }
436
+ },
281
437
  });
282
438
  }
439
+ if (changesTagIdentity) wrapRemainingTagWriteForms(statement);
283
440
  }
284
441
  return statement;
285
442
  },
@@ -330,6 +487,43 @@ function installTransactionRouting(db: BetterSqlite3.Database, readonly: boolean
330
487
  });
331
488
  }
332
489
 
490
+ /**
491
+ * Count tag identity writes made through the statement forms the transaction
492
+ * routing does not wrap. `iterate()` runs the statement while its result is
493
+ * consumed, so the generation moves both when iteration starts and when it
494
+ * ends: a summary read in between is then rebuilt on its next read.
495
+ */
496
+ // biome-ignore lint/suspicious/noExplicitAny: statements from both backends share these method names.
497
+ function wrapRemainingTagWriteForms(statement: any): void {
498
+ if (typeof statement.iterate === "function") {
499
+ const iterate = statement.iterate.bind(statement);
500
+ Object.defineProperty(statement, "iterate", {
501
+ configurable: true,
502
+ value: function* (...args: unknown[]) {
503
+ tagIdentityWriteGeneration += 1;
504
+ try {
505
+ yield* iterate(...args);
506
+ } finally {
507
+ tagIdentityWriteGeneration += 1;
508
+ }
509
+ },
510
+ });
511
+ }
512
+ if (typeof statement.values === "function") {
513
+ const values = statement.values.bind(statement);
514
+ Object.defineProperty(statement, "values", {
515
+ configurable: true,
516
+ value: (...args: unknown[]) => {
517
+ try {
518
+ return values(...args);
519
+ } finally {
520
+ tagIdentityWriteGeneration += 1;
521
+ }
522
+ },
523
+ });
524
+ }
525
+ }
526
+
333
527
  function trackSqliteConnection(
334
528
  db: BetterSqlite3.Database,
335
529
  filename: unknown,
@@ -440,6 +634,11 @@ export function getSqliteDatabasePath(db: Database): string | null {
440
634
  : connection.filename;
441
635
  }
442
636
  }
637
+ // Native Bun/better-sqlite3 handles can reach readers without our constructor
638
+ // wrapper. Their filename is still available without a SQLite query.
639
+ const native = db as unknown as { filename?: unknown; name?: unknown };
640
+ const path = native.filename ?? native.name;
641
+ if (typeof path === "string" && path !== ":memory:" && path !== "") return path;
443
642
  return null;
444
643
  }
445
644
 
@@ -569,6 +768,33 @@ interface StatementOwner {
569
768
  const statementOwners = new WeakMap<Database, StatementOwner>();
570
769
  const FIRST_PRUNE_AT = 256;
571
770
 
771
+ /**
772
+ * Bun's default close (`sqlite3_close_v2`) only releases the native connection
773
+ * once every statement created by prepare() is finalized. The close override
774
+ * in ownPreparedStatement can finalize only statements that are still
775
+ * reachable. Once the garbage collector
776
+ * has marked a one-shot statement dead, its weak reference is already cleared,
777
+ * but the statement is finalized only when its memory is later swept. Until
778
+ * then the closed connection keeps the database, WAL and shared-memory files
779
+ * open, for an unbounded time.
780
+ *
781
+ * `close(true)` finalizes every outstanding statement, reachable or not, and
782
+ * releases the connection immediately. When the caller did not ask for errors
783
+ * and that close fails, fall back to the caller's own close so a teardown path
784
+ * never starts throwing; the fallback can defer the release as before.
785
+ */
786
+ function closeReleasingStatements(
787
+ close: (...args: unknown[]) => unknown,
788
+ args: unknown[],
789
+ ): unknown {
790
+ if (args[0] === true) return close(true);
791
+ try {
792
+ return close(true);
793
+ } catch {
794
+ return close(...args);
795
+ }
796
+ }
797
+
572
798
  function ownPreparedStatement(db: Database, statement: Statement): Statement {
573
799
  const finalizable = statement as FinalizableStatement;
574
800
  // Node finalizes its statements on close and exposes no finalize() method.
@@ -592,7 +818,7 @@ function ownPreparedStatement(db: Database, statement: Statement): Statement {
592
818
  }
593
819
  }
594
820
  references.clear();
595
- const result = close(...args);
821
+ const result = closeReleasingStatements(close, args);
596
822
  if (failure) throw failure;
597
823
  return result;
598
824
  },
@@ -650,22 +876,31 @@ export function withSqliteBackgroundWriter<T>(operation: () => T): T {
650
876
  export function withSqliteTransformPass<T>(operation: () => T): T {
651
877
  const existing = transformPassScope.getStore();
652
878
  if (existing?.active) return operation();
653
- const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
654
- return transformPassScope.run(lease, () => {
655
- try {
656
- const result = operation();
657
- if (result && typeof (result as { then?: unknown }).then === "function") {
658
- return Promise.resolve(result).finally(() => {
659
- lease.active = false;
660
- }) as T;
879
+ const reservation = reserveForegroundSqliteTurn();
880
+ const run = (release: () => void): T => {
881
+ const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
882
+ return transformPassScope.run(lease, () => {
883
+ try {
884
+ const result = operation();
885
+ if (result && typeof (result as { then?: unknown }).then === "function") {
886
+ return Promise.resolve(result).finally(() => {
887
+ lease.active = false;
888
+ release();
889
+ }) as T;
890
+ }
891
+ lease.active = false;
892
+ release();
893
+ return result;
894
+ } catch (error) {
895
+ lease.active = false;
896
+ release();
897
+ throw error;
661
898
  }
662
- lease.active = false;
663
- return result;
664
- } catch (error) {
665
- lease.active = false;
666
- throw error;
667
- }
668
- });
899
+ });
900
+ };
901
+ // Foreground callers racing maintenance are awaited transforms. Idle
902
+ // synchronous callers keep the existing immediate return/throw behavior.
903
+ return typeof reservation === "function" ? run(reservation) : (reservation.then(run) as T);
669
904
  }
670
905
 
671
906
  /** Start detached maintenance work without borrowing a foreground pass's budget. */
@@ -836,16 +1071,20 @@ function acquireShort(db: Database, acquire: () => unknown, site: string): void
836
1071
  * `acquireShort`, then report how long the writer waited for the lock
837
1072
  * (`acquire_ms`) and how long it then held it (`hold_ms`) when either crosses
838
1073
  * the threshold. A long hold blocks every other process's writer, so it is
839
- * worth seeing even when the lock itself came quickly.
1074
+ * worth seeing even when the lock itself came quickly. The native end statement's
1075
+ * `end_ms` is reported separately, so a slow FTS merge or checkpoint at COMMIT
1076
+ * is not mistaken for acquisition time.
840
1077
  */
841
1078
  function endWriterTransaction(db: Database, end: () => unknown, sql: string): unknown {
842
1079
  let ended = false;
1080
+ const endStarted = performance.now();
843
1081
  try {
844
1082
  const result = end();
845
1083
  ended = true;
846
1084
  return result;
847
1085
  } finally {
848
1086
  const open = openWriterTransactions.get(db);
1087
+ const endMs = performance.now() - endStarted;
849
1088
  // A COMMIT that failed with the transaction still open has not ended it.
850
1089
  if (open && !isInTransaction(db)) {
851
1090
  openWriterTransactions.delete(db);
@@ -857,7 +1096,7 @@ function endWriterTransaction(db: Database, end: () => unknown, sql: string): un
857
1096
  ? "committed"
858
1097
  : "failed";
859
1098
  reportSqliteDiagnostic(
860
- `sqlite writer site=${open.site} lane=${open.lane} acquire_ms=${Math.round(open.acquireMs)} hold_ms=${Math.round(holdMs)} attempts=${open.attempts} outcome=${outcome}`,
1099
+ `sqlite writer site=${open.site} lane=${open.lane} acquire_ms=${Math.round(open.acquireMs)} hold_ms=${Math.round(holdMs)} end_ms=${Math.round(endMs)} attempts=${open.attempts} outcome=${outcome}`,
861
1100
  );
862
1101
  }
863
1102
  }
@@ -1056,3 +1295,80 @@ export function withPrivilegedWriter<T>(db: Database, operation: () => T): T {
1056
1295
  throw error;
1057
1296
  }
1058
1297
  }
1298
+
1299
+ // Process-local admission only; SQLite remains the cross-process writer arbiter.
1300
+ const indexCoordinationKey = Symbol.for("magic-context.index-foreground-coordination.v1");
1301
+ const indexCoordinationHeap = globalThis as unknown as Record<
1302
+ symbol,
1303
+ SharedArrayBuffer | undefined
1304
+ >;
1305
+ const indexCoordinationBuffer =
1306
+ indexCoordinationHeap[indexCoordinationKey] ??
1307
+ new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT);
1308
+ indexCoordinationHeap[indexCoordinationKey] = indexCoordinationBuffer;
1309
+ const indexCoordinationState = new Int32Array(indexCoordinationBuffer);
1310
+ // Slots: foreground interest, current maintenance owner, next owner token.
1311
+
1312
+ export function indexCoordinationInput(): {
1313
+ indexCoordination: SharedArrayBuffer;
1314
+ indexOwner: number;
1315
+ } {
1316
+ return {
1317
+ indexCoordination: indexCoordinationBuffer,
1318
+ indexOwner: Atomics.add(indexCoordinationState, 2, 1) + 1,
1319
+ };
1320
+ }
1321
+
1322
+ const yieldIndexTurn = () => new Promise<void>((resolve) => setTimeout(resolve, 1));
1323
+
1324
+ /** Announce interest before waiting, so no maintenance chunk overtakes a foreground turn. */
1325
+ function reserveForegroundSqliteTurn(): (() => void) | Promise<() => void> {
1326
+ Atomics.add(indexCoordinationState, 0, 1);
1327
+ const release = () => {
1328
+ Atomics.sub(indexCoordinationState, 0, 1);
1329
+ Atomics.notify(indexCoordinationState, 0);
1330
+ };
1331
+ if (Atomics.load(indexCoordinationState, 1) === 0) return release;
1332
+ return (async () => {
1333
+ while (Atomics.load(indexCoordinationState, 1) !== 0) await yieldIndexTurn();
1334
+ return release;
1335
+ })();
1336
+ }
1337
+
1338
+ export function releaseIndexOwner(input: {
1339
+ indexCoordination?: SharedArrayBuffer;
1340
+ indexOwner?: number;
1341
+ }): void {
1342
+ if (!input.indexCoordination || !input.indexOwner) return;
1343
+ const shared = new Int32Array(input.indexCoordination);
1344
+ Atomics.compareExchange(shared, 1, input.indexOwner, 0);
1345
+ Atomics.notify(shared, 1);
1346
+ }
1347
+
1348
+ /** One synchronous transaction per permit; release the permit before yielding. */
1349
+ export async function withIndexChunk<T>(
1350
+ input: { indexCoordination?: SharedArrayBuffer; indexOwner?: number },
1351
+ operation: () => T,
1352
+ ): Promise<T> {
1353
+ if (!input.indexCoordination || !input.indexOwner) return operation();
1354
+ const shared = new Int32Array(input.indexCoordination);
1355
+ for (;;) {
1356
+ if (
1357
+ Atomics.load(shared, 0) === 0 &&
1358
+ Atomics.compareExchange(shared, 1, 0, input.indexOwner) === 0
1359
+ ) {
1360
+ if (Atomics.load(shared, 0) === 0) break;
1361
+ releaseIndexOwner(input);
1362
+ }
1363
+ await yieldIndexTurn();
1364
+ }
1365
+ try {
1366
+ return await operation();
1367
+ } finally {
1368
+ releaseIndexOwner(input);
1369
+ }
1370
+ }
1371
+
1372
+ export async function yieldIndexChunk(): Promise<void> {
1373
+ await yieldIndexTurn();
1374
+ }