@cortexkit/opencode-magic-context 0.41.3 → 0.42.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 (384) hide show
  1. package/README.md +21 -3
  2. package/dist/agents/hidden-agent-registrations.d.ts +1 -3
  3. package/dist/agents/hidden-agent-registrations.d.ts.map +1 -1
  4. package/dist/agents/magic-context-prompt.d.ts +1 -1
  5. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  6. package/dist/agents/permissions.d.ts +1 -22
  7. package/dist/agents/permissions.d.ts.map +1 -1
  8. package/dist/config/agent-disable.d.ts +0 -5
  9. package/dist/config/agent-disable.d.ts.map +1 -1
  10. package/dist/config/index.d.ts +14 -0
  11. package/dist/config/index.d.ts.map +1 -1
  12. package/dist/config/migrate-config-location.d.ts.map +1 -1
  13. package/dist/config/project-security.d.ts +17 -0
  14. package/dist/config/project-security.d.ts.map +1 -1
  15. package/dist/config/removed-agent-config.d.ts +8 -0
  16. package/dist/config/removed-agent-config.d.ts.map +1 -0
  17. package/dist/config/schema/magic-context.d.ts +23 -201
  18. package/dist/config/schema/magic-context.d.ts.map +1 -1
  19. package/dist/{conflict-warning-hook-z5sd697e.js → conflict-warning-hook-6p3xqeac.js} +4 -4
  20. package/dist/{derive-budgets-901a0pk0.js → derive-budgets-s2pgnnm4.js} +4 -2
  21. package/dist/features/builtin-commands/commands.d.ts +0 -4
  22. package/dist/features/builtin-commands/commands.d.ts.map +1 -1
  23. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  24. package/dist/features/magic-context/compartment-chunk-embedding.d.ts +7 -0
  25. package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
  26. package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
  27. package/dist/features/magic-context/context-authority.d.ts +31 -4
  28. package/dist/features/magic-context/context-authority.d.ts.map +1 -1
  29. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  30. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  31. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  32. package/dist/features/magic-context/dreamer/open-opencode-db.d.ts.map +1 -1
  33. package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
  34. package/dist/features/magic-context/dreamer/retrospective-orphan-sweep.d.ts +19 -9
  35. package/dist/features/magic-context/dreamer/retrospective-orphan-sweep.d.ts.map +1 -1
  36. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +13 -0
  37. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
  38. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  39. package/dist/features/magic-context/dreamer/task-scheduler.d.ts +7 -0
  40. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  41. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  42. package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
  43. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts.map +1 -1
  44. package/dist/features/magic-context/index.d.ts +0 -1
  45. package/dist/features/magic-context/index.d.ts.map +1 -1
  46. package/dist/features/magic-context/memory/embedding-local.d.ts +13 -0
  47. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  48. package/dist/features/magic-context/memory/embedding-synapse.d.ts +43 -10
  49. package/dist/features/magic-context/memory/embedding-synapse.d.ts.map +1 -1
  50. package/dist/features/magic-context/memory/embedding.d.ts +1 -1
  51. package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
  52. package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
  53. package/dist/features/magic-context/memory/project-identity.d.ts +1 -0
  54. package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
  55. package/dist/features/magic-context/memory/promotion.d.ts +7 -1
  56. package/dist/features/magic-context/memory/promotion.d.ts.map +1 -1
  57. package/dist/features/magic-context/merged-reasoning-decisions.d.ts +6 -0
  58. package/dist/features/magic-context/merged-reasoning-decisions.d.ts.map +1 -0
  59. package/dist/features/magic-context/message-fts-rowid-map.d.ts.map +1 -1
  60. package/dist/features/magic-context/message-index-async.d.ts +12 -0
  61. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  62. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  63. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  64. package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
  65. package/dist/features/magic-context/mural/render-trigger.d.ts +6 -0
  66. package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
  67. package/dist/features/magic-context/no-content-compartment.d.ts +12 -0
  68. package/dist/features/magic-context/no-content-compartment.d.ts.map +1 -0
  69. package/dist/features/magic-context/pi-content-decisions.d.ts +9 -0
  70. package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -0
  71. package/dist/features/magic-context/project-docs-hash.d.ts +4 -0
  72. package/dist/features/magic-context/project-docs-hash.d.ts.map +1 -1
  73. package/dist/features/magic-context/project-embedding-registry.d.ts +13 -4
  74. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  75. package/dist/features/magic-context/protection-window.d.ts +79 -0
  76. package/dist/features/magic-context/protection-window.d.ts.map +1 -0
  77. package/dist/features/magic-context/search.d.ts.map +1 -1
  78. package/dist/features/magic-context/session-project-backfill.d.ts.map +1 -1
  79. package/dist/features/magic-context/shadow-backfill-state.d.ts +26 -0
  80. package/dist/features/magic-context/shadow-backfill-state.d.ts.map +1 -0
  81. package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
  82. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +4 -0
  83. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
  84. package/dist/features/magic-context/smart-notes/storage.d.ts.map +1 -1
  85. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  86. package/dist/features/magic-context/storage-db.d.ts +1 -1
  87. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  88. package/dist/features/magic-context/storage-embedding-measurements.d.ts +2 -2
  89. package/dist/features/magic-context/storage-embedding-measurements.d.ts.map +1 -1
  90. package/dist/features/magic-context/storage-historian-runs.d.ts +4 -0
  91. package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
  92. package/dist/features/magic-context/storage-memory-mutation-log.d.ts +1 -0
  93. package/dist/features/magic-context/storage-memory-mutation-log.d.ts.map +1 -1
  94. package/dist/features/magic-context/storage-meta-persisted.d.ts +81 -15
  95. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  96. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  97. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  98. package/dist/features/magic-context/storage-native-replay.d.ts +29 -0
  99. package/dist/features/magic-context/storage-native-replay.d.ts.map +1 -0
  100. package/dist/features/magic-context/storage-notes.d.ts +6 -0
  101. package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
  102. package/dist/features/magic-context/storage-replay-document.d.ts +33 -0
  103. package/dist/features/magic-context/storage-replay-document.d.ts.map +1 -0
  104. package/dist/features/magic-context/storage-subagent-invocations.d.ts +1 -1
  105. package/dist/features/magic-context/storage-subagent-invocations.d.ts.map +1 -1
  106. package/dist/features/magic-context/storage-tags.d.ts +19 -11
  107. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  108. package/dist/features/magic-context/storage.d.ts +3 -3
  109. package/dist/features/magic-context/storage.d.ts.map +1 -1
  110. package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
  111. package/dist/features/magic-context/tagger.d.ts +13 -0
  112. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  113. package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -1
  114. package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
  115. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
  116. package/dist/features/magic-context/workspaces.d.ts.map +1 -1
  117. package/dist/hooks/magic-context/apply-operations.d.ts +11 -5
  118. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  119. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  120. package/dist/hooks/magic-context/cache-busting-signals.d.ts +9 -1
  121. package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
  122. package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -1
  123. package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
  124. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  125. package/dist/hooks/magic-context/child-session-spawn.d.ts +3 -3
  126. package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -1
  127. package/dist/hooks/magic-context/command-handler.d.ts +5 -10
  128. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  129. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  130. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +10 -21
  131. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts.map +1 -1
  132. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  133. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  134. package/dist/hooks/magic-context/compartment-runner-types.d.ts +4 -0
  135. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  136. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +26 -2
  137. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  138. package/dist/hooks/magic-context/decay-render.d.ts.map +1 -1
  139. package/dist/hooks/magic-context/derive-budgets.d.ts +1 -0
  140. package/dist/hooks/magic-context/derive-budgets.d.ts.map +1 -1
  141. package/dist/hooks/magic-context/dropped-input-guard.d.ts +9 -0
  142. package/dist/hooks/magic-context/dropped-input-guard.d.ts.map +1 -0
  143. package/dist/hooks/magic-context/emergency-drop.d.ts +9 -2
  144. package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
  145. package/dist/hooks/magic-context/event-handler.d.ts +0 -1
  146. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  147. package/dist/hooks/magic-context/event-payloads.d.ts +7 -6
  148. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  149. package/dist/hooks/magic-context/event-resolvers.d.ts +1 -1
  150. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  151. package/dist/hooks/magic-context/execute-status.d.ts +8 -2
  152. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  153. package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -1
  154. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +11 -4
  155. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  156. package/dist/hooks/magic-context/hook-handlers.d.ts +5 -1
  157. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  158. package/dist/hooks/magic-context/hook.d.ts +17 -5
  159. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  160. package/dist/hooks/magic-context/inject-compartments.d.ts +14 -1
  161. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  162. package/dist/hooks/magic-context/issue-423-fixture.d.ts +37 -0
  163. package/dist/hooks/magic-context/issue-423-fixture.d.ts.map +1 -0
  164. package/dist/hooks/magic-context/issue-424-fixture.d.ts +11 -0
  165. package/dist/hooks/magic-context/issue-424-fixture.d.ts.map +1 -0
  166. package/dist/hooks/magic-context/live-session-state.d.ts +1 -1
  167. package/dist/hooks/magic-context/lkg-slot.d.ts +34 -0
  168. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  169. package/dist/hooks/magic-context/module-state-sync-timing.d.ts +21 -0
  170. package/dist/hooks/magic-context/module-state-sync-timing.d.ts.map +1 -0
  171. package/dist/hooks/magic-context/module-state-sync.d.ts +21 -7
  172. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  173. package/dist/hooks/magic-context/module-transport.d.ts +12 -0
  174. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  175. package/dist/hooks/magic-context/module-wire.d.ts +8 -0
  176. package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
  177. package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
  178. package/dist/hooks/magic-context/persist-filtered-noise.d.ts +5 -0
  179. package/dist/hooks/magic-context/persist-filtered-noise.d.ts.map +1 -0
  180. package/dist/hooks/magic-context/producer-window-guard.d.ts +8 -0
  181. package/dist/hooks/magic-context/producer-window-guard.d.ts.map +1 -0
  182. package/dist/hooks/magic-context/protected-tail-boundary.d.ts +65 -0
  183. package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
  184. package/dist/hooks/magic-context/read-session-chunk.d.ts +20 -18
  185. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  186. package/dist/hooks/magic-context/read-session-db.d.ts +22 -12
  187. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  188. package/dist/hooks/magic-context/read-session-formatting.d.ts +8 -0
  189. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  190. package/dist/hooks/magic-context/read-session-raw.d.ts +10 -1
  191. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  192. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts +4 -1
  193. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  194. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +2 -0
  195. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  196. package/dist/hooks/magic-context/reference-retrieval.d.ts.map +1 -1
  197. package/dist/hooks/magic-context/rust-mode-transform.d.ts +79 -11
  198. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  199. package/dist/hooks/magic-context/send-session-notification.d.ts +11 -10
  200. package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
  201. package/dist/hooks/magic-context/sentinel.d.ts +3 -3
  202. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  203. package/dist/hooks/magic-context/strip-content.d.ts +7 -0
  204. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  205. package/dist/hooks/magic-context/supersession-reclaim.d.ts +13 -0
  206. package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
  207. package/dist/hooks/magic-context/system-prompt-hash.d.ts +2 -3
  208. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  209. package/dist/hooks/magic-context/tag-messages.d.ts +2 -0
  210. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  211. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +16 -6
  212. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
  213. package/dist/hooks/magic-context/tool-drop-target.d.ts +1 -0
  214. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  215. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -0
  216. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  217. package/dist/hooks/magic-context/transform-context-state.d.ts +7 -1
  218. package/dist/hooks/magic-context/transform-context-state.d.ts.map +1 -1
  219. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +11 -4
  220. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  221. package/dist/hooks/magic-context/transform.d.ts +11 -2
  222. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  223. package/dist/hooks/magic-context/upgrade-reminder.d.ts +7 -9
  224. package/dist/hooks/magic-context/upgrade-reminder.d.ts.map +1 -1
  225. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  226. package/dist/index-1974jvn3.js +783 -0
  227. package/dist/{index-npx8vhmy.js → index-36wd1d6k.js} +7 -30
  228. package/dist/{index-kby1sng5.js → index-avwt563y.js} +8 -1
  229. package/dist/{index-vmmjdpc0.js → index-c9hamh36.js} +18357 -16010
  230. package/dist/{index-0bycg0np.js → index-jmtjh2d3.js} +4 -4
  231. package/dist/{index-q264erdp.js → index-nqgjwfzn.js} +70 -38
  232. package/dist/index-sd3egckz.js +1583 -0
  233. package/dist/{index-dn1rwpd8.js → index-xdtge3y2.js} +58 -16
  234. package/dist/index.d.ts.map +1 -1
  235. package/dist/index.js +6826 -6660
  236. package/dist/plugin/conflict-warning-hook.d.ts +5 -6
  237. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  238. package/dist/plugin/dream-timer.d.ts +1 -0
  239. package/dist/plugin/dream-timer.d.ts.map +1 -1
  240. package/dist/plugin/embedding-routing.d.ts +4 -1
  241. package/dist/plugin/embedding-routing.d.ts.map +1 -1
  242. package/dist/plugin/hooks/create-session-hooks.d.ts +19 -2
  243. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  244. package/dist/plugin/rpc-handlers.d.ts +31 -1
  245. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  246. package/dist/plugin/rust-tool-backends.d.ts +1 -0
  247. package/dist/plugin/rust-tool-backends.d.ts.map +1 -1
  248. package/dist/plugin/tool-registry.d.ts.map +1 -1
  249. package/dist/{recomp-orchestrator-f5m2wk74.js → recomp-orchestrator-3atsasjy.js} +6 -6
  250. package/dist/{send-session-notification-hdh02wy9.js → send-session-notification-1qeqkswb.js} +6 -4
  251. package/dist/shared/cache-ttl-display.d.ts +23 -0
  252. package/dist/shared/cache-ttl-display.d.ts.map +1 -0
  253. package/dist/shared/cache-ttl-seed.d.ts +10 -0
  254. package/dist/shared/cache-ttl-seed.d.ts.map +1 -0
  255. package/dist/shared/child-session-teardown.d.ts +34 -0
  256. package/dist/shared/child-session-teardown.d.ts.map +1 -0
  257. package/dist/shared/config-diagnostics.d.ts +30 -0
  258. package/dist/shared/config-diagnostics.d.ts.map +1 -0
  259. package/dist/shared/config-warning-surface.d.ts +3 -0
  260. package/dist/shared/config-warning-surface.d.ts.map +1 -0
  261. package/dist/shared/data-path.d.ts +10 -9
  262. package/dist/shared/data-path.d.ts.map +1 -1
  263. package/dist/shared/harness-provider-map.d.ts +7 -0
  264. package/dist/shared/harness-provider-map.d.ts.map +1 -1
  265. package/dist/shared/jsonc-parser.d.ts +12 -0
  266. package/dist/shared/jsonc-parser.d.ts.map +1 -1
  267. package/dist/shared/keep-subagents.d.ts +1 -1
  268. package/dist/shared/keep-subagents.d.ts.map +1 -1
  269. package/dist/shared/model-suggestion-retry.d.ts +15 -11
  270. package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
  271. package/dist/shared/opencode-db-path.d.ts +24 -0
  272. package/dist/shared/opencode-db-path.d.ts.map +1 -0
  273. package/dist/shared/pi-executable.d.ts +6 -0
  274. package/dist/shared/pi-executable.d.ts.map +1 -0
  275. package/dist/shared/prompt-surface-runtime.d.ts +10 -5
  276. package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
  277. package/dist/shared/resolve-fallbacks.d.ts +1 -1
  278. package/dist/shared/rpc-types.d.ts +117 -0
  279. package/dist/shared/rpc-types.d.ts.map +1 -1
  280. package/dist/shared/rpc-utils.d.ts.map +1 -1
  281. package/dist/shared/sqlite.d.ts +42 -0
  282. package/dist/shared/sqlite.d.ts.map +1 -1
  283. package/dist/shared/status-detail-text.d.ts.map +1 -1
  284. package/dist/shared/subagent-runner.d.ts +17 -10
  285. package/dist/shared/subagent-runner.d.ts.map +1 -1
  286. package/dist/shared/tag-transcript.d.ts.map +1 -1
  287. package/dist/shared/transcript.d.ts +6 -0
  288. package/dist/shared/transcript.d.ts.map +1 -1
  289. package/dist/shared/window-geometry.d.ts +23 -0
  290. package/dist/shared/window-geometry.d.ts.map +1 -1
  291. package/dist/shared/write-transaction-timing.d.ts +1 -1
  292. package/dist/shared/write-transaction-timing.d.ts.map +1 -1
  293. package/dist/{storage-meta-session-3rxx9ekm.js → storage-meta-session-e6g0mmec.js} +2 -2
  294. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  295. package/dist/tools/ctx-memory/verification-recording.d.ts.map +1 -1
  296. package/dist/tools/ctx-note/constants.d.ts +1 -1
  297. package/dist/tools/ctx-note/constants.d.ts.map +1 -1
  298. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  299. package/dist/tools/ctx-note/types.d.ts +1 -0
  300. package/dist/tools/ctx-note/types.d.ts.map +1 -1
  301. package/dist/tools/ctx-reduce/tools.d.ts +10 -1
  302. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  303. package/dist/tools/ctx-search/constants.d.ts +1 -1
  304. package/dist/tools/ctx-search/constants.d.ts.map +1 -1
  305. package/dist/tools/light-descriptions.d.ts +2 -2
  306. package/dist/tools/light-descriptions.d.ts.map +1 -1
  307. package/package.json +2 -3
  308. package/src/agents/hidden-agent-registrations.ts +3 -15
  309. package/src/agents/magic-context-prompt.test.ts +7 -5
  310. package/src/agents/magic-context-prompt.ts +22 -30
  311. package/src/agents/permissions.test.ts +1 -42
  312. package/src/agents/permissions.ts +1 -24
  313. package/src/config/agent-disable.ts +5 -22
  314. package/src/config/index.test.ts +169 -14
  315. package/src/config/index.ts +137 -21
  316. package/src/config/latch-permanence-guard.test.ts +4 -0
  317. package/src/config/migrate-config-location.test.ts +39 -6
  318. package/src/config/migrate-config-location.ts +18 -31
  319. package/src/config/profiles.ts +1 -1
  320. package/src/config/project-security.hostile-matrix.test.ts +0 -8
  321. package/src/config/project-security.test.ts +11 -22
  322. package/src/config/project-security.ts +103 -9
  323. package/src/config/protected-tokens.test.ts +235 -0
  324. package/src/config/removed-agent-config.ts +45 -0
  325. package/src/config/removed-agent-source-fence.test.ts +56 -0
  326. package/src/config/schema/magic-context.test.ts +21 -42
  327. package/src/config/schema/magic-context.ts +46 -37
  328. package/src/shared/cache-ttl-display.test.ts +84 -0
  329. package/src/shared/cache-ttl-display.ts +64 -0
  330. package/src/shared/cache-ttl-seed.ts +23 -0
  331. package/src/shared/child-session-teardown.test.ts +218 -0
  332. package/src/shared/child-session-teardown.ts +66 -0
  333. package/src/shared/config-diagnostics.test.ts +29 -0
  334. package/src/shared/config-diagnostics.ts +64 -0
  335. package/src/shared/config-warning-surface.ts +28 -0
  336. package/src/shared/data-path.ts +10 -9
  337. package/src/shared/harness-provider-map.test.ts +16 -0
  338. package/src/shared/harness-provider-map.ts +15 -0
  339. package/src/shared/jsonc-parser.test.ts +14 -1
  340. package/src/shared/jsonc-parser.ts +93 -0
  341. package/src/shared/keep-subagents.ts +9 -14
  342. package/src/shared/model-suggestion-retry.ts +144 -29
  343. package/src/shared/opencode-db-path.test.ts +196 -0
  344. package/src/shared/opencode-db-path.ts +215 -0
  345. package/src/shared/pi-executable.ts +20 -0
  346. package/src/shared/prompt-surface-a1-golden.md +50 -35
  347. package/src/shared/prompt-surface-runtime.test.ts +28 -0
  348. package/src/shared/prompt-surface-runtime.ts +22 -5
  349. package/src/shared/resolve-fallbacks.ts +1 -1
  350. package/src/shared/rpc-types.ts +124 -0
  351. package/src/shared/rpc-utils.ts +2 -3
  352. package/src/shared/session-messages-bounded.test.ts +1 -1
  353. package/src/shared/sqlite-import-fence.test.ts +19 -0
  354. package/src/shared/sqlite.test.ts +27 -1
  355. package/src/shared/sqlite.ts +204 -1
  356. package/src/shared/status-detail-text.test.ts +8 -4
  357. package/src/shared/status-detail-text.ts +20 -1
  358. package/src/shared/subagent-runner.ts +17 -10
  359. package/src/shared/tag-transcript.ts +53 -22
  360. package/src/shared/transcript.ts +7 -0
  361. package/src/shared/window-geometry.test.ts +44 -0
  362. package/src/shared/window-geometry.ts +89 -11
  363. package/src/shared/write-transaction-attribution.test.ts +241 -0
  364. package/src/shared/write-transaction-timing.ts +2 -1
  365. package/src/tui/index.tsx +18 -4
  366. package/src/tui/slots/sidebar-content.tsx +12 -7
  367. package/src/tui-compiled/index.tsx +237 -225
  368. package/src/tui-compiled/slots/sidebar-content.tsx +301 -305
  369. package/dist/agents/sidekick.d.ts +0 -2
  370. package/dist/agents/sidekick.d.ts.map +0 -1
  371. package/dist/features/magic-context/defaults.d.ts +0 -2
  372. package/dist/features/magic-context/defaults.d.ts.map +0 -1
  373. package/dist/features/magic-context/sidekick/agent.d.ts +0 -14
  374. package/dist/features/magic-context/sidekick/agent.d.ts.map +0 -1
  375. package/dist/features/magic-context/sidekick/core.d.ts +0 -38
  376. package/dist/features/magic-context/sidekick/core.d.ts.map +0 -1
  377. package/dist/features/magic-context/sidekick/index.d.ts +0 -3
  378. package/dist/features/magic-context/sidekick/index.d.ts.map +0 -1
  379. package/dist/hooks/magic-context/boundary-execution.d.ts +0 -27
  380. package/dist/hooks/magic-context/boundary-execution.d.ts.map +0 -1
  381. package/dist/index-jfpk81yt.js +0 -146
  382. package/dist/index-raatkvw5.js +0 -360
  383. package/src/agents/sidekick.ts +0 -1
  384. package/src/features/magic-context/defaults.ts +0 -1
