@cortexkit/opencode-magic-context 0.46.1 → 0.48.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 (462) 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 +112 -0
  5. package/dist/config/index.d.ts.map +1 -1
  6. package/dist/config/live-run-config.d.ts.map +1 -1
  7. package/dist/config/migrate-config-location.d.ts.map +1 -1
  8. package/dist/config/mural-model-deprecation.d.ts +15 -0
  9. package/dist/config/mural-model-deprecation.d.ts.map +1 -0
  10. package/dist/config/project-security.d.ts +0 -3
  11. package/dist/config/project-security.d.ts.map +1 -1
  12. package/dist/config/schema/magic-context.d.ts +21 -9
  13. package/dist/config/schema/magic-context.d.ts.map +1 -1
  14. package/dist/{conflict-warning-hook-7fj2b9jw.js → conflict-warning-hook-w8cykrnf.js} +12 -9
  15. package/dist/{context-sh2m2vde.js → context-m0cy262m.js} +1785 -913
  16. package/dist/{derive-budgets-7nj7r1rv.js → derive-budgets-sg0xsq7s.js} +4 -3
  17. package/dist/embedding-activity-g1dhes6g.js +10 -0
  18. package/dist/embedding-backfill-9rcm1mfy.js +21 -0
  19. package/dist/embedding-cache-mgyr73ef.js +16 -0
  20. package/dist/embedding-worker.js +11 -9
  21. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  22. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +1 -0
  23. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  24. package/dist/features/magic-context/dreamer/task-config.d.ts +3 -3
  25. package/dist/features/magic-context/dreamer/task-config.d.ts.map +1 -1
  26. package/dist/features/magic-context/dreamer/task-executor.d.ts +1 -1
  27. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  28. package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
  29. package/dist/features/magic-context/dreamer/task-registry.d.ts +1 -0
  30. package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
  31. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  32. package/dist/features/magic-context/message-index-async.d.ts +16 -3
  33. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  34. package/dist/features/magic-context/message-index-bounded.d.ts +17 -0
  35. package/dist/features/magic-context/message-index-bounded.d.ts.map +1 -0
  36. package/dist/features/magic-context/message-index.d.ts +7 -1
  37. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  38. package/dist/features/magic-context/mural/render-trigger.d.ts +7 -4
  39. package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
  40. package/dist/features/magic-context/overflow-detection.d.ts +2 -0
  41. package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
  42. package/dist/features/magic-context/pi-content-decisions.d.ts +7 -1
  43. package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -1
  44. package/dist/features/magic-context/project-embedding-registry.d.ts +7 -0
  45. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  46. package/dist/features/magic-context/search.d.ts +31 -0
  47. package/dist/features/magic-context/search.d.ts.map +1 -1
  48. package/dist/features/magic-context/session-activity.d.ts.map +1 -1
  49. package/dist/features/magic-context/session-project-storage.d.ts +8 -2
  50. package/dist/features/magic-context/session-project-storage.d.ts.map +1 -1
  51. package/dist/features/magic-context/smart-notes/runner.d.ts +1 -0
  52. package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
  53. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  54. package/dist/features/magic-context/storage-historian-runs.d.ts +2 -5
  55. package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
  56. package/dist/features/magic-context/storage-meta-persisted.d.ts +12 -0
  57. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  58. package/dist/features/magic-context/storage-meta-session.d.ts +6 -0
  59. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  60. package/dist/features/magic-context/storage-tag-identity-revision.d.ts +30 -0
  61. package/dist/features/magic-context/storage-tag-identity-revision.d.ts.map +1 -0
  62. package/dist/features/magic-context/storage-tags.d.ts +148 -3
  63. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  64. package/dist/features/magic-context/storage.d.ts +1 -0
  65. package/dist/features/magic-context/storage.d.ts.map +1 -1
  66. package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
  67. package/dist/features/magic-context/transform-decision-log.d.ts +4 -2
  68. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  69. package/dist/harness-e4avh192.js +10 -0
  70. package/dist/hooks/magic-context/active-anthropic-turn.d.ts +10 -0
  71. package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -0
  72. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  73. package/dist/hooks/magic-context/auto-search-blocking.fixture.d.ts +2 -0
  74. package/dist/hooks/magic-context/auto-search-blocking.fixture.d.ts.map +1 -0
  75. package/dist/hooks/magic-context/auto-search-deadline.d.ts +7 -0
  76. package/dist/hooks/magic-context/auto-search-deadline.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/auto-search-host-review.fixture.d.ts +3 -0
  78. package/dist/hooks/magic-context/auto-search-host-review.fixture.d.ts.map +1 -0
  79. package/dist/hooks/magic-context/auto-search-lock-review.fixture.d.ts +2 -0
  80. package/dist/hooks/magic-context/auto-search-lock-review.fixture.d.ts.map +1 -0
  81. package/dist/hooks/magic-context/auto-search-long-lock-review.fixture.d.ts +2 -0
  82. package/dist/hooks/magic-context/auto-search-long-lock-review.fixture.d.ts.map +1 -0
  83. package/dist/hooks/magic-context/auto-search-omp-host-review.fixture.d.ts +4 -0
  84. package/dist/hooks/magic-context/auto-search-omp-host-review.fixture.d.ts.map +1 -0
  85. package/dist/hooks/magic-context/auto-search-omp-loader.fixture.d.ts +3 -0
  86. package/dist/hooks/magic-context/auto-search-omp-loader.fixture.d.ts.map +1 -0
  87. package/dist/hooks/magic-context/auto-search-runner.d.ts +1 -1
  88. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  89. package/dist/hooks/magic-context/auto-search-snapshot.fixture.d.ts +4 -0
  90. package/dist/hooks/magic-context/auto-search-snapshot.fixture.d.ts.map +1 -0
  91. package/dist/hooks/magic-context/auto-search-worker-client.d.ts +98 -0
  92. package/dist/hooks/magic-context/auto-search-worker-client.d.ts.map +1 -0
  93. package/dist/hooks/magic-context/auto-search-worker.d.ts +2 -0
  94. package/dist/hooks/magic-context/auto-search-worker.d.ts.map +1 -0
  95. package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
  96. package/dist/hooks/magic-context/command-handler.d.ts +1 -0
  97. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  98. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  99. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  100. package/dist/hooks/magic-context/compartment-runner-types.d.ts +3 -5
  101. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  102. package/dist/hooks/magic-context/compartment-runner.d.ts +2 -1
  103. package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
  104. package/dist/hooks/magic-context/compartment-trigger.d.ts +3 -1
  105. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  106. package/dist/hooks/magic-context/event-handler.d.ts +1 -1
  107. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  108. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  109. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  110. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +2 -0
  111. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
  112. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  113. package/dist/hooks/magic-context/historian-drain-gate.d.ts +11 -0
  114. package/dist/hooks/magic-context/historian-drain-gate.d.ts.map +1 -0
  115. package/dist/hooks/magic-context/hook.d.ts +3 -2
  116. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  117. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  118. package/dist/hooks/magic-context/latest-assistant-turn.d.ts +25 -0
  119. package/dist/hooks/magic-context/latest-assistant-turn.d.ts.map +1 -0
  120. package/dist/hooks/magic-context/latest-thinking-recovery.d.ts +61 -0
  121. package/dist/hooks/magic-context/latest-thinking-recovery.d.ts.map +1 -0
  122. package/dist/hooks/magic-context/lkg-persist.d.ts +4 -3
  123. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
  124. package/dist/hooks/magic-context/lkg-replay.d.ts +13 -8
  125. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  126. package/dist/hooks/magic-context/lkg-slot.d.ts +139 -2
  127. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  128. package/dist/hooks/magic-context/message-search-accepting-silent-worker.fixture.d.ts +2 -0
  129. package/dist/hooks/magic-context/message-search-accepting-silent-worker.fixture.d.ts.map +1 -0
  130. package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
  131. package/dist/hooks/magic-context/oc-stall-r2-warmup-worker.fixture.d.ts +2 -0
  132. package/dist/hooks/magic-context/oc-stall-r2-warmup-worker.fixture.d.ts.map +1 -0
  133. package/dist/hooks/magic-context/oc-stall-search-review.fixture.d.ts +2 -0
  134. package/dist/hooks/magic-context/oc-stall-search-review.fixture.d.ts.map +1 -0
  135. package/dist/hooks/magic-context/opencode-proven-floor.d.ts +6 -0
  136. package/dist/hooks/magic-context/opencode-proven-floor.d.ts.map +1 -0
  137. package/dist/hooks/magic-context/opencode1-to-model-messages.fixture.d.ts +63 -0
  138. package/dist/hooks/magic-context/opencode1-to-model-messages.fixture.d.ts.map +1 -0
  139. package/dist/hooks/magic-context/pass-outcome.d.ts.map +1 -1
  140. package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
  141. package/dist/hooks/magic-context/provider-visible-parts.d.ts +45 -0
  142. package/dist/hooks/magic-context/provider-visible-parts.d.ts.map +1 -0
  143. package/dist/hooks/magic-context/raw-ordinal-warmup-silent-worker.fixture.d.ts +2 -0
  144. package/dist/hooks/magic-context/raw-ordinal-warmup-silent-worker.fixture.d.ts.map +1 -0
  145. package/dist/hooks/magic-context/raw-ordinal-warmup-worker.d.ts +2 -0
  146. package/dist/hooks/magic-context/raw-ordinal-warmup-worker.d.ts.map +1 -0
  147. package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts +87 -0
  148. package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts.map +1 -0
  149. package/dist/hooks/magic-context/read-session-chunk.d.ts +12 -0
  150. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  151. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  152. package/dist/hooks/magic-context/read-session-formatting.d.ts +5 -0
  153. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  154. package/dist/hooks/magic-context/read-session-raw.d.ts +83 -1
  155. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  156. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  157. package/dist/hooks/magic-context/reasoning-budget-status.d.ts +15 -0
  158. package/dist/hooks/magic-context/reasoning-budget-status.d.ts.map +1 -0
  159. package/dist/hooks/magic-context/reasoning-budget.d.ts +40 -0
  160. package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -0
  161. package/dist/hooks/magic-context/reasoning-removal.d.ts +6 -6
  162. package/dist/hooks/magic-context/reasoning-removal.d.ts.map +1 -1
  163. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  164. package/dist/hooks/magic-context/sentinel.d.ts +45 -1
  165. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  166. package/dist/hooks/magic-context/strip-content.d.ts +21 -5
  167. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  168. package/dist/hooks/magic-context/system-prompt-hash.d.ts +1 -0
  169. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  170. package/dist/hooks/magic-context/tag-messages.d.ts +21 -0
  171. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  172. package/dist/hooks/magic-context/tag-owner-warmup.d.ts +6 -0
  173. package/dist/hooks/magic-context/tag-owner-warmup.d.ts.map +1 -0
  174. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
  175. package/dist/hooks/magic-context/token-count-exact.d.ts +35 -0
  176. package/dist/hooks/magic-context/token-count-exact.d.ts.map +1 -0
  177. package/dist/hooks/magic-context/tool-drop-target.d.ts +5 -0
  178. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  179. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -28
  180. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  181. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +28 -6
  182. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  183. package/dist/hooks/magic-context/transform.d.ts +11 -3
  184. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  185. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  186. package/dist/{index-hpecdzec.js → index-0an7742d.js} +6820 -6587
  187. package/dist/index-0qhce371.js +120 -0
  188. package/dist/{index-q6hef7fh.js → index-0sahzkwr.js} +6 -19
  189. package/dist/{index-k5bdx1bx.js → index-0w56v4fm.js} +3 -5
  190. package/dist/index-14cjxt20.js +819 -0
  191. package/dist/{v2/server-51dv1pr5.js → index-1qb0z7wa.js} +301 -27
  192. package/dist/index-2bvekkfy.js +11 -0
  193. package/dist/{index-sj29b3z2.js → index-2y0s2hep.js} +3 -3
  194. package/dist/index-4dhv7mqz.js +131 -0
  195. package/dist/{index-b35pxds2.js → index-58e3y90n.js} +1 -18
  196. package/dist/index-5zygecde.js +152 -0
  197. package/dist/{index-sjthx78w.js → index-692jzy0p.js} +16879 -22907
  198. package/dist/index-72nhv486.js +2307 -0
  199. package/dist/index-7jzrrxap.js +931 -0
  200. package/dist/index-7mx8v0pw.js +3591 -0
  201. package/dist/index-8ajstryz.js +18 -0
  202. package/dist/{index-sfpzqvdz.js → index-8cd2j99z.js} +2 -3
  203. package/dist/{index-hybnrwq0.js → index-8j7gqj40.js} +131 -128
  204. package/dist/index-bdckkfw6.js +3261 -0
  205. package/dist/{index-8x2v7gg9.js → index-bhvjj0ys.js} +158 -2455
  206. package/dist/index-c43kr12f.js +3030 -0
  207. package/dist/index-cb7emxgj.js +984 -0
  208. package/dist/index-d9vfe7xq.js +16 -0
  209. package/dist/{index-5gq3b2ya.js → index-db7zwa3v.js} +147 -209
  210. package/dist/index-dhwzyd54.js +1142 -0
  211. package/dist/{index-8da2zjhz.js → index-eeezr5zz.js} +9 -7
  212. package/dist/{index-pkbcrdqs.js → index-f0h6xwqm.js} +3412 -4552
  213. package/dist/index-fb3zxn06.js +13 -0
  214. package/dist/index-fbv1dn35.js +171 -0
  215. package/dist/index-fr0wbhdn.js +43 -0
  216. package/dist/{index-mc22ayvf.js → index-ghc9f0n7.js} +11 -9
  217. package/dist/{index-fzbvakh0.js → index-jcbd3znx.js} +3 -3
  218. package/dist/{index-5p8jdswx.js → index-k1xb52kd.js} +20 -13
  219. package/dist/index-k9nnwad5.js +3532 -0
  220. package/dist/{v2/server-fg34g6a5.js → index-kb9nn0a0.js} +309 -52
  221. package/dist/index-knh4y330.js +566 -0
  222. package/dist/{index-q4yp4b52.js → index-mfkzp7qj.js} +28 -28
  223. package/dist/{index-sgthv3f7.js → index-mmyh6d8f.js} +4 -4
  224. package/dist/index-pavzsdc6.js +17 -0
  225. package/dist/{index-0d3g3461.js → index-pdecwzrd.js} +19 -16
  226. package/dist/index-pg50n748.js +1060 -0
  227. package/dist/index-q2tqeqde.js +2106 -0
  228. package/dist/index-q7h7csab.js +3469 -0
  229. package/dist/index-qt1qqasc.js +22 -0
  230. package/dist/{index-majxda46.js → index-ty64eqcn.js} +28 -18
  231. package/dist/{index-7qw742ev.js → index-yq8n9qcb.js} +25 -44
  232. package/dist/{index-sfnj6h8h.js → index-z8e0psj8.js} +20 -20
  233. package/dist/index.d.ts.map +1 -1
  234. package/dist/index.js +1061 -260
  235. package/dist/logger-d3wex3dz.js +20 -0
  236. package/dist/message-index-bounded-7ezbgddp.js +124 -0
  237. package/dist/{v2/message-time-backfill-r6qkem1x.js → message-time-backfill-brbd63hv.js} +8 -6
  238. package/dist/migration-worker.js +25 -16
  239. package/dist/{model-resolution-xhfmyyvy.js → model-resolution-nx97z3x7.js} +1 -1
  240. package/dist/plugin/dream-timer.d.ts +1 -1
  241. package/dist/plugin/dream-timer.d.ts.map +1 -1
  242. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -1
  243. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  244. package/dist/plugin/host-stall-profiler-report.d.ts +2 -0
  245. package/dist/plugin/host-stall-profiler-report.d.ts.map +1 -0
  246. package/dist/plugin/host-stall-profiler.d.ts +3 -0
  247. package/dist/plugin/host-stall-profiler.d.ts.map +1 -0
  248. package/dist/plugin/messages-transform.d.ts +21 -19
  249. package/dist/plugin/messages-transform.d.ts.map +1 -1
  250. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  251. package/dist/plugin/tool-registry.d.ts.map +1 -1
  252. package/dist/project-embedding-registry-7qzptcw7.js +88 -0
  253. package/dist/raw-ordinal-warmup-worker.js +60 -0
  254. package/dist/{read-session-chunk-egz7r68d.js → read-session-chunk-6k83m4fj.js} +15 -9
  255. package/dist/read-session-raw-3g9qy1xa.js +58 -0
  256. package/dist/recomp-orchestrator-d3fp1cy0.js +57 -0
  257. package/dist/{v2/safe-notification-target-eb1n1vfb.js → safe-notification-target-5ffqq32m.js} +4 -2
  258. package/dist/search-epax1ecv.js +45 -0
  259. package/dist/{v2/send-session-notification-qs0j78v6.js → send-session-notification-erecz4y4.js} +10 -6
  260. package/dist/shared/auto-search-lifecycle.d.ts +7 -0
  261. package/dist/shared/auto-search-lifecycle.d.ts.map +1 -0
  262. package/dist/shared/conflict-detector.d.ts.map +1 -1
  263. package/dist/shared/historian-tool-expansions.d.ts +1 -1
  264. package/dist/shared/historian-tool-expansions.d.ts.map +1 -1
  265. package/dist/shared/historian-tool-template.d.ts +2 -2
  266. package/dist/shared/historian-tool-template.d.ts.map +1 -1
  267. package/dist/shared/host-stall-profiler-bun.d.ts +46 -0
  268. package/dist/shared/host-stall-profiler-bun.d.ts.map +1 -0
  269. package/dist/shared/host-stall-profiler-common.d.ts +58 -0
  270. package/dist/shared/host-stall-profiler-common.d.ts.map +1 -0
  271. package/dist/shared/host-stall-profiler-node.d.ts +36 -0
  272. package/dist/shared/host-stall-profiler-node.d.ts.map +1 -0
  273. package/dist/shared/host-stall-profiler-report.d.ts +68 -0
  274. package/dist/shared/host-stall-profiler-report.d.ts.map +1 -0
  275. package/dist/shared/host-stall-profiler.d.ts +5 -0
  276. package/dist/shared/host-stall-profiler.d.ts.map +1 -0
  277. package/dist/shared/model-resolution.d.ts +2 -3
  278. package/dist/shared/model-resolution.d.ts.map +1 -1
  279. package/dist/shared/sqlite.d.ts +46 -1
  280. package/dist/shared/sqlite.d.ts.map +1 -1
  281. package/dist/shared/stable-json.d.ts.map +1 -1
  282. package/dist/shared/status-view.d.ts.map +1 -1
  283. package/dist/shared/user-facing-codes.d.ts +0 -5
  284. package/dist/shared/user-facing-codes.d.ts.map +1 -1
  285. package/dist/shared/window-geometry.d.ts +4 -0
  286. package/dist/shared/window-geometry.d.ts.map +1 -1
  287. package/dist/sqlite-context-pragmas-mv9jqwjh.js +6 -0
  288. package/dist/sqlite-mhjkerzq.js +46 -0
  289. package/dist/storage-memory-07fx57k6.js +71 -0
  290. package/dist/storage-tags-d3b0v2ss.js +131 -0
  291. package/dist/{task-config-v81k8jvk.js → task-config-cgpg0nvh.js} +2 -2
  292. package/dist/tools/ctx-expand/render.d.ts.map +1 -1
  293. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  294. package/dist/tools/ctx-note/constants.d.ts +4 -0
  295. package/dist/tools/ctx-note/constants.d.ts.map +1 -1
  296. package/dist/tools/ctx-note/tools.d.ts +1 -0
  297. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  298. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  299. package/dist/tools/parameter-descriptions.d.ts +2 -2
  300. package/dist/tools/parameter-descriptions.d.ts.map +1 -1
  301. package/dist/{transform-postprocess-phase-bqhz6vdr.js → transform-postprocess-phase-spcy2qre.js} +35 -17
  302. package/dist/transformers-node-wasm.js +18415 -18415
  303. package/dist/v2/auto-search-worker.js +112 -0
  304. package/dist/v2/{context-zecmzk5s.js → context-sf98kstd.js} +8378 -8530
  305. package/dist/v2/{derive-budgets-kdgp4s3r.js → derive-budgets-3vc0y84t.js} +4 -3
  306. package/dist/v2/embedding-activity-g1dhes6g.js +10 -0
  307. package/dist/v2/embedding-backfill-vn8g1nh1.js +21 -0
  308. package/dist/v2/embedding-cache-mgyr73ef.js +16 -0
  309. package/dist/v2/embedding-worker.js +12 -9
  310. package/dist/v2/fold/host-media.d.ts +4 -0
  311. package/dist/v2/fold/host-media.d.ts.map +1 -1
  312. package/dist/v2/fold/memory-cache.d.ts +70 -0
  313. package/dist/v2/fold/memory-cache.d.ts.map +1 -0
  314. package/dist/v2/fold/native-codec.d.ts +10 -0
  315. package/dist/v2/fold/native-codec.d.ts.map +1 -0
  316. package/dist/v2/fold/native-replay.d.ts +58 -0
  317. package/dist/v2/fold/native-replay.d.ts.map +1 -0
  318. package/dist/v2/fold/policy.d.ts +24 -0
  319. package/dist/v2/fold/policy.d.ts.map +1 -0
  320. package/dist/v2/harness-e4avh192.js +10 -0
  321. package/dist/v2/hidden-completion.d.ts.map +1 -1
  322. package/dist/v2/hooks/context.d.ts +2 -2
  323. package/dist/v2/hooks/context.d.ts.map +1 -1
  324. package/dist/v2/hooks/dream-manual.d.ts +1 -1
  325. package/dist/v2/hooks/dream-manual.d.ts.map +1 -1
  326. package/dist/v2/hooks/dream-timer.d.ts.map +1 -1
  327. package/dist/v2/hooks/dream-trigger.d.ts +1 -2
  328. package/dist/v2/hooks/dream-trigger.d.ts.map +1 -1
  329. package/dist/v2/hooks/payload.d.ts.map +1 -1
  330. package/dist/v2/hooks/tools.d.ts.map +1 -1
  331. package/dist/v2/hooks/types.d.ts +6 -0
  332. package/dist/v2/hooks/types.d.ts.map +1 -1
  333. package/dist/v2/logger-mxd0ym09.js +19 -0
  334. package/dist/v2/message-index-bounded-dy3s2k1e.js +123 -0
  335. package/dist/{message-time-backfill-68gj6t3e.js → v2/message-time-backfill-gtx1pwk1.js} +7 -7
  336. package/dist/v2/migration-worker.js +24 -15
  337. package/dist/v2/{model-resolution-xhfmyyvy.js → model-resolution-nx97z3x7.js} +1 -1
  338. package/dist/v2/project-embedding-registry-1tgba5dv.js +86 -0
  339. package/dist/v2/raw-ordinal-warmup-worker.js +60 -0
  340. package/dist/v2/{read-session-chunk-8vjp6581.js → read-session-chunk-rh4khdkv.js} +14 -8
  341. package/dist/v2/read-session-raw-bsbmf0vj.js +57 -0
  342. package/dist/v2/recomp-orchestrator-az61rfcg.js +57 -0
  343. package/dist/{safe-notification-target-43r8jsxw.js → v2/safe-notification-target-73ax1p5v.js} +3 -3
  344. package/dist/v2/search-nzkwrc6n.js +45 -0
  345. package/dist/{send-session-notification-dyw42dtr.js → v2/send-session-notification-6km439kn.js} +9 -7
  346. package/dist/{index-5eqk6z3s.js → v2/server-1r6pmz4f.js} +301 -27
  347. package/dist/v2/server-2bvekkfy.js +11 -0
  348. package/dist/v2/server-2fbhp79z.js +3205 -0
  349. package/dist/v2/{server-ajzeteqw.js → server-2ra6pwf3.js} +1 -18
  350. package/dist/v2/server-4dhv7mqz.js +131 -0
  351. package/dist/v2/{server-xa4xe3rv.js → server-4k748ke6.js} +11 -1053
  352. package/dist/v2/{server-vs5gpnd4.js → server-52awr2mw.js} +81 -145
  353. package/dist/v2/{server-1qbnzc80.js → server-5m3dt5r1.js} +136 -148
  354. package/dist/v2/server-686jg6eb.js +3508 -0
  355. package/dist/v2/server-7jzrrxap.js +931 -0
  356. package/dist/v2/server-7wwc2nga.js +3023 -0
  357. package/dist/v2/server-8ajstryz.js +18 -0
  358. package/dist/v2/{server-sfpzqvdz.js → server-8cd2j99z.js} +2 -3
  359. package/dist/v2/server-8pyt7hev.js +3408 -0
  360. package/dist/v2/server-8tqanswc.js +984 -0
  361. package/dist/v2/{server-14vq4kpm.js → server-aan4q8mx.js} +3 -3
  362. package/dist/v2/server-d9vfe7xq.js +16 -0
  363. package/dist/v2/server-e0vdaae2.js +3591 -0
  364. package/dist/v2/server-ezhze64f.js +1048 -0
  365. package/dist/v2/server-fb3zxn06.js +13 -0
  366. package/dist/v2/server-fr0wbhdn.js +43 -0
  367. package/dist/v2/server-g5r0n3b6.js +1137 -0
  368. package/dist/v2/server-gjcr4w98.js +154 -0
  369. package/dist/v2/server-k228xhjg.js +171 -0
  370. package/dist/v2/{server-jc7y02q0.js → server-k8x2rbxc.js} +5119 -10948
  371. package/dist/{index-fg34g6a5.js → v2/server-kb9nn0a0.js} +309 -52
  372. package/dist/v2/{server-g0fjrkta.js → server-mfkzp7qj.js} +28 -28
  373. package/dist/v2/server-pavzsdc6.js +17 -0
  374. package/dist/v2/server-psqc7e9n.js +2106 -0
  375. package/dist/v2/server-qt1qqasc.js +22 -0
  376. package/dist/v2/server-qye6ykf7.js +120 -0
  377. package/dist/v2/{server-qdjnb2z1.js → server-r5p3b1wy.js} +11 -9
  378. package/dist/v2/server-sggvvzhb.js +759 -0
  379. package/dist/v2/{server-y8maxeft.js → server-v06jcv0w.js} +16 -13
  380. package/dist/v2/server-vqpf3167.js +1048 -0
  381. package/dist/v2/{server-c5gz8wph.js → server-x2g40a71.js} +2048 -2059
  382. package/dist/v2/{server-wvjvfhck.js → server-xdfwt6kh.js} +20 -16
  383. package/dist/v2/{server-mcfawjgc.js → server-xxm59eww.js} +25 -44
  384. package/dist/v2/server-yczc713m.js +564 -0
  385. package/dist/v2/{server-3n4q9xme.js → server-z2037m6z.js} +4 -4
  386. package/dist/v2/{server-e29d7p7c.js → server-zm570tr2.js} +166 -160
  387. package/dist/v2/{server-6r257mf5.js → server-zxhgvsf9.js} +3 -3
  388. package/dist/v2/server.js +16 -14
  389. package/dist/v2/sqlite-context-pragmas-mv9jqwjh.js +6 -0
  390. package/dist/v2/sqlite-mhjkerzq.js +46 -0
  391. package/dist/v2/storage-memory-07fx57k6.js +71 -0
  392. package/dist/v2/storage-tags-w4tv77sn.js +130 -0
  393. package/dist/v2/store-reader.d.ts +16 -0
  394. package/dist/v2/store-reader.d.ts.map +1 -1
  395. package/dist/v2/{task-config-tbndqyny.js → task-config-x0hq3qfd.js} +2 -2
  396. package/dist/v2/transformers-node-wasm.js +18415 -18415
  397. package/dist/v2/tui/index.d.ts.map +1 -1
  398. package/dist/v2/tui/types.d.ts +1 -10
  399. package/dist/v2/tui/types.d.ts.map +1 -1
  400. package/dist/v2/{wrapup-orchestrator-0snhvwcv.js → wrapup-orchestrator-mz7efs7w.js} +51 -27
  401. package/dist/wrapup-orchestrator-amzta1t4.js +44 -0
  402. package/package.json +4 -3
  403. package/src/agents/magic-context-prompt.ts +27 -2
  404. package/src/agents/smart-notes-enabled.test.ts +294 -0
  405. package/src/agents/smart-notes-on.golden.json +70 -0
  406. package/src/config/index.test.ts +67 -1
  407. package/src/config/index.ts +8 -0
  408. package/src/config/latch-permanence-guard.test.ts +4 -0
  409. package/src/config/live-run-config.test.ts +37 -1
  410. package/src/config/live-run-config.ts +28 -0
  411. package/src/config/migrate-config-location.ts +10 -3
  412. package/src/config/mural-model-deprecation.ts +24 -0
  413. package/src/config/project-security.hostile-matrix.test.ts +4 -44
  414. package/src/config/project-security.test.ts +9 -14
  415. package/src/config/project-security.ts +13 -55
  416. package/src/config/schema/magic-context.test.ts +6 -5
  417. package/src/config/schema/magic-context.ts +34 -16
  418. package/src/features/magic-context/dreamer/task-registry.ts +6 -0
  419. package/src/shared/auto-search-lifecycle.ts +27 -0
  420. package/src/shared/conflict-detector.test.ts +56 -0
  421. package/src/shared/conflict-detector.ts +16 -5
  422. package/src/shared/historian-tool-defaults.json +6 -6
  423. package/src/shared/historian-tool-expansions.ts +65 -8
  424. package/src/shared/historian-tool-preview-defaults.json +13 -0
  425. package/src/shared/historian-tool-template.ts +48 -14
  426. package/src/shared/host-stall-profiler-bun.ts +358 -0
  427. package/src/shared/host-stall-profiler-common.ts +159 -0
  428. package/src/shared/host-stall-profiler-node.test.ts +234 -0
  429. package/src/shared/host-stall-profiler-node.ts +274 -0
  430. package/src/shared/host-stall-profiler-report.ts +248 -0
  431. package/src/shared/host-stall-profiler.ts +57 -0
  432. package/src/shared/model-resolution.test.ts +54 -17
  433. package/src/shared/model-resolution.ts +6 -17
  434. package/src/shared/sqlite-640-review-r2.test.ts +136 -0
  435. package/src/shared/sqlite-640-review.test.ts +183 -0
  436. package/src/shared/sqlite-acquisition-routing.test.ts +18 -0
  437. package/src/shared/sqlite.test.ts +52 -0
  438. package/src/shared/sqlite.ts +423 -37
  439. package/src/shared/stable-json-linear.test.ts +109 -0
  440. package/src/shared/stable-json.ts +37 -7
  441. package/src/shared/status-view.test.ts +6 -1
  442. package/src/shared/status-view.ts +15 -1
  443. package/src/shared/tag-transcript.ts +6 -0
  444. package/src/shared/user-facing-codes.ts +2 -6
  445. package/src/shared/window-geometry.ts +10 -0
  446. package/src/shared/write-transaction-attribution.test.ts +2 -2
  447. package/src/v2/tui/host-contract.test.ts +41 -67
  448. package/src/v2/tui/index.ts +6 -214
  449. package/src/v2/tui/types.ts +5 -17
  450. package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts +0 -45
  451. package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts.map +0 -1
  452. package/dist/features/magic-context/checkout-claim.d.ts +0 -155
  453. package/dist/features/magic-context/checkout-claim.d.ts.map +0 -1
  454. package/dist/index-3h0ybwah.js +0 -6540
  455. package/dist/index-r5hmbx3t.js +0 -7773
  456. package/dist/recomp-orchestrator-y15j0rb9.js +0 -39
  457. package/dist/v2/recomp-orchestrator-8ejxvrzz.js +0 -39
  458. package/dist/v2/server-frbm99q5.js +0 -7698
  459. package/dist/v2/server-qp5hx1ad.js +0 -6366
  460. package/dist/wrapup-orchestrator-39amhv9s.js +0 -26
  461. /package/dist/{index-en1k8h8m.js → index-2pp04pmg.js} +0 -0
  462. /package/dist/v2/{index-en1k8h8m.js → index-2pp04pmg.js} +0 -0
