@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
@@ -37,11 +37,13 @@ describe("detectConflicts", () => {
37
37
  OPENCODE_CONFIG_DIR: process.env.OPENCODE_CONFIG_DIR,
38
38
  XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
39
39
  OPENCODE_DISABLE_AUTOCOMPACT: process.env.OPENCODE_DISABLE_AUTOCOMPACT,
40
+ OPENCODE_DISABLE_PROJECT_CONFIG: process.env.OPENCODE_DISABLE_PROJECT_CONFIG,
40
41
  HOME: process.env.HOME,
41
42
  };
42
43
  process.env.OPENCODE_CONFIG_DIR = userConfigDir;
43
44
  process.env.HOME = homeDir;
44
45
  delete process.env.XDG_CONFIG_HOME;
46
+ delete process.env.OPENCODE_DISABLE_PROJECT_CONFIG;
45
47
  // Disable auto-compaction default during tests so we isolate plugin
46
48
  // detection from compaction detection.
47
49
  process.env.OPENCODE_DISABLE_AUTOCOMPACT = "1";
@@ -734,6 +736,60 @@ describe("detectConflicts", () => {
734
736
  expect(result.conflicts.compactionAuto).toBe(false);
735
737
  });
736
738
 
739
+ it("v1 boot fallback ignores project compaction when project config is disabled", async () => {
740
+ writeCompaction(globalDir, "opencode.json", { auto: false, prune: false });
741
+ writeCompaction(projectDir, "opencode.json", { auto: true, prune: true });
742
+ writeCompaction(join(projectDir, ".opencode"), "opencode.jsonc", {
743
+ auto: true,
744
+ prune: true,
745
+ });
746
+ const resolvedCompaction = await resolveCompactionForBoot(
747
+ {
748
+ config: { get: () => new Promise<never>(() => {}) },
749
+ },
750
+ 20,
751
+ );
752
+ expect(resolvedCompaction).toBeNull();
753
+
754
+ for (const value of ["true", "1", "TRUE"]) {
755
+ process.env.OPENCODE_DISABLE_PROJECT_CONFIG = value;
756
+ const result = detectConflicts(projectDir, {
757
+ compactionEnabled: true,
758
+ hostGeneration: "v1",
759
+ resolvedCompaction,
760
+ });
761
+ expect(result.nativeCompaction).toEqual({ auto: false, prune: false });
762
+ expect(result.hasConflict).toBe(false);
763
+ }
764
+ });
765
+
766
+ it("v1 still reads project compaction when the project-disable flag is false", () => {
767
+ writeCompaction(globalDir, "opencode.json", { auto: false });
768
+ writeCompaction(projectDir, "opencode.jsonc", { auto: true });
769
+ for (const value of ["false", "0", ""]) {
770
+ process.env.OPENCODE_DISABLE_PROJECT_CONFIG = value;
771
+ const result = detectConflicts(projectDir, { compactionEnabled: true });
772
+ expect(result.nativeCompaction.auto).toBe(true);
773
+ expect(result.conflicts.compactionAuto).toBe(true);
774
+ }
775
+ });
776
+
777
+ it("v1 project-disable flag preserves explicit and user compaction layers", () => {
778
+ process.env.OPENCODE_DISABLE_PROJECT_CONFIG = "true";
779
+ writeCompaction(globalDir, "opencode.json", { auto: false });
780
+ const explicit = join(root, "explicit-opencode.json");
781
+ writeFileSync(explicit, JSON.stringify({ compaction: { auto: true } }));
782
+ process.env.OPENCODE_CONFIG = explicit;
783
+ expect(detectConflicts(projectDir).conflicts.compactionAuto).toBe(true);
784
+
785
+ delete process.env.OPENCODE_CONFIG;
786
+ writeCompaction(join(homeDir, ".opencode"), "opencode.json", { auto: true });
787
+ expect(detectConflicts(projectDir).conflicts.compactionAuto).toBe(true);
788
+
789
+ writeCompaction(launcherDir, "opencode.json", { auto: false });
790
+ expect(detectConflicts(projectDir).conflicts.compactionAuto).toBe(false);
791
+ });
792
+
737
793
  it("v2 ranks the project .opencode overlay above global supplementary config", () => {
738
794
  writeCompaction(launcherDir, "opencode.json", { auto: false });
739
795
  writeCompaction(join(projectDir, ".opencode"), "opencode.json", { auto: true });
@@ -414,11 +414,22 @@ function openCodeConfigFileChain(
414
414
  .flatMap((dir) => [join(dir, "opencode.json"), join(dir, "opencode.jsonc")]);
415
415
  const explicitConfig = process.env.OPENCODE_CONFIG?.trim();
416
416
  const explicit = explicitConfig ? [explicitConfig] : [];
417
- const direct = [join(directory, "opencode.json"), join(directory, "opencode.jsonc")];
418
- const projectSupplementary = [
419
- join(directory, ".opencode", "opencode.json"),
420
- join(directory, ".opencode", "opencode.jsonc"),
421
- ];
417
+ // OpenCode 1 can ignore project opencode.json/jsonc and .opencode configs.
418
+ // If config.get() times out during bootstrap, compaction detection falls
419
+ // back here and must not read settings from those host-disabled files.
420
+ const disableProjectConfig = process.env.OPENCODE_DISABLE_PROJECT_CONFIG?.toLowerCase();
421
+ const skipProject =
422
+ hostGeneration === "v1" &&
423
+ (disableProjectConfig === "true" || disableProjectConfig === "1");
424
+ const direct = skipProject
425
+ ? []
426
+ : [join(directory, "opencode.json"), join(directory, "opencode.jsonc")];
427
+ const projectSupplementary = skipProject
428
+ ? []
429
+ : [
430
+ join(directory, ".opencode", "opencode.json"),
431
+ join(directory, ".opencode", "opencode.jsonc"),
432
+ ];
422
433
 
423
434
  const files =
424
435
  hostGeneration === "v2"
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Host stall profiler: an off-by-default diagnostic that names the JavaScript
3
+ * blocking OpenCode's main thread during multi-second host stalls.
4
+ *
5
+ * Why it exists: OpenCode runs every plugin in one Bun runtime. When that
6
+ * runtime stops processing for seconds, native stack samples show only the
7
+ * native side (for example a synchronous SQLite step) and never which plugin's
8
+ * JavaScript made the call. JavaScriptCore's sampling profiler samples the
9
+ * main thread from its own thread, so it records stacks *during* the block,
10
+ * while a main-thread timer can only measure the lag afterwards. This module
11
+ * pairs the two: a 250 ms watchdog measures the lag, and when a gap exceeds
12
+ * the threshold it drains the sampler, keeps the samples inside the stall
13
+ * window, attributes each one to the plugin whose code owns it, and writes one
14
+ * JSON report per stall.
15
+ *
16
+ * Control is a file switch, not a config key: the profiler runs only while
17
+ * `<magic-context storage>/host-profiler/enable` exists. The switch is checked
18
+ * every 5 s, so creating or deleting the file turns the profiler on or off
19
+ * without restarting the host. With the file absent the only work is one
20
+ * `existsSync` per 5 s: `bun:jsc` is never imported and no watchdog runs.
21
+ *
22
+ * Every path fails open: a failure is logged once and never reaches a turn.
23
+ * If `bun:jsc` cannot be loaded (not running under Bun), the profiler
24
+ * disables itself for the rest of the process.
25
+ */
26
+
27
+ import { existsSync, readFileSync } from "node:fs";
28
+ import * as path from "node:path";
29
+ import {
30
+ DEFAULT_SAMPLE_INTERVAL_MS,
31
+ DEFAULT_STALL_THRESHOLD_MS,
32
+ emitStallReport,
33
+ HOST_PROFILER_ENABLE_FILENAME,
34
+ type HostStallProfilerHandle,
35
+ type HostStallProfilerOptions,
36
+ type MemorySnapshot,
37
+ type ProfilerRuntimeDeps,
38
+ parseEnableFile,
39
+ SWITCH_POLL_MS,
40
+ WATCHDOG_INTERVAL_MS,
41
+ } from "./host-stall-profiler-common";
42
+ import type { JscSampleTrace } from "./host-stall-profiler-report";
43
+
44
+ const MEMORY_SNAPSHOT_EVERY_TICKS = 4;
45
+ const CALIBRATION_CHUNK_MS = 3;
46
+ const CALIBRATION_MAX_CHUNKS = 30;
47
+
48
+ /** The subset of `bun:jsc` the profiler uses. */
49
+ export interface JscProfilerApi {
50
+ startSamplingProfiler(directory?: string): void;
51
+ samplingProfilerStackTraces(): unknown;
52
+ /**
53
+ * Runs a callback under the sampler at the given interval (microseconds)
54
+ * and leaves the sampler PAUSED afterwards. `bun:jsc` exposes no stop or
55
+ * interval setter, so this is how the profiler sets its sampling interval
56
+ * and how it stops sampling when the switch is turned off.
57
+ */
58
+ profile(callback: () => unknown, sampleIntervalMicros?: number): unknown;
59
+ heapSize?(): number;
60
+ memoryUsage?(): unknown;
61
+ percentAvailableMemoryInUse?(): number | null;
62
+ }
63
+
64
+ export interface HostStallProfilerDeps extends ProfilerRuntimeDeps {
65
+ loadJsc(): Promise<unknown>;
66
+ }
67
+
68
+ function asJscApi(module: unknown): JscProfilerApi | null {
69
+ const candidate = (module as { default?: unknown } | null) ?? null;
70
+ for (const value of [candidate, candidate?.default]) {
71
+ const api = value as Partial<JscProfilerApi> | null | undefined;
72
+ if (
73
+ api &&
74
+ typeof api.startSamplingProfiler === "function" &&
75
+ typeof api.samplingProfilerStackTraces === "function" &&
76
+ typeof api.profile === "function"
77
+ ) {
78
+ return api as JscProfilerApi;
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+
84
+ export function createHostStallProfiler(deps: HostStallProfilerDeps): HostStallProfilerHandle {
85
+ let jsc: JscProfilerApi | null = null;
86
+ let unavailable = false;
87
+ let pollTimer: unknown = null;
88
+ let watchdog: unknown = null;
89
+ let options: HostStallProfilerOptions = {
90
+ thresholdMs: DEFAULT_STALL_THRESHOLD_MS,
91
+ sampleIntervalMs: DEFAULT_SAMPLE_INTERVAL_MS,
92
+ };
93
+ let lastTickAt = 0;
94
+ let tickCount = 0;
95
+ let memoryBefore: MemorySnapshot | null = null;
96
+ /** JSC sample timestamp (ms) minus `deps.now()` for the same instant; null when unknown. */
97
+ let clockOffsetMs: number | null = null;
98
+ let checking: Promise<void> | null = null;
99
+ const loggedOnce = new Set<string>();
100
+
101
+ const safeLog = (message: string): void => {
102
+ try {
103
+ deps.log(message);
104
+ } catch {
105
+ // Logging must never take the profiler, or a turn, down with it.
106
+ }
107
+ };
108
+ const logOnce = (key: string, message: string, error?: unknown): void => {
109
+ if (loggedOnce.has(key)) return;
110
+ loggedOnce.add(key);
111
+ const detail =
112
+ error === undefined
113
+ ? ""
114
+ : `: ${error instanceof Error ? error.message : String(error)}`;
115
+ safeLog(`[magic-context] host stall profiler: ${message}${detail}`);
116
+ };
117
+
118
+ const drain = (api: JscProfilerApi): JscSampleTrace[] => {
119
+ const raw = api.samplingProfilerStackTraces() as { traces?: unknown } | null;
120
+ return Array.isArray(raw?.traces) ? (raw.traces as JscSampleTrace[]) : [];
121
+ };
122
+
123
+ /** Pause the sampler (see `JscProfilerApi.profile`), discarding what it held. */
124
+ const pauseSampler = (api: JscProfilerApi, intervalMs: number): void => {
125
+ // Drain first so `profile` has little to process. Before the sampler
126
+ // has ever run, `samplingProfilerStackTraces` throws ("never
127
+ // started"); `profile` itself creates the sampler, so that is benign.
128
+ try {
129
+ drain(api);
130
+ } catch {
131
+ // Nothing buffered yet.
132
+ }
133
+ api.profile(() => undefined, Math.round(intervalMs * 1000));
134
+ drain(api);
135
+ };
136
+
137
+ /**
138
+ * Relate JSC's sample clock to `deps.now()`. Sample timestamps come from
139
+ * JavaScriptCore's monotonic clock, whose origin differs from
140
+ * `performance.now()`. Short busy-waits at a 1 ms interval bracket a
141
+ * handful of samples between two known `now()` readings; the midpoint of
142
+ * the feasible offsets is accurate to a few milliseconds, far below any
143
+ * stall threshold. A restarted sampler thread can take one old interval
144
+ * to wake, so the busy-wait repeats in short chunks until a chunk yields
145
+ * samples, bounded by `CALIBRATION_MAX_CHUNKS`.
146
+ */
147
+ const calibrate = (api: JscProfilerApi): number | null => {
148
+ pauseSampler(api, 1);
149
+ api.startSamplingProfiler();
150
+ let offset: number | null = null;
151
+ for (let chunk = 0; chunk < CALIBRATION_MAX_CHUNKS && offset === null; chunk++) {
152
+ const start = deps.now();
153
+ let end = start;
154
+ for (let i = 0; i < 50_000_000 && end - start < CALIBRATION_CHUNK_MS; i++) {
155
+ end = deps.now();
156
+ }
157
+ const stamps = drain(api)
158
+ .map((trace) => trace.timestamp)
159
+ .filter(
160
+ (value): value is number => typeof value === "number" && Number.isFinite(value),
161
+ )
162
+ .map((seconds) => seconds * 1000);
163
+ if (stamps.length >= 2) {
164
+ offset = (Math.max(...stamps) - end + (Math.min(...stamps) - start)) / 2;
165
+ }
166
+ }
167
+ pauseSampler(api, 1);
168
+ return offset;
169
+ };
170
+
171
+ const snapshot = (api: JscProfilerApi): MemorySnapshot => {
172
+ const read = <T>(fn: () => T): T | null => {
173
+ try {
174
+ return fn();
175
+ } catch {
176
+ return null;
177
+ }
178
+ };
179
+ return {
180
+ at: new Date(deps.wallNow()).toISOString(),
181
+ heap_size_bytes: read(() => api.heapSize?.() ?? null),
182
+ jsc_memory: read(() => api.memoryUsage?.() ?? null),
183
+ rss_bytes: read(() => deps.rss()),
184
+ percent_available_memory_in_use: read(
185
+ () => api.percentAvailableMemoryInUse?.() ?? null,
186
+ ),
187
+ };
188
+ };
189
+
190
+ const recordStall = (
191
+ api: JscProfilerApi,
192
+ startMs: number,
193
+ endMs: number,
194
+ traces: JscSampleTrace[],
195
+ ): void => {
196
+ const after = snapshot(api);
197
+ let windowed = traces;
198
+ let windowFilter = "since-last-collection";
199
+ if (clockOffsetMs !== null) {
200
+ const slackMs = 2 * options.sampleIntervalMs + 20;
201
+ const lo = startMs + clockOffsetMs - slackMs;
202
+ const hi = endMs + clockOffsetMs + slackMs;
203
+ const inWindow = traces.filter((trace) => {
204
+ const at =
205
+ typeof trace.timestamp === "number" ? trace.timestamp * 1000 : Number.NaN;
206
+ return at >= lo && at <= hi;
207
+ });
208
+ if (inWindow.length > 0 || traces.length === 0) {
209
+ windowed = inWindow;
210
+ windowFilter = "timestamp";
211
+ } else {
212
+ // A calibration that no longer matches (for example after the
213
+ // machine slept) must not silently empty the report.
214
+ windowFilter = "since-last-collection (timestamp filter matched no sample)";
215
+ }
216
+ }
217
+ emitStallReport(deps, {
218
+ startMs,
219
+ endMs,
220
+ traces,
221
+ windowed,
222
+ windowFilter,
223
+ options,
224
+ before: memoryBefore,
225
+ after,
226
+ backend: "jsc",
227
+ });
228
+ memoryBefore = after;
229
+ };
230
+
231
+ const tick = (): void => {
232
+ const api = jsc;
233
+ if (!api || watchdog === null) return;
234
+ try {
235
+ const now = deps.now();
236
+ const startMs = lastTickAt;
237
+ lastTickAt = now;
238
+ const traces = drain(api);
239
+ if (now - startMs - WATCHDOG_INTERVAL_MS >= options.thresholdMs) {
240
+ try {
241
+ recordStall(api, startMs, now, traces);
242
+ } catch (error) {
243
+ logOnce("report", "could not write a stall report", error);
244
+ }
245
+ }
246
+ tickCount += 1;
247
+ if (tickCount % MEMORY_SNAPSHOT_EVERY_TICKS === 0) memoryBefore = snapshot(api);
248
+ } catch (error) {
249
+ logOnce("tick", "watchdog tick failed", error);
250
+ }
251
+ };
252
+
253
+ const enable = (api: JscProfilerApi, next: HostStallProfilerOptions): void => {
254
+ const startedAt = deps.now();
255
+ try {
256
+ options = next;
257
+ clockOffsetMs = calibrate(api);
258
+ pauseSampler(api, options.sampleIntervalMs);
259
+ api.startSamplingProfiler();
260
+ lastTickAt = deps.now();
261
+ tickCount = 0;
262
+ memoryBefore = snapshot(api);
263
+ watchdog = deps.setInterval(tick, WATCHDOG_INTERVAL_MS);
264
+ safeLog(
265
+ `[magic-context] host stall profiler enabled: threshold ${options.thresholdMs}ms, sample interval ${options.sampleIntervalMs}ms, clock calibration ${clockOffsetMs === null ? "unavailable" : "ok"}, setup ${Math.round(deps.now() - startedAt)}ms, reports in ${deps.profilerDir()}`,
266
+ );
267
+ } catch (error) {
268
+ watchdog = null;
269
+ try {
270
+ pauseSampler(api, options.sampleIntervalMs);
271
+ } catch {
272
+ // Already failing open; the enable error below is the one worth logging.
273
+ }
274
+ logOnce("enable", "could not start", error);
275
+ }
276
+ };
277
+
278
+ const disable = (reason: string): void => {
279
+ if (watchdog !== null) deps.clearInterval(watchdog);
280
+ watchdog = null;
281
+ memoryBefore = null;
282
+ if (jsc) {
283
+ try {
284
+ pauseSampler(jsc, options.sampleIntervalMs);
285
+ } catch (error) {
286
+ logOnce("disable", "could not pause the sampler", error);
287
+ }
288
+ }
289
+ safeLog(`[magic-context] host stall profiler disabled: ${reason}`);
290
+ };
291
+
292
+ const ensureJsc = async (): Promise<JscProfilerApi | null> => {
293
+ if (jsc) return jsc;
294
+ try {
295
+ const api = asJscApi(await deps.loadJsc());
296
+ if (!api) throw new Error("bun:jsc lacks the sampling profiler API");
297
+ jsc = api;
298
+ return api;
299
+ } catch (error) {
300
+ unavailable = true;
301
+ if (pollTimer !== null) deps.clearInterval(pollTimer);
302
+ pollTimer = null;
303
+ logOnce("jsc", "bun:jsc is unavailable, profiler disabled for this process", error);
304
+ return null;
305
+ }
306
+ };
307
+
308
+ const runCheck = async (): Promise<void> => {
309
+ if (unavailable) return;
310
+ try {
311
+ const enablePath = path.join(deps.profilerDir(), HOST_PROFILER_ENABLE_FILENAME);
312
+ if (!existsSync(enablePath)) {
313
+ if (watchdog !== null) disable("enable file removed");
314
+ return;
315
+ }
316
+ const parsed = parseEnableFile(readFileSync(enablePath, "utf8"));
317
+ if (parsed.error) logOnce(`options:${parsed.error}`, parsed.error);
318
+ if (watchdog !== null) {
319
+ if (jsc && parsed.options.sampleIntervalMs !== options.sampleIntervalMs) {
320
+ pauseSampler(jsc, parsed.options.sampleIntervalMs);
321
+ jsc.startSamplingProfiler();
322
+ }
323
+ options = parsed.options;
324
+ return;
325
+ }
326
+ const api = await ensureJsc();
327
+ if (!api || watchdog !== null || !existsSync(enablePath)) return;
328
+ enable(api, parsed.options);
329
+ } catch (error) {
330
+ logOnce("check", "enable-file check failed", error);
331
+ }
332
+ };
333
+
334
+ const checkNow = (): Promise<void> => {
335
+ if (!checking) {
336
+ checking = runCheck().finally(() => {
337
+ checking = null;
338
+ });
339
+ }
340
+ return checking;
341
+ };
342
+
343
+ return {
344
+ start() {
345
+ if (pollTimer !== null || unavailable) return;
346
+ pollTimer = deps.setInterval(() => void checkNow(), SWITCH_POLL_MS);
347
+ void checkNow();
348
+ },
349
+ checkNow,
350
+ stop() {
351
+ if (pollTimer !== null) deps.clearInterval(pollTimer);
352
+ pollTimer = null;
353
+ if (watchdog !== null) disable("stopped");
354
+ },
355
+ isActive: () => watchdog !== null,
356
+ isUnavailable: () => unavailable,
357
+ };
358
+ }
@@ -0,0 +1,159 @@
1
+ import { chmodSync, existsSync, readdirSync, unlinkSync } from "node:fs";
2
+ import * as path from "node:path";
3
+ import { aggregateTraces, type JscSampleTrace } from "./host-stall-profiler-report";
4
+ import { ensureStorageDirectorySync, writeStorageFileAtomicSync } from "./storage-permissions";
5
+
6
+ export const HOST_PROFILER_DIRNAME = "host-profiler";
7
+ export const HOST_PROFILER_ENABLE_FILENAME = "enable";
8
+ export const SWITCH_POLL_MS = 5_000;
9
+ export const WATCHDOG_INTERVAL_MS = 250;
10
+ export const DEFAULT_STALL_THRESHOLD_MS = 3_000;
11
+ export const DEFAULT_SAMPLE_INTERVAL_MS = 10;
12
+ export const MAX_REPORT_FILES = 50;
13
+ export const MAX_SAMPLES_PER_WINDOW = 20_000;
14
+ export const PROFILE_WINDOW_MS = 10_000;
15
+
16
+ export interface HostStallProfilerOptions {
17
+ thresholdMs: number;
18
+ sampleIntervalMs: number;
19
+ }
20
+ export interface HostStallProfilerHandle {
21
+ start(): void;
22
+ checkNow(): Promise<void>;
23
+ stop(): void;
24
+ isActive(): boolean;
25
+ isUnavailable(): boolean;
26
+ }
27
+ export interface MemorySnapshot {
28
+ at: string;
29
+ heap_size_bytes: number | null;
30
+ jsc_memory: unknown;
31
+ rss_bytes: number | null;
32
+ percent_available_memory_in_use: number | null;
33
+ }
34
+ export interface ProfilerRuntimeDeps {
35
+ profilerDir(): string;
36
+ now(): number;
37
+ wallNow(): number;
38
+ setInterval(callback: () => void, ms: number): unknown;
39
+ clearInterval(handle: unknown): void;
40
+ rss(): number;
41
+ log(message: string): void;
42
+ pid: number;
43
+ bunVersion: string | null;
44
+ }
45
+
46
+ /** An empty enable file uses defaults; JSON allows bounded diagnostic tuning. */
47
+ export function parseEnableFile(content: string): {
48
+ options: HostStallProfilerOptions;
49
+ error: string | null;
50
+ } {
51
+ const options = {
52
+ thresholdMs: DEFAULT_STALL_THRESHOLD_MS,
53
+ sampleIntervalMs: DEFAULT_SAMPLE_INTERVAL_MS,
54
+ };
55
+ if (!content.trim()) return { options, error: null };
56
+ try {
57
+ const parsed = JSON.parse(content) as Record<string, unknown>;
58
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
59
+ return { options, error: "enable file is not a JSON object" };
60
+ }
61
+ if (typeof parsed.threshold_ms === "number" && Number.isFinite(parsed.threshold_ms)) {
62
+ options.thresholdMs = Math.min(600_000, Math.max(500, parsed.threshold_ms));
63
+ }
64
+ if (
65
+ typeof parsed.sample_interval_ms === "number" &&
66
+ Number.isFinite(parsed.sample_interval_ms)
67
+ ) {
68
+ options.sampleIntervalMs = Math.min(100, Math.max(1, parsed.sample_interval_ms));
69
+ }
70
+ return { options, error: null };
71
+ } catch (error) {
72
+ return {
73
+ options,
74
+ error: `enable file is not valid JSON (${error instanceof Error ? error.message : String(error)})`,
75
+ };
76
+ }
77
+ }
78
+
79
+ export interface StallReportInput {
80
+ startMs: number;
81
+ endMs: number;
82
+ traces: JscSampleTrace[];
83
+ windowed: JscSampleTrace[];
84
+ windowFilter: string;
85
+ options: HostStallProfilerOptions;
86
+ before: MemorySnapshot | null;
87
+ after: MemorySnapshot;
88
+ backend: "jsc" | "v8";
89
+ }
90
+
91
+ /** Write the same report schema and rank code owners by sampled stacks on every runtime. */
92
+ export function emitStallReport(deps: ProfilerRuntimeDeps, input: StallReportInput): void {
93
+ const { traces, windowed, options, startMs, endMs } = input;
94
+ const stride = windowed.length / MAX_SAMPLES_PER_WINDOW;
95
+ const kept =
96
+ windowed.length <= MAX_SAMPLES_PER_WINDOW
97
+ ? windowed
98
+ : Array.from(
99
+ { length: MAX_SAMPLES_PER_WINDOW },
100
+ (_, i) => windowed[Math.floor(i * stride)],
101
+ );
102
+ const aggregate = aggregateTraces(kept);
103
+ const gapMs = endMs - startMs;
104
+ const endWallMs = deps.wallNow();
105
+ const report = {
106
+ kind: "magic-context-host-stall",
107
+ schema_version: 1,
108
+ pid: deps.pid,
109
+ bun_version: deps.bunVersion,
110
+ node_version: input.backend === "v8" ? process.versions.node : undefined,
111
+ backend: input.backend,
112
+ platform: process.platform,
113
+ window: {
114
+ start: new Date(endWallMs - gapMs).toISOString(),
115
+ end: new Date(endWallMs).toISOString(),
116
+ duration_ms: Math.round(gapMs),
117
+ lag_ms: Math.round(gapMs - WATCHDOG_INTERVAL_MS),
118
+ threshold_ms: options.thresholdMs,
119
+ },
120
+ sampling: {
121
+ interval_ms: options.sampleIntervalMs,
122
+ samples_collected: traces.length,
123
+ samples_in_window: windowed.length,
124
+ samples_aggregated: kept.length,
125
+ window_filter: input.windowFilter,
126
+ note:
127
+ aggregate.samples === 0
128
+ ? "No JavaScript samples in the window: the main thread was not executing inside the JavaScript VM (idle wait, process suspension, or native work outside any JS call)."
129
+ : undefined,
130
+ },
131
+ memory: { before: input.before, after: input.after },
132
+ owners: aggregate.owners,
133
+ top_frames: aggregate.top_frames,
134
+ top_js_frames: aggregate.top_js_frames,
135
+ top_stacks: aggregate.top_stacks,
136
+ };
137
+ const dir = deps.profilerDir();
138
+ ensureStorageDirectorySync(dir, true);
139
+ chmodSync(dir, 0o700);
140
+ const stamp = new Date(endWallMs).toISOString().replaceAll(":", "-");
141
+ let name = `stall-${stamp}.json`;
142
+ for (let n = 1; existsSync(path.join(dir, name)); n++) name = `stall-${stamp}-${n}.json`;
143
+ const file = path.join(dir, name);
144
+ writeStorageFileAtomicSync(file, `${JSON.stringify(report, null, 2)}\n`, true);
145
+ const reports = readdirSync(dir)
146
+ .filter((entry) => /^stall-.*\.json$/.test(entry))
147
+ .sort();
148
+ for (const stale of reports.slice(0, Math.max(0, reports.length - MAX_REPORT_FILES))) {
149
+ try {
150
+ unlinkSync(path.join(dir, stale));
151
+ } catch {
152
+ /* Pruning must not suppress a captured report. */
153
+ }
154
+ }
155
+ const top = aggregate.owners[0];
156
+ deps.log(
157
+ `[magic-context] host stall profiler: ${Math.round(gapMs)}ms main-thread stall, ${aggregate.samples} samples, top owner ${top ? `${top.owner} (${top.percent}%)` : "none"} → ${file}`,
158
+ );
159
+ }