@@ -3,6 +3,7 @@ import type { createOpencodeClient } from "@opencode-ai/sdk";
3
3
  import { detectOverflow } from "../features/magic-context/overflow-detection";
4
4
  import { log } from "./logger";
5
5
  import type { ModelInput } from "./model-resolution";
6
+ import { sanitizeDiagnosticText } from "./redaction";
6
7
  import { parseProviderModel, toModelEntry } from "./resolve-fallbacks";
7
8
 
8
9
  type Client = ReturnType<typeof createOpencodeClient>;
@@ -70,7 +71,7 @@ export interface PromptRetryOptions {
70
71
  fallbackModels?: readonly ModelInput[];
71
72
  /**
72
73
  * Identifier for structured logging (e.g. "dreamer:consolidate",
73
- * "historian", "compressor", "sidekick"). Helps correlate fallback
74
+ * "historian", "compressor", "dreamer"). Helps correlate fallback
74
75
  * attempts to a specific call site in `magic-context.log`. Defaults to
75
76
  * "subagent" if not provided.
76
77
  */
@@ -100,6 +101,33 @@ export interface ValidatedPromptRetryResult<TOutput, TValidated> {
100
101
  attempt: PromptAttemptInfo;
101
102
  }
102
103
 
104
+ export type PromptFailureClass =
105
+ | "provider_timeout"
106
+ | "provider_error"
107
+ | "empty_completion"
108
+ | "no_models"
109
+ | "child_aborted"
110
+ | "parse_failed"
111
+ | "unknown";
112
+
113
+ /** Diagnostic facts retained when a validated child prompt ultimately fails. */
114
+ export interface PromptFailureDetail {
115
+ failureClass: PromptFailureClass;
116
+ modelAttempted: string | null;
117
+ modelsTried: string[];
118
+ providerError: string | null;
119
+ timeoutMs: number | null;
120
+ childSessionId: string | null;
121
+ }
122
+
123
+ const promptFailureDetails = new WeakMap<object, PromptFailureDetail>();
124
+
125
+ export function getPromptFailureDetail(error: unknown): PromptFailureDetail | null {
126
+ return error !== null && typeof error === "object"
127
+ ? (promptFailureDetails.get(error) ?? null)
128
+ : null;
129
+ }
130
+
103
131
  export interface ModelSuggestionInfo {
104
132
  providerID: string;
105
133
  modelID: string;
@@ -279,6 +307,61 @@ function shortErr(error: unknown): string {
279
307
  return extractMessage(error);
280
308
  }
281
309
 
310
+ type PromptFailurePhase = "prompt" | "output" | "validation";
311
+
312
+ interface FailedAttempt {
313
+ error: unknown;
314
+ failureClass: PromptFailureClass;
315
+ attempt: PromptAttemptInfo;
316
+ }
317
+
318
+ function classifyPromptFailure(
319
+ error: unknown,
320
+ phase: PromptFailurePhase,
321
+ externalSignal?: AbortSignal,
322
+ ): PromptFailureClass {
323
+ const message = extractMessage(error);
324
+ if (externalSignal?.aborted || message === "prompt aborted by external signal") {
325
+ return "child_aborted";
326
+ }
327
+ if (/^prompt timed out after \d+ms$/.test(message)) return "provider_timeout";
328
+ if (phase === "validation") {
329
+ if (/returned no (?:assistant )?output|no assistant output/i.test(message)) {
330
+ return "empty_completion";
331
+ }
332
+ if (error instanceof Error && error.name === "DreamerProviderOutputFailureError") {
333
+ return "provider_error";
334
+ }
335
+ return "parse_failed";
336
+ }
337
+ return phase === "prompt" || phase === "output" ? "provider_error" : "unknown";
338
+ }
339
+
340
+ function throwWithPromptFailure(
341
+ legacyError: unknown,
342
+ failedAttempts: FailedAttempt[],
343
+ args: PromptArgs,
344
+ timeoutMs: number,
345
+ ): never {
346
+ const error =
347
+ legacyError instanceof Error ? legacyError : new Error(extractMessage(legacyError));
348
+ const last = failedAttempts.at(-1);
349
+ const failureClass = last?.failureClass ?? "unknown";
350
+ const providerError =
351
+ failureClass === "provider_error"
352
+ ? sanitizeDiagnosticText(shortErr(last?.error ?? legacyError)).slice(0, 500)
353
+ : null;
354
+ promptFailureDetails.set(error, {
355
+ failureClass,
356
+ modelAttempted: last?.attempt.label ?? null,
357
+ modelsTried: failedAttempts.map((failure) => failure.attempt.label),
358
+ providerError,
359
+ timeoutMs: failureClass === "provider_timeout" ? timeoutMs : null,
360
+ childSessionId: args.path.id || null,
361
+ });
362
+ throw error;
363
+ }
364
+
282
365
  /**
283
366
  * Try a single prompt attempt against the supplied body, with the existing
284
367
  * single-suggestion retry layered inside (so "did you mean X?" still self-heals
@@ -449,23 +532,43 @@ async function attemptAndValidate<TOutput, TValidated>(
449
532
  attempt: PromptAttemptInfo,
450
533
  options: ValidatedPromptRetryOptions<TOutput, TValidated>,
451
534
  ): Promise<ValidatedPromptRetryResult<TOutput, TValidated>> {
452
- await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
453
- const output = await options.fetchOutput(args, attempt);
454
- const validated = await options.validateOutput(output, attempt);
455
- return { output, validated, attempt };
535
+ try {
536
+ await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
537
+ } catch (error) {
538
+ throw {
539
+ error,
540
+ failureClass: classifyPromptFailure(error, "prompt", signal),
541
+ attempt,
542
+ } satisfies FailedAttempt;
543
+ }
544
+
545
+ let output: TOutput;
546
+ try {
547
+ output = await options.fetchOutput(args, attempt);
548
+ } catch (error) {
549
+ throw {
550
+ error,
551
+ failureClass: classifyPromptFailure(error, "output", signal),
552
+ attempt,
553
+ } satisfies FailedAttempt;
554
+ }
555
+
556
+ try {
557
+ const validated = await options.validateOutput(output, attempt);
558
+ return { output, validated, attempt };
559
+ } catch (error) {
560
+ throw {
561
+ error,
562
+ failureClass: classifyPromptFailure(error, "validation", signal),
563
+ attempt,
564
+ } satisfies FailedAttempt;
565
+ }
456
566
  }
457
567
 
458
568
  /**
459
- * Run a prompt with model fallback support, but accept an attempt only after the
460
- * caller validates the model's actual output. This covers "empty success" cases
461
- * where the provider/OpenCode prompt call completes successfully but the subagent
462
- * produced no usable assistant text / JSON.
463
- *
464
- * The happy path is still one prompt + one caller-owned output fetch: callers
465
- * should use the returned output instead of fetching messages a second time.
466
- * Validation failures are retryable across configured fallback models. If every
467
- * attempt produces invalid output (or otherwise fails retryably), the first
468
- * failure is re-thrown so callers surface the original failure semantics.
569
+ * Run a prompt across the configured model chain and retain which attempt failed.
570
+ * Output validation remains caller-owned so empty completions and malformed
571
+ * responses can be distinguished from provider transport failures.
469
572
  */
470
573
  export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = TOutput>(
471
574
  client: Client,
@@ -486,7 +589,7 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
486
589
  ? `${baseBody.model.providerID}/${baseBody.model.modelID}`
487
590
  : "primary";
488
591
  const totalAttempts = fallbacks.length + 1;
489
-
592
+ const failedAttempts: FailedAttempt[] = [];
490
593
  let firstError: unknown = null;
491
594
  let lastError: unknown = null;
492
595
 
@@ -506,17 +609,21 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
506
609
  },
507
610
  options,
508
611
  );
509
- } catch (error) {
510
- firstError = error;
511
- lastError = error;
512
- if (isNonRetryable(error, options.signal)) throw error;
612
+ } catch (caught) {
613
+ const failure = caught as FailedAttempt;
614
+ failedAttempts.push(failure);
615
+ firstError = failure.error;
616
+ lastError = failure.error;
617
+ if (isNonRetryable(failure.error, options.signal)) {
618
+ throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
619
+ }
513
620
 
514
621
  if (fallbacks.length === 0) {
515
- throw error;
622
+ throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
516
623
  }
517
624
 
518
625
  log(
519
- `[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`,
626
+ `[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(failure.error)}; trying ${fallbacks.length} fallback(s)`,
520
627
  );
521
628
  }