@@ -1,3 +1,10 @@
1
+ import {
2
+ log2
3
+ } from "./server-5m3dt5r1.js";
4
+ import {
5
+ setWindowOverlayPath,
6
+ setOutputReserveConfig
7
+ } from "./server-v06jcv0w.js";
1
8
  import {
2
9
  DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
3
10
  PROTECTED_TOKENS_MIN,
@@ -5,1039 +12,355 @@ import {
5
12
  PER_HARNESS_MODEL_KEYS,
6
13
  ConfigProfilesSchema,
7
14
  MagicContextConfigSchema
8
- } from "./server-e29d7p7c.js";
9
- import {
10
- log
11
- } from "./server-1qbnzc80.js";
12
- import {
13
- setWindowOverlayPath,
14
- setOutputReserveConfig
15
- } from "./server-y8maxeft.js";
15
+ } from "./server-zm570tr2.js";
16
16
 
17
- // src/config/agent-disable.ts
18
- var COMPACTION_ENABLED_PATH = `compaction${"."}enabled`;
19
- function isDreamerRunnable(config) {
20
- return !!config.dreamer && config.dreamer.disable !== true;
21
- }
22
- function isCompactionEnabled(config) {
23
- return config.compaction?.enabled !== false;
24
- }
25
- function clonePlainObject(value) {
26
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
27
- return;
28
- }
29
- return { ...value };
30
- }
31
- function migrateLegacyEnabledForAgent(args) {
32
- const agent = clonePlainObject(args.patched[args.agentName]);
33
- if (!agent || !("enabled" in agent))
34
- return;
35
- const enabled = agent.enabled;
36
- const disable = agent.disable;
37
- delete agent.enabled;
38
- if (args.agentName === "historian") {
39
- args.warnings.push('Removed invalid "historian.enabled" in-memory (run doctor to persist).');
40
- args.patched.historian = agent;
41
- return;
42
- }
43
- if (disable !== true && enabled === false) {
44
- agent.disable = true;
45
- args.warnings.push('Migrated "dreamer.enabled=false" → "dreamer.disable=true" in-memory (run doctor to persist). This now also disables manual /ctx-dream; for manual-only remove disable and set schedule="".');
46
- }
47
- args.patched.dreamer = agent;
48
- }
49
- function migrateLegacyAgentEnabledInMemory(rawConfig, warnings) {
50
- const shouldPatch = ["dreamer", "historian"].some((key) => {
51
- const agent = rawConfig[key];
52
- return typeof agent === "object" && agent !== null && !Array.isArray(agent) && "enabled" in agent;
53
- });
54
- if (!shouldPatch)
55
- return rawConfig;
56
- const patched = { ...rawConfig };
57
- migrateLegacyEnabledForAgent({ patched, agentName: "dreamer", warnings });
58
- migrateLegacyEnabledForAgent({ patched, agentName: "historian", warnings });
59
- return patched;
60
- }
17
+ // src/config/index.ts
18
+ import { existsSync as existsSync4 } from "node:fs";
61
19
 