522
629
 
@@ -557,22 +664,30 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
557
664
  `[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`,
558
665
  );
559
666
  return result;
560
- } catch (error) {
561
- if (firstError === null) firstError = error;
562
- lastError = error;
563
- if (isNonRetryable(error, options.signal)) throw error;
667
+ } catch (caught) {
668
+ const failure = caught as FailedAttempt;
669
+ failedAttempts.push(failure);
670
+ lastError = failure.error;
671
+ if (isNonRetryable(failure.error, options.signal)) {
672
+ throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
673
+ }
564
674
 
565
675
  const remaining = fallbacks.length - i - 1;
566
676
  if (remaining > 0) {
567
677
  log(
568
- `[${callContext}] ${label} failed validation/prompt: ${shortErr(error)}; ${remaining} fallback(s) left`,
678
+ `[${callContext}] ${label} failed validation/prompt: ${shortErr(failure.error)}; ${remaining} fallback(s) left`,
569
679
  );
570
680
  }
571
681
  }
572
682
  }
573
683
 
574
684
  log(
575
- `[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks.map((fallback) => toModelEntry(fallback)?.model ?? String(fallback))].join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`,
685
+ `[${callContext}] all models exhausted; tried: ${failedAttempts.map((failure) => failure.attempt.label).join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`,
686
+ );
687
+ throwWithPromptFailure(
688
+ firstError ?? lastError ?? new Error("All fallback models failed validation"),
689
+ failedAttempts,
690
+ args,
691
+ timeoutMs,
576
692
  );