62
- // src/config/project-security.ts
63
- var HIDDEN_AGENT_KEYS = ["historian", "dreamer"];
64
- var HARNESS_KEYS = PER_HARNESS_MODEL_KEYS;
65
- var HISTORIAN_USER_ONLY_FIELDS = [
66
- ...PER_HARNESS_MIGRATION_INVENTORY.historian.migrated_execution,
67
- "runner",
68
- "host_runner"
69
- ];
70
- var PROMPT_SURFACE_USER_ONLY_FIELDS = ["guidance_override_path", "tool_descriptions"];
71
- var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools"];
72
- var EMBEDDING_USER_ONLY_FIELDS = [
73
- "endpoint",
74
- "provider",
75
- "fallback_provider",
76
- "query_instruction",
77
- "document_prefix"
78
- ];
79
- var PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.";
80
- var TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.";
81
- var TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
82
- var PROTECTED_TOKENS_REASON = "security: a repository may only raise protected_tokens above the resolved user-or-derived floor; it cannot lower protection.";
83
- function isPlainObject(value) {
84
- return typeof value === "object" && value !== null && !Array.isArray(value);
85
- }
86
- function resolveProtectedTokensScalar(value) {
87
- if (typeof value === "number" && Number.isInteger(value) && value >= 4000 && value <= 1e6) {
88
- return value;
89
- }
90
- return;
91
- }
92
- var PROTECTED_TOKENS_TIER_OVERRIDES = Symbol.for("@cortexkit/magic-context/protected-tokens-tier-overrides");
93
- function attachProtectedTokensTierOverrides(config, args) {
94
- const user = resolveProtectedTokensScalar(args.trustedUser);
95
- const rawProject = resolveProtectedTokensScalar(args.project);
96
- const project = rawProject !== undefined && (user === undefined || rawProject >= user) ? rawProject : undefined;
97
- if (user === undefined && project === undefined)
98
- return config;
99
- Object.defineProperty(config, PROTECTED_TOKENS_TIER_OVERRIDES, {
100
- value: {
101
- ...user !== undefined ? { user } : {},
102
- ...project !== undefined ? { project } : {}
20
+ // src/shared/builtin-commands.ts
21
+ var COMPACTION_ENABLED_PATH2 = `compaction${".enabled"}`;
22
+ function getMagicContextBuiltinCommands(compactionEnabled = true) {
23
+ const unavailableInCompactionOff = (command) => `Unavailable when ${COMPACTION_ENABLED_PATH2} is false: /${command} manages compacted history.`;
24
+ return {
25
+ "ctx-status": {
26
+ template: "ctx-status",
27
+ description: "Show magic context status, pending queue, cache TTL, and debug info"
103
28
  },
104
- configurable: false,
105
- enumerable: false,
106
- writable: false
107
- });
108
- return config;
109
- }
110
- function getProtectedTokensTierOverrides(config) {
111
- return config[PROTECTED_TOKENS_TIER_OVERRIDES];
112
- }
113
- function stripListedFields(target, fields, path, removed) {
114
- for (const field of fields) {
115
- if (field in target) {
116
- delete target[field];
117
- removed.push(path.length > 0 ? `${path}.${field}` : field);
29
+ "ctx-recomp": {
30
+ template: "ctx-recomp",
31
+ description: compactionEnabled ? "Rebuild compressed history from raw history (full or <start>-<end> range); memories are not changed" : unavailableInCompactionOff("ctx-recomp")
32
+ },
33
+ "ctx-wrapup": {
34
+ template: "ctx-wrapup",
35
+ description: compactionEnabled ? "Compact older live history while keeping the newest messages raw" : unavailableInCompactionOff("ctx-wrapup")
36
+ },
37
+ "ctx-flush": {
38
+ template: "ctx-flush",
39
+ description: compactionEnabled ? "Force-process all pending magic context operations immediately" : unavailableInCompactionOff("ctx-flush")
40
+ },
41
+ "ctx-dream": {
42
+ template: "ctx-dream",
43
+ description: "Run the hidden dreamer maintenance pass for this project now"
44
+ },
45
+ "ctx-embed": {
46
+ template: "ctx-embed",
47
+ description: "Embedding status, or start/pause history compartment embedding (start | pause)"
118
48
  }
119
- }
49
+ };
120
50
  }
121
- function stripEscalationAtExecutableSite(block, path, removed) {
122
- stripListedFields(block, AGENT_ESCALATION_FIELDS, path, removed);
123
- if (isPlainObject(block.model)) {
124
- stripListedFields(block.model, AGENT_ESCALATION_FIELDS, `${path}.model`, removed);
125
- }
126
- if (Array.isArray(block.fallback_models)) {
127
- for (let index = 0;index < block.fallback_models.length; index++) {
128
- const entry = block.fallback_models[index];
129
- if (isPlainObject(entry)) {
130
- stripListedFields(entry, AGENT_ESCALATION_FIELDS, `${path}.fallback_models.${index}`, removed);
51
+
52
+ // src/shared/config-diagnostics.ts
53
+ var CONFIG_WARNING_CLASS = {
54
+ FILE_PARSE: "file-parse",
55
+ FILE_IO: "file-io",
56
+ INVALID_LEAF: "invalid-leaf"
57
+ };
58
+ var claimedFailures = new Set;
59
+
60
+ // src/shared/jsonc-parser.ts
61
+ import { existsSync, readFileSync } from "node:fs";
62
+
63
+ // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
64
+ function createScanner(text, ignoreTrivia = false) {
65
+ const len = text.length;
66
+ let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
67
+ function scanHexDigits(count, exact) {
68
+ let digits = 0;
69
+ let value = 0;
70
+ while (digits < count || !exact) {
71
+ let ch = text.charCodeAt(pos);
72
+ if (ch >= 48 && ch <= 57) {
73
+ value = value * 16 + ch - 48;
74
+ } else if (ch >= 65 && ch <= 70) {
75
+ value = value * 16 + ch - 65 + 10;
76
+ } else if (ch >= 97 && ch <= 102) {
77
+ value = value * 16 + ch - 97 + 10;
78
+ } else {
79
+ break;
131
80
  }
81
+ pos++;
82
+ digits++;
132
83
  }
133
- }
134
- }
135
- function stripNestedMuralModels(node, path, removed) {
136
- for (const [key, value] of Object.entries(node)) {
137
- if (key === "mural" && isPlainObject(value) && "model" in value) {
138
- delete value.model;
139
- removed.push(`${path}.${key}.model`);
140
- } else if (isPlainObject(value)) {
141
- stripNestedMuralModels(value, `${path}.${key}`, removed);
84
+ if (digits < count) {
85
+ value = -1;
142
86
  }
87
+ return value;
143
88
  }
144
- }
145
- function isValidPercentageThreshold(value) {
146
- return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 90;
147
- }
148
- function isValidTokenThreshold(value) {
149
- return typeof value === "number" && Number.isFinite(value) && value >= 5000 && value <= 2000000;
150
- }
151
- function normalizeTrustedPercentageThresholds(value) {
152
- if (typeof value === "number" && Number.isFinite(value)) {
153
- return { defaultValue: value, overrides: new Map };
89
+ function setPosition(newPosition) {
90
+ pos = newPosition;
91
+ value = "";
92
+ tokenOffset = 0;
93
+ token = 16;
94
+ scanError = 0;
154
95
  }
155
- if (isPlainObject(value) && typeof value.default === "number" && Number.isFinite(value.default)) {
156
- const overrides = new Map;
157
- for (const [key, child] of Object.entries(value)) {
158
- if (key === "default")
159
- continue;
160
- if (typeof child === "number" && Number.isFinite(child)) {
161
- overrides.set(key, child);
96
+ function scanNumber() {
97
+ let start = pos;
98
+ if (text.charCodeAt(pos) === 48) {
99
+ pos++;
100
+ } else {
101
+ pos++;
102
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
103
+ pos++;
162
104
  }
163
105
  }
164
- return { defaultValue: value.default, overrides };
165
- }
166
- return { defaultValue: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, overrides: new Map };
167
- }
168
- function normalizeTrustedTokenThresholds(value) {
169
- if (!isPlainObject(value)) {
170
- return { defaultValue: undefined, overrides: new Map };
171
- }
172
- const overrides = new Map;
173
- for (const [key, child] of Object.entries(value)) {
174
- if (key === "default")
175
- continue;
176
- if (typeof child === "number" && Number.isFinite(child)) {
177
- overrides.set(key, child);
106
+ if (pos < text.length && text.charCodeAt(pos) === 46) {
107
+ pos++;
108
+ if (pos < text.length && isDigit(text.charCodeAt(pos))) {
109
+ pos++;
110
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
111
+ pos++;
112
+ }
113
+ } else {
114
+ scanError = 3;
115
+ return text.substring(start, pos);
116
+ }
178
117
  }
179
- }
180
- return {
181
- defaultValue: typeof value.default === "number" && Number.isFinite(value.default) ? value.default : undefined,
182
- overrides
183
- };
184
- }
185
- function clonePercentageThresholds(value) {
186
- return {
187
- defaultValue: value.defaultValue,
188
- overrides: new Map(value.overrides)
189
- };
190
- }
191
- function cloneTokenThresholds(value) {
192
- return {
193
- defaultValue: value.defaultValue,
194
- overrides: new Map(value.overrides)
195
- };
196
- }
197
- function percentageThresholdsEqual(left, right) {
198
- if (left.defaultValue !== right.defaultValue)
199
- return false;
200
- if (left.overrides.size !== right.overrides.size)
201
- return false;
202
- for (const [key, value] of left.overrides) {
203
- if (right.overrides.get(key) !== value)
204
- return false;
205
- }
206
- return true;
207
- }
208
- function setMergedPercentageThreshold(mergedRaw, value) {
209
- if (value.overrides.size === 0) {
210
- mergedRaw.execute_threshold_percentage = value.defaultValue;
211
- return;
212
- }
213
- const serialized = { default: value.defaultValue };
214
- for (const [key, threshold] of value.overrides) {
215
- serialized[key] = threshold;
216
- }
217
- mergedRaw.execute_threshold_percentage = serialized;
218
- }
219
- function setMergedTokenThreshold(mergedRaw, value) {
220
- if (value.defaultValue === undefined && value.overrides.size === 0) {
221
- delete mergedRaw.execute_threshold_tokens;
222
- return;
223
- }
224
- const serialized = {};
225
- if (value.defaultValue !== undefined) {
226
- serialized.default = value.defaultValue;
227
- }
228
- for (const [key, threshold] of value.overrides) {
229
- serialized[key] = threshold;
230
- }
231
- mergedRaw.execute_threshold_tokens = serialized;
232
- }
233
- function makeProjectThresholdWarning(field, reason) {
234
- return `Ignoring ${field} from project config (${reason})`;
235
- }
236
- function stripUnsafeProjectConfigFields(projectRaw) {
237
- const warnings = [];
238
- if ("profiles" in projectRaw) {
239
- delete projectRaw.profiles;
240
- warnings.push("Ignoring profiles from project config (security: profile definitions are user-level only; a repository may select a named user profile with profile).");
241
- }
242
- if ("auto_update" in projectRaw) {
243
- delete projectRaw.auto_update;
244
- warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
245
- }
246
- if ("fail_closed_blocking" in projectRaw) {
247
- delete projectRaw.fail_closed_blocking;
248
- warnings.push("Ignoring fail_closed_blocking from project config (security: only user-level config may disable or force the loud inoperability gate).");
249
- }
250
- if ("debug_rpc" in projectRaw) {
251
- delete projectRaw.debug_rpc;
252
- warnings.push("Ignoring debug_rpc from project config (security: only user-level config may enable process heap diagnostics).");
253
- }
254
- if ("allow_home_project" in projectRaw) {
255
- delete projectRaw.allow_home_project;
256
- warnings.push("Ignoring allow_home_project from project config (security: only user-level config may opt the user's home directory into Magic Context).");
257
- }
258
- const compaction = projectRaw.compaction;
259
- if (isPlainObject(compaction) && "enabled" in compaction) {
260
- delete compaction.enabled;
261
- warnings.push("Ignoring compaction.enabled from project config (security: only user-level config may disable Magic Context's context-window management; a cloned repo cannot change how the user's window is owned).");
262
- }
263
- if ("output_reserve" in projectRaw) {
264
- delete projectRaw.output_reserve;
265
- warnings.push("Ignoring output_reserve from project config (security: output-token reservation only honors user-level config).");
266
- }
267
- const models = projectRaw.models;
268
- if (isPlainObject(models) && "window_overlay_path" in models) {
269
- delete models.window_overlay_path;
270
- warnings.push("Ignoring models.window_overlay_path from project config (security: only user-level config may select model geometry metadata).");
271
- }
272
- if ("language" in projectRaw) {
273
- delete projectRaw.language;
274
- warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
275
- }
276
- if ("sqlite" in projectRaw) {
277
- delete projectRaw.sqlite;
278
- warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
279
- }
280
- const storage = projectRaw.storage;
281
- if (isPlainObject(storage) && "enforce_private_permissions" in storage) {
282
- delete storage.enforce_private_permissions;
283
- warnings.push("Ignoring storage.enforce_private_permissions from project config (security: only user-level config may opt into externally managed shared storage permissions).");
284
- }
285
- const promptSurface = projectRaw.prompt_surface;
286
- if (isPlainObject(promptSurface)) {
287
- const removed = [];
288
- for (const field of PROMPT_SURFACE_USER_ONLY_FIELDS) {
289
- if (field in promptSurface) {
290
- delete promptSurface[field];
291
- removed.push(field);
292
- }
293
- }
294
- if (removed.length > 0) {
295
- warnings.push(`Ignoring prompt_surface.${removed.join("/")} from project config (security: repositories may select prompt presets but only user config may provide guidance or tool-description text).`);
296
- }
297
- }
298
- const pi = projectRaw.pi;
299
- if (isPlainObject(pi) && "subagent_extensions" in pi) {
300
- delete pi.subagent_extensions;
301
- warnings.push("Ignoring pi.subagent_extensions from project config (security: only user-level config may choose extensions loaded by Pi subagent children).");
302
- }
303
- for (const field of ["subc", "shadow_embedding"]) {
304
- if (field in projectRaw) {
305
- delete projectRaw[field];
306
- warnings.push(`Ignoring ${field} from project config (security: daemon routing and developer-only embedding traffic are user-level settings).`);
307
- }
308
- }
309
- const embedding = projectRaw.embedding;
310
- if (isPlainObject(embedding)) {
311
- const removed = [];
312
- for (const field of EMBEDDING_USER_ONLY_FIELDS) {
313
- if (field in embedding) {
314
- delete embedding[field];
315
- removed.push(field);
316
- }
317
- }
318
- if (removed.length > 0) {
319
- warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where or how private text is embedded).");
320
- }
321
- }
322
- for (const agentKey of HIDDEN_AGENT_KEYS) {
323
- const block = projectRaw[agentKey];
324
- if (!isPlainObject(block))
325
- continue;
326
- const removed = [];
327
- stripEscalationAtExecutableSite(block, agentKey, removed);
328
- for (const harness of HARNESS_KEYS) {
329
- const harnessBlock = block[harness];
330
- if (!isPlainObject(harnessBlock))
331
- continue;
332
- stripEscalationAtExecutableSite(harnessBlock, `${agentKey}.${harness}`, removed);
333
- const tasks = harnessBlock.tasks;
334
- if (isPlainObject(tasks)) {
335
- for (const [taskName, taskBlock] of Object.entries(tasks)) {
336
- if (isPlainObject(taskBlock)) {
337
- stripEscalationAtExecutableSite(taskBlock, `${agentKey}.${harness}.tasks.${taskName}`, removed);
338
- }
339
- }
118
+ let end = pos;
119
+ if (pos < text.length && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
120
+ pos++;
121
+ if (pos < text.length && text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45) {
122
+ pos++;
340
123
  }
341
- }
342
- const schedulingTasks = block.tasks;
343
- if (isPlainObject(schedulingTasks)) {
344
- for (const [taskName, taskBlock] of Object.entries(schedulingTasks)) {
345
- if (isPlainObject(taskBlock)) {
346
- stripListedFields(taskBlock, AGENT_ESCALATION_FIELDS, `${agentKey}.tasks.${taskName}`, removed);
124
+ if (pos < text.length && isDigit(text.charCodeAt(pos))) {
125
+ pos++;
126
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
127
+ pos++;
347
128
  }
129
+ end = pos;
130
+ } else {
131
+ scanError = 3;
348
132
  }
349
133
  }
350
- if (removed.length > 0) {
351
- warnings.push(`Ignoring ${removed.join(", ")} from project config ` + "(security: a repository cannot reprogram or re-permission hidden agents).");
352
- }
134
+ return text.substring(start, end);
353
135
  }
354
- const historian = projectRaw.historian;
355
- if (isPlainObject(historian)) {
356
- const removed = [];
357
- for (const field of HISTORIAN_USER_ONLY_FIELDS) {
358
- if (field in historian) {
359
- delete historian[field];
360
- removed.push(field);
136
+ function scanString() {
137
+ let result = "", start = pos;
138
+ while (true) {
139
+ if (pos >= len) {
140
+ result += text.substring(start, pos);
141
+ scanError = 2;
142
+ break;
361
143
  }
362
- }
363
- for (const harness of HARNESS_KEYS) {
364
- const harnessBlock = historian[harness];
365
- if (!isPlainObject(harnessBlock))
144
+ const ch = text.charCodeAt(pos);
145
+ if (ch === 34) {
146
+ result += text.substring(start, pos);
147
+ pos++;
148
+ break;
149
+ }
150
+ if (ch === 92) {
151
+ result += text.substring(start, pos);
152
+ pos++;
153
+ if (pos >= len) {
154
+ scanError = 2;
155
+ break;
156
+ }
157
+ const ch2 = text.charCodeAt(pos++);
158
+ switch (ch2) {
159
+ case 34:
160
+ result += '"';
161
+ break;
162
+ case 92:
163
+ result += "\\";
164
+ break;
165
+ case 47:
166
+ result += "/";
167
+ break;
168
+ case 98:
169
+ result += "\b";
170
+ break;
171
+ case 102:
172
+ result += "\f";
173
+ break;
174
+ case 110:
175
+ result += `
176
+ `;
177
+ break;
178
+ case 114:
179
+ result += "\r";
180
+ break;
181
+ case 116:
182
+ result += "\t";
183
+ break;
184
+ case 117:
185
+ const ch3 = scanHexDigits(4, true);
186
+ if (ch3 >= 0) {
187
+ result += String.fromCharCode(ch3);
188
+ } else {
189
+ scanError = 4;
190
+ }
191
+ break;
192
+ default:
193
+ scanError = 5;
194
+ }
195
+ start = pos;
366
196
  continue;
367
- for (const field of HISTORIAN_USER_ONLY_FIELDS) {
368
- if (field in harnessBlock) {
369
- delete harnessBlock[field];
370
- removed.push(`${harness}.${field}`);
197
+ }
198
+ if (ch >= 0 && ch <= 31) {
199
+ if (isLineBreak(ch)) {
200
+ result += text.substring(start, pos);
201
+ scanError = 2;
202
+ break;
203
+ } else {
204
+ scanError = 6;
371
205
  }
372
206
  }
207
+ pos++;
373
208
  }
374
- if (removed.length > 0) {
375
- warnings.push(`Ignoring ${removed.map((path) => `historian.${path}`).join(", ")} from project config ` + "(security: historian model selection is user-level only; a repository cannot force extra compaction cost).");
376
- }
209
+ return result;
377
210
  }
378
- const dreamer = projectRaw.dreamer;
379
- if (isPlainObject(dreamer) && "runner" in dreamer) {
380
- delete dreamer.runner;
381
- warnings.push("Ignoring dreamer.runner from project config (security: which process and provider account run dreamer completions is a user-level setting).");
382
- }
383
- const mural = projectRaw.mural;
384
- if (isPlainObject(mural) && "model" in mural) {
385
- delete mural.model;
386
- warnings.push("Ignoring mural.model from project config (security: the mural cue-compressor model is a user-level setting; a repository cannot choose where project memory is sent).");
387
- }
388
- const experimental = projectRaw.experimental;
389
- const legacyMural = isPlainObject(experimental) ? experimental.mural : undefined;
390
- if (isPlainObject(legacyMural) && "model" in legacyMural) {
391
- delete legacyMural.model;
392
- warnings.push("Ignoring experimental.mural.model from project config (security: the mural cue-compressor model is a user-level setting; use user-level mural.model).");
393
- }
394
- const nestedMuralRemoved = [];
395
- for (const agentKey of HIDDEN_AGENT_KEYS) {
396
- const block = projectRaw[agentKey];
397
- if (!isPlainObject(block))
398
- continue;
399
- stripNestedMuralModels(block, agentKey, nestedMuralRemoved);
400
- }
401
- if (nestedMuralRemoved.length > 0) {
402
- warnings.push(`Ignoring ${nestedMuralRemoved.join(", ")} from project config (security: the mural cue-compressor model is a user-level setting; a repository cannot choose where project memory is sent).`);
403
- }
404
- return warnings;
405
- }
406
- function constrainProjectThresholdOverrides(args) {
407
- const warnings = [];
408
- const basePercentage = normalizeTrustedPercentageThresholds(args.trustedBaseConfig.execute_threshold_percentage);
409
- const baseTokens = normalizeTrustedTokenThresholds(args.trustedBaseConfig.execute_threshold_tokens);
410
- if ("execute_threshold_percentage" in args.projectRaw) {
411
- const projectValue = args.projectRaw.execute_threshold_percentage;
412
- if (isValidPercentageThreshold(projectValue)) {
413
- const constrained = clonePercentageThresholds(basePercentage);
414
- constrained.defaultValue = Math.max(basePercentage.defaultValue, projectValue);
415
- for (const [modelKey, threshold] of basePercentage.overrides) {
416
- const raisedThreshold = Math.max(threshold, projectValue);
417
- if (raisedThreshold === constrained.defaultValue) {
418
- constrained.overrides.delete(modelKey);
419
- } else {
420
- constrained.overrides.set(modelKey, raisedThreshold);
421
- }
422
- }
423
- setMergedPercentageThreshold(args.mergedRaw, constrained);
424
- if (percentageThresholdsEqual(constrained, basePercentage)) {
425
- warnings.push(makeProjectThresholdWarning("execute_threshold_percentage", PERCENTAGE_THRESHOLD_REASON));
211
+ function scanNext() {
212
+ value = "";
213
+ scanError = 0;
214
+ tokenOffset = pos;
215
+ lineStartOffset = lineNumber;
216
+ prevTokenLineStartOffset = tokenLineStartOffset;
217
+ if (pos >= len) {
218
+ tokenOffset = len;
219
+ return token = 17;
220
+ }
221
+ let code = text.charCodeAt(pos);
222
+ if (isWhiteSpace(code)) {
223
+ do {
224
+ pos++;
225
+ value += String.fromCharCode(code);
226
+ code = text.charCodeAt(pos);
227
+ } while (isWhiteSpace(code));
228
+ return token = 15;
229
+ }
230
+ if (isLineBreak(code)) {
231
+ pos++;
232
+ value += String.fromCharCode(code);
233
+ if (code === 13 && text.charCodeAt(pos) === 10) {
234
+ pos++;
235
+ value += `
236
+ `;
426
237
  }
427
- } else if (isPlainObject(projectValue)) {
428
- const constrained = clonePercentageThresholds(basePercentage);
429
- let touchedValidEntry = false;
430
- if (isValidPercentageThreshold(projectValue.default)) {
431
- touchedValidEntry = true;
432
- if (projectValue.default > basePercentage.defaultValue) {
433
- constrained.defaultValue = projectValue.default;
434
- } else {
435
- warnings.push(makeProjectThresholdWarning("execute_threshold_percentage.default", PERCENTAGE_THRESHOLD_REASON));
238
+ lineNumber++;
239
+ tokenLineStartOffset = pos;
240
+ return token = 14;
241
+ }
242
+ switch (code) {
243
+ case 123:
244
+ pos++;
245
+ return token = 1;
246
+ case 125:
247
+ pos++;
248
+ return token = 2;
249
+ case 91:
250
+ pos++;
251
+ return token = 3;
252
+ case 93:
253
+ pos++;
254
+ return token = 4;
255
+ case 58:
256
+ pos++;
257
+ return token = 6;
258
+ case 44:
259
+ pos++;
260
+ return token = 5;
261
+ case 34:
262
+ pos++;
263
+ value = scanString();
264
+ return token = 10;
265
+ case 47:
266
+ const start = pos - 1;
267
+ if (text.charCodeAt(pos + 1) === 47) {
268
+ pos += 2;
269
+ while (pos < len) {
270
+ if (isLineBreak(text.charCodeAt(pos))) {
271
+ break;
272
+ }
273
+ pos++;
274
+ }
275
+ value = text.substring(start, pos);
276
+ return token = 12;
436
277
  }
437
- }
438
- for (const [modelKey, rawValue] of Object.entries(projectValue)) {
439
- if (modelKey === "default")
440
- continue;
441
- if (!isValidPercentageThreshold(rawValue))
442
- continue;
443
- touchedValidEntry = true;
444
- const baseValue = basePercentage.overrides.get(modelKey) ?? basePercentage.defaultValue;
445
- if (rawValue > baseValue) {
446
- if (rawValue === constrained.defaultValue) {
447
- constrained.overrides.delete(modelKey);
448
- } else {
449
- constrained.overrides.set(modelKey, rawValue);
278
+ if (text.charCodeAt(pos + 1) === 42) {
279
+ pos += 2;
280
+ const safeLength = len - 1;
281
+ let commentClosed = false;
282
+ while (pos < safeLength) {
283
+ const ch = text.charCodeAt(pos);
284
+ if (ch === 42 && text.charCodeAt(pos + 1) === 47) {
285
+ pos += 2;
286
+ commentClosed = true;
287
+ break;
288
+ }
289
+ pos++;
290
+ if (isLineBreak(ch)) {
291
+ if (ch === 13 && text.charCodeAt(pos) === 10) {
292
+ pos++;
293
+ }
294
+ lineNumber++;
295
+ tokenLineStartOffset = pos;
296
+ }
450
297
  }
451
- } else {
452
- warnings.push(makeProjectThresholdWarning(`execute_threshold_percentage.${modelKey}`, PERCENTAGE_THRESHOLD_REASON));
298
+ if (!commentClosed) {
299
+ pos++;
300
+ scanError = 1;
301
+ }
302
+ value = text.substring(start, pos);
303
+ return token = 13;
453
304
  }
454
- }
455
- if (touchedValidEntry) {
456
- setMergedPercentageThreshold(args.mergedRaw, constrained);
457
- }
305
+ value += String.fromCharCode(code);
306
+ pos++;
307
+ return token = 16;
308
+ case 45:
309
+ value += String.fromCharCode(code);
310
+ pos++;
311
+ if (pos === len || !isDigit(text.charCodeAt(pos))) {
312
+ return token = 16;
313
+ }
314
+ case 48:
315
+ case 49:
316
+ case 50:
317
+ case 51:
318
+ case 52:
319
+ case 53:
320
+ case 54:
321
+ case 55:
322
+ case 56:
323
+ case 57:
324
+ value += scanNumber();
325
+ return token = 11;
326
+ default:
327
+ while (pos < len && isUnknownContentCharacter(code)) {
328
+ pos++;
329
+ code = text.charCodeAt(pos);
330
+ }
331
+ if (tokenOffset !== pos) {
332
+ value = text.substring(tokenOffset, pos);
333
+ switch (value) {
334
+ case "true":
335
+ return token = 8;
336
+ case "false":
337
+ return token = 9;
338
+ case "null":
339
+ return token = 7;
340
+ }
341
+ return token = 16;
342
+ }
343
+ value += String.fromCharCode(code);
344
+ pos++;
345
+ return token = 16;
458
346
  }
459
347
  }
460
- if ("execute_threshold_tokens" in args.projectRaw && isPlainObject(args.projectRaw.execute_threshold_tokens)) {
461
- const projectValue = args.projectRaw.execute_threshold_tokens;
462
- const constrained = cloneTokenThresholds(baseTokens);
463
- let touchedValidEntry = false;
464
- if (isValidTokenThreshold(projectValue.default)) {
465
- touchedValidEntry = true;
466
- if (baseTokens.defaultValue === undefined) {
467
- warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_INTRODUCTION_REASON));
468
- } else if (projectValue.default > baseTokens.defaultValue) {
469
- constrained.defaultValue = projectValue.default;
470
- } else {
471
- warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_REASON));
472
- }
473
- }
474
- for (const [modelKey, rawValue] of Object.entries(projectValue)) {
475
- if (modelKey === "default")
476
- continue;
477
- if (!isValidTokenThreshold(rawValue))
478
- continue;
479
- touchedValidEntry = true;
480
- const baseValue = baseTokens.overrides.get(modelKey) ?? baseTokens.defaultValue;
481
- if (baseValue === undefined) {
482
- warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_INTRODUCTION_REASON));
483
- continue;
484
- }
485
- if (rawValue > baseValue) {
486
- if (rawValue === constrained.defaultValue) {
487
- constrained.overrides.delete(modelKey);
488
- } else {
489
- constrained.overrides.set(modelKey, rawValue);
490
- }
491
- } else {
492
- warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_REASON));
493
- }
348
+ function isUnknownContentCharacter(code) {
349
+ if (isWhiteSpace(code) || isLineBreak(code)) {
350
+ return false;
494
351
  }
495
- if (touchedValidEntry) {
496
- setMergedTokenThreshold(args.mergedRaw, constrained);
352
+ switch (code) {
353
+ case 125:
354
+ case 93:
355
+ case 123:
356
+ case 91:
357
+ case 34:
358
+ case 58:
359
+ case 44:
360
+ case 47:
361
+ return false;
497
362
  }
498
- }
499
- if ("protected_tokens" in args.projectRaw) {
500
- const rawProject = args.projectRaw.protected_tokens;
501
- const projectVal = resolveProtectedTokensScalar(rawProject);
502
- const trustedUserVal = resolveProtectedTokensScalar(args.trustedBaseConfig.protected_tokens);
503
- if (projectVal !== undefined) {
504
- if (trustedUserVal !== undefined) {
505
- if (projectVal >= trustedUserVal) {
506
- args.mergedRaw.protected_tokens = projectVal;
507
- } else {
508
- args.mergedRaw.protected_tokens = trustedUserVal;
509
- warnings.push(makeProjectThresholdWarning("protected_tokens", PROTECTED_TOKENS_REASON));
510
- }
511
- } else {
512
- args.mergedRaw.protected_tokens = projectVal;
513
- }
514
- } else {
515
- if (trustedUserVal !== undefined) {
516
- args.mergedRaw.protected_tokens = trustedUserVal;
517
- } else {
518
- delete args.mergedRaw.protected_tokens;
519
- }
520
- warnings.push(makeProjectThresholdWarning("protected_tokens", PROTECTED_TOKENS_REASON));
521
- }
522
- }
523
- return warnings;
524
- }
525
- function normalizeEndpoint(value) {
526
- if (typeof value !== "string")
527
- return;
528
- const trimmed = value.trim().replace(/\/+$/, "");
529
- return trimmed.length > 0 ? trimmed.toLowerCase() : undefined;
530
- }
531
- function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
532
- const projectEmbedding = projectRaw.embedding;
533
- if (!isPlainObject(projectEmbedding))
534
- return [];
535
- const redirectsEndpoint = "endpoint" in projectEmbedding;
536
- if (!redirectsEndpoint)
537
- return [];
538
- const userEmbedding = userRaw?.embedding;
539
- if (isPlainObject(userEmbedding)) {
540
- const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
541
- const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
542
- if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
543
- return [];
544
- }
545
- }
546
- const providesOwnKey = typeof projectEmbedding.api_key === "string" && projectEmbedding.api_key.length > 0;
547
- if (providesOwnKey)
548
- return [];
549
- const mergedEmbedding = mergedRaw.embedding;
550
- if (!isPlainObject(mergedEmbedding))
551
- return [];
552
- if (!("api_key" in mergedEmbedding))
553
- return [];
554
- delete mergedEmbedding.api_key;
555
- return [
556
- "Dropped inherited user embedding api_key because project config redirected " + "embedding.endpoint without supplying its own key (security: prevents key " + "exfiltration to a repository-chosen endpoint)."
557
- ];
558
- }
559
- function projectContributionPath(projectRaw, issuePath) {
560
- let node = projectRaw;
561
- for (let index = 0;index < issuePath.length; index++) {
562
- const segment = String(issuePath[index]);
563
- if (!isPlainObject(node) || !Object.hasOwn(node, segment))
564
- return;
565
- node = node[segment];
566
- if (index === issuePath.length - 1 || !isPlainObject(node)) {
567
- return issuePath.slice(0, index + 1);
568
- }
569
- }
570
- return;
571
- }
572
- function displacedTrustedValue(trustedRaw, contribution) {
573
- let node = trustedRaw;
574
- for (let index = 0;index < contribution.length; index++) {
575
- const segment = String(contribution[index]);
576
- if (!isPlainObject(node) || !Object.hasOwn(node, segment))
577
- return;
578
- node = node[segment];
579
- if (index === contribution.length - 1 || !isPlainObject(node)) {
580
- return { path: contribution.slice(0, index + 1), value: node };
581
- }
582
- }
583
- return;
584
- }
585
- function defineOwnValue(target, key, value) {
586
- Object.defineProperty(target, key, {
587
- value,
588
- enumerable: true,
589
- configurable: true,
590
- writable: true
591
- });
592
- }
593
- function writeOwnPath(root, path, value) {
594
- let node = root;
595
- for (let index = 0;index < path.length - 1; index++) {
596
- const segment = String(path[index]);
597
- const child = node[segment];
598
- if (!isPlainObject(child))
599
- return;
600
- const copy = { ...child };
601
- defineOwnValue(node, segment, copy);
602
- node = copy;
603
- }
604
- defineOwnValue(node, String(path[path.length - 1]), value);
605
- }
606
- var MAX_PROJECT_RESTORE_ROUNDS = 16;
607
- function restoreTrustedValuesOverInvalidProjectValues(args) {
608
- const warnings = [];
609
- const restoredTopLevelKeys = new Set;
610
- const restored = new Set;
611
- for (let round = 0;round < MAX_PROJECT_RESTORE_ROUNDS; round++) {
612
- let changed = false;
613
- for (const issuePath of args.collectIssuePaths(args.mergedRaw)) {
614
- const contribution = projectContributionPath(args.projectRaw, issuePath);
615
- if (contribution === undefined || contribution.length === 0)
616
- continue;
617
- const trusted = displacedTrustedValue(args.trustedRaw, contribution);
618
- if (trusted === undefined)
619
- continue;
620
- const key = trusted.path.map(String).join(".");
621
- if (restored.has(key))
622
- continue;
623
- writeOwnPath(args.mergedRaw, trusted.path, trusted.value);
624
- restored.add(key);
625
- restoredTopLevelKeys.add(String(trusted.path[0]));
626
- changed = true;
627
- warnings.push(`Ignoring invalid ${key} from project config and keeping the user-level value (security: a repository cannot reset user settings by supplying an invalid value).`);
628
- }
629
- if (!changed)
630
- break;
631
- }
632
- return { warnings, restoredTopLevelKeys: [...restoredTopLevelKeys] };
633
- }
634
- var PROJECT_COMMAND_STRING_FIELDS = ["description", "agent", "model"];
635
- function wellFormedCommand(value) {
636
- if (!isPlainObject(value))
637
- return;
638
- if (typeof value.template !== "string" || value.template.trim().length === 0)
639
- return;
640
- const command = { template: value.template };
641
- for (const field of PROJECT_COMMAND_STRING_FIELDS) {
642
- if (value[field] === undefined)
643
- continue;
644
- if (typeof value[field] !== "string")
645
- return;
646
- command[field] = value[field];
647
- }
648
- if (value.subtask !== undefined) {
649
- if (typeof value.subtask !== "boolean")
650
- return;
651
- command.subtask = value.subtask;
652
- }
653
- return command;
654
- }
655
- function constrainProjectCommands(args) {
656
- if (!("command" in args.projectRaw))
657
- return [];
658
- const warnings = [];
659
- const trustedCommands = isPlainObject(args.trustedRaw.command) ? args.trustedRaw.command : undefined;
660
- const merged = { ...trustedCommands ?? {} };
661
- const projectCommands = args.projectRaw.command;
662
- if (!isPlainObject(projectCommands)) {
663
- warnings.push("Ignoring command from project config (it must be an object of named commands).");
664
- } else {
665
- for (const [name, value] of Object.entries(projectCommands)) {
666
- if (trustedCommands && Object.hasOwn(trustedCommands, name)) {
667
- warnings.push(`Ignoring command.${name} from project config (security: a repository cannot replace a command defined in user config).`);
668
- continue;
669
- }
670
- if (args.reservedNames.includes(name)) {
671
- warnings.push(`Ignoring command.${name} from project config (security: a repository cannot replace a built-in Magic Context command).`);
672
- continue;
673
- }
674
- const command = wellFormedCommand(value);
675
- if (!command) {
676
- warnings.push(`Ignoring command.${name} from project config (it needs a non-empty string template; description, agent and model must be strings and subtask a boolean).`);
677
- continue;
678
- }
679
- Object.defineProperty(merged, name, {
680
- value: command,
681
- enumerable: true,
682
- configurable: true,
683
- writable: true
684
- });
685
- }
686
- }
687
- if (Object.keys(merged).length === 0)
688
- delete args.mergedRaw.command;
689
- else
690
- args.mergedRaw.command = merged;
691
- return warnings;
692
- }
693
-
694
- // src/config/index.ts
695
- import { existsSync as existsSync4 } from "node:fs";
696
-
697
- // src/shared/builtin-commands.ts
698
- var COMPACTION_ENABLED_PATH2 = `compaction${".enabled"}`;
699
- function getMagicContextBuiltinCommands(compactionEnabled = true) {
700
- const unavailableInCompactionOff = (command) => `Unavailable when ${COMPACTION_ENABLED_PATH2} is false: /${command} manages compacted history.`;
701
- return {
702
- "ctx-status": {
703
- template: "ctx-status",
704
- description: "Show magic context status, pending queue, cache TTL, and debug info"
705
- },
706
- "ctx-recomp": {
707
- template: "ctx-recomp",
708
- description: compactionEnabled ? "Rebuild compressed history from raw history (full or <start>-<end> range); memories are not changed" : unavailableInCompactionOff("ctx-recomp")
709
- },
710
- "ctx-wrapup": {
711
- template: "ctx-wrapup",
712
- description: compactionEnabled ? "Compact older live history while keeping the newest messages raw" : unavailableInCompactionOff("ctx-wrapup")
713
- },
714
- "ctx-flush": {
715
- template: "ctx-flush",
716
- description: compactionEnabled ? "Force-process all pending magic context operations immediately" : unavailableInCompactionOff("ctx-flush")
717
- },
718
- "ctx-dream": {
719
- template: "ctx-dream",
720
- description: "Run the hidden dreamer maintenance pass for this project now"
721
- },
722
- "ctx-embed": {
723
- template: "ctx-embed",
724
- description: "Embedding status, or start/pause history compartment embedding (start | pause)"
725
- }
726
- };
727
- }
728
-
729
- // src/shared/config-diagnostics.ts
730
- var CONFIG_WARNING_CLASS = {
731
- FILE_PARSE: "file-parse",
732
- FILE_IO: "file-io",
733
- INVALID_LEAF: "invalid-leaf"
734
- };
735
- var claimedFailures = new Set;
736
-
737
- // src/shared/jsonc-parser.ts
738
- import { existsSync, readFileSync } from "node:fs";
739
-
740
- // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
741
- function createScanner(text, ignoreTrivia = false) {
742
- const len = text.length;
743
- let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
744
- function scanHexDigits(count, exact) {
745
- let digits = 0;
746
- let value = 0;
747
- while (digits < count || !exact) {
748
- let ch = text.charCodeAt(pos);
749
- if (ch >= 48 && ch <= 57) {
750
- value = value * 16 + ch - 48;
751
- } else if (ch >= 65 && ch <= 70) {
752
- value = value * 16 + ch - 65 + 10;
753
- } else if (ch >= 97 && ch <= 102) {
754
- value = value * 16 + ch - 97 + 10;
755
- } else {
756
- break;
757
- }
758
- pos++;
759
- digits++;
760
- }
761
- if (digits < count) {
762
- value = -1;
763
- }
764
- return value;
765
- }
766
- function setPosition(newPosition) {
767
- pos = newPosition;
768
- value = "";
769
- tokenOffset = 0;
770
- token = 16;
771
- scanError = 0;
772
- }
773
- function scanNumber() {
774
- let start = pos;
775
- if (text.charCodeAt(pos) === 48) {
776
- pos++;
777
- } else {
778
- pos++;
779
- while (pos < text.length && isDigit(text.charCodeAt(pos))) {
780
- pos++;
781
- }
782
- }
783
- if (pos < text.length && text.charCodeAt(pos) === 46) {
784
- pos++;
785
- if (pos < text.length && isDigit(text.charCodeAt(pos))) {
786
- pos++;
787
- while (pos < text.length && isDigit(text.charCodeAt(pos))) {
788
- pos++;
789
- }
790
- } else {
791
- scanError = 3;
792
- return text.substring(start, pos);
793
- }
794
- }
795
- let end = pos;
796
- if (pos < text.length && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
797
- pos++;
798
- if (pos < text.length && text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45) {
799
- pos++;
800
- }
801
- if (pos < text.length && isDigit(text.charCodeAt(pos))) {
802
- pos++;
803
- while (pos < text.length && isDigit(text.charCodeAt(pos))) {
804
- pos++;
805
- }
806
- end = pos;
807
- } else {
808
- scanError = 3;
809
- }
810
- }
811
- return text.substring(start, end);
812
- }
813
- function scanString() {
814
- let result = "", start = pos;
815
- while (true) {
816
- if (pos >= len) {
817
- result += text.substring(start, pos);
818
- scanError = 2;
819
- break;
820
- }
821
- const ch = text.charCodeAt(pos);
822
- if (ch === 34) {
823
- result += text.substring(start, pos);
824
- pos++;
825
- break;
826
- }
827
- if (ch === 92) {
828
- result += text.substring(start, pos);
829
- pos++;
830
- if (pos >= len) {
831
- scanError = 2;
832
- break;
833
- }
834
- const ch2 = text.charCodeAt(pos++);
835
- switch (ch2) {
836
- case 34:
837
- result += '"';
838
- break;
839
- case 92:
840
- result += "\\";
841
- break;
842
- case 47:
843
- result += "/";
844
- break;
845
- case 98:
846
- result += "\b";
847
- break;
848
- case 102:
849
- result += "\f";
850
- break;
851
- case 110:
852
- result += `
853
- `;
854
- break;
855
- case 114:
856
- result += "\r";
857
- break;
858
- case 116:
859
- result += "\t";
860
- break;
861
- case 117:
862
- const ch3 = scanHexDigits(4, true);
863
- if (ch3 >= 0) {
864
- result += String.fromCharCode(ch3);
865
- } else {
866
- scanError = 4;
867
- }
868
- break;
869
- default:
870
- scanError = 5;
871
- }
872
- start = pos;
873
- continue;
874
- }
875
- if (ch >= 0 && ch <= 31) {
876
- if (isLineBreak(ch)) {
877
- result += text.substring(start, pos);
878
- scanError = 2;
879
- break;
880
- } else {
881
- scanError = 6;
882
- }
883
- }
884
- pos++;
885
- }
886
- return result;
887
- }
888
- function scanNext() {
889
- value = "";
890
- scanError = 0;
891
- tokenOffset = pos;
892
- lineStartOffset = lineNumber;
893
- prevTokenLineStartOffset = tokenLineStartOffset;
894
- if (pos >= len) {
895
- tokenOffset = len;
896
- return token = 17;
897
- }
898
- let code = text.charCodeAt(pos);
899
- if (isWhiteSpace(code)) {
900
- do {
901
- pos++;
902
- value += String.fromCharCode(code);
903
- code = text.charCodeAt(pos);
904
- } while (isWhiteSpace(code));
905
- return token = 15;
906
- }
907
- if (isLineBreak(code)) {
908
- pos++;
909
- value += String.fromCharCode(code);
910
- if (code === 13 && text.charCodeAt(pos) === 10) {
911
- pos++;
912
- value += `
913
- `;
914
- }
915
- lineNumber++;
916
- tokenLineStartOffset = pos;
917
- return token = 14;
918
- }
919
- switch (code) {
920
- case 123:
921
- pos++;
922
- return token = 1;
923
- case 125:
924
- pos++;
925
- return token = 2;
926
- case 91:
927
- pos++;
928
- return token = 3;
929
- case 93:
930
- pos++;
931
- return token = 4;
932
- case 58:
933
- pos++;
934
- return token = 6;
935
- case 44:
936
- pos++;
937
- return token = 5;
938
- case 34:
939
- pos++;
940
- value = scanString();
941
- return token = 10;
942
- case 47:
943
- const start = pos - 1;
944
- if (text.charCodeAt(pos + 1) === 47) {
945
- pos += 2;
946
- while (pos < len) {
947
- if (isLineBreak(text.charCodeAt(pos))) {
948
- break;
949
- }
950
- pos++;
951
- }
952
- value = text.substring(start, pos);
953
- return token = 12;
954
- }
955
- if (text.charCodeAt(pos + 1) === 42) {
956
- pos += 2;
957
- const safeLength = len - 1;
958
- let commentClosed = false;
959
- while (pos < safeLength) {
960
- const ch = text.charCodeAt(pos);
961
- if (ch === 42 && text.charCodeAt(pos + 1) === 47) {
962
- pos += 2;
963
- commentClosed = true;
964
- break;
965
- }
966
- pos++;
967
- if (isLineBreak(ch)) {
968
- if (ch === 13 && text.charCodeAt(pos) === 10) {
969
- pos++;
970
- }
971
- lineNumber++;
972
- tokenLineStartOffset = pos;
973
- }
974
- }
975
- if (!commentClosed) {
976
- pos++;
977
- scanError = 1;
978
- }
979
- value = text.substring(start, pos);
980
- return token = 13;
981
- }
982
- value += String.fromCharCode(code);
983
- pos++;
984
- return token = 16;
985
- case 45:
986
- value += String.fromCharCode(code);
987
- pos++;
988
- if (pos === len || !isDigit(text.charCodeAt(pos))) {
989
- return token = 16;
990
- }
991
- case 48:
992
- case 49:
993
- case 50:
994
- case 51:
995
- case 52:
996
- case 53:
997
- case 54:
998
- case 55:
999
- case 56:
1000
- case 57:
1001
- value += scanNumber();
1002
- return token = 11;
1003
- default:
1004
- while (pos < len && isUnknownContentCharacter(code)) {
1005
- pos++;
1006
- code = text.charCodeAt(pos);
1007
- }
1008
- if (tokenOffset !== pos) {
1009
- value = text.substring(tokenOffset, pos);
1010
- switch (value) {
1011
- case "true":
1012
- return token = 8;
1013
- case "false":
1014
- return token = 9;
1015
- case "null":
1016
- return token = 7;
1017
- }
1018
- return token = 16;
1019
- }
1020
- value += String.fromCharCode(code);
1021
- pos++;
1022
- return token = 16;
1023
- }
1024
- }
1025
- function isUnknownContentCharacter(code) {
1026
- if (isWhiteSpace(code) || isLineBreak(code)) {
1027
- return false;
1028
- }
1029
- switch (code) {
1030
- case 125:
1031
- case 93:
1032
- case 123:
1033
- case 91:
1034
- case 34:
1035
- case 58:
1036
- case 44:
1037
- case 47:
1038
- return false;
1039
- }
1040
- return true;
363
+ return true;
1041
364
  }
1042
365
  function scanNextNonTrivia() {
1043
366
  let result;
@@ -1673,1186 +996,1846 @@ function visit(text, visitor, options = ParseOptions.DEFAULT) {
1673
996
  }
1674
997
  }
1675
998
  }
1676
- function handleError(error, skipUntilAfter = [], skipUntil = []) {
1677
- onError(error);
1678
- if (skipUntilAfter.length + skipUntil.length > 0) {
1679
- let token = _scanner.getToken();
1680
- while (token !== 17) {
1681
- if (skipUntilAfter.indexOf(token) !== -1) {
1682
- scanNext();
1683
- break;
1684
- } else if (skipUntil.indexOf(token) !== -1) {
1685
- break;
999
+ function handleError(error, skipUntilAfter = [], skipUntil = []) {
1000
+ onError(error);
1001
+ if (skipUntilAfter.length + skipUntil.length > 0) {
1002
+ let token = _scanner.getToken();
1003
+ while (token !== 17) {
1004
+ if (skipUntilAfter.indexOf(token) !== -1) {
1005
+ scanNext();
1006
+ break;
1007
+ } else if (skipUntil.indexOf(token) !== -1) {
1008
+ break;
1009
+ }
1010
+ token = scanNext();
1011
+ }
1012
+ }
1013
+ }
1014
+ function parseString(isValue) {
1015
+ const value = _scanner.getTokenValue();
1016
+ if (isValue) {
1017
+ onLiteralValue(value);
1018
+ } else {
1019
+ onObjectProperty(value);
1020
+ _jsonPath.push(value);
1021
+ }
1022
+ scanNext();
1023
+ return true;
1024
+ }
1025
+ function parseLiteral() {
1026
+ switch (_scanner.getToken()) {
1027
+ case 11:
1028
+ const tokenValue = _scanner.getTokenValue();
1029
+ let value = Number(tokenValue);
1030
+ if (isNaN(value)) {
1031
+ handleError(2);
1032
+ value = 0;
1033
+ }
1034
+ onLiteralValue(value);
1035
+ break;
1036
+ case 7:
1037
+ onLiteralValue(null);
1038
+ break;
1039
+ case 8:
1040
+ onLiteralValue(true);
1041
+ break;
1042
+ case 9:
1043
+ onLiteralValue(false);
1044
+ break;
1045
+ default:
1046
+ return false;
1047
+ }
1048
+ scanNext();
1049
+ return true;
1050
+ }
1051
+ function parseProperty() {
1052
+ if (_scanner.getToken() !== 10) {
1053
+ handleError(3, [], [2, 5]);
1054
+ return false;
1055
+ }
1056
+ parseString(false);
1057
+ if (_scanner.getToken() === 6) {
1058
+ onSeparator(":");
1059
+ scanNext();
1060
+ if (!parseValue()) {
1061
+ handleError(4, [], [2, 5]);
1062
+ }
1063
+ } else {
1064
+ handleError(5, [], [2, 5]);
1065
+ }
1066
+ _jsonPath.pop();
1067
+ return true;
1068
+ }
1069
+ function parseObject() {
1070
+ onObjectBegin();
1071
+ scanNext();
1072
+ let needsComma = false;
1073
+ while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
1074
+ if (_scanner.getToken() === 5) {
1075
+ if (!needsComma) {
1076
+ handleError(4, [], []);
1077
+ }
1078
+ onSeparator(",");
1079
+ scanNext();
1080
+ if (_scanner.getToken() === 2 && allowTrailingComma) {
1081
+ break;
1082
+ }
1083
+ } else if (needsComma) {
1084
+ handleError(6, [], []);
1085
+ }
1086
+ if (!parseProperty()) {
1087
+ handleError(4, [], [2, 5]);
1088
+ }
1089
+ needsComma = true;
1090
+ }
1091
+ onObjectEnd();
1092
+ if (_scanner.getToken() !== 2) {
1093
+ handleError(7, [2], []);
1094
+ } else {
1095
+ scanNext();
1096
+ }
1097
+ return true;
1098
+ }
1099
+ function parseArray() {
1100
+ onArrayBegin();
1101
+ scanNext();
1102
+ let isFirstElement = true;
1103
+ let needsComma = false;
1104
+ while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
1105
+ if (_scanner.getToken() === 5) {
1106
+ if (!needsComma) {
1107
+ handleError(4, [], []);
1108
+ }
1109
+ onSeparator(",");
1110
+ scanNext();
1111
+ if (_scanner.getToken() === 4 && allowTrailingComma) {
1112
+ break;
1113
+ }
1114
+ } else if (needsComma) {
1115
+ handleError(6, [], []);
1116
+ }
1117
+ if (isFirstElement) {
1118
+ _jsonPath.push(0);
1119
+ isFirstElement = false;
1120
+ } else {
1121
+ _jsonPath[_jsonPath.length - 1]++;
1122
+ }
1123
+ if (!parseValue()) {
1124
+ handleError(4, [], [4, 5]);
1125
+ }
1126
+ needsComma = true;
1127
+ }
1128
+ onArrayEnd();
1129
+ if (!isFirstElement) {
1130
+ _jsonPath.pop();
1131
+ }
1132
+ if (_scanner.getToken() !== 4) {
1133
+ handleError(8, [4], []);
1134
+ } else {
1135
+ scanNext();
1136
+ }
1137
+ return true;
1138
+ }
1139
+ function parseValue() {
1140
+ switch (_scanner.getToken()) {
1141
+ case 3:
1142
+ return parseArray();
1143
+ case 1:
1144
+ return parseObject();
1145
+ case 10:
1146
+ return parseString(true);
1147
+ default:
1148
+ return parseLiteral();
1149
+ }
1150
+ }
1151
+ scanNext();
1152
+ if (_scanner.getToken() === 17) {
1153
+ if (options.allowEmptyContent) {
1154
+ return true;
1155
+ }
1156
+ handleError(4, [], []);
1157
+ return false;
1158
+ }
1159
+ if (!parseValue()) {
1160
+ handleError(4, [], []);
1161
+ return false;
1162
+ }
1163
+ if (_scanner.getToken() !== 17) {
1164
+ handleError(9, [], []);
1165
+ }
1166
+ return true;
1167
+ }
1168
+ function getNodeType(value) {
1169
+ switch (typeof value) {
1170
+ case "boolean":
1171
+ return "boolean";
1172
+ case "number":
1173
+ return "number";
1174
+ case "string":
1175
+ return "string";
1176
+ case "object": {
1177
+ if (!value) {
1178
+ return "null";
1179
+ } else if (Array.isArray(value)) {
1180
+ return "array";
1181
+ }
1182
+ return "object";
1183
+ }
1184
+ default:
1185
+ return "null";
1186
+ }
1187
+ }
1188
+
1189
+ // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/edit.js
1190
+ function setProperty(text, originalPath, value, options) {
1191
+ const path = originalPath.slice();
1192
+ const errors = [];
1193
+ const root = parseTree(text, errors);
1194
+ let parent = undefined;
1195
+ let lastSegment = undefined;
1196
+ while (path.length > 0) {
1197
+ lastSegment = path.pop();
1198
+ parent = findNodeAtLocation(root, path);
1199
+ if (parent === undefined && value !== undefined) {
1200
+ if (typeof lastSegment === "string") {
1201
+ value = { [lastSegment]: value };
1202
+ } else {
1203
+ value = [value];
1204
+ }
1205
+ } else {
1206
+ break;
1207
+ }
1208
+ }
1209
+ if (!parent) {
1210
+ if (value === undefined) {
1211
+ throw new Error("Can not delete in empty document");
1212
+ }
1213
+ return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
1214
+ } else if (parent.type === "object" && typeof lastSegment === "string" && Array.isArray(parent.children)) {
1215
+ const existing = findNodeAtLocation(parent, [lastSegment]);
1216
+ if (existing !== undefined) {
1217
+ if (value === undefined) {
1218
+ if (!existing.parent) {
1219
+ throw new Error("Malformed AST");
1686
1220
  }
1687
- token = scanNext();
1221
+ const propertyIndex = parent.children.indexOf(existing.parent);
1222
+ let removeBegin;
1223
+ let removeEnd = existing.parent.offset + existing.parent.length;
1224
+ if (propertyIndex > 0) {
1225
+ let previous = parent.children[propertyIndex - 1];
1226
+ removeBegin = previous.offset + previous.length;
1227
+ } else {
1228
+ removeBegin = parent.offset + 1;
1229
+ if (parent.children.length > 1) {
1230
+ let next = parent.children[1];
1231
+ removeEnd = next.offset;
1232
+ }
1233
+ }
1234
+ return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: "" }, options);
1235
+ } else {
1236
+ return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
1237
+ }
1238
+ } else {
1239
+ if (value === undefined) {
1240
+ return [];
1241
+ }
1242
+ const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
1243
+ const index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map((p) => p.children[0].value)) : parent.children.length;
1244
+ let edit;
1245
+ if (index > 0) {
1246
+ let previous = parent.children[index - 1];
1247
+ edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
1248
+ } else if (parent.children.length === 0) {
1249
+ edit = { offset: parent.offset + 1, length: 0, content: newProperty };
1250
+ } else {
1251
+ edit = { offset: parent.offset + 1, length: 0, content: newProperty + "," };
1688
1252
  }
1253
+ return withFormatting(text, edit, options);
1689
1254
  }
1690
- }
1691
- function parseString(isValue) {
1692
- const value = _scanner.getTokenValue();
1693
- if (isValue) {
1694
- onLiteralValue(value);
1255
+ } else if (parent.type === "array" && typeof lastSegment === "number" && Array.isArray(parent.children)) {
1256
+ const insertIndex = lastSegment;
1257
+ if (insertIndex === -1) {
1258
+ const newProperty = `${JSON.stringify(value)}`;
1259
+ let edit;
1260
+ if (parent.children.length === 0) {
1261
+ edit = { offset: parent.offset + 1, length: 0, content: newProperty };
1262
+ } else {
1263
+ const previous = parent.children[parent.children.length - 1];
1264
+ edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
1265
+ }
1266
+ return withFormatting(text, edit, options);
1267
+ } else if (value === undefined && parent.children.length >= 0) {
1268
+ const removalIndex = lastSegment;
1269
+ const toRemove = parent.children[removalIndex];
1270
+ let edit;
1271
+ if (parent.children.length === 1) {
1272
+ edit = { offset: parent.offset + 1, length: parent.length - 2, content: "" };
1273
+ } else if (parent.children.length - 1 === removalIndex) {
1274
+ let previous = parent.children[removalIndex - 1];
1275
+ let offset = previous.offset + previous.length;
1276
+ let parentEndOffset = parent.offset + parent.length;
1277
+ edit = { offset, length: parentEndOffset - 2 - offset, content: "" };
1278
+ } else {
1279
+ edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: "" };
1280
+ }
1281
+ return withFormatting(text, edit, options);
1282
+ } else if (value !== undefined) {
1283
+ let edit;
1284
+ const newProperty = `${JSON.stringify(value)}`;
1285
+ if (!options.isArrayInsertion && parent.children.length > lastSegment) {
1286
+ const toModify = parent.children[lastSegment];
1287
+ edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
1288
+ } else if (parent.children.length === 0 || lastSegment === 0) {
1289
+ edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + "," };
1290
+ } else {
1291
+ const index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
1292
+ const previous = parent.children[index - 1];
1293
+ edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
1294
+ }
1295
+ return withFormatting(text, edit, options);
1695
1296
  } else {
1696
- onObjectProperty(value);
1697
- _jsonPath.push(value);
1297
+ throw new Error(`Can not ${value === undefined ? "remove" : options.isArrayInsertion ? "insert" : "modify"} Array index ${insertIndex} as length is not sufficient`);
1698
1298
  }
1699
- scanNext();
1700
- return true;
1299
+ } else {
1300
+ throw new Error(`Can not add ${typeof lastSegment !== "number" ? "index" : "property"} to parent of type ${parent.type}`);
1701
1301
  }
1702
- function parseLiteral() {
1703
- switch (_scanner.getToken()) {
1704
- case 11:
1705
- const tokenValue = _scanner.getTokenValue();
1706
- let value = Number(tokenValue);
1707
- if (isNaN(value)) {
1708
- handleError(2);
1709
- value = 0;
1710
- }
1711
- onLiteralValue(value);
1712
- break;
1713
- case 7:
1714
- onLiteralValue(null);
1715
- break;
1716
- case 8:
1717
- onLiteralValue(true);
1718
- break;
1719
- case 9:
1720
- onLiteralValue(false);
1721
- break;
1722
- default:
1723
- return false;
1302
+ }
1303
+ function withFormatting(text, edit, options) {
1304
+ if (!options.formattingOptions) {
1305
+ return [edit];
1306
+ }
1307
+ let newText = applyEdit(text, edit);
1308
+ let begin = edit.offset;
1309
+ let end = edit.offset + edit.content.length;
1310
+ if (edit.length === 0 || edit.content.length === 0) {
1311
+ while (begin > 0 && !isEOL(newText, begin - 1)) {
1312
+ begin--;
1313
+ }
1314
+ while (end < newText.length && !isEOL(newText, end)) {
1315
+ end++;
1724
1316
  }
1725
- scanNext();
1726
- return true;
1727
1317
  }
1728
- function parseProperty() {
1729
- if (_scanner.getToken() !== 10) {
1730
- handleError(3, [], [2, 5]);
1731
- return false;
1318
+ const edits = format(newText, { offset: begin, length: end - begin }, { ...options.formattingOptions, keepLines: false });
1319
+ for (let i = edits.length - 1;i >= 0; i--) {
1320
+ const edit = edits[i];
1321
+ newText = applyEdit(newText, edit);
1322
+ begin = Math.min(begin, edit.offset);
1323
+ end = Math.max(end, edit.offset + edit.length);
1324
+ end += edit.content.length - edit.length;
1325
+ }
1326
+ const editLength = text.length - (newText.length - end) - begin;
1327
+ return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
1328
+ }
1329
+ function applyEdit(text, edit) {
1330
+ return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
1331
+ }
1332
+
1333
+ // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
1334
+ var createScanner2 = createScanner;
1335
+ var ScanError;
1336
+ (function(ScanError) {
1337
+ ScanError[ScanError["None"] = 0] = "None";
1338
+ ScanError[ScanError["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
1339
+ ScanError[ScanError["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
1340
+ ScanError[ScanError["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
1341
+ ScanError[ScanError["InvalidUnicode"] = 4] = "InvalidUnicode";
1342
+ ScanError[ScanError["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
1343
+ ScanError[ScanError["InvalidCharacter"] = 6] = "InvalidCharacter";
1344
+ })(ScanError || (ScanError = {}));
1345
+ var SyntaxKind;
1346
+ (function(SyntaxKind) {
1347
+ SyntaxKind[SyntaxKind["OpenBraceToken"] = 1] = "OpenBraceToken";
1348
+ SyntaxKind[SyntaxKind["CloseBraceToken"] = 2] = "CloseBraceToken";
1349
+ SyntaxKind[SyntaxKind["OpenBracketToken"] = 3] = "OpenBracketToken";
1350
+ SyntaxKind[SyntaxKind["CloseBracketToken"] = 4] = "CloseBracketToken";
1351
+ SyntaxKind[SyntaxKind["CommaToken"] = 5] = "CommaToken";
1352
+ SyntaxKind[SyntaxKind["ColonToken"] = 6] = "ColonToken";
1353
+ SyntaxKind[SyntaxKind["NullKeyword"] = 7] = "NullKeyword";
1354
+ SyntaxKind[SyntaxKind["TrueKeyword"] = 8] = "TrueKeyword";
1355
+ SyntaxKind[SyntaxKind["FalseKeyword"] = 9] = "FalseKeyword";
1356
+ SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral";
1357
+ SyntaxKind[SyntaxKind["NumericLiteral"] = 11] = "NumericLiteral";
1358
+ SyntaxKind[SyntaxKind["LineCommentTrivia"] = 12] = "LineCommentTrivia";
1359
+ SyntaxKind[SyntaxKind["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
1360
+ SyntaxKind[SyntaxKind["LineBreakTrivia"] = 14] = "LineBreakTrivia";
1361
+ SyntaxKind[SyntaxKind["Trivia"] = 15] = "Trivia";
1362
+ SyntaxKind[SyntaxKind["Unknown"] = 16] = "Unknown";
1363
+ SyntaxKind[SyntaxKind["EOF"] = 17] = "EOF";
1364
+ })(SyntaxKind || (SyntaxKind = {}));
1365
+ var parse2 = parse;
1366
+ var parseTree2 = parseTree;
1367
+ var findNodeAtLocation2 = findNodeAtLocation;
1368
+ var getNodeValue2 = getNodeValue;
1369
+ var ParseErrorCode;
1370
+ (function(ParseErrorCode) {
1371
+ ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 1] = "InvalidSymbol";
1372
+ ParseErrorCode[ParseErrorCode["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
1373
+ ParseErrorCode[ParseErrorCode["PropertyNameExpected"] = 3] = "PropertyNameExpected";
1374
+ ParseErrorCode[ParseErrorCode["ValueExpected"] = 4] = "ValueExpected";
1375
+ ParseErrorCode[ParseErrorCode["ColonExpected"] = 5] = "ColonExpected";
1376
+ ParseErrorCode[ParseErrorCode["CommaExpected"] = 6] = "CommaExpected";
1377
+ ParseErrorCode[ParseErrorCode["CloseBraceExpected"] = 7] = "CloseBraceExpected";
1378
+ ParseErrorCode[ParseErrorCode["CloseBracketExpected"] = 8] = "CloseBracketExpected";
1379
+ ParseErrorCode[ParseErrorCode["EndOfFileExpected"] = 9] = "EndOfFileExpected";
1380
+ ParseErrorCode[ParseErrorCode["InvalidCommentToken"] = 10] = "InvalidCommentToken";
1381
+ ParseErrorCode[ParseErrorCode["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
1382
+ ParseErrorCode[ParseErrorCode["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
1383
+ ParseErrorCode[ParseErrorCode["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
1384
+ ParseErrorCode[ParseErrorCode["InvalidUnicode"] = 14] = "InvalidUnicode";
1385
+ ParseErrorCode[ParseErrorCode["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
1386
+ ParseErrorCode[ParseErrorCode["InvalidCharacter"] = 16] = "InvalidCharacter";
1387
+ })(ParseErrorCode || (ParseErrorCode = {}));
1388
+ function printParseErrorCode(code) {
1389
+ switch (code) {
1390
+ case 1:
1391
+ return "InvalidSymbol";
1392
+ case 2:
1393
+ return "InvalidNumberFormat";
1394
+ case 3:
1395
+ return "PropertyNameExpected";
1396
+ case 4:
1397
+ return "ValueExpected";
1398
+ case 5:
1399
+ return "ColonExpected";
1400
+ case 6:
1401
+ return "CommaExpected";
1402
+ case 7:
1403
+ return "CloseBraceExpected";
1404
+ case 8:
1405
+ return "CloseBracketExpected";
1406
+ case 9:
1407
+ return "EndOfFileExpected";
1408
+ case 10:
1409
+ return "InvalidCommentToken";
1410
+ case 11:
1411
+ return "UnexpectedEndOfComment";
1412
+ case 12:
1413
+ return "UnexpectedEndOfString";
1414
+ case 13:
1415
+ return "UnexpectedEndOfNumber";
1416
+ case 14:
1417
+ return "InvalidUnicode";
1418
+ case 15:
1419
+ return "InvalidEscapeCharacter";
1420
+ case 16:
1421
+ return "InvalidCharacter";
1422
+ }
1423
+ return "<unknown ParseErrorCode>";
1424
+ }
1425
+ function modify(text, path, value, options) {
1426
+ return setProperty(text, path, value, options);
1427
+ }
1428
+ function applyEdits(text, edits) {
1429
+ let sortedEdits = edits.slice(0).sort((a, b) => {
1430
+ const diff = a.offset - b.offset;
1431
+ if (diff === 0) {
1432
+ return a.length - b.length;
1732
1433
  }
1733
- parseString(false);
1734
- if (_scanner.getToken() === 6) {
1735
- onSeparator(":");
1736
- scanNext();
1737
- if (!parseValue()) {
1738
- handleError(4, [], [2, 5]);
1739
- }
1434
+ return diff;
1435
+ });
1436
+ let lastModifiedOffset = text.length;
1437
+ for (let i = sortedEdits.length - 1;i >= 0; i--) {
1438
+ let e = sortedEdits[i];
1439
+ if (e.offset + e.length <= lastModifiedOffset) {
1440
+ text = applyEdit(text, e);
1740
1441
  } else {
1741
- handleError(5, [], [2, 5]);
1442
+ throw new Error("Overlapping edit");
1742
1443
  }
1743
- _jsonPath.pop();
1744
- return true;
1444
+ lastModifiedOffset = e.offset;
1745
1445
  }
1746
- function parseObject() {
1747
- onObjectBegin();
1748
- scanNext();
1749
- let needsComma = false;
1750
- while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
1751
- if (_scanner.getToken() === 5) {
1752
- if (!needsComma) {
1753
- handleError(4, [], []);
1754
- }
1755
- onSeparator(",");
1756
- scanNext();
1757
- if (_scanner.getToken() === 2 && allowTrailingComma) {
1758
- break;
1759
- }
1760
- } else if (needsComma) {
1761
- handleError(6, [], []);
1446
+ return text;
1447
+ }
1448
+
1449
+ // src/shared/jsonc-parser.ts
1450
+ function stripJsonComments(content) {
1451
+ let result = "";
1452
+ let inString = false;
1453
+ let escaped = false;
1454
+ let inLineComment = false;
1455
+ let inBlockComment = false;
1456
+ for (let index = 0;index < content.length; index += 1) {
1457
+ const char = content[index];
1458
+ const next = content[index + 1];
1459
+ if (inLineComment) {
1460
+ if (char === `
1461
+ `) {
1462
+ inLineComment = false;
1463
+ result += char;
1762
1464
  }
1763
- if (!parseProperty()) {
1764
- handleError(4, [], [2, 5]);
1465
+ continue;
1466
+ }
1467
+ if (inBlockComment) {
1468
+ if (char === "*" && next === "/") {
1469
+ inBlockComment = false;
1470
+ index += 1;
1765
1471
  }
1766
- needsComma = true;
1472
+ continue;
1767
1473
  }
1768
- onObjectEnd();
1769
- if (_scanner.getToken() !== 2) {
1770
- handleError(7, [2], []);
1771
- } else {
1772
- scanNext();
1474
+ if (inString) {
1475
+ result += char;
1476
+ if (escaped) {
1477
+ escaped = false;
1478
+ } else if (char === "\\") {
1479
+ escaped = true;
1480
+ } else if (char === '"') {
1481
+ inString = false;
1482
+ }
1483
+ continue;
1773
1484
  }
1774
- return true;
1485
+ if (char === '"') {
1486
+ inString = true;
1487
+ result += char;
1488
+ continue;
1489
+ }
1490
+ if (char === "/" && next === "/") {
1491
+ inLineComment = true;
1492
+ index += 1;
1493
+ continue;
1494
+ }
1495
+ if (char === "/" && next === "*") {
1496
+ inBlockComment = true;
1497
+ index += 1;
1498
+ continue;
1499
+ }
1500
+ result += char;
1775
1501
  }
1776
- function parseArray() {
1777
- onArrayBegin();
1778
- scanNext();
1779
- let isFirstElement = true;
1780
- let needsComma = false;
1781
- while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
1782
- if (_scanner.getToken() === 5) {
1783
- if (!needsComma) {
1784
- handleError(4, [], []);
1785
- }
1786
- onSeparator(",");
1787
- scanNext();
1788
- if (_scanner.getToken() === 4 && allowTrailingComma) {
1789
- break;
1790
- }
1791
- } else if (needsComma) {
1792
- handleError(6, [], []);
1793
- }
1794
- if (isFirstElement) {
1795
- _jsonPath.push(0);
1796
- isFirstElement = false;
1797
- } else {
1798
- _jsonPath[_jsonPath.length - 1]++;
1799
- }
1800
- if (!parseValue()) {
1801
- handleError(4, [], [4, 5]);
1502
+ return result;
1503
+ }
1504
+ function stripTrailingCommas(content) {
1505
+ let result = "";
1506
+ let inString = false;
1507
+ let escaped = false;
1508
+ for (let index = 0;index < content.length; index += 1) {
1509
+ const char = content[index];
1510
+ if (inString) {
1511
+ result += char;
1512
+ if (escaped) {
1513
+ escaped = false;
1514
+ } else if (char === "\\") {
1515
+ escaped = true;
1516
+ } else if (char === '"') {
1517
+ inString = false;
1802
1518
  }
1803
- needsComma = true;
1519
+ continue;
1804
1520
  }
1805
- onArrayEnd();
1806
- if (!isFirstElement) {
1807
- _jsonPath.pop();
1521
+ if (char === '"') {
1522
+ inString = true;
1523
+ result += char;
1524
+ continue;
1808
1525
  }
1809
- if (_scanner.getToken() !== 4) {
1810
- handleError(8, [4], []);
1811
- } else {
1812
- scanNext();
1526
+ if (char === ",") {
1527
+ let lookahead = index + 1;
1528
+ while (lookahead < content.length && /\s/.test(content[lookahead] ?? "")) {
1529
+ lookahead += 1;
1530
+ }
1531
+ const next = content[lookahead];
1532
+ if (next === "}" || next === "]") {
1533
+ continue;
1534
+ }
1813
1535
  }
1814
- return true;
1536
+ result += char;
1815
1537
  }
1816
- function parseValue() {
1817
- switch (_scanner.getToken()) {
1818
- case 3:
1819
- return parseArray();
1820
- case 1:
1821
- return parseObject();
1822
- case 10:
1823
- return parseString(true);
1824
- default:
1825
- return parseLiteral();
1538
+ return result;
1539
+ }
1540
+ var PROTOTYPE_POLLUTION_KEYS = new Set(["__proto__", "constructor", "prototype"]);
1541
+ function isPrototypePollutionKey(key) {
1542
+ return PROTOTYPE_POLLUTION_KEYS.has(key);
1543
+ }
1544
+ function sanitizeParsedJson(value, options = {}, path = []) {
1545
+ if (Array.isArray(value)) {
1546
+ return value.map((entry, index) => sanitizeParsedJson(entry, options, [...path, index]));
1547
+ }
1548
+ if (value === null || typeof value !== "object")
1549
+ return value;
1550
+ const source = value;
1551
+ const sourcePrototype = Object.getPrototypeOf(source);
1552
+ if (sourcePrototype !== null && sourcePrototype !== Object.prototype) {
1553
+ options.onRejectedKey?.([...path, "__proto__"]);
1554
+ }
1555
+ const sanitized = {};
1556
+ for (const key of Object.keys(source)) {
1557
+ if (isPrototypePollutionKey(key)) {
1558
+ options.onRejectedKey?.([...path, key]);
1559
+ continue;
1826
1560
  }
1561
+ Object.defineProperty(sanitized, key, {
1562
+ value: sanitizeParsedJson(source[key], options, [...path, key]),
1563
+ enumerable: true,
1564
+ configurable: true,
1565
+ writable: true
1566
+ });
1567
+ }
1568
+ return sanitized;
1569
+ }
1570
+ function lineAndColumnAt(content, offset) {
1571
+ const before = content.slice(0, Math.max(0, offset));
1572
+ const lines = before.split(/\r?\n/);
1573
+ return { line: lines.length, column: (lines.at(-1)?.length ?? 0) + 1 };
1574
+ }
1575
+ function parseIssue(content, error) {
1576
+ const location = lineAndColumnAt(content, error.offset);
1577
+ const code = printParseErrorCode(error.error);
1578
+ const message = code.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
1579
+ return {
1580
+ ...location,
1581
+ offset: error.offset,
1582
+ length: error.length,
1583
+ message
1584
+ };
1585
+ }
1586
+ function normalizeJsoncParserObjects(value, options, path = []) {
1587
+ if (Array.isArray(value)) {
1588
+ return value.map((entry, index) => normalizeJsoncParserObjects(entry, options, [...path, String(index)]));
1589
+ }
1590
+ if (value === null || typeof value !== "object")
1591
+ return value;
1592
+ const prototype = Object.getPrototypeOf(value);
1593
+ if (prototype !== Object.prototype && prototype !== null) {
1594
+ options.onRejectedKey?.([...path, "__proto__"]);
1595
+ }
1596
+ const normalized = {};
1597
+ for (const [key, entry] of Object.entries(value)) {
1598
+ Object.defineProperty(normalized, key, {
1599
+ configurable: true,
1600
+ enumerable: true,
1601
+ writable: true,
1602
+ value: normalizeJsoncParserObjects(entry, options, [...path, key])
1603
+ });
1604
+ }
1605
+ return normalized;
1606
+ }
1607
+ function parseJsoncRecovering(content, options = {}) {
1608
+ const errors = [];
1609
+ const parsed = parse2(content, errors, {
1610
+ allowTrailingComma: true,
1611
+ disallowComments: false,
1612
+ allowEmptyContent: false
1613
+ });
1614
+ return {
1615
+ value: sanitizeParsedJson(normalizeJsoncParserObjects(parsed, options), options),
1616
+ issues: errors.map((error) => parseIssue(content, error))
1617
+ };
1618
+ }
1619
+ function parseJsonc(content, options = {}) {
1620
+ const normalized = stripTrailingCommas(stripJsonComments(content));
1621
+ return sanitizeParsedJson(JSON.parse(normalized), options);
1622
+ }
1623
+ function readJsoncFile(filePath) {
1624
+ try {
1625
+ return parseJsonc(readFileSync(filePath, "utf-8"));
1626
+ } catch (_error) {
1627
+ return null;
1827
1628
  }
1828
- scanNext();
1829
- if (_scanner.getToken() === 17) {
1830
- if (options.allowEmptyContent) {
1831
- return true;
1832
- }
1833
- handleError(4, [], []);
1834
- return false;
1629
+ }
1630
+ function detectConfigFile(basePath) {
1631
+ const jsoncPath = `${basePath}.jsonc`;
1632
+ const jsonPath = `${basePath}.json`;
1633
+ if (existsSync(jsoncPath)) {
1634
+ return { format: "jsonc", path: jsoncPath };
1835
1635
  }
1836
- if (!parseValue()) {
1837
- handleError(4, [], []);
1838
- return false;
1636
+ if (existsSync(jsonPath)) {
1637
+ return { format: "json", path: jsonPath };
1839
1638
  }
1840
- if (_scanner.getToken() !== 17) {
1841
- handleError(9, [], []);
1639
+ return { format: "none", path: jsoncPath };
1640
+ }
1641
+
1642
+ // src/config/agent-disable.ts
1643
+ var COMPACTION_ENABLED_PATH = `compaction${"."}enabled`;
1644
+ function isDreamerRunnable(config) {
1645
+ return !!config.dreamer && config.dreamer.disable !== true;
1646
+ }
1647
+ function isCompactionEnabled(config) {
1648
+ return config.compaction?.enabled !== false;
1649
+ }
1650
+ function clonePlainObject(value) {
1651
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1652
+ return;
1842
1653
  }
1843
- return true;
1654
+ return { ...value };
1844
1655
  }
1845
- function getNodeType(value) {
1846
- switch (typeof value) {
1847
- case "boolean":
1848
- return "boolean";
1849
- case "number":
1850
- return "number";
1851
- case "string":
1852
- return "string";
1853
- case "object": {
1854
- if (!value) {
1855
- return "null";
1856
- } else if (Array.isArray(value)) {
1857
- return "array";
1858
- }
1859
- return "object";
1860
- }
1861
- default:
1862
- return "null";
1656
+ function migrateLegacyEnabledForAgent(args) {
1657
+ const agent = clonePlainObject(args.patched[args.agentName]);
1658
+ if (!agent || !("enabled" in agent))
1659
+ return;
1660
+ const enabled = agent.enabled;
1661
+ const disable = agent.disable;
1662
+ delete agent.enabled;
1663
+ if (args.agentName === "historian") {
1664
+ args.warnings.push('Removed invalid "historian.enabled" in-memory (run doctor to persist).');
1665
+ args.patched.historian = agent;
1666
+ return;
1667
+ }
1668
+ if (disable !== true && enabled === false) {
1669
+ agent.disable = true;
1670
+ args.warnings.push('Migrated "dreamer.enabled=false" → "dreamer.disable=true" in-memory (run doctor to persist). This now also disables manual /ctx-dream; for manual-only remove disable and set schedule="".');
1863
1671
  }
1672
+ args.patched.dreamer = agent;
1673
+ }
1674
+ function migrateLegacyAgentEnabledInMemory(rawConfig, warnings) {
1675
+ const shouldPatch = ["dreamer", "historian"].some((key) => {
1676
+ const agent = rawConfig[key];
1677
+ return typeof agent === "object" && agent !== null && !Array.isArray(agent) && "enabled" in agent;
1678
+ });
1679
+ if (!shouldPatch)
1680
+ return rawConfig;
1681
+ const patched = { ...rawConfig };
1682
+ migrateLegacyEnabledForAgent({ patched, agentName: "dreamer", warnings });
1683
+ migrateLegacyEnabledForAgent({ patched, agentName: "historian", warnings });
1684
+ return patched;
1864
1685
  }
1865
1686
 
1866
- // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/edit.js
1867
- function setProperty(text, originalPath, value, options) {
1868
- const path = originalPath.slice();
1687
+ // src/config/migrate-config-location.ts
1688
+ import { homedir } from "node:os";
1689
+ import { basename, dirname, isAbsolute, join } from "node:path";
1690
+
1691
+ // src/shared/jsonc-edit.ts
1692
+ var TOKEN_COMMA = 5;
1693
+ var TOKEN_EOF = 17;
1694
+ function parseDocument(text) {
1869
1695
  const errors = [];
1870
- const root = parseTree(text, errors);
1871
- let parent = undefined;
1872
- let lastSegment = undefined;
1873
- while (path.length > 0) {
1874
- lastSegment = path.pop();
1875
- parent = findNodeAtLocation(root, path);
1876
- if (parent === undefined && value !== undefined) {
1877
- if (typeof lastSegment === "string") {
1878
- value = { [lastSegment]: value };
1879
- } else {
1880
- value = [value];
1881
- }
1882
- } else {
1883
- break;
1884
- }
1696
+ const root = parseTree2(text, errors, { allowTrailingComma: true });
1697
+ if (!root || errors.length > 0) {
1698
+ throw new Error("Cannot edit invalid JSONC");
1885
1699
  }
1886
- if (!parent) {
1887
- if (value === undefined) {
1888
- throw new Error("Can not delete in empty document");
1889
- }
1890
- return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
1891
- } else if (parent.type === "object" && typeof lastSegment === "string" && Array.isArray(parent.children)) {
1892
- const existing = findNodeAtLocation(parent, [lastSegment]);
1893
- if (existing !== undefined) {
1894
- if (value === undefined) {
1895
- if (!existing.parent) {
1896
- throw new Error("Malformed AST");
1897
- }
1898
- const propertyIndex = parent.children.indexOf(existing.parent);
1899
- let removeBegin;
1900
- let removeEnd = existing.parent.offset + existing.parent.length;
1901
- if (propertyIndex > 0) {
1902
- let previous = parent.children[propertyIndex - 1];
1903
- removeBegin = previous.offset + previous.length;
1904
- } else {
1905
- removeBegin = parent.offset + 1;
1906
- if (parent.children.length > 1) {
1907
- let next = parent.children[1];
1908
- removeEnd = next.offset;
1909
- }
1910
- }
1911
- return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: "" }, options);
1912
- } else {
1913
- return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
1914
- }
1915
- } else {
1916
- if (value === undefined) {
1917
- return [];
1918
- }
1919
- const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
1920
- const index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map((p) => p.children[0].value)) : parent.children.length;
1921
- let edit;
1922
- if (index > 0) {
1923
- let previous = parent.children[index - 1];
1924
- edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
1925
- } else if (parent.children.length === 0) {
1926
- edit = { offset: parent.offset + 1, length: 0, content: newProperty };
1927
- } else {
1928
- edit = { offset: parent.offset + 1, length: 0, content: newProperty + "," };
1929
- }
1930
- return withFormatting(text, edit, options);
1931
- }
1932
- } else if (parent.type === "array" && typeof lastSegment === "number" && Array.isArray(parent.children)) {
1933
- const insertIndex = lastSegment;
1934
- if (insertIndex === -1) {
1935
- const newProperty = `${JSON.stringify(value)}`;
1936
- let edit;
1937
- if (parent.children.length === 0) {
1938
- edit = { offset: parent.offset + 1, length: 0, content: newProperty };
1939
- } else {
1940
- const previous = parent.children[parent.children.length - 1];
1941
- edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
1942
- }
1943
- return withFormatting(text, edit, options);
1944
- } else if (value === undefined && parent.children.length >= 0) {
1945
- const removalIndex = lastSegment;
1946
- const toRemove = parent.children[removalIndex];
1947
- let edit;
1948
- if (parent.children.length === 1) {
1949
- edit = { offset: parent.offset + 1, length: parent.length - 2, content: "" };
1950
- } else if (parent.children.length - 1 === removalIndex) {
1951
- let previous = parent.children[removalIndex - 1];
1952
- let offset = previous.offset + previous.length;
1953
- let parentEndOffset = parent.offset + parent.length;
1954
- edit = { offset, length: parentEndOffset - 2 - offset, content: "" };
1955
- } else {
1956
- edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: "" };
1957
- }
1958
- return withFormatting(text, edit, options);
1959
- } else if (value !== undefined) {
1960
- let edit;
1961
- const newProperty = `${JSON.stringify(value)}`;
1962
- if (!options.isArrayInsertion && parent.children.length > lastSegment) {
1963
- const toModify = parent.children[lastSegment];
1964
- edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
1965
- } else if (parent.children.length === 0 || lastSegment === 0) {
1966
- edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + "," };
1967
- } else {
1968
- const index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
1969
- const previous = parent.children[index - 1];
1970
- edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
1971
- }
1972
- return withFormatting(text, edit, options);
1973
- } else {
1974
- throw new Error(`Can not ${value === undefined ? "remove" : options.isArrayInsertion ? "insert" : "modify"} Array index ${insertIndex} as length is not sufficient`);
1700
+ return root;
1701
+ }
1702
+ function findNode(text, path) {
1703
+ return findNodeAtLocation2(parseDocument(text), path);
1704
+ }
1705
+ function findComma(text, start, end) {
1706
+ const scanner = createScanner2(text, false);
1707
+ scanner.setPosition(start);
1708
+ for (;; ) {
1709
+ const kind = scanner.scan();
1710
+ const offset = scanner.getTokenOffset();
1711
+ if (kind === TOKEN_EOF || offset >= end)
1712
+ return;
1713
+ if (kind === TOKEN_COMMA) {
1714
+ return {
1715
+ offset,
1716
+ length: scanner.getTokenLength(),
1717
+ line: scanner.getTokenStartLine()
1718
+ };
1975
1719
  }
1976
- } else {
1977
- throw new Error(`Can not add ${typeof lastSegment !== "number" ? "index" : "property"} to parent of type ${parent.type}`);
1978
1720
  }
1979
1721
  }
1980
- function withFormatting(text, edit, options) {
1981
- if (!options.formattingOptions) {
1982
- return [edit];
1983
- }
1984
- let newText = applyEdit(text, edit);
1985
- let begin = edit.offset;
1986
- let end = edit.offset + edit.content.length;
1987
- if (edit.length === 0 || edit.content.length === 0) {
1988
- while (begin > 0 && !isEOL(newText, begin - 1)) {
1989
- begin--;
1990
- }
1991
- while (end < newText.length && !isEOL(newText, end)) {
1992
- end++;
1993
- }
1722
+ function setJsoncValue(text, path, value) {
1723
+ const node = findNode(text, path);
1724
+ if (node) {
1725
+ if (Object.is(getNodeValue2(node), value))
1726
+ return text;
1727
+ const serialized = JSON.stringify(value);
1728
+ return text.slice(0, node.offset) + serialized + text.slice(node.offset + node.length);
1729
+ }
1730
+ return applyEdits(text, modify(text, path, value, { formattingOptions: documentFormatting(text) }));
1731
+ }
1732
+ function documentFormatting(text) {
1733
+ const eol = text.includes(`\r
1734
+ `) ? `\r
1735
+ ` : `
1736
+ `;
1737
+ const indent = /\n([ \t]+)\S/.exec(text)?.[1];
1738
+ if (indent?.startsWith("\t"))
1739
+ return { eol, insertSpaces: false, tabSize: 1 };
1740
+ return { eol, insertSpaces: true, tabSize: indent ? indent.length : 2 };
1741
+ }
1742
+ function removeObjectProperty(text, object, key) {
1743
+ const properties = object.children ?? [];
1744
+ const index = properties.findIndex((property) => {
1745
+ const propertyKey = property.children?.[0];
1746
+ return propertyKey !== undefined && getNodeValue2(propertyKey) === key;
1747
+ });
1748
+ if (index === -1)
1749
+ return text;
1750
+ const property = properties[index];
1751
+ if (!property)
1752
+ return text;
1753
+ const closingBrace = object.offset + object.length - 1;
1754
+ const next = properties[index + 1];
1755
+ if (next) {
1756
+ const followingComma = findComma(text, property.offset + property.length, next.offset);
1757
+ if (!followingComma)
1758
+ return text;
1759
+ return text.slice(0, property.offset) + text.slice(followingComma.offset + followingComma.length);
1994
1760
  }
1995
- const edits = format(newText, { offset: begin, length: end - begin }, { ...options.formattingOptions, keepLines: false });
1996
- for (let i = edits.length - 1;i >= 0; i--) {
1997
- const edit = edits[i];
1998
- newText = applyEdit(newText, edit);
1999
- begin = Math.min(begin, edit.offset);
2000
- end = Math.max(end, edit.offset + edit.length);
2001
- end += edit.content.length - edit.length;
1761
+ const previous = properties[index - 1];
1762
+ const trailingComma = findComma(text, property.offset + property.length, closingBrace);
1763
+ if (!previous) {
1764
+ const afterProperty = property.offset + property.length;
1765
+ if (!trailingComma)
1766
+ return text.slice(0, property.offset) + text.slice(afterProperty);
1767
+ return text.slice(0, property.offset) + text.slice(afterProperty, trailingComma.offset) + text.slice(trailingComma.offset + trailingComma.length);
2002
1768
  }
2003
- const editLength = text.length - (newText.length - end) - begin;
2004
- return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
1769
+ const precedingComma = findComma(text, previous.offset + previous.length, property.offset);
1770
+ if (!precedingComma)
1771
+ return text;
1772
+ const afterProperty = property.offset + property.length;
1773
+ const withoutProperty = text.slice(0, precedingComma.offset) + text.slice(precedingComma.offset + precedingComma.length, property.offset) + text.slice(afterProperty);
1774
+ if (!trailingComma)
1775
+ return withoutProperty;
1776
+ const shiftedTrailingComma = trailingComma.offset - 1;
1777
+ return withoutProperty.slice(0, shiftedTrailingComma) + withoutProperty.slice(shiftedTrailingComma + trailingComma.length);
2005
1778
  }
2006
- function applyEdit(text, edit) {
2007
- return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
1779
+ function removeJsoncValue(text, path) {
1780
+ const key = path.at(-1);
1781
+ if (typeof key !== "string")
1782
+ return text;
1783
+ const parent = findNode(text, path.slice(0, -1));
1784
+ if (parent?.type !== "object")
1785
+ return text;
1786
+ return removeObjectProperty(text, parent, key);
2008
1787
  }
2009
1788
 
2010
- // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
2011
- var createScanner2 = createScanner;
2012
- var ScanError;
2013
- (function(ScanError) {
2014
- ScanError[ScanError["None"] = 0] = "None";
2015
- ScanError[ScanError["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
2016
- ScanError[ScanError["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
2017
- ScanError[ScanError["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
2018
- ScanError[ScanError["InvalidUnicode"] = 4] = "InvalidUnicode";
2019
- ScanError[ScanError["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
2020
- ScanError[ScanError["InvalidCharacter"] = 6] = "InvalidCharacter";
2021
- })(ScanError || (ScanError = {}));
2022
- var SyntaxKind;
2023
- (function(SyntaxKind) {
2024
- SyntaxKind[SyntaxKind["OpenBraceToken"] = 1] = "OpenBraceToken";
2025
- SyntaxKind[SyntaxKind["CloseBraceToken"] = 2] = "CloseBraceToken";
2026
- SyntaxKind[SyntaxKind["OpenBracketToken"] = 3] = "OpenBracketToken";
2027
- SyntaxKind[SyntaxKind["CloseBracketToken"] = 4] = "CloseBracketToken";
2028
- SyntaxKind[SyntaxKind["CommaToken"] = 5] = "CommaToken";
2029
- SyntaxKind[SyntaxKind["ColonToken"] = 6] = "ColonToken";
2030
- SyntaxKind[SyntaxKind["NullKeyword"] = 7] = "NullKeyword";
2031
- SyntaxKind[SyntaxKind["TrueKeyword"] = 8] = "TrueKeyword";
2032
- SyntaxKind[SyntaxKind["FalseKeyword"] = 9] = "FalseKeyword";
2033
- SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral";
2034
- SyntaxKind[SyntaxKind["NumericLiteral"] = 11] = "NumericLiteral";
2035
- SyntaxKind[SyntaxKind["LineCommentTrivia"] = 12] = "LineCommentTrivia";
2036
- SyntaxKind[SyntaxKind["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
2037
- SyntaxKind[SyntaxKind["LineBreakTrivia"] = 14] = "LineBreakTrivia";
2038
- SyntaxKind[SyntaxKind["Trivia"] = 15] = "Trivia";
2039
- SyntaxKind[SyntaxKind["Unknown"] = 16] = "Unknown";
2040
- SyntaxKind[SyntaxKind["EOF"] = 17] = "EOF";
2041
- })(SyntaxKind || (SyntaxKind = {}));
2042
- var parse2 = parse;
2043
- var parseTree2 = parseTree;
2044
- var findNodeAtLocation2 = findNodeAtLocation;
2045
- var getNodeValue2 = getNodeValue;
2046
- var ParseErrorCode;
2047
- (function(ParseErrorCode) {
2048
- ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 1] = "InvalidSymbol";
2049
- ParseErrorCode[ParseErrorCode["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
2050
- ParseErrorCode[ParseErrorCode["PropertyNameExpected"] = 3] = "PropertyNameExpected";
2051
- ParseErrorCode[ParseErrorCode["ValueExpected"] = 4] = "ValueExpected";
2052
- ParseErrorCode[ParseErrorCode["ColonExpected"] = 5] = "ColonExpected";
2053
- ParseErrorCode[ParseErrorCode["CommaExpected"] = 6] = "CommaExpected";
2054
- ParseErrorCode[ParseErrorCode["CloseBraceExpected"] = 7] = "CloseBraceExpected";
2055
- ParseErrorCode[ParseErrorCode["CloseBracketExpected"] = 8] = "CloseBracketExpected";
2056
- ParseErrorCode[ParseErrorCode["EndOfFileExpected"] = 9] = "EndOfFileExpected";
2057
- ParseErrorCode[ParseErrorCode["InvalidCommentToken"] = 10] = "InvalidCommentToken";
2058
- ParseErrorCode[ParseErrorCode["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
2059
- ParseErrorCode[ParseErrorCode["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
2060
- ParseErrorCode[ParseErrorCode["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
2061
- ParseErrorCode[ParseErrorCode["InvalidUnicode"] = 14] = "InvalidUnicode";
2062
- ParseErrorCode[ParseErrorCode["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
2063
- ParseErrorCode[ParseErrorCode["InvalidCharacter"] = 16] = "InvalidCharacter";
2064
- })(ParseErrorCode || (ParseErrorCode = {}));
2065
- function printParseErrorCode(code) {
2066
- switch (code) {
2067
- case 1:
2068
- return "InvalidSymbol";
2069
- case 2:
2070
- return "InvalidNumberFormat";
2071
- case 3:
2072
- return "PropertyNameExpected";
2073
- case 4:
2074
- return "ValueExpected";
2075
- case 5:
2076
- return "ColonExpected";
2077
- case 6:
2078
- return "CommaExpected";
2079
- case 7:
2080
- return "CloseBraceExpected";
2081
- case 8:
2082
- return "CloseBracketExpected";
2083
- case 9:
2084
- return "EndOfFileExpected";
2085
- case 10:
2086
- return "InvalidCommentToken";
2087
- case 11:
2088
- return "UnexpectedEndOfComment";
2089
- case 12:
2090
- return "UnexpectedEndOfString";
2091
- case 13:
2092
- return "UnexpectedEndOfNumber";
2093
- case 14:
2094
- return "InvalidUnicode";
2095
- case 15:
2096
- return "InvalidEscapeCharacter";
2097
- case 16:
2098
- return "InvalidCharacter";
1789
+ // src/config/migrate-config-location.ts
1790
+ var CONFIG_FILE_BASENAME = "magic-context";
1791
+ function homeDir() {
1792
+ if (process.platform === "win32") {
1793
+ return process.env.USERPROFILE || process.env.HOME || homedir();
2099
1794
  }
2100
- return "<unknown ParseErrorCode>";
1795
+ return process.env.HOME || homedir();
1796
+ }
1797
+ function configHome() {
1798
+ const xdg = process.env.XDG_CONFIG_HOME;
1799
+ if (xdg && isAbsolute(xdg))
1800
+ return xdg;
1801
+ return join(homeDir(), ".config");
1802
+ }
1803
+ function cortexKitUserConfigBasePath() {
1804
+ return join(configHome(), "cortexkit", CONFIG_FILE_BASENAME);
1805
+ }
1806
+ function cortexKitProjectConfigBasePath(directory) {
1807
+ return join(directory, ".cortexkit", CONFIG_FILE_BASENAME);
1808
+ }
1809
+ function legacySourcesForBase(basePath, label) {
1810
+ return [
1811
+ { path: `${basePath}.jsonc`, label: `${label} magic-context.jsonc` },
1812
+ { path: `${basePath}.json`, label: `${label} magic-context.json` }
1813
+ ];
1814
+ }
1815
+ function userScopeConfigPaths() {
1816
+ return new Set([
1817
+ `${cortexKitUserConfigBasePath()}.jsonc`,
1818
+ `${cortexKitUserConfigBasePath()}.json`,
1819
+ join(configHome(), "opencode", `${CONFIG_FILE_BASENAME}.jsonc`),
1820
+ join(configHome(), "opencode", `${CONFIG_FILE_BASENAME}.json`),
1821
+ join(homeDir(), ".pi", "agent", `${CONFIG_FILE_BASENAME}.jsonc`),
1822
+ join(homeDir(), ".pi", "agent", `${CONFIG_FILE_BASENAME}.json`)
1823
+ ]);
1824
+ }
1825
+ function resolveLegacyConfigSources(directory) {
1826
+ const userPaths = userScopeConfigPaths();
1827
+ return {
1828
+ user: [
1829
+ ...legacySourcesForBase(join(configHome(), "opencode", CONFIG_FILE_BASENAME), "OpenCode user"),
1830
+ ...legacySourcesForBase(join(homeDir(), ".pi", "agent", CONFIG_FILE_BASENAME), "Pi user")
1831
+ ],
1832
+ project: [
1833
+ ...legacySourcesForBase(join(directory, CONFIG_FILE_BASENAME), "project root"),
1834
+ ...legacySourcesForBase(join(directory, ".opencode", CONFIG_FILE_BASENAME), "OpenCode project"),
1835
+ ...legacySourcesForBase(join(directory, ".pi", CONFIG_FILE_BASENAME), "Pi project")
1836
+ ].filter((source) => !userPaths.has(source.path))
1837
+ };
1838
+ }
1839
+ function resolveLegacyConfigSourcesForHarness(directory, harness) {
1840
+ if (harness === "pi") {
1841
+ return {
1842
+ user: legacySourcesForBase(join(homeDir(), ".pi", "agent", CONFIG_FILE_BASENAME), "Pi user"),
1843
+ project: legacySourcesForBase(join(directory, ".pi", CONFIG_FILE_BASENAME), "Pi project")
1844
+ };
1845
+ }
1846
+ return {
1847
+ user: legacySourcesForBase(join(configHome(), "opencode", CONFIG_FILE_BASENAME), "OpenCode user"),
1848
+ project: [
1849
+ ...legacySourcesForBase(join(directory, CONFIG_FILE_BASENAME), "project root"),
1850
+ ...legacySourcesForBase(join(directory, ".opencode", CONFIG_FILE_BASENAME), "OpenCode project")
1851
+ ]
1852
+ };
1853
+ }
1854
+
1855
+ // src/config/migrate-dreamer-v2.ts
1856
+ var OLD_VERIFY_TASK = "verify";
1857
+ var OLD_CURATE_TASKS = ["consolidate", "archive-stale", "improve"];
1858
+ var RETIRED_OBJECT_MEMORY_TASKS = ["maintain-memory", ...OLD_CURATE_TASKS];
1859
+ var CANONICAL = [
1860
+ "map-memories",
1861
+ "verify",
1862
+ "verify-broad",
1863
+ "curate",
1864
+ "classify-memories",
1865
+ "retrospective",
1866
+ "maintain-docs",
1867
+ "evaluate-smart-notes",
1868
+ "review-user-memories",
1869
+ "promote-primers",
1870
+ "refresh-primers"
1871
+ ];
1872
+ var DEFAULT_BASE_CRON = "0 2 * * *";
1873
+ var DEFAULT_CLASSIFY_CRON = "0 6 * * *";
1874
+ var DEFAULT_RETROSPECTIVE_CRON = "0 5 * * *";
1875
+ var DEFAULT_VERIFY_BROAD_CRON = "0 4 * * 0";
1876
+ function windowToCron(schedule) {
1877
+ if (typeof schedule !== "string")
1878
+ return DEFAULT_BASE_CRON;
1879
+ const m = /^(\d{1,2}):(\d{2})\s*-/.exec(schedule.trim());
1880
+ if (!m)
1881
+ return DEFAULT_BASE_CRON;
1882
+ const hour = Number(m[1]);
1883
+ const minute = Number(m[2]);
1884
+ if (hour >= 24 || minute >= 60)
1885
+ return DEFAULT_BASE_CRON;
1886
+ return `${minute} ${hour} * * *`;
2101
1887
  }
2102
- function modify(text, path, value, options) {
2103
- return setProperty(text, path, value, options);
1888
+ function asObject(value) {
1889
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
2104
1890
  }
2105
- function applyEdits(text, edits) {
2106
- let sortedEdits = edits.slice(0).sort((a, b) => {
2107
- const diff = a.offset - b.offset;
2108
- if (diff === 0) {
2109
- return a.length - b.length;
2110
- }
2111
- return diff;
2112
- });
2113
- let lastModifiedOffset = text.length;
2114
- for (let i = sortedEdits.length - 1;i >= 0; i--) {
2115
- let e = sortedEdits[i];
2116
- if (e.offset + e.length <= lastModifiedOffset) {
2117
- text = applyEdit(text, e);
2118
- } else {
2119
- throw new Error("Overlapping edit");
2120
- }
2121
- lastModifiedOffset = e.offset;
1891
+ function cronIntervalScore(schedule) {
1892
+ const parts = schedule.trim().split(/\s+/);
1893
+ if (parts.length !== 5)
1894
+ return Number.POSITIVE_INFINITY;
1895
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
1896
+ if (month !== "*")
1897
+ return 366 * 24 * 60;
1898
+ if (dayOfMonth !== "*")
1899
+ return 31 * 24 * 60;
1900
+ if (dayOfWeek !== "*")
1901
+ return 7 * 24 * 60;
1902
+ const everyHour = /^\*\/(\d+)$/.exec(hour ?? "");
1903
+ if (everyHour)
1904
+ return Math.max(1, Number(everyHour[1])) * 60;
1905
+ if (hour === "*") {
1906
+ const everyMinute = /^\*\/(\d+)$/.exec(minute ?? "");
1907
+ return everyMinute ? Math.max(1, Number(everyMinute[1])) : 60;
2122
1908
  }
2123
- return text;
1909
+ return 24 * 60;
2124
1910
  }
2125
-
2126
- // src/shared/jsonc-parser.ts
2127
- function stripJsonComments(content) {
2128
- let result = "";
2129
- let inString = false;
2130
- let escaped = false;
2131
- let inLineComment = false;
2132
- let inBlockComment = false;
2133
- for (let index = 0;index < content.length; index += 1) {
2134
- const char = content[index];
2135
- const next = content[index + 1];
2136
- if (inLineComment) {
2137
- if (char === `
2138
- `) {
2139
- inLineComment = false;
2140
- result += char;
2141
- }
2142
- continue;
2143
- }
2144
- if (inBlockComment) {
2145
- if (char === "*" && next === "/") {
2146
- inBlockComment = false;
2147
- index += 1;
2148
- }
2149
- continue;
2150
- }
2151
- if (inString) {
2152
- result += char;
2153
- if (escaped) {
2154
- escaped = false;
2155
- } else if (char === "\\") {
2156
- escaped = true;
2157
- } else if (char === '"') {
2158
- inString = false;
2159
- }
1911
+ function mostFrequentSchedule(schedules) {
1912
+ const enabled = schedules.map((s) => s.trim()).filter(Boolean);
1913
+ if (enabled.length === 0)
1914
+ return "";
1915
+ return enabled.sort((a, b) => cronIntervalScore(a) - cronIntervalScore(b))[0] ?? "";
1916
+ }
1917
+ function withoutBroadInterval(entry) {
1918
+ const { broad_interval_days: _broad, ...rest } = entry;
1919
+ return rest;
1920
+ }
1921
+ function reconcileV2TasksObject(rawConfig, dreamer, tasksObject) {
1922
+ const hasVerifyBroad = "verify-broad" in tasksObject;
1923
+ const hasBroadIntervalAnywhere = Object.values(tasksObject).some((v) => asObject(v) && ("broad_interval_days" in v));
1924
+ const hasStaleKeyFiles = "key-files" in tasksObject;
1925
+ if (hasVerifyBroad && !hasBroadIntervalAnywhere && !hasStaleKeyFiles)
1926
+ return rawConfig;
1927
+ const nextTasks = {};
1928
+ for (const [key, value] of Object.entries(tasksObject)) {
1929
+ if (key === "key-files")
2160
1930
  continue;
1931
+ const obj = asObject(value);
1932
+ nextTasks[key] = obj ? withoutBroadInterval(obj) : value;
1933
+ }
1934
+ if (!hasVerifyBroad) {
1935
+ const verify = asObject(tasksObject.verify);
1936
+ const verifyEnabled = typeof verify?.schedule === "string" && verify.schedule.trim() !== "";
1937
+ nextTasks["verify-broad"] = {
1938
+ schedule: verifyEnabled ? DEFAULT_VERIFY_BROAD_CRON : ""
1939
+ };
1940
+ }
1941
+ return { ...rawConfig, dreamer: { ...dreamer, tasks: nextTasks } };
1942
+ }
1943
+ function migrateDreamerV2(rawConfig, warnings) {
1944
+ const dreamer = asObject(rawConfig.dreamer);
1945
+ if (!dreamer)
1946
+ return rawConfig;
1947
+ const tasksObject = asObject(dreamer.tasks);
1948
+ const hasRetiredObjectTasks = tasksObject ? RETIRED_OBJECT_MEMORY_TASKS.some((task) => (task in tasksObject)) : false;
1949
+ if (tasksObject && !hasRetiredObjectTasks) {
1950
+ const hasLegacyOutsideTasks = "schedule" in dreamer || "user_memories" in dreamer || "pin_key_files" in dreamer || "task_timeout_minutes" in dreamer || "max_runtime_minutes" in dreamer;
1951
+ if (!hasLegacyOutsideTasks) {
1952
+ return reconcileV2TasksObject(rawConfig, dreamer, tasksObject);
2161
1953
  }
2162
- if (char === '"') {
2163
- inString = true;
2164
- result += char;
2165
- continue;
1954
+ }
1955
+ const hasLegacy = "schedule" in dreamer || Array.isArray(dreamer.tasks) || hasRetiredObjectTasks || "user_memories" in dreamer || "pin_key_files" in dreamer || "task_timeout_minutes" in dreamer || "max_runtime_minutes" in dreamer;
1956
+ if (!hasLegacy)
1957
+ return rawConfig;
1958
+ const baseCron = windowToCron(dreamer.schedule);
1959
+ const timeout = typeof dreamer.task_timeout_minutes === "number" ? dreamer.task_timeout_minutes : undefined;
1960
+ const withTimeout = (entry) => timeout !== undefined ? { ...entry, timeout_minutes: timeout } : entry;
1961
+ const classifySchedule = dreamer.disable === true ? "" : DEFAULT_CLASSIFY_CRON;
1962
+ const retrospectiveSchedule = dreamer.disable === true ? "" : DEFAULT_RETROSPECTIVE_CRON;
1963
+ const tasks = {};
1964
+ if (tasksObject) {
1965
+ for (const [key, value] of Object.entries(tasksObject)) {
1966
+ if (RETIRED_OBJECT_MEMORY_TASKS.includes(key))
1967
+ continue;
1968
+ if (asObject(value))
1969
+ tasks[key] = { ...value };
2166
1970
  }
2167
- if (char === "/" && next === "/") {
2168
- inLineComment = true;
2169
- index += 1;
2170
- continue;
1971
+ const maintainMemoryEntry = asObject(tasksObject["maintain-memory"]);
1972
+ if (maintainMemoryEntry) {
1973
+ const schedule = typeof maintainMemoryEntry.schedule === "string" ? maintainMemoryEntry.schedule : baseCron;
1974
+ tasks.verify = withTimeout({
1975
+ ...withoutBroadInterval(maintainMemoryEntry),
1976
+ ...tasks.verify ?? {},
1977
+ schedule: tasks.verify?.schedule ?? schedule
1978
+ });
1979
+ tasks.curate = withTimeout({
1980
+ ...withoutBroadInterval(maintainMemoryEntry),
1981
+ ...tasks.curate ?? {},
1982
+ schedule: tasks.curate?.schedule ?? schedule
1983
+ });
2171
1984
  }
2172
- if (char === "/" && next === "*") {
2173
- inBlockComment = true;
2174
- index += 1;
2175
- continue;
1985
+ const oldVerifyEntry = asObject(tasksObject[OLD_VERIFY_TASK]);
1986
+ if (oldVerifyEntry) {
1987
+ tasks.verify = withTimeout({
1988
+ ...withoutBroadInterval(oldVerifyEntry),
1989
+ ...tasks.verify ?? {},
1990
+ schedule: tasks.verify?.schedule ?? (typeof oldVerifyEntry.schedule === "string" ? oldVerifyEntry.schedule : baseCron)
1991
+ });
2176
1992
  }
2177
- result += char;
2178
- }
2179
- return result;
2180
- }
2181
- function stripTrailingCommas(content) {
2182
- let result = "";
2183
- let inString = false;
2184
- let escaped = false;
2185
- for (let index = 0;index < content.length; index += 1) {
2186
- const char = content[index];
2187
- if (inString) {
2188
- result += char;
2189
- if (escaped) {
2190
- escaped = false;
2191
- } else if (char === "\\") {
2192
- escaped = true;
2193
- } else if (char === '"') {
2194
- inString = false;
2195
- }
2196
- continue;
1993
+ if (!tasks["verify-broad"]) {
1994
+ const verifyEnabled = typeof tasks.verify?.schedule === "string" && tasks.verify.schedule.trim() !== "";
1995
+ tasks["verify-broad"] = withTimeout({
1996
+ schedule: verifyEnabled ? DEFAULT_VERIFY_BROAD_CRON : ""
1997
+ });
2197
1998
  }
2198
- if (char === '"') {
2199
- inString = true;
2200
- result += char;
2201
- continue;
1999
+ const oldCurateEntries = OLD_CURATE_TASKS.map((task) => asObject(tasksObject[task])).filter((entry) => Boolean(entry));
2000
+ if (oldCurateEntries.length > 0) {
2001
+ const oldSchedules = oldCurateEntries.map((entry) => typeof entry.schedule === "string" ? entry.schedule : baseCron);
2002
+ tasks.curate = withTimeout({
2003
+ ...tasks.curate ?? {},
2004
+ schedule: mostFrequentSchedule(oldSchedules)
2005
+ });
2202
2006
  }
2203
- if (char === ",") {
2204
- let lookahead = index + 1;
2205
- while (lookahead < content.length && /\s/.test(content[lookahead] ?? "")) {
2206
- lookahead += 1;
2207
- }
2208
- const next = content[lookahead];
2209
- if (next === "}" || next === "]") {
2210
- continue;
2007
+ for (const task of CANONICAL) {
2008
+ if (!tasks[task]) {
2009
+ const schedule = task === "verify" || task === "curate" || task === "verify-broad" ? "" : task === "classify-memories" ? classifySchedule : task === "retrospective" ? retrospectiveSchedule : task === "maintain-docs" ? "" : baseCron;
2010
+ tasks[task] = withTimeout({ schedule });
2211
2011
  }
2212
2012
  }
2213
- result += char;
2214
- }
2215
- return result;
2216
- }
2217
- var PROTOTYPE_POLLUTION_KEYS = new Set(["__proto__", "constructor", "prototype"]);
2218
- function isPrototypePollutionKey(key) {
2219
- return PROTOTYPE_POLLUTION_KEYS.has(key);
2220
- }
2221
- function sanitizeParsedJson(value, options = {}, path = []) {
2222
- if (Array.isArray(value)) {
2223
- return value.map((entry, index) => sanitizeParsedJson(entry, options, [...path, index]));
2224
- }
2225
- if (value === null || typeof value !== "object")
2226
- return value;
2227
- const source = value;
2228
- const sourcePrototype = Object.getPrototypeOf(source);
2229
- if (sourcePrototype !== null && sourcePrototype !== Object.prototype) {
2230
- options.onRejectedKey?.([...path, "__proto__"]);
2231
- }
2232
- const sanitized = {};
2233
- for (const key of Object.keys(source)) {
2234
- if (isPrototypePollutionKey(key)) {
2235
- options.onRejectedKey?.([...path, key]);
2236
- continue;
2237
- }
2238
- Object.defineProperty(sanitized, key, {
2239
- value: sanitizeParsedJson(source[key], options, [...path, key]),
2240
- enumerable: true,
2241
- configurable: true,
2242
- writable: true
2013
+ } else {
2014
+ const legacyArray = Array.isArray(dreamer.tasks) ? dreamer.tasks.filter((t) => typeof t === "string") : null;
2015
+ const verifySelected = legacyArray ? legacyArray.includes(OLD_VERIFY_TASK) : true;
2016
+ const curateSelected = legacyArray ? legacyArray.some((task) => OLD_CURATE_TASKS.includes(task)) : true;
2017
+ tasks.verify = withTimeout({
2018
+ schedule: verifySelected ? baseCron : ""
2019
+ });
2020
+ tasks["verify-broad"] = withTimeout({
2021
+ schedule: verifySelected ? DEFAULT_VERIFY_BROAD_CRON : ""
2022
+ });
2023
+ tasks.curate = withTimeout({
2024
+ schedule: curateSelected ? baseCron : ""
2025
+ });
2026
+ tasks["classify-memories"] = withTimeout({
2027
+ schedule: classifySchedule
2243
2028
  });
2244
- }
2245
- return sanitized;
2246
- }
2247
- function lineAndColumnAt(content, offset) {
2248
- const before = content.slice(0, Math.max(0, offset));
2249
- const lines = before.split(/\r?\n/);
2250
- return { line: lines.length, column: (lines.at(-1)?.length ?? 0) + 1 };
2251
- }
2252
- function parseIssue(content, error) {
2253
- const location = lineAndColumnAt(content, error.offset);
2254
- const code = printParseErrorCode(error.error);
2255
- const message = code.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
2256
- return {
2257
- ...location,
2258
- offset: error.offset,
2259
- length: error.length,
2260
- message
2261
- };
2262
- }
2263
- function normalizeJsoncParserObjects(value, options, path = []) {
2264
- if (Array.isArray(value)) {
2265
- return value.map((entry, index) => normalizeJsoncParserObjects(entry, options, [...path, String(index)]));
2266
- }
2267
- if (value === null || typeof value !== "object")
2268
- return value;
2269
- const prototype = Object.getPrototypeOf(value);
2270
- if (prototype !== Object.prototype && prototype !== null) {
2271
- options.onRejectedKey?.([...path, "__proto__"]);
2272
- }
2273
- const normalized = {};
2274
- for (const [key, entry] of Object.entries(value)) {
2275
- Object.defineProperty(normalized, key, {
2276
- configurable: true,
2277
- enumerable: true,
2278
- writable: true,
2279
- value: normalizeJsoncParserObjects(entry, options, [...path, key])
2029
+ tasks.retrospective = withTimeout({
2030
+ schedule: retrospectiveSchedule
2031
+ });
2032
+ tasks["maintain-docs"] = withTimeout({
2033
+ schedule: legacyArray?.includes("maintain-docs") ? baseCron : ""
2280
2034
  });
2281
2035
  }
2282
- return normalized;
2283
- }
2284
- function parseJsoncRecovering(content, options = {}) {
2285
- const errors = [];
2286
- const parsed = parse2(content, errors, {
2287
- allowTrailingComma: true,
2288
- disallowComments: false,
2289
- allowEmptyContent: false
2290
- });
2291
- return {
2292
- value: sanitizeParsedJson(normalizeJsoncParserObjects(parsed, options), options),
2293
- issues: errors.map((error) => parseIssue(content, error))
2294
- };
2295
- }
2296
- function parseJsonc(content, options = {}) {
2297
- const normalized = stripTrailingCommas(stripJsonComments(content));
2298
- return sanitizeParsedJson(JSON.parse(normalized), options);
2299
- }
2300
- function readJsoncFile(filePath) {
2301
- try {
2302
- return parseJsonc(readFileSync(filePath, "utf-8"));
2303
- } catch (_error) {
2304
- return null;
2305
- }
2306
- }
2307
- function detectConfigFile(basePath) {
2308
- const jsoncPath = `${basePath}.jsonc`;
2309
- const jsonPath = `${basePath}.json`;
2310
- if (existsSync(jsoncPath)) {
2311
- return { format: "jsonc", path: jsoncPath };
2312
- }
2313
- if (existsSync(jsonPath)) {
2314
- return { format: "json", path: jsonPath };
2036
+ tasks["map-memories"] ??= withTimeout({ schedule: baseCron });
2037
+ tasks["evaluate-smart-notes"] ??= withTimeout({ schedule: baseCron });
2038
+ const um = asObject(dreamer.user_memories);
2039
+ const umEnabled = um ? um.enabled !== false : true;
2040
+ if (um || !tasks["review-user-memories"]) {
2041
+ tasks["review-user-memories"] = withTimeout({
2042
+ ...tasks["review-user-memories"] ?? {},
2043
+ schedule: umEnabled ? baseCron : "",
2044
+ ...um && typeof um.promotion_threshold === "number" ? { promotion_threshold: um.promotion_threshold } : {}
2045
+ });
2315
2046
  }
2316
- return { format: "none", path: jsoncPath };
2047
+ const {
2048
+ schedule: _schedule,
2049
+ tasks: _tasks,
2050
+ task_timeout_minutes: _tto,
2051
+ max_runtime_minutes: _max,
2052
+ user_memories: _um,
2053
+ pin_key_files: _pkf,
2054
+ ...rest
2055
+ } = dreamer;
2056
+ warnings.push('Migrated legacy dreamer scheduling (schedule window / tasks array / user_memories / pin_key_files) → per-task "dreamer.tasks" in-memory (run `doctor` to persist).');
2057
+ return { ...rawConfig, dreamer: { ...rest, tasks } };
2317
2058
  }
2318
2059
 
2319
- // src/config/migrate-config-location.ts
2320
- import { homedir } from "node:os";
2321
- import { basename, dirname, isAbsolute, join } from "node:path";
2322
-
2323
- // src/shared/jsonc-edit.ts
2324
- var TOKEN_COMMA = 5;
2325
- var TOKEN_EOF = 17;
2326
- function parseDocument(text) {
2327
- const errors = [];
2328
- const root = parseTree2(text, errors, { allowTrailingComma: true });
2329
- if (!root || errors.length > 0) {
2330
- throw new Error("Cannot edit invalid JSONC");
2060
+ // src/config/migrate-experimental.ts
2061
+ function migrateLegacyExperimental(rawConfig, warnings) {
2062
+ const experimental = rawConfig.experimental;
2063
+ if (typeof experimental !== "object" || experimental === null) {
2064
+ return rawConfig;
2331
2065
  }
2332
- return root;
2333
- }
2334
- function findNode(text, path) {
2335
- return findNodeAtLocation2(parseDocument(text), path);
2336
- }
2337
- function findComma(text, start, end) {
2338
- const scanner = createScanner2(text, false);
2339
- scanner.setPosition(start);
2340
- for (;; ) {
2341
- const kind = scanner.scan();
2342
- const offset = scanner.getTokenOffset();
2343
- if (kind === TOKEN_EOF || offset >= end)
2066
+ const exp = experimental;
2067
+ const hasUM = "user_memories" in exp;
2068
+ const hasPKF = "pin_key_files" in exp;
2069
+ const hasMural = "mural" in exp;
2070
+ const TOP_LEVEL_GRADUATED = ["temporal_awareness", "caveman_text_compression"];
2071
+ const MEMORY_GRADUATED = ["auto_search", "git_commit_indexing"];
2072
+ const hasGraduated = TOP_LEVEL_GRADUATED.some((k) => (k in exp)) || MEMORY_GRADUATED.some((k) => (k in exp)) || hasMural;
2073
+ if (!hasUM && !hasPKF && !hasGraduated) {
2074
+ return rawConfig;
2075
+ }
2076
+ const patched = { ...rawConfig };
2077
+ const dreamer = typeof patched.dreamer === "object" && patched.dreamer !== null ? { ...patched.dreamer } : {};
2078
+ const memory = typeof patched.memory === "object" && patched.memory !== null ? { ...patched.memory } : {};
2079
+ const newExperimental = { ...exp };
2080
+ const coerceToObject = (value) => {
2081
+ if (typeof value === "boolean") {
2082
+ return { enabled: value };
2083
+ }
2084
+ if (typeof value === "object" && value !== null) {
2085
+ return { ...value };
2086
+ }
2087
+ return;
2088
+ };
2089
+ const relocate = (key, dest, destLabel) => {
2090
+ if (!(key in exp))
2344
2091
  return;
2345
- if (kind === TOKEN_COMMA) {
2346
- return {
2347
- offset,
2348
- length: scanner.getTokenLength(),
2349
- line: scanner.getTokenStartLine()
2092
+ const oldValue = exp[key];
2093
+ const existing = dest[key];
2094
+ if (existing === undefined) {
2095
+ dest[key] = oldValue;
2096
+ warnings.push(`Migrated "experimental.${key}" → "${destLabel}${key}" in-memory (run \`doctor\` to persist).`);
2097
+ } else if (typeof oldValue === "object" && oldValue !== null && typeof existing === "object" && existing !== null) {
2098
+ dest[key] = {
2099
+ ...oldValue,
2100
+ ...existing
2350
2101
  };
2351
2102
  }
2103
+ delete newExperimental[key];
2104
+ };
2105
+ for (const key of TOP_LEVEL_GRADUATED)
2106
+ relocate(key, patched, "");
2107
+ for (const key of MEMORY_GRADUATED)
2108
+ relocate(key, memory, "memory.");
2109
+ if (hasMural) {
2110
+ const oldMural = coerceToObject(exp.mural);
2111
+ const existingMural = patched.mural;
2112
+ if (existingMural === undefined) {
2113
+ patched.mural = oldMural ?? exp.mural;
2114
+ warnings.push('Deprecated "experimental.mural"; use top-level "mural" instead (migrated in memory; run `doctor` to persist).');
2115
+ } else if (oldMural !== undefined && typeof existingMural === "object" && existingMural !== null && !Array.isArray(existingMural)) {
2116
+ patched.mural = {
2117
+ ...oldMural,
2118
+ ...existingMural
2119
+ };
2120
+ }
2121
+ delete newExperimental.mural;
2352
2122
  }
2353
- }
2354
- function setJsoncValue(text, path, value) {
2355
- const node = findNode(text, path);
2356
- if (node) {
2357
- if (Object.is(getNodeValue2(node), value))
2358
- return text;
2359
- const serialized = JSON.stringify(value);
2360
- return text.slice(0, node.offset) + serialized + text.slice(node.offset + node.length);
2361
- }
2362
- return applyEdits(text, modify(text, path, value, { formattingOptions: documentFormatting(text) }));
2363
- }
2364
- function documentFormatting(text) {
2365
- const eol = text.includes(`\r
2366
- `) ? `\r
2367
- ` : `
2368
- `;
2369
- const indent = /\n([ \t]+)\S/.exec(text)?.[1];
2370
- if (indent?.startsWith("\t"))
2371
- return { eol, insertSpaces: false, tabSize: 1 };
2372
- return { eol, insertSpaces: true, tabSize: indent ? indent.length : 2 };
2373
- }
2374
- function removeObjectProperty(text, object, key) {
2375
- const properties = object.children ?? [];
2376
- const index = properties.findIndex((property) => {
2377
- const propertyKey = property.children?.[0];
2378
- return propertyKey !== undefined && getNodeValue2(propertyKey) === key;
2379
- });
2380
- if (index === -1)
2381
- return text;
2382
- const property = properties[index];
2383
- if (!property)
2384
- return text;
2385
- const closingBrace = object.offset + object.length - 1;
2386
- const next = properties[index + 1];
2387
- if (next) {
2388
- const followingComma = findComma(text, property.offset + property.length, next.offset);
2389
- if (!followingComma)
2390
- return text;
2391
- return text.slice(0, property.offset) + text.slice(followingComma.offset + followingComma.length);
2123
+ if (hasUM) {
2124
+ const oldUM = coerceToObject(exp.user_memories);
2125
+ if (oldUM !== undefined) {
2126
+ if (dreamer.user_memories === undefined) {
2127
+ dreamer.user_memories = oldUM;
2128
+ warnings.push('Migrated "experimental.user_memories" → "dreamer.user_memories" in-memory (run `doctor` to persist).');
2129
+ } else if (typeof dreamer.user_memories === "object" && dreamer.user_memories !== null) {
2130
+ dreamer.user_memories = {
2131
+ ...oldUM,
2132
+ ...dreamer.user_memories
2133
+ };
2134
+ }
2135
+ }
2136
+ delete newExperimental.user_memories;
2392
2137
  }
2393
- const previous = properties[index - 1];
2394
- const trailingComma = findComma(text, property.offset + property.length, closingBrace);
2395
- if (!previous) {
2396
- const afterProperty = property.offset + property.length;
2397
- if (!trailingComma)
2398
- return text.slice(0, property.offset) + text.slice(afterProperty);
2399
- return text.slice(0, property.offset) + text.slice(afterProperty, trailingComma.offset) + text.slice(trailingComma.offset + trailingComma.length);
2138
+ if (hasPKF) {
2139
+ const oldPKF = coerceToObject(exp.pin_key_files);
2140
+ if (oldPKF !== undefined) {
2141
+ if (dreamer.pin_key_files === undefined) {
2142
+ dreamer.pin_key_files = oldPKF;
2143
+ warnings.push('Migrated "experimental.pin_key_files" → "dreamer.pin_key_files" in-memory (run `doctor` to persist).');
2144
+ } else if (typeof dreamer.pin_key_files === "object" && dreamer.pin_key_files !== null) {
2145
+ dreamer.pin_key_files = {
2146
+ ...oldPKF,
2147
+ ...dreamer.pin_key_files
2148
+ };
2149
+ } else if (typeof dreamer.pin_key_files === "boolean") {
2150
+ dreamer.pin_key_files = { ...oldPKF, enabled: dreamer.pin_key_files };
2151
+ }
2152
+ }
2153
+ delete newExperimental.pin_key_files;
2400
2154
  }
2401
- const precedingComma = findComma(text, previous.offset + previous.length, property.offset);
2402
- if (!precedingComma)
2403
- return text;
2404
- const afterProperty = property.offset + property.length;
2405
- const withoutProperty = text.slice(0, precedingComma.offset) + text.slice(precedingComma.offset + precedingComma.length, property.offset) + text.slice(afterProperty);
2406
- if (!trailingComma)
2407
- return withoutProperty;
2408
- const shiftedTrailingComma = trailingComma.offset - 1;
2409
- return withoutProperty.slice(0, shiftedTrailingComma) + withoutProperty.slice(shiftedTrailingComma + trailingComma.length);
2410
- }
2411
- function removeJsoncValue(text, path) {
2412
- const key = path.at(-1);
2413
- if (typeof key !== "string")
2414
- return text;
2415
- const parent = findNode(text, path.slice(0, -1));
2416
- if (parent?.type !== "object")
2417
- return text;
2418
- return removeObjectProperty(text, parent, key);
2155
+ patched.experimental = newExperimental;
2156
+ patched.dreamer = dreamer;
2157
+ if (Object.keys(memory).length > 0) {
2158
+ patched.memory = memory;
2159
+ }
2160
+ return patched;
2419
2161
  }
2420
2162
 
2421
- // src/config/migrate-config-location.ts
2422
- var CONFIG_FILE_BASENAME = "magic-context";
2423
- function homeDir() {
2424
- if (process.platform === "win32") {
2425
- return process.env.USERPROFILE || process.env.HOME || homedir();
2163
+ // src/config/mural-model-deprecation.ts
2164
+ var MURAL_MODEL_DEPRECATION_WARNING = "mural.model is deprecated and ignored; compress-cues now uses the dreamer model for each harness, like every other dreamer task. To override it, set dreamer.opencode.tasks.compress-cues.model (or the pi/omp equivalent). This key no longer does anything; remove it.";
2165
+ function warnDeprecatedMuralModel(config, warnings) {
2166
+ const mural = config.mural;
2167
+ if (typeof mural === "object" && mural !== null && Object.hasOwn(mural, "model")) {
2168
+ warnings.push(MURAL_MODEL_DEPRECATION_WARNING);
2426
2169
  }
2427
- return process.env.HOME || homedir();
2428
- }
2429
- function configHome() {
2430
- const xdg = process.env.XDG_CONFIG_HOME;
2431
- if (xdg && isAbsolute(xdg))
2432
- return xdg;
2433
- return join(homeDir(), ".config");
2434
- }
2435
- function cortexKitUserConfigBasePath() {
2436
- return join(configHome(), "cortexkit", CONFIG_FILE_BASENAME);
2437
- }
2438
- function cortexKitProjectConfigBasePath(directory) {
2439
- return join(directory, ".cortexkit", CONFIG_FILE_BASENAME);
2440
- }
2441
- function legacySourcesForBase(basePath, label) {
2442
- return [
2443
- { path: `${basePath}.jsonc`, label: `${label} magic-context.jsonc` },
2444
- { path: `${basePath}.json`, label: `${label} magic-context.json` }
2445
- ];
2446
2170
  }
2447
- function userScopeConfigPaths() {
2448
- return new Set([
2449
- `${cortexKitUserConfigBasePath()}.jsonc`,
2450
- `${cortexKitUserConfigBasePath()}.json`,
2451
- join(configHome(), "opencode", `${CONFIG_FILE_BASENAME}.jsonc`),
2452
- join(configHome(), "opencode", `${CONFIG_FILE_BASENAME}.json`),
2453
- join(homeDir(), ".pi", "agent", `${CONFIG_FILE_BASENAME}.jsonc`),
2454
- join(homeDir(), ".pi", "agent", `${CONFIG_FILE_BASENAME}.json`)
2455
- ]);
2171
+
2172
+ // src/config/profiles.ts
2173
+ function withoutProfileFields(raw) {
2174
+ const copy = { ...raw };
2175
+ delete copy.profile;
2176
+ delete copy.profiles;
2177
+ return copy;
2456
2178
  }
2457
- function resolveLegacyConfigSources(directory) {
2458
- const userPaths = userScopeConfigPaths();
2459
- return {
2460
- user: [
2461
- ...legacySourcesForBase(join(configHome(), "opencode", CONFIG_FILE_BASENAME), "OpenCode user"),
2462
- ...legacySourcesForBase(join(homeDir(), ".pi", "agent", CONFIG_FILE_BASENAME), "Pi user")
2463
- ],
2464
- project: [
2465
- ...legacySourcesForBase(join(directory, CONFIG_FILE_BASENAME), "project root"),
2466
- ...legacySourcesForBase(join(directory, ".opencode", CONFIG_FILE_BASENAME), "OpenCode project"),
2467
- ...legacySourcesForBase(join(directory, ".pi", CONFIG_FILE_BASENAME), "Pi project")
2468
- ].filter((source) => !userPaths.has(source.path))
2469
- };
2179
+ function readProfileSelection(raw) {
2180
+ if (!Object.hasOwn(raw, "profile"))
2181
+ return { declared: false };
2182
+ const value = raw.profile;
2183
+ if (typeof value !== "string")
2184
+ return { declared: true };
2185
+ const name = value.trim();
2186
+ return name.length > 0 ? { declared: true, name } : { declared: true };
2470
2187
  }
2471
- function resolveLegacyConfigSourcesForHarness(directory, harness) {
2472
- if (harness === "pi") {
2188
+ function resolveConfigProfile(args) {
2189
+ const warnings = [];
2190
+ const userSelection = readProfileSelection(args.userRaw);
2191
+ const projectSelection = readProfileSelection(args.projectRaw);
2192
+ const selection = projectSelection.name ? { name: projectSelection.name, source: "project" } : userSelection.name ? { name: userSelection.name, source: "user" } : undefined;
2193
+ if (projectSelection.declared && !projectSelection.name) {
2194
+ warnings.push("Ignoring invalid profile selection from project config; expected a non-empty string.");
2195
+ }
2196
+ if (!projectSelection.declared && userSelection.declared && !userSelection.name) {
2197
+ warnings.push("Ignoring invalid profile selection from user config; expected a non-empty string.");
2198
+ }
2199
+ let profiles = {};
2200
+ if (Object.hasOwn(args.userRaw, "profiles")) {
2201
+ const parsed = ConfigProfilesSchema.safeParse(args.userRaw.profiles);
2202
+ if (parsed.success) {
2203
+ profiles = parsed.data;
2204
+ } else {
2205
+ warnings.push("Ignoring profiles from user config: invalid profile configuration; profiles may contain only historian/dreamer harness model blocks.");
2206
+ }
2207
+ }
2208
+ if (!selection) {
2473
2209
  return {
2474
- user: legacySourcesForBase(join(homeDir(), ".pi", "agent", CONFIG_FILE_BASENAME), "Pi user"),
2475
- project: legacySourcesForBase(join(directory, ".pi", CONFIG_FILE_BASENAME), "Pi project")
2210
+ userBase: withoutProfileFields(args.userRaw),
2211
+ projectBase: withoutProfileFields(args.projectRaw),
2212
+ overlay: {},
2213
+ warnings
2214
+ };
2215
+ }
2216
+ if (!Object.hasOwn(profiles, selection.name)) {
2217
+ warnings.push(`Unknown profile "${selection.name}" selected by ${selection.source} config; using base config without a profile.`);
2218
+ return {
2219
+ userBase: withoutProfileFields(args.userRaw),
2220
+ projectBase: withoutProfileFields(args.projectRaw),
2221
+ overlay: {},
2222
+ warnings
2476
2223
  };
2477
2224
  }
2225
+ const overlay = profiles[selection.name];
2478
2226
  return {
2479
- user: legacySourcesForBase(join(configHome(), "opencode", CONFIG_FILE_BASENAME), "OpenCode user"),
2480
- project: [
2481
- ...legacySourcesForBase(join(directory, CONFIG_FILE_BASENAME), "project root"),
2482
- ...legacySourcesForBase(join(directory, ".opencode", CONFIG_FILE_BASENAME), "OpenCode project")
2483
- ]
2227
+ userBase: withoutProfileFields(args.userRaw),
2228
+ projectBase: withoutProfileFields(args.projectRaw),
2229
+ overlay,
2230
+ activeProfile: selection.name,
2231
+ warnings
2484
2232
  };
2485
2233
  }
2486
2234
 
2487
- // src/config/migrate-dreamer-v2.ts
2488
- var OLD_VERIFY_TASK = "verify";
2489
- var OLD_CURATE_TASKS = ["consolidate", "archive-stale", "improve"];
2490
- var RETIRED_OBJECT_MEMORY_TASKS = ["maintain-memory", ...OLD_CURATE_TASKS];
2491
- var CANONICAL = [
2492
- "map-memories",
2493
- "verify",
2494
- "verify-broad",
2495
- "curate",
2496
- "classify-memories",
2497
- "retrospective",
2498
- "maintain-docs",
2499
- "evaluate-smart-notes",
2500
- "review-user-memories",
2501
- "promote-primers",
2502
- "refresh-primers"
2235
+ // src/config/project-security.ts
2236
+ var HIDDEN_AGENT_KEYS = ["historian", "dreamer"];
2237
+ var HARNESS_KEYS = PER_HARNESS_MODEL_KEYS;
2238
+ var HISTORIAN_USER_ONLY_FIELDS = [
2239
+ ...PER_HARNESS_MIGRATION_INVENTORY.historian.migrated_execution,
2240
+ "runner",
2241
+ "host_runner"
2503
2242
  ];
2504
- var DEFAULT_BASE_CRON = "0 2 * * *";
2505
- var DEFAULT_CLASSIFY_CRON = "0 6 * * *";
2506
- var DEFAULT_RETROSPECTIVE_CRON = "0 5 * * *";
2507
- var DEFAULT_VERIFY_BROAD_CRON = "0 4 * * 0";
2508
- function windowToCron(schedule) {
2509
- if (typeof schedule !== "string")
2510
- return DEFAULT_BASE_CRON;
2511
- const m = /^(\d{1,2}):(\d{2})\s*-/.exec(schedule.trim());
2512
- if (!m)
2513
- return DEFAULT_BASE_CRON;
2514
- const hour = Number(m[1]);
2515
- const minute = Number(m[2]);
2516
- if (hour >= 24 || minute >= 60)
2517
- return DEFAULT_BASE_CRON;
2518
- return `${minute} ${hour} * * *`;
2243
+ var PROMPT_SURFACE_USER_ONLY_FIELDS = ["guidance_override_path", "tool_descriptions"];
2244
+ var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools"];
2245
+ var EMBEDDING_USER_ONLY_FIELDS = [
2246
+ "endpoint",
2247
+ "provider",
2248
+ "fallback_provider",
2249
+ "query_instruction",
2250
+ "document_prefix"
2251
+ ];
2252
+ var PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.";
2253
+ var TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.";
2254
+ var TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
2255
+ var PROTECTED_TOKENS_REASON = "security: a repository may only raise protected_tokens above the resolved user-or-derived floor; it cannot lower protection.";
2256
+ function isPlainObject(value) {
2257
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2519
2258
  }
2520
- function asObject(value) {
2521
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
2259
+ function resolveProtectedTokensScalar(value) {
2260
+ if (typeof value === "number" && Number.isInteger(value) && value >= 4000 && value <= 1e6) {
2261
+ return value;
2262
+ }
2263
+ return;
2522
2264
  }
2523
- function cronIntervalScore(schedule) {
2524
- const parts = schedule.trim().split(/\s+/);
2525
- if (parts.length !== 5)
2526
- return Number.POSITIVE_INFINITY;
2527
- const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
2528
- if (month !== "*")
2529
- return 366 * 24 * 60;
2530
- if (dayOfMonth !== "*")
2531
- return 31 * 24 * 60;
2532
- if (dayOfWeek !== "*")
2533
- return 7 * 24 * 60;
2534
- const everyHour = /^\*\/(\d+)$/.exec(hour ?? "");
2535
- if (everyHour)
2536
- return Math.max(1, Number(everyHour[1])) * 60;
2537
- if (hour === "*") {
2538
- const everyMinute = /^\*\/(\d+)$/.exec(minute ?? "");
2539
- return everyMinute ? Math.max(1, Number(everyMinute[1])) : 60;
2265
+ var PROTECTED_TOKENS_TIER_OVERRIDES = Symbol.for("@cortexkit/magic-context/protected-tokens-tier-overrides");
2266
+ function attachProtectedTokensTierOverrides(config, args) {
2267
+ const user = resolveProtectedTokensScalar(args.trustedUser);
2268
+ const rawProject = resolveProtectedTokensScalar(args.project);
2269
+ const project = rawProject !== undefined && (user === undefined || rawProject >= user) ? rawProject : undefined;
2270
+ if (user === undefined && project === undefined)
2271
+ return config;
2272
+ Object.defineProperty(config, PROTECTED_TOKENS_TIER_OVERRIDES, {
2273
+ value: {
2274
+ ...user !== undefined ? { user } : {},
2275
+ ...project !== undefined ? { project } : {}
2276
+ },
2277
+ configurable: false,
2278
+ enumerable: false,
2279
+ writable: false
2280
+ });
2281
+ return config;
2282
+ }
2283
+ function getProtectedTokensTierOverrides(config) {
2284
+ return config[PROTECTED_TOKENS_TIER_OVERRIDES];
2285
+ }
2286
+ function stripListedFields(target, fields, path, removed) {
2287
+ for (const field of fields) {
2288
+ if (field in target) {
2289
+ delete target[field];
2290
+ removed.push(path.length > 0 ? `${path}.${field}` : field);
2291
+ }
2292
+ }
2293
+ }
2294
+ function stripEscalationAtExecutableSite(block, path, removed) {
2295
+ stripListedFields(block, AGENT_ESCALATION_FIELDS, path, removed);
2296
+ if (isPlainObject(block.model)) {
2297
+ stripListedFields(block.model, AGENT_ESCALATION_FIELDS, `${path}.model`, removed);
2298
+ }
2299
+ if (Array.isArray(block.fallback_models)) {
2300
+ for (let index = 0;index < block.fallback_models.length; index++) {
2301
+ const entry = block.fallback_models[index];
2302
+ if (isPlainObject(entry)) {
2303
+ stripListedFields(entry, AGENT_ESCALATION_FIELDS, `${path}.fallback_models.${index}`, removed);
2304
+ }
2305
+ }
2540
2306
  }
2541
- return 24 * 60;
2542
2307
  }
2543
- function mostFrequentSchedule(schedules) {
2544
- const enabled = schedules.map((s) => s.trim()).filter(Boolean);
2545
- if (enabled.length === 0)
2546
- return "";
2547
- return enabled.sort((a, b) => cronIntervalScore(a) - cronIntervalScore(b))[0] ?? "";
2308
+ function isValidPercentageThreshold(value) {
2309
+ return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 90;
2548
2310
  }
2549
- function withoutBroadInterval(entry) {
2550
- const { broad_interval_days: _broad, ...rest } = entry;
2551
- return rest;
2311
+ function isValidTokenThreshold(value) {
2312
+ return typeof value === "number" && Number.isFinite(value) && value >= 5000 && value <= 2000000;
2552
2313
  }
2553
- function reconcileV2TasksObject(rawConfig, dreamer, tasksObject) {
2554
- const hasVerifyBroad = "verify-broad" in tasksObject;
2555
- const hasBroadIntervalAnywhere = Object.values(tasksObject).some((v) => asObject(v) && ("broad_interval_days" in v));
2556
- const hasStaleKeyFiles = "key-files" in tasksObject;
2557
- if (hasVerifyBroad && !hasBroadIntervalAnywhere && !hasStaleKeyFiles)
2558
- return rawConfig;
2559
- const nextTasks = {};
2560
- for (const [key, value] of Object.entries(tasksObject)) {
2561
- if (key === "key-files")
2314
+ function normalizeTrustedPercentageThresholds(value) {
2315
+ if (typeof value === "number" && Number.isFinite(value)) {
2316
+ return { defaultValue: value, overrides: new Map };
2317
+ }
2318
+ if (isPlainObject(value) && typeof value.default === "number" && Number.isFinite(value.default)) {
2319
+ const overrides = new Map;
2320
+ for (const [key, child] of Object.entries(value)) {
2321
+ if (key === "default")
2322
+ continue;
2323
+ if (typeof child === "number" && Number.isFinite(child)) {
2324
+ overrides.set(key, child);
2325
+ }
2326
+ }
2327
+ return { defaultValue: value.default, overrides };
2328
+ }
2329
+ return { defaultValue: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, overrides: new Map };
2330
+ }
2331
+ function normalizeTrustedTokenThresholds(value) {
2332
+ if (!isPlainObject(value)) {
2333
+ return { defaultValue: undefined, overrides: new Map };
2334
+ }
2335
+ const overrides = new Map;
2336
+ for (const [key, child] of Object.entries(value)) {
2337
+ if (key === "default")
2562
2338
  continue;
2563
- const obj = asObject(value);
2564
- nextTasks[key] = obj ? withoutBroadInterval(obj) : value;
2339
+ if (typeof child === "number" && Number.isFinite(child)) {
2340
+ overrides.set(key, child);
2341
+ }
2565
2342
  }
2566
- if (!hasVerifyBroad) {
2567
- const verify = asObject(tasksObject.verify);
2568
- const verifyEnabled = typeof verify?.schedule === "string" && verify.schedule.trim() !== "";
2569
- nextTasks["verify-broad"] = {
2570
- schedule: verifyEnabled ? DEFAULT_VERIFY_BROAD_CRON : ""
2571
- };
2343
+ return {
2344
+ defaultValue: typeof value.default === "number" && Number.isFinite(value.default) ? value.default : undefined,
2345
+ overrides
2346
+ };
2347
+ }
2348
+ function clonePercentageThresholds(value) {
2349
+ return {
2350
+ defaultValue: value.defaultValue,
2351
+ overrides: new Map(value.overrides)
2352
+ };
2353
+ }
2354
+ function cloneTokenThresholds(value) {
2355
+ return {
2356
+ defaultValue: value.defaultValue,
2357
+ overrides: new Map(value.overrides)
2358
+ };
2359
+ }
2360
+ function percentageThresholdsEqual(left, right) {
2361
+ if (left.defaultValue !== right.defaultValue)
2362
+ return false;
2363
+ if (left.overrides.size !== right.overrides.size)
2364
+ return false;
2365
+ for (const [key, value] of left.overrides) {
2366
+ if (right.overrides.get(key) !== value)
2367
+ return false;
2572
2368
  }
2573
- return { ...rawConfig, dreamer: { ...dreamer, tasks: nextTasks } };
2369
+ return true;
2574
2370
  }
2575
- function migrateDreamerV2(rawConfig, warnings) {
2576
- const dreamer = asObject(rawConfig.dreamer);
2577
- if (!dreamer)
2578
- return rawConfig;
2579
- const tasksObject = asObject(dreamer.tasks);
2580
- const hasRetiredObjectTasks = tasksObject ? RETIRED_OBJECT_MEMORY_TASKS.some((task) => (task in tasksObject)) : false;
2581
- if (tasksObject && !hasRetiredObjectTasks) {
2582
- const hasLegacyOutsideTasks = "schedule" in dreamer || "user_memories" in dreamer || "pin_key_files" in dreamer || "task_timeout_minutes" in dreamer || "max_runtime_minutes" in dreamer;
2583
- if (!hasLegacyOutsideTasks) {
2584
- return reconcileV2TasksObject(rawConfig, dreamer, tasksObject);
2371
+ function setMergedPercentageThreshold(mergedRaw, value) {
2372
+ if (value.overrides.size === 0) {
2373
+ mergedRaw.execute_threshold_percentage = value.defaultValue;
2374
+ return;
2375
+ }
2376
+ const serialized = { default: value.defaultValue };
2377
+ for (const [key, threshold] of value.overrides) {
2378
+ serialized[key] = threshold;
2379
+ }
2380
+ mergedRaw.execute_threshold_percentage = serialized;
2381
+ }
2382
+ function setMergedTokenThreshold(mergedRaw, value) {
2383
+ if (value.defaultValue === undefined && value.overrides.size === 0) {
2384
+ delete mergedRaw.execute_threshold_tokens;
2385
+ return;
2386
+ }
2387
+ const serialized = {};
2388
+ if (value.defaultValue !== undefined) {
2389
+ serialized.default = value.defaultValue;
2390
+ }
2391
+ for (const [key, threshold] of value.overrides) {
2392
+ serialized[key] = threshold;
2393
+ }
2394
+ mergedRaw.execute_threshold_tokens = serialized;
2395
+ }
2396
+ function makeProjectThresholdWarning(field, reason) {
2397
+ return `Ignoring ${field} from project config (${reason})`;
2398
+ }
2399
+ function stripUnsafeProjectConfigFields(projectRaw) {
2400
+ const warnings = [];
2401
+ if ("profiles" in projectRaw) {
2402
+ delete projectRaw.profiles;
2403
+ warnings.push("Ignoring profiles from project config (security: profile definitions are user-level only; a repository may select a named user profile with profile).");
2404
+ }
2405
+ if ("auto_update" in projectRaw) {
2406
+ delete projectRaw.auto_update;
2407
+ warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
2408
+ }
2409
+ if ("fail_closed_blocking" in projectRaw) {
2410
+ delete projectRaw.fail_closed_blocking;
2411
+ warnings.push("Ignoring fail_closed_blocking from project config (security: only user-level config may disable or force the loud inoperability gate).");
2412
+ }
2413
+ if ("debug_rpc" in projectRaw) {
2414
+ delete projectRaw.debug_rpc;
2415
+ warnings.push("Ignoring debug_rpc from project config (security: only user-level config may enable process heap diagnostics).");
2416
+ }
2417
+ if ("allow_home_project" in projectRaw) {
2418
+ delete projectRaw.allow_home_project;
2419
+ warnings.push("Ignoring allow_home_project from project config (security: only user-level config may opt the user's home directory into Magic Context).");
2420
+ }
2421
+ const compaction = projectRaw.compaction;
2422
+ if (isPlainObject(compaction) && "enabled" in compaction) {
2423
+ delete compaction.enabled;
2424
+ warnings.push("Ignoring compaction.enabled from project config (security: only user-level config may disable Magic Context's context-window management; a cloned repo cannot change how the user's window is owned).");
2425
+ }
2426
+ if ("output_reserve" in projectRaw) {
2427
+ delete projectRaw.output_reserve;
2428
+ warnings.push("Ignoring output_reserve from project config (security: output-token reservation only honors user-level config).");
2429
+ }
2430
+ const models = projectRaw.models;
2431
+ if (isPlainObject(models) && "window_overlay_path" in models) {
2432
+ delete models.window_overlay_path;
2433
+ warnings.push("Ignoring models.window_overlay_path from project config (security: only user-level config may select model geometry metadata).");
2434
+ }
2435
+ if ("language" in projectRaw) {
2436
+ delete projectRaw.language;
2437
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
2438
+ }
2439
+ if ("sqlite" in projectRaw) {
2440
+ delete projectRaw.sqlite;
2441
+ warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
2442
+ }
2443
+ const storage = projectRaw.storage;
2444
+ if (isPlainObject(storage) && "enforce_private_permissions" in storage) {
2445
+ delete storage.enforce_private_permissions;
2446
+ warnings.push("Ignoring storage.enforce_private_permissions from project config (security: only user-level config may opt into externally managed shared storage permissions).");
2447
+ }
2448
+ const smartNotes = projectRaw.smart_notes;
2449
+ if (isPlainObject(smartNotes) && "enabled" in smartNotes) {
2450
+ delete smartNotes.enabled;
2451
+ warnings.push("Ignoring smart_notes.enabled from project config (only user-level config may change process-wide tool definitions; restart required).");
2452
+ }
2453
+ const promptSurface = projectRaw.prompt_surface;
2454
+ if (isPlainObject(promptSurface)) {
2455
+ const removed = [];
2456
+ for (const field of PROMPT_SURFACE_USER_ONLY_FIELDS) {
2457
+ if (field in promptSurface) {
2458
+ delete promptSurface[field];
2459
+ removed.push(field);
2460
+ }
2461
+ }
2462
+ if (removed.length > 0) {
2463
+ warnings.push(`Ignoring prompt_surface.${removed.join("/")} from project config (security: repositories may select prompt presets but only user config may provide guidance or tool-description text).`);
2585
2464
  }
2586
2465
  }
2587
- const hasLegacy = "schedule" in dreamer || Array.isArray(dreamer.tasks) || hasRetiredObjectTasks || "user_memories" in dreamer || "pin_key_files" in dreamer || "task_timeout_minutes" in dreamer || "max_runtime_minutes" in dreamer;
2588
- if (!hasLegacy)
2589
- return rawConfig;
2590
- const baseCron = windowToCron(dreamer.schedule);
2591
- const timeout = typeof dreamer.task_timeout_minutes === "number" ? dreamer.task_timeout_minutes : undefined;
2592
- const withTimeout = (entry) => timeout !== undefined ? { ...entry, timeout_minutes: timeout } : entry;
2593
- const classifySchedule = dreamer.disable === true ? "" : DEFAULT_CLASSIFY_CRON;
2594
- const retrospectiveSchedule = dreamer.disable === true ? "" : DEFAULT_RETROSPECTIVE_CRON;
2595
- const tasks = {};
2596
- if (tasksObject) {
2597
- for (const [key, value] of Object.entries(tasksObject)) {
2598
- if (RETIRED_OBJECT_MEMORY_TASKS.includes(key))
2599
- continue;
2600
- if (asObject(value))
2601
- tasks[key] = { ...value };
2466
+ const pi = projectRaw.pi;
2467
+ if (isPlainObject(pi) && "subagent_extensions" in pi) {
2468
+ delete pi.subagent_extensions;
2469
+ warnings.push("Ignoring pi.subagent_extensions from project config (security: only user-level config may choose extensions loaded by Pi subagent children).");
2470
+ }
2471
+ for (const field of ["subc", "shadow_embedding"]) {
2472
+ if (field in projectRaw) {
2473
+ delete projectRaw[field];
2474
+ warnings.push(`Ignoring ${field} from project config (security: daemon routing and developer-only embedding traffic are user-level settings).`);
2602
2475
  }
2603
- const maintainMemoryEntry = asObject(tasksObject["maintain-memory"]);
2604
- if (maintainMemoryEntry) {
2605
- const schedule = typeof maintainMemoryEntry.schedule === "string" ? maintainMemoryEntry.schedule : baseCron;
2606
- tasks.verify = withTimeout({
2607
- ...withoutBroadInterval(maintainMemoryEntry),
2608
- ...tasks.verify ?? {},
2609
- schedule: tasks.verify?.schedule ?? schedule
2610
- });
2611
- tasks.curate = withTimeout({
2612
- ...withoutBroadInterval(maintainMemoryEntry),
2613
- ...tasks.curate ?? {},
2614
- schedule: tasks.curate?.schedule ?? schedule
2615
- });
2476
+ }
2477
+ const embedding = projectRaw.embedding;
2478
+ if (isPlainObject(embedding)) {
2479
+ const removed = [];
2480
+ for (const field of EMBEDDING_USER_ONLY_FIELDS) {
2481
+ if (field in embedding) {
2482
+ delete embedding[field];
2483
+ removed.push(field);
2484
+ }
2616
2485
  }
2617
- const oldVerifyEntry = asObject(tasksObject[OLD_VERIFY_TASK]);
2618
- if (oldVerifyEntry) {
2619
- tasks.verify = withTimeout({
2620
- ...withoutBroadInterval(oldVerifyEntry),
2621
- ...tasks.verify ?? {},
2622
- schedule: tasks.verify?.schedule ?? (typeof oldVerifyEntry.schedule === "string" ? oldVerifyEntry.schedule : baseCron)
2623
- });
2486
+ if (removed.length > 0) {
2487
+ warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where or how private text is embedded).");
2488
+ }
2489
+ }
2490
+ for (const agentKey of HIDDEN_AGENT_KEYS) {
2491
+ const block = projectRaw[agentKey];
2492
+ if (!isPlainObject(block))
2493
+ continue;
2494
+ const removed = [];
2495
+ stripEscalationAtExecutableSite(block, agentKey, removed);
2496
+ for (const harness of HARNESS_KEYS) {
2497
+ const harnessBlock = block[harness];
2498
+ if (!isPlainObject(harnessBlock))
2499
+ continue;
2500
+ stripEscalationAtExecutableSite(harnessBlock, `${agentKey}.${harness}`, removed);
2501
+ const tasks = harnessBlock.tasks;
2502
+ if (isPlainObject(tasks)) {
2503
+ for (const [taskName, taskBlock] of Object.entries(tasks)) {
2504
+ if (isPlainObject(taskBlock)) {
2505
+ stripEscalationAtExecutableSite(taskBlock, `${agentKey}.${harness}.tasks.${taskName}`, removed);
2506
+ }
2507
+ }
2508
+ }
2624
2509
  }
2625
- if (!tasks["verify-broad"]) {
2626
- const verifyEnabled = typeof tasks.verify?.schedule === "string" && tasks.verify.schedule.trim() !== "";
2627
- tasks["verify-broad"] = withTimeout({
2628
- schedule: verifyEnabled ? DEFAULT_VERIFY_BROAD_CRON : ""
2629
- });
2510
+ const schedulingTasks = block.tasks;
2511
+ if (isPlainObject(schedulingTasks)) {
2512
+ for (const [taskName, taskBlock] of Object.entries(schedulingTasks)) {
2513
+ if (isPlainObject(taskBlock)) {
2514
+ stripListedFields(taskBlock, AGENT_ESCALATION_FIELDS, `${agentKey}.tasks.${taskName}`, removed);
2515
+ }
2516
+ }
2630
2517
  }
2631
- const oldCurateEntries = OLD_CURATE_TASKS.map((task) => asObject(tasksObject[task])).filter((entry) => Boolean(entry));
2632
- if (oldCurateEntries.length > 0) {
2633
- const oldSchedules = oldCurateEntries.map((entry) => typeof entry.schedule === "string" ? entry.schedule : baseCron);
2634
- tasks.curate = withTimeout({
2635
- ...tasks.curate ?? {},
2636
- schedule: mostFrequentSchedule(oldSchedules)
2637
- });
2518
+ if (removed.length > 0) {
2519
+ warnings.push(`Ignoring ${removed.join(", ")} from project config ` + "(security: a repository cannot reprogram or re-permission hidden agents).");
2638
2520
  }
2639
- for (const task of CANONICAL) {
2640
- if (!tasks[task]) {
2641
- const schedule = task === "verify" || task === "curate" || task === "verify-broad" ? "" : task === "classify-memories" ? classifySchedule : task === "retrospective" ? retrospectiveSchedule : task === "maintain-docs" ? "" : baseCron;
2642
- tasks[task] = withTimeout({ schedule });
2521
+ }
2522
+ const historian = projectRaw.historian;
2523
+ if (isPlainObject(historian)) {
2524
+ const removed = [];
2525
+ for (const field of HISTORIAN_USER_ONLY_FIELDS) {
2526
+ if (field in historian) {
2527
+ delete historian[field];
2528
+ removed.push(field);
2643
2529
  }
2644
2530
  }
2645
- } else {
2646
- const legacyArray = Array.isArray(dreamer.tasks) ? dreamer.tasks.filter((t) => typeof t === "string") : null;
2647
- const verifySelected = legacyArray ? legacyArray.includes(OLD_VERIFY_TASK) : true;
2648
- const curateSelected = legacyArray ? legacyArray.some((task) => OLD_CURATE_TASKS.includes(task)) : true;
2649
- tasks.verify = withTimeout({
2650
- schedule: verifySelected ? baseCron : ""
2651
- });
2652
- tasks["verify-broad"] = withTimeout({
2653
- schedule: verifySelected ? DEFAULT_VERIFY_BROAD_CRON : ""
2654
- });
2655
- tasks.curate = withTimeout({
2656
- schedule: curateSelected ? baseCron : ""
2657
- });
2658
- tasks["classify-memories"] = withTimeout({
2659
- schedule: classifySchedule
2660
- });
2661
- tasks.retrospective = withTimeout({
2662
- schedule: retrospectiveSchedule
2663
- });
2664
- tasks["maintain-docs"] = withTimeout({
2665
- schedule: legacyArray?.includes("maintain-docs") ? baseCron : ""
2666
- });
2531
+ for (const harness of HARNESS_KEYS) {
2532
+ const harnessBlock = historian[harness];
2533
+ if (!isPlainObject(harnessBlock))
2534
+ continue;
2535
+ for (const field of HISTORIAN_USER_ONLY_FIELDS) {
2536
+ if (field in harnessBlock) {
2537
+ delete harnessBlock[field];
2538
+ removed.push(`${harness}.${field}`);
2539
+ }
2540
+ }
2541
+ }
2542
+ if (removed.length > 0) {
2543
+ warnings.push(`Ignoring ${removed.map((path) => `historian.${path}`).join(", ")} from project config ` + "(security: historian model selection is user-level only; a repository cannot force extra compaction cost).");
2544
+ }
2667
2545
  }
2668
- tasks["map-memories"] ??= withTimeout({ schedule: baseCron });
2669
- tasks["evaluate-smart-notes"] ??= withTimeout({ schedule: baseCron });
2670
- const um = asObject(dreamer.user_memories);
2671
- const umEnabled = um ? um.enabled !== false : true;
2672
- if (um || !tasks["review-user-memories"]) {
2673
- tasks["review-user-memories"] = withTimeout({
2674
- ...tasks["review-user-memories"] ?? {},
2675
- schedule: umEnabled ? baseCron : "",
2676
- ...um && typeof um.promotion_threshold === "number" ? { promotion_threshold: um.promotion_threshold } : {}
2677
- });
2546
+ const dreamer = projectRaw.dreamer;
2547
+ if (isPlainObject(dreamer) && "runner" in dreamer) {
2548
+ delete dreamer.runner;
2549
+ warnings.push("Ignoring dreamer.runner from project config (security: which process and provider account run dreamer completions is a user-level setting).");
2678
2550
  }
2679
- const {
2680
- schedule: _schedule,
2681
- tasks: _tasks,
2682
- task_timeout_minutes: _tto,
2683
- max_runtime_minutes: _max,
2684
- user_memories: _um,
2685
- pin_key_files: _pkf,
2686
- ...rest
2687
- } = dreamer;
2688
- warnings.push('Migrated legacy dreamer scheduling (schedule window / tasks array / user_memories / pin_key_files) → per-task "dreamer.tasks" in-memory (run `doctor` to persist).');
2689
- return { ...rawConfig, dreamer: { ...rest, tasks } };
2551
+ return warnings;
2690
2552
  }
2691
-
2692
- // src/config/migrate-experimental.ts
2693
- function migrateLegacyExperimental(rawConfig, warnings) {
2694
- const experimental = rawConfig.experimental;
2695
- if (typeof experimental !== "object" || experimental === null) {
2696
- return rawConfig;
2697
- }
2698
- const exp = experimental;
2699
- const hasUM = "user_memories" in exp;
2700
- const hasPKF = "pin_key_files" in exp;
2701
- const hasMural = "mural" in exp;
2702
- const TOP_LEVEL_GRADUATED = ["temporal_awareness", "caveman_text_compression"];
2703
- const MEMORY_GRADUATED = ["auto_search", "git_commit_indexing"];
2704
- const hasGraduated = TOP_LEVEL_GRADUATED.some((k) => (k in exp)) || MEMORY_GRADUATED.some((k) => (k in exp)) || hasMural;
2705
- if (!hasUM && !hasPKF && !hasGraduated) {
2706
- return rawConfig;
2553
+ function constrainProjectThresholdOverrides(args) {
2554
+ const warnings = [];
2555
+ const basePercentage = normalizeTrustedPercentageThresholds(args.trustedBaseConfig.execute_threshold_percentage);
2556
+ const baseTokens = normalizeTrustedTokenThresholds(args.trustedBaseConfig.execute_threshold_tokens);
2557
+ if ("execute_threshold_percentage" in args.projectRaw) {
2558
+ const projectValue = args.projectRaw.execute_threshold_percentage;
2559
+ if (isValidPercentageThreshold(projectValue)) {
2560
+ const constrained = clonePercentageThresholds(basePercentage);
2561
+ constrained.defaultValue = Math.max(basePercentage.defaultValue, projectValue);
2562
+ for (const [modelKey, threshold] of basePercentage.overrides) {
2563
+ const raisedThreshold = Math.max(threshold, projectValue);
2564
+ if (raisedThreshold === constrained.defaultValue) {
2565
+ constrained.overrides.delete(modelKey);
2566
+ } else {
2567
+ constrained.overrides.set(modelKey, raisedThreshold);
2568
+ }
2569
+ }
2570
+ setMergedPercentageThreshold(args.mergedRaw, constrained);
2571
+ if (percentageThresholdsEqual(constrained, basePercentage)) {
2572
+ warnings.push(makeProjectThresholdWarning("execute_threshold_percentage", PERCENTAGE_THRESHOLD_REASON));
2573
+ }
2574
+ } else if (isPlainObject(projectValue)) {
2575
+ const constrained = clonePercentageThresholds(basePercentage);
2576
+ let touchedValidEntry = false;
2577
+ if (isValidPercentageThreshold(projectValue.default)) {
2578
+ touchedValidEntry = true;
2579
+ if (projectValue.default > basePercentage.defaultValue) {
2580
+ constrained.defaultValue = projectValue.default;
2581
+ } else {
2582
+ warnings.push(makeProjectThresholdWarning("execute_threshold_percentage.default", PERCENTAGE_THRESHOLD_REASON));
2583
+ }
2584
+ }
2585
+ for (const [modelKey, rawValue] of Object.entries(projectValue)) {
2586
+ if (modelKey === "default")
2587
+ continue;
2588
+ if (!isValidPercentageThreshold(rawValue))
2589
+ continue;
2590
+ touchedValidEntry = true;
2591
+ const baseValue = basePercentage.overrides.get(modelKey) ?? basePercentage.defaultValue;
2592
+ if (rawValue > baseValue) {
2593
+ if (rawValue === constrained.defaultValue) {
2594
+ constrained.overrides.delete(modelKey);
2595
+ } else {
2596
+ constrained.overrides.set(modelKey, rawValue);
2597
+ }
2598
+ } else {
2599
+ warnings.push(makeProjectThresholdWarning(`execute_threshold_percentage.${modelKey}`, PERCENTAGE_THRESHOLD_REASON));
2600
+ }
2601
+ }
2602
+ if (touchedValidEntry) {
2603
+ setMergedPercentageThreshold(args.mergedRaw, constrained);
2604
+ }
2605
+ }
2707
2606
  }
2708
- const patched = { ...rawConfig };
2709
- const dreamer = typeof patched.dreamer === "object" && patched.dreamer !== null ? { ...patched.dreamer } : {};
2710
- const memory = typeof patched.memory === "object" && patched.memory !== null ? { ...patched.memory } : {};
2711
- const newExperimental = { ...exp };
2712
- const coerceToObject = (value) => {
2713
- if (typeof value === "boolean") {
2714
- return { enabled: value };
2607
+ if ("execute_threshold_tokens" in args.projectRaw && isPlainObject(args.projectRaw.execute_threshold_tokens)) {
2608
+ const projectValue = args.projectRaw.execute_threshold_tokens;
2609
+ const constrained = cloneTokenThresholds(baseTokens);
2610
+ let touchedValidEntry = false;
2611
+ if (isValidTokenThreshold(projectValue.default)) {
2612
+ touchedValidEntry = true;
2613
+ if (baseTokens.defaultValue === undefined) {
2614
+ warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_INTRODUCTION_REASON));
2615
+ } else if (projectValue.default > baseTokens.defaultValue) {
2616
+ constrained.defaultValue = projectValue.default;
2617
+ } else {
2618
+ warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_REASON));
2619
+ }
2715
2620
  }
2716
- if (typeof value === "object" && value !== null) {
2717
- return { ...value };
2621
+ for (const [modelKey, rawValue] of Object.entries(projectValue)) {
2622
+ if (modelKey === "default")
2623
+ continue;
2624
+ if (!isValidTokenThreshold(rawValue))
2625
+ continue;
2626
+ touchedValidEntry = true;
2627
+ const baseValue = baseTokens.overrides.get(modelKey) ?? baseTokens.defaultValue;
2628
+ if (baseValue === undefined) {
2629
+ warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_INTRODUCTION_REASON));
2630
+ continue;
2631
+ }
2632
+ if (rawValue > baseValue) {
2633
+ if (rawValue === constrained.defaultValue) {
2634
+ constrained.overrides.delete(modelKey);
2635
+ } else {
2636
+ constrained.overrides.set(modelKey, rawValue);
2637
+ }
2638
+ } else {
2639
+ warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_REASON));
2640
+ }
2718
2641
  }
2719
- return;
2720
- };
2721
- const relocate = (key, dest, destLabel) => {
2722
- if (!(key in exp))
2723
- return;
2724
- const oldValue = exp[key];
2725
- const existing = dest[key];
2726
- if (existing === undefined) {
2727
- dest[key] = oldValue;
2728
- warnings.push(`Migrated "experimental.${key}" → "${destLabel}${key}" in-memory (run \`doctor\` to persist).`);
2729
- } else if (typeof oldValue === "object" && oldValue !== null && typeof existing === "object" && existing !== null) {
2730
- dest[key] = {
2731
- ...oldValue,
2732
- ...existing
2733
- };
2642
+ if (touchedValidEntry) {
2643
+ setMergedTokenThreshold(args.mergedRaw, constrained);
2734
2644
  }
2735
- delete newExperimental[key];
2736
- };
2737
- for (const key of TOP_LEVEL_GRADUATED)
2738
- relocate(key, patched, "");
2739
- for (const key of MEMORY_GRADUATED)
2740
- relocate(key, memory, "memory.");
2741
- if (hasMural) {
2742
- const oldMural = coerceToObject(exp.mural);
2743
- const existingMural = patched.mural;
2744
- if (existingMural === undefined) {
2745
- patched.mural = oldMural ?? exp.mural;
2746
- warnings.push('Deprecated "experimental.mural"; use top-level "mural" instead (migrated in memory; run `doctor` to persist).');
2747
- } else if (oldMural !== undefined && typeof existingMural === "object" && existingMural !== null && !Array.isArray(existingMural)) {
2748
- patched.mural = {
2749
- ...oldMural,
2750
- ...existingMural
2751
- };
2645
+ }
2646
+ if ("protected_tokens" in args.projectRaw) {
2647
+ const rawProject = args.projectRaw.protected_tokens;
2648
+ const projectVal = resolveProtectedTokensScalar(rawProject);
2649
+ const trustedUserVal = resolveProtectedTokensScalar(args.trustedBaseConfig.protected_tokens);
2650
+ if (projectVal !== undefined) {
2651
+ if (trustedUserVal !== undefined) {
2652
+ if (projectVal >= trustedUserVal) {
2653
+ args.mergedRaw.protected_tokens = projectVal;
2654
+ } else {
2655
+ args.mergedRaw.protected_tokens = trustedUserVal;
2656
+ warnings.push(makeProjectThresholdWarning("protected_tokens", PROTECTED_TOKENS_REASON));
2657
+ }
2658
+ } else {
2659
+ args.mergedRaw.protected_tokens = projectVal;
2660
+ }
2661
+ } else {
2662
+ if (trustedUserVal !== undefined) {
2663
+ args.mergedRaw.protected_tokens = trustedUserVal;
2664
+ } else {
2665
+ delete args.mergedRaw.protected_tokens;
2666
+ }
2667
+ warnings.push(makeProjectThresholdWarning("protected_tokens", PROTECTED_TOKENS_REASON));
2752
2668
  }
2753
- delete newExperimental.mural;
2754
2669
  }
2755
- if (hasUM) {
2756
- const oldUM = coerceToObject(exp.user_memories);
2757
- if (oldUM !== undefined) {
2758
- if (dreamer.user_memories === undefined) {
2759
- dreamer.user_memories = oldUM;
2760
- warnings.push('Migrated "experimental.user_memories" → "dreamer.user_memories" in-memory (run `doctor` to persist).');
2761
- } else if (typeof dreamer.user_memories === "object" && dreamer.user_memories !== null) {
2762
- dreamer.user_memories = {
2763
- ...oldUM,
2764
- ...dreamer.user_memories
2765
- };
2766
- }
2670
+ return warnings;
2671
+ }
2672
+ function normalizeEndpoint(value) {
2673
+ if (typeof value !== "string")
2674
+ return;
2675
+ const trimmed = value.trim().replace(/\/+$/, "");
2676
+ return trimmed.length > 0 ? trimmed.toLowerCase() : undefined;
2677
+ }
2678
+ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
2679
+ const projectEmbedding = projectRaw.embedding;
2680
+ if (!isPlainObject(projectEmbedding))
2681
+ return [];
2682
+ const redirectsEndpoint = "endpoint" in projectEmbedding;
2683
+ if (!redirectsEndpoint)
2684
+ return [];
2685
+ const userEmbedding = userRaw?.embedding;
2686
+ if (isPlainObject(userEmbedding)) {
2687
+ const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
2688
+ const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
2689
+ if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
2690
+ return [];
2767
2691
  }
2768
- delete newExperimental.user_memories;
2769
2692
  }
2770
- if (hasPKF) {
2771
- const oldPKF = coerceToObject(exp.pin_key_files);
2772
- if (oldPKF !== undefined) {
2773
- if (dreamer.pin_key_files === undefined) {
2774
- dreamer.pin_key_files = oldPKF;
2775
- warnings.push('Migrated "experimental.pin_key_files" → "dreamer.pin_key_files" in-memory (run `doctor` to persist).');
2776
- } else if (typeof dreamer.pin_key_files === "object" && dreamer.pin_key_files !== null) {
2777
- dreamer.pin_key_files = {
2778
- ...oldPKF,
2779
- ...dreamer.pin_key_files
2780
- };
2781
- } else if (typeof dreamer.pin_key_files === "boolean") {
2782
- dreamer.pin_key_files = { ...oldPKF, enabled: dreamer.pin_key_files };
2783
- }
2693
+ const providesOwnKey = typeof projectEmbedding.api_key === "string" && projectEmbedding.api_key.length > 0;
2694
+ if (providesOwnKey)
2695
+ return [];
2696
+ const mergedEmbedding = mergedRaw.embedding;
2697
+ if (!isPlainObject(mergedEmbedding))
2698
+ return [];
2699
+ if (!("api_key" in mergedEmbedding))
2700
+ return [];
2701
+ delete mergedEmbedding.api_key;
2702
+ return [
2703
+ "Dropped inherited user embedding api_key because project config redirected " + "embedding.endpoint without supplying its own key (security: prevents key " + "exfiltration to a repository-chosen endpoint)."
2704
+ ];
2705
+ }
2706
+ function projectContributionPath(projectRaw, issuePath) {
2707
+ let node = projectRaw;
2708
+ for (let index = 0;index < issuePath.length; index++) {
2709
+ const segment = String(issuePath[index]);
2710
+ if (!isPlainObject(node) || !Object.hasOwn(node, segment))
2711
+ return;
2712
+ node = node[segment];
2713
+ if (index === issuePath.length - 1 || !isPlainObject(node)) {
2714
+ return issuePath.slice(0, index + 1);
2784
2715
  }
2785
- delete newExperimental.pin_key_files;
2786
2716
  }
2787
- patched.experimental = newExperimental;
2788
- patched.dreamer = dreamer;
2789
- if (Object.keys(memory).length > 0) {
2790
- patched.memory = memory;
2717
+ return;
2718
+ }
2719
+ function displacedTrustedValue(trustedRaw, contribution) {
2720
+ let node = trustedRaw;
2721
+ for (let index = 0;index < contribution.length; index++) {
2722
+ const segment = String(contribution[index]);
2723
+ if (!isPlainObject(node) || !Object.hasOwn(node, segment))
2724
+ return;
2725
+ node = node[segment];
2726
+ if (index === contribution.length - 1 || !isPlainObject(node)) {
2727
+ return { path: contribution.slice(0, index + 1), value: node };
2728
+ }
2791
2729
  }
2792
- return patched;
2730
+ return;
2793
2731
  }
2794
-
2795
- // src/config/profiles.ts
2796
- function withoutProfileFields(raw) {
2797
- const copy = { ...raw };
2798
- delete copy.profile;
2799
- delete copy.profiles;
2800
- return copy;
2732
+ function defineOwnValue(target, key, value) {
2733
+ Object.defineProperty(target, key, {
2734
+ value,
2735
+ enumerable: true,
2736
+ configurable: true,
2737
+ writable: true
2738
+ });
2801
2739
  }
2802
- function readProfileSelection(raw) {
2803
- if (!Object.hasOwn(raw, "profile"))
2804
- return { declared: false };
2805
- const value = raw.profile;
2806
- if (typeof value !== "string")
2807
- return { declared: true };
2808
- const name = value.trim();
2809
- return name.length > 0 ? { declared: true, name } : { declared: true };
2740
+ function writeOwnPath(root, path, value) {
2741
+ let node = root;
2742
+ for (let index = 0;index < path.length - 1; index++) {
2743
+ const segment = String(path[index]);
2744
+ const child = node[segment];
2745
+ if (!isPlainObject(child))
2746
+ return;
2747
+ const copy = { ...child };
2748
+ defineOwnValue(node, segment, copy);
2749
+ node = copy;
2750
+ }
2751
+ defineOwnValue(node, String(path[path.length - 1]), value);
2810
2752
  }
2811
- function resolveConfigProfile(args) {
2753
+ var MAX_PROJECT_RESTORE_ROUNDS = 16;
2754
+ function restoreTrustedValuesOverInvalidProjectValues(args) {
2812
2755
  const warnings = [];
2813
- const userSelection = readProfileSelection(args.userRaw);
2814
- const projectSelection = readProfileSelection(args.projectRaw);
2815
- const selection = projectSelection.name ? { name: projectSelection.name, source: "project" } : userSelection.name ? { name: userSelection.name, source: "user" } : undefined;
2816
- if (projectSelection.declared && !projectSelection.name) {
2817
- warnings.push("Ignoring invalid profile selection from project config; expected a non-empty string.");
2818
- }
2819
- if (!projectSelection.declared && userSelection.declared && !userSelection.name) {
2820
- warnings.push("Ignoring invalid profile selection from user config; expected a non-empty string.");
2821
- }
2822
- let profiles = {};
2823
- if (Object.hasOwn(args.userRaw, "profiles")) {
2824
- const parsed = ConfigProfilesSchema.safeParse(args.userRaw.profiles);
2825
- if (parsed.success) {
2826
- profiles = parsed.data;
2827
- } else {
2828
- warnings.push("Ignoring profiles from user config: invalid profile configuration; profiles may contain only historian/dreamer harness model blocks.");
2756
+ const restoredTopLevelKeys = new Set;
2757
+ const restored = new Set;
2758
+ for (let round = 0;round < MAX_PROJECT_RESTORE_ROUNDS; round++) {
2759
+ let changed = false;
2760
+ for (const issuePath of args.collectIssuePaths(args.mergedRaw)) {
2761
+ const contribution = projectContributionPath(args.projectRaw, issuePath);
2762
+ if (contribution === undefined || contribution.length === 0)
2763
+ continue;
2764
+ const trusted = displacedTrustedValue(args.trustedRaw, contribution);
2765
+ if (trusted === undefined)
2766
+ continue;
2767
+ const key = trusted.path.map(String).join(".");
2768
+ if (restored.has(key))
2769
+ continue;
2770
+ writeOwnPath(args.mergedRaw, trusted.path, trusted.value);
2771
+ restored.add(key);
2772
+ restoredTopLevelKeys.add(String(trusted.path[0]));
2773
+ changed = true;
2774
+ warnings.push(`Ignoring invalid ${key} from project config and keeping the user-level value (security: a repository cannot reset user settings by supplying an invalid value).`);
2829
2775
  }
2776
+ if (!changed)
2777
+ break;
2830
2778
  }
2831
- if (!selection) {
2832
- return {
2833
- userBase: withoutProfileFields(args.userRaw),
2834
- projectBase: withoutProfileFields(args.projectRaw),
2835
- overlay: {},
2836
- warnings
2837
- };
2779
+ return { warnings, restoredTopLevelKeys: [...restoredTopLevelKeys] };
2780
+ }
2781
+ var PROJECT_COMMAND_STRING_FIELDS = ["description", "agent", "model"];
2782
+ function wellFormedCommand(value) {
2783
+ if (!isPlainObject(value))
2784
+ return;
2785
+ if (typeof value.template !== "string" || value.template.trim().length === 0)
2786
+ return;
2787
+ const command = { template: value.template };
2788
+ for (const field of PROJECT_COMMAND_STRING_FIELDS) {
2789
+ if (value[field] === undefined)
2790
+ continue;
2791
+ if (typeof value[field] !== "string")
2792
+ return;
2793
+ command[field] = value[field];
2838
2794
  }
2839
- if (!Object.hasOwn(profiles, selection.name)) {
2840
- warnings.push(`Unknown profile "${selection.name}" selected by ${selection.source} config; using base config without a profile.`);
2841
- return {
2842
- userBase: withoutProfileFields(args.userRaw),
2843
- projectBase: withoutProfileFields(args.projectRaw),
2844
- overlay: {},
2845
- warnings
2846
- };
2795
+ if (value.subtask !== undefined) {
2796
+ if (typeof value.subtask !== "boolean")
2797
+ return;
2798
+ command.subtask = value.subtask;
2847
2799
  }
2848
- const overlay = profiles[selection.name];
2849
- return {
2850
- userBase: withoutProfileFields(args.userRaw),
2851
- projectBase: withoutProfileFields(args.projectRaw),
2852
- overlay,
2853
- activeProfile: selection.name,
2854
- warnings
2855
- };
2800
+ return command;
2801
+ }
2802
+ function constrainProjectCommands(args) {
2803
+ if (!("command" in args.projectRaw))
2804
+ return [];
2805
+ const warnings = [];
2806
+ const trustedCommands = isPlainObject(args.trustedRaw.command) ? args.trustedRaw.command : undefined;
2807
+ const merged = { ...trustedCommands ?? {} };
2808
+ const projectCommands = args.projectRaw.command;
2809
+ if (!isPlainObject(projectCommands)) {
2810
+ warnings.push("Ignoring command from project config (it must be an object of named commands).");
2811
+ } else {
2812
+ for (const [name, value] of Object.entries(projectCommands)) {
2813
+ if (trustedCommands && Object.hasOwn(trustedCommands, name)) {
2814
+ warnings.push(`Ignoring command.${name} from project config (security: a repository cannot replace a command defined in user config).`);
2815
+ continue;
2816
+ }
2817
+ if (args.reservedNames.includes(name)) {
2818
+ warnings.push(`Ignoring command.${name} from project config (security: a repository cannot replace a built-in Magic Context command).`);
2819
+ continue;
2820
+ }
2821
+ const command = wellFormedCommand(value);
2822
+ if (!command) {
2823
+ warnings.push(`Ignoring command.${name} from project config (it needs a non-empty string template; description, agent and model must be strings and subtask a boolean).`);
2824
+ continue;
2825
+ }
2826
+ Object.defineProperty(merged, name, {
2827
+ value: command,
2828
+ enumerable: true,
2829
+ configurable: true,
2830
+ writable: true
2831
+ });
2832
+ }
2833
+ }
2834
+ if (Object.keys(merged).length === 0)
2835
+ delete args.mergedRaw.command;
2836
+ else
2837
+ args.mergedRaw.command = merged;
2838
+ return warnings;
2856
2839
  }
2857
2840
 
2858
2841
  // src/config/prune-config-leaf.ts
@@ -3566,12 +3549,17 @@ function misplacedAgentModelWarnings(rawConfig) {
3566
3549
  function warnProtectedTagsDeprecationOnce() {
3567
3550
  if (!warnedProtectedTagsDeprecation) {
3568
3551
  warnedProtectedTagsDeprecation = true;
3569
- log("[magic-context] protected_tags is deprecated and ignored; use protected_tokens instead.");
3552
+ log2("[magic-context] protected_tags is deprecated and ignored; use protected_tokens instead.");
3570
3553
  }
3571
3554
  }
3572
3555
  function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
3573
3556
  const preMigrationWarnings = [];
3574
3557
  const configWithoutRemovedAgent = stripRemovedAgentConfig(rawConfig, preMigrationWarnings);
3558
+ if (Object.hasOwn(rawConfig, "clear_reasoning_age")) {
3559
+ const warning = "clear_reasoning_age is deprecated and ignored. Magic Context now keeps reasoning up to a token budget, keep_reasoning_tokens (default 10,000). Remove the key, or set keep_reasoning_tokens to a token count.";
3560
+ preMigrationWarnings.push(warning);
3561
+ log2(`[magic-context] ${warning}`);
3562
+ }
3575
3563
  if (Object.hasOwn(rawConfig, "protected_tags")) {
3576
3564
  warnProtectedTagsDeprecationOnce();
3577
3565
  preMigrationWarnings.push("protected_tags is deprecated and ignored; use protected_tokens instead.");
@@ -3581,6 +3569,7 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
3581
3569
  }
3582
3570
  preMigrationWarnings.push(...misplacedAgentModelWarnings(rawConfig));
3583
3571
  const migratedExperimental = migrateLegacyExperimental(configWithoutRemovedAgent, preMigrationWarnings);
3572
+ warnDeprecatedMuralModel(migratedExperimental, preMigrationWarnings);
3584
3573
  const migratedDreamer = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
3585
3574
  const migrated = migrateLegacyAgentEnabledInMemory(migratedDreamer, preMigrationWarnings);
3586
3575
  const parsed = MagicContextConfigSchema.safeParse(migrated);