577
- throw firstError ?? lastError ?? new Error("All fallback models failed validation");
578
693
  }
@@ -0,0 +1,196 @@
1
+ /// <reference types="bun-types" />
2
+
3
+ import { afterEach, describe, expect, it } from "bun:test";
4
+ import { mkdirSync, mkdtempSync, rmSync, unlinkSync, utimesSync, writeFileSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ import {
8
+ formatOpenCodeDbDoctorLine,
9
+ formatOpenCodeDbMissingBanner,
10
+ formatOpenCodeDbMissingStatusLine,
11
+ openCodeDbPathExists,
12
+ resetOpenCodeDbPathStateForTesting,
13
+ resolveOpenCodeDbPath,
14
+ } from "./opencode-db-path";
15
+
16
+ const ORIGINAL_ENV = {
17
+ XDG_DATA_HOME: process.env.XDG_DATA_HOME,
18
+ OPENCODE_DB: process.env.OPENCODE_DB,
19
+ OPENCODE_DISABLE_CHANNEL_DB: process.env.OPENCODE_DISABLE_CHANNEL_DB,
20
+ OPENCODE_CHANNEL: process.env.OPENCODE_CHANNEL,
21
+ };
22
+ const tempDirs: string[] = [];
23
+
24
+ function restore(name: keyof typeof ORIGINAL_ENV): void {
25
+ const value = ORIGINAL_ENV[name];
26
+ if (value === undefined) delete process.env[name];
27
+ else process.env[name] = value;
28
+ }
29
+
30
+ afterEach(() => {
31
+ restore("XDG_DATA_HOME");
32
+ restore("OPENCODE_DB");
33
+ restore("OPENCODE_DISABLE_CHANNEL_DB");
34
+ restore("OPENCODE_CHANNEL");
35
+ resetOpenCodeDbPathStateForTesting();
36
+ for (const directory of tempDirs.splice(0)) rmSync(directory, { recursive: true, force: true });
37
+ });
38
+
39
+ function useDataHome(): { dataHome: string; openCodeDir: string } {
40
+ const dataHome = mkdtempSync(join(tmpdir(), "opencode-db-path-"));
41
+ const openCodeDir = join(dataHome, "opencode");
42
+ mkdirSync(openCodeDir, { recursive: true });
43
+ tempDirs.push(dataHome);
44
+ process.env.XDG_DATA_HOME = dataHome;
45
+ delete process.env.OPENCODE_DB;
46
+ delete process.env.OPENCODE_DISABLE_CHANNEL_DB;
47
+ delete process.env.OPENCODE_CHANNEL;
48
+ resetOpenCodeDbPathStateForTesting();
49
+ return { dataHome, openCodeDir };
50
+ }
51
+
52
+ function createCandidate(openCodeDir: string, name: string, mtimeMs: number): string {
53
+ const path = join(openCodeDir, name);
54
+ writeFileSync(path, "sqlite fixture");
55
+ const time = new Date(mtimeMs);
56
+ utimesSync(path, time, time);
57
+ return path;
58
+ }
59
+
60
+ describe("resolveOpenCodeDbPath", () => {
61
+ it("honors absolute and relative OPENCODE_DB overrides before every other source", () => {
62
+ const { openCodeDir } = useDataHome();
63
+ const absolute = join(openCodeDir, "elsewhere.db");
64
+ process.env.OPENCODE_DB = absolute;
65
+ process.env.OPENCODE_DISABLE_CHANNEL_DB = "true";
66
+ process.env.OPENCODE_CHANNEL = "dev";
67
+ expect(resolveOpenCodeDbPath()).toEqual({
68
+ path: absolute,
69
+ source: "OPENCODE_DB",
70
+ channel: null,
71
+ });
72
+
73
+ process.env.OPENCODE_DB = "relative.db";
74
+ expect(resolveOpenCodeDbPath()).toEqual({
75
+ path: join(openCodeDir, "relative.db"),
76
+ source: "OPENCODE_DB",
77
+ channel: null,
78
+ });
79
+ });
80
+
81
+ it("treats OPENCODE_DB=:memory: as an explicit but absent store", () => {
82
+ useDataHome();
83
+ process.env.OPENCODE_DB = ":memory:";
84
+ const resolution = resolveOpenCodeDbPath();
85
+ expect(resolution).toEqual({ path: ":memory:", source: "OPENCODE_DB", channel: null });
86
+ expect(openCodeDbPathExists(resolution)).toBe(false);
87
+ });
88
+
89
+ it("uses the default DB when channel databases are disabled", () => {
90
+ const { openCodeDir } = useDataHome();
91
+ process.env.OPENCODE_DISABLE_CHANNEL_DB = "1";
92
+ process.env.OPENCODE_CHANNEL = "dev";
93
+ expect(resolveOpenCodeDbPath()).toEqual({
94
+ path: join(openCodeDir, "opencode.db"),
95
+ source: "default",
96
+ channel: null,
97
+ });
98
+ });
99
+
100
+ it("honors an OPENCODE_CHANNEL visible to the plugin", () => {
101
+ const { openCodeDir } = useDataHome();
102
+ process.env.OPENCODE_CHANNEL = "dev";
103
+ expect(resolveOpenCodeDbPath()).toEqual({
104
+ path: join(openCodeDir, "opencode-dev.db"),
105
+ source: "channel",
106
+ channel: "dev",
107
+ });
108
+ });
109
+
110
+ for (const selectedName of [
111
+ "opencode.db",
112
+ "opencode-local.db",
113
+ "opencode-dev.db",
114
+ "opencode-nightly.db",
115
+ ]) {
116
+ it(`discovers ${selectedName} when it is the newest existing candidate`, () => {
117
+ const { openCodeDir } = useDataHome();
118
+ const names = [
119
+ "opencode.db",
120
+ "opencode-local.db",
121
+ "opencode-dev.db",
122
+ "opencode-nightly.db",
123
+ ];
124
+ for (const [index, name] of names.entries()) {
125
+ createCandidate(openCodeDir, name, name === selectedName ? 10_000 : 1_000 + index);
126
+ }
127
+
128
+ expect(resolveOpenCodeDbPath()).toEqual({
129
+ path: join(openCodeDir, selectedName),
130
+ source: "discovered",
131
+ channel:
132
+ selectedName === "opencode.db"
133
+ ? null
134
+ : selectedName.slice("opencode-".length, -".db".length),
135
+ });
136
+ });
137
+ }
138
+
139
+ it("uses candidate order as the deterministic tie-breaker for equal mtimes", () => {
140
+ const { openCodeDir } = useDataHome();
141
+ for (const name of ["opencode-local.db", "opencode-dev.db", "opencode.db"]) {
142
+ createCandidate(openCodeDir, name, 5_000);
143
+ }
144
+ expect(resolveOpenCodeDbPath()).toEqual({
145
+ path: join(openCodeDir, "opencode.db"),
146
+ source: "discovered",
147
+ channel: null,
148
+ });
149
+ });
150
+
151
+ it("returns the default path by value when no candidate exists", () => {
152
+ const { openCodeDir } = useDataHome();
153
+ const resolution = resolveOpenCodeDbPath();
154
+ expect(resolution).toEqual({
155
+ path: join(openCodeDir, "opencode.db"),
156
+ source: "default",
157
+ channel: null,
158
+ });
159
+ expect(openCodeDbPathExists(resolution)).toBe(false);
160
+ });
161
+
162
+ it("re-probes discovery when the memoized chosen file disappears", () => {
163
+ const { openCodeDir } = useDataHome();
164
+ const primary = createCandidate(openCodeDir, "opencode-local.db", 2_000);
165
+ const fallback = createCandidate(openCodeDir, "opencode.db", 1_000);
166
+ expect(resolveOpenCodeDbPath().path).toBe(primary);
167
+
168
+ unlinkSync(primary);
169
+ expect(resolveOpenCodeDbPath()).toEqual({
170
+ path: fallback,
171
+ source: "discovered",
172
+ channel: null,
173
+ });
174
+ });
175
+
176
+ it("formats the missing banner, status, and doctor lines by value", () => {
177
+ const { openCodeDir } = useDataHome();
178
+ const lookedFor = [
179
+ join(openCodeDir, "opencode.db"),
180
+ join(openCodeDir, "opencode-local.db"),
181
+ join(openCodeDir, "opencode-dev.db"),
182
+ join(openCodeDir, "opencode-<channel>.db"),
183
+ ].join(", ");
184
+ const resolution = resolveOpenCodeDbPath();
185
+
186
+ expect(formatOpenCodeDbMissingBanner(resolution)).toBe(
187
+ `Magic Context cannot find OpenCode's session database (looked for ${lookedFor}). History compaction (historian) and the mid-turn valve are disabled until it is found; set OPENCODE_DB if OpenCode stores it elsewhere.`,
188
+ );
189
+ expect(formatOpenCodeDbMissingStatusLine(resolution)).toBe(
190
+ `OpenCode DB: MISSING (looked for ${lookedFor}). History compaction (historian) and the mid-turn valve are disabled; set OPENCODE_DB if OpenCode stores it elsewhere.`,
191
+ );
192
+ expect(formatOpenCodeDbDoctorLine(resolution)).toBe(
193
+ `FAIL OpenCode session database: not found (looked for ${lookedFor}); set OPENCODE_DB if OpenCode stores it elsewhere.`,
194
+ );
195
+ });
196
+ });
@@ -0,0 +1,215 @@
1
+ import { existsSync, readdirSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join } from "node:path";
4
+
5
+ export type OpenCodeDbPathSource = "OPENCODE_DB" | "channel" | "default" | "discovered";
6
+
7
+ export interface OpenCodeDbPathResolution {
8
+ path: string;
9
+ source: OpenCodeDbPathSource;
10
+ channel: string | null;
11
+ }
12
+
13
+ export interface OpenCodeDbReadFailure extends OpenCodeDbPathResolution {
14
+ message: string;
15
+ }
16
+
17
+ interface CachedResolution {
18
+ key: string;
19
+ resolution: OpenCodeDbPathResolution;
20
+ existed: boolean;
21
+ }
22
+
23
+ let cachedResolution: CachedResolution | null = null;
24
+ let lastReadFailure: OpenCodeDbReadFailure | null = null;
25
+ const claimedDiagnostics = new Set<string>();
26
+
27
+ function openCodeDataDir(): string {
28
+ return join(process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), "opencode");
29
+ }
30
+
31
+ function environmentKey(dataDir: string): string {
32
+ return [
33
+ dataDir,
34
+ process.env.OPENCODE_DB ?? "",
35
+ process.env.OPENCODE_DISABLE_CHANNEL_DB ?? "",
36
+ process.env.OPENCODE_CHANNEL ?? "",
37
+ ].join("\0");
38
+ }
39
+
40
+ function channelPath(dataDir: string, channel: string): string {
41
+ return ["latest", "beta", "prod"].includes(channel)
42
+ ? join(dataDir, "opencode.db")
43
+ : join(dataDir, `opencode-${channel}.db`);
44
+ }
45
+
46
+ function discoveredCandidateNames(dataDir: string): string[] {
47
+ const names = ["opencode.db", "opencode-local.db", "opencode-dev.db"];
48
+ try {
49
+ const discovered = readdirSync(dataDir, { withFileTypes: true })
50
+ .filter((entry) => /^opencode-.+\.db$/.test(entry.name) && !names.includes(entry.name))
51
+ .map((entry) => entry.name)
52
+ .sort();
53
+ names.push(...discovered);
54
+ } catch {
55
+ // A missing or unreadable data directory has no discovered candidates.
56
+ }
57
+ return names;
58
+ }
59
+
60
+ function discoverOpenCodeDb(dataDir: string): OpenCodeDbPathResolution {
61
+ const candidates = discoveredCandidateNames(dataDir).map((name, order) => {
62
+ const path = join(dataDir, name);
63
+ try {
64
+ const metadata = statSync(path);
65
+ return metadata.isFile() ? { path, order, mtimeMs: metadata.mtimeMs } : null;
66
+ } catch {
67
+ return null;
68
+ }
69
+ });
70
+ const existing = candidates
71
+ .filter((candidate): candidate is NonNullable<typeof candidate> => candidate !== null)
72
+ .sort((left, right) => right.mtimeMs - left.mtimeMs || left.order - right.order)[0];
73
+
74
+ if (!existing) {
75
+ return { path: join(dataDir, "opencode.db"), source: "default", channel: null };
76
+ }
77
+
78
+ const name = existing.path.slice(dataDir.length + 1);
79
+ const channel =
80
+ name === "opencode.db" ? null : name.slice("opencode-".length, -".db".length) || null;
81
+ return { path: existing.path, source: "discovered", channel };
82
+ }
83
+
84
+ function resolveFresh(dataDir: string): OpenCodeDbPathResolution {
85
+ const explicit = process.env.OPENCODE_DB;
86
+ if (explicit !== undefined && explicit.length > 0) {
87
+ if (explicit === ":memory:") {
88
+ return { path: explicit, source: "OPENCODE_DB", channel: null };
89
+ }
90
+ return {
91
+ path: isAbsolute(explicit) ? explicit : join(dataDir, explicit),
92
+ source: "OPENCODE_DB",
93
+ channel: null,
94
+ };
95
+ }
96
+
97
+ const disableChannelDb = process.env.OPENCODE_DISABLE_CHANNEL_DB;
98
+ if (disableChannelDb === "1" || disableChannelDb === "true") {
99
+ return { path: join(dataDir, "opencode.db"), source: "default", channel: null };
100
+ }
101
+
102
+ const channel = process.env.OPENCODE_CHANNEL;
103
+ if (channel !== undefined && channel.length > 0) {
104
+ return { path: channelPath(dataDir, channel), source: "channel", channel };
105
+ }
106
+
107
+ return discoverOpenCodeDb(dataDir);
108
+ }
109
+
110
+ /** Resolve an explicit DB override first, then channel paths; discover candidates when the compiled channel is unknown. */
111
+ export function resolveOpenCodeDbPath(): OpenCodeDbPathResolution {
112
+ const dataDir = openCodeDataDir();
113
+ const key = environmentKey(dataDir);
114
+ if (
115
+ cachedResolution?.key === key &&
116
+ (!cachedResolution.existed || existsSync(cachedResolution.resolution.path))
117
+ ) {
118
+ if (cachedResolution.existed) return cachedResolution.resolution;
119
+ // An absent result is re-probed so a DB created after plugin boot is detected.
120
+ }
121
+
122
+ const resolution = resolveFresh(dataDir);
123
+ cachedResolution = {
124
+ key,
125
+ resolution,
126
+ existed: resolution.path !== ":memory:" && existsSync(resolution.path),
127
+ };
128
+ return resolution;
129
+ }
130
+
131
+ export function openCodeDbPathExists(
132
+ resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
133
+ ): boolean {
134
+ return resolution.path !== ":memory:" && existsSync(resolution.path);
135
+ }
136
+
137
+ export function getOpenCodeDbProbeDescriptions(
138
+ resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
139
+ ): string[] {
140
+ if (resolution.source === "OPENCODE_DB") return [resolution.path];
141
+ if (
142
+ resolution.source === "channel" ||
143
+ process.env.OPENCODE_DISABLE_CHANNEL_DB === "1" ||
144
+ process.env.OPENCODE_DISABLE_CHANNEL_DB === "true"
145
+ ) {
146
+ return [resolution.path];
147
+ }
148
+ const dataDir = openCodeDataDir();
149
+ return [
150
+ join(dataDir, "opencode.db"),
151
+ join(dataDir, "opencode-local.db"),
152
+ join(dataDir, "opencode-dev.db"),
153
+ join(dataDir, "opencode-<channel>.db"),
154
+ ];
155
+ }
156
+
157
+ function lookedForText(resolution: OpenCodeDbPathResolution): string {
158
+ return getOpenCodeDbProbeDescriptions(resolution).join(", ");
159
+ }
160
+
161
+ export function formatOpenCodeDbMissingBanner(
162
+ resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
163
+ ): string {
164
+ return `Magic Context cannot find OpenCode's session database (looked for ${lookedForText(resolution)}). History compaction (historian) and the mid-turn valve are disabled until it is found; set OPENCODE_DB if OpenCode stores it elsewhere.`;
165
+ }
166
+
167
+ export function formatOpenCodeDbMissingStatusLine(
168
+ resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
169
+ ): string {
170
+ return `OpenCode DB: MISSING (looked for ${lookedForText(resolution)}). History compaction (historian) and the mid-turn valve are disabled; set OPENCODE_DB if OpenCode stores it elsewhere.`;
171
+ }
172
+
173
+ export function formatOpenCodeDbDoctorLine(
174
+ resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
175
+ ): string {
176
+ return `FAIL OpenCode session database: not found (looked for ${lookedForText(resolution)}); set OPENCODE_DB if OpenCode stores it elsewhere.`;
177
+ }
178
+
179
+ export function formatOpenCodeDbReadFailureStatusLine(failure: OpenCodeDbReadFailure): string {
180
+ return `OpenCode DB: READ FAILED (path=${failure.path}, source=${failure.source}) — ${failure.message}`;
181
+ }
182
+
183
+ export function recordOpenCodeDbReadFailure(
184
+ resolution: OpenCodeDbPathResolution,
185
+ error: unknown,
186
+ ): OpenCodeDbReadFailure {
187
+ const message = error instanceof Error ? error.message : String(error);
188
+ lastReadFailure = { ...resolution, message };
189
+ return lastReadFailure;
190
+ }
191
+
192
+ export function clearOpenCodeDbReadFailure(path?: string): void {
193
+ if (path === undefined || lastReadFailure?.path === path) lastReadFailure = null;
194
+ }
195
+
196
+ export function getOpenCodeDbReadFailure(): OpenCodeDbReadFailure | null {
197
+ return lastReadFailure;
198
+ }
199
+
200
+ export function claimOpenCodeDbDiagnosticOnce(
201
+ surface: string,
202
+ resolution: OpenCodeDbPathResolution,
203
+ ): boolean {
204
+ const key = `${surface}\0${resolution.path}\0${resolution.source}`;
205
+ if (claimedDiagnostics.has(key)) return false;
206
+ claimedDiagnostics.add(key);
207
+ return true;
208
+ }
209
+
210
+ /** Test-only reset for process memoization and once-only diagnostics. */
211
+ export function resetOpenCodeDbPathStateForTesting(): void {
212
+ cachedResolution = null;
213
+ lastReadFailure = null;
214
+ claimedDiagnostics.clear();
215
+ }
@@ -0,0 +1,20 @@
1
+ export type PiExecutableHarnessKind = "pi" | "omp";
2
+
3
+ /** Executable images recognized as Pi-compatible hosts. */
4
+ export const PI_IMAGE_NAMES = new Set(["pi", "pi.cmd", "omp", "oh-my-pi"]);
5
+
6
+ /** Classify a process title or executable path using the shared Pi image vocabulary. */
7
+ export function piHarnessKindFromExecutable(
8
+ value: string | undefined,
9
+ ): PiExecutableHarnessKind | undefined {
10
+ const executable = (value ?? "")
11
+ .trim()
12
+ .replace(/^['"]|['"]$/g, "")
13
+ .replaceAll("\\", "/")
14
+ .split("/")
15
+ .at(-1)
16
+ ?.toLowerCase()
17
+ .replace(/\.(?:exe|cmd)$/, "");
18
+ if (!executable || !PI_IMAGE_NAMES.has(executable)) return undefined;
19
+ return executable === "pi" ? "pi" : "omp";
20
+ }