@cortexkit/opencode-magic-context 0.41.4 → 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 (328) 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 +5 -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-ejkc9sjn.js → conflict-warning-hook-6p3xqeac.js} +3 -3
  20. package/dist/{derive-budgets-bc366cz8.js → derive-budgets-s2pgnnm4.js} +1 -1
  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/task-executor.d.ts.map +1 -1
  37. package/dist/features/magic-context/dreamer/task-scheduler.d.ts +4 -0
  38. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  39. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  40. package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
  41. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts.map +1 -1
  42. package/dist/features/magic-context/index.d.ts +0 -1
  43. package/dist/features/magic-context/index.d.ts.map +1 -1
  44. package/dist/features/magic-context/memory/embedding-local.d.ts +13 -0
  45. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  46. package/dist/features/magic-context/memory/embedding-synapse.d.ts +43 -10
  47. package/dist/features/magic-context/memory/embedding-synapse.d.ts.map +1 -1
  48. package/dist/features/magic-context/memory/embedding.d.ts +1 -1
  49. package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
  50. package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
  51. package/dist/features/magic-context/memory/project-identity.d.ts +1 -0
  52. package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
  53. package/dist/features/magic-context/memory/promotion.d.ts +7 -1
  54. package/dist/features/magic-context/memory/promotion.d.ts.map +1 -1
  55. package/dist/features/magic-context/merged-reasoning-decisions.d.ts +6 -0
  56. package/dist/features/magic-context/merged-reasoning-decisions.d.ts.map +1 -0
  57. package/dist/features/magic-context/message-fts-rowid-map.d.ts.map +1 -1
  58. package/dist/features/magic-context/message-index-async.d.ts +12 -0
  59. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  60. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  61. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  62. package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
  63. package/dist/features/magic-context/mural/render-trigger.d.ts +6 -0
  64. package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
  65. package/dist/features/magic-context/no-content-compartment.d.ts +12 -0
  66. package/dist/features/magic-context/no-content-compartment.d.ts.map +1 -0
  67. package/dist/features/magic-context/pi-content-decisions.d.ts +9 -0
  68. package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -0
  69. package/dist/features/magic-context/project-docs-hash.d.ts +4 -0
  70. package/dist/features/magic-context/project-docs-hash.d.ts.map +1 -1
  71. package/dist/features/magic-context/project-embedding-registry.d.ts +13 -4
  72. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  73. package/dist/features/magic-context/protection-window.d.ts +79 -0
  74. package/dist/features/magic-context/protection-window.d.ts.map +1 -0
  75. package/dist/features/magic-context/search.d.ts.map +1 -1
  76. package/dist/features/magic-context/session-project-backfill.d.ts.map +1 -1
  77. package/dist/features/magic-context/shadow-backfill-state.d.ts +26 -0
  78. package/dist/features/magic-context/shadow-backfill-state.d.ts.map +1 -0
  79. package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
  80. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +4 -0
  81. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
  82. package/dist/features/magic-context/smart-notes/storage.d.ts.map +1 -1
  83. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  84. package/dist/features/magic-context/storage-db.d.ts +1 -1
  85. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  86. package/dist/features/magic-context/storage-embedding-measurements.d.ts +2 -2
  87. package/dist/features/magic-context/storage-embedding-measurements.d.ts.map +1 -1
  88. package/dist/features/magic-context/storage-historian-runs.d.ts +4 -0
  89. package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
  90. package/dist/features/magic-context/storage-memory-mutation-log.d.ts +1 -0
  91. package/dist/features/magic-context/storage-memory-mutation-log.d.ts.map +1 -1
  92. package/dist/features/magic-context/storage-meta-persisted.d.ts +79 -13
  93. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  94. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  95. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  96. package/dist/features/magic-context/storage-native-replay.d.ts +29 -0
  97. package/dist/features/magic-context/storage-native-replay.d.ts.map +1 -0
  98. package/dist/features/magic-context/storage-notes.d.ts +6 -0
  99. package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
  100. package/dist/features/magic-context/storage-replay-document.d.ts +33 -0
  101. package/dist/features/magic-context/storage-replay-document.d.ts.map +1 -0
  102. package/dist/features/magic-context/storage-subagent-invocations.d.ts +1 -1
  103. package/dist/features/magic-context/storage-subagent-invocations.d.ts.map +1 -1
  104. package/dist/features/magic-context/storage-tags.d.ts +19 -11
  105. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  106. package/dist/features/magic-context/storage.d.ts +3 -3
  107. package/dist/features/magic-context/storage.d.ts.map +1 -1
  108. package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
  109. package/dist/features/magic-context/tagger.d.ts +13 -0
  110. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  111. package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -1
  112. package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
  113. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
  114. package/dist/features/magic-context/workspaces.d.ts.map +1 -1
  115. package/dist/hooks/magic-context/apply-operations.d.ts +11 -5
  116. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  117. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  118. package/dist/hooks/magic-context/cache-busting-signals.d.ts +9 -1
  119. package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
  120. package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -1
  121. package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
  122. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  123. package/dist/hooks/magic-context/child-session-spawn.d.ts +3 -3
  124. package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -1
  125. package/dist/hooks/magic-context/command-handler.d.ts +1 -10
  126. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  127. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  128. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +10 -21
  129. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts.map +1 -1
  130. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  131. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  132. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +26 -2
  133. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  134. package/dist/hooks/magic-context/decay-render.d.ts.map +1 -1
  135. package/dist/hooks/magic-context/dropped-input-guard.d.ts +9 -0
  136. package/dist/hooks/magic-context/dropped-input-guard.d.ts.map +1 -0
  137. package/dist/hooks/magic-context/emergency-drop.d.ts +6 -1
  138. package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
  139. package/dist/hooks/magic-context/event-handler.d.ts +0 -1
  140. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  141. package/dist/hooks/magic-context/event-payloads.d.ts +7 -6
  142. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  143. package/dist/hooks/magic-context/event-resolvers.d.ts +1 -1
  144. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  145. package/dist/hooks/magic-context/execute-status.d.ts +1 -1
  146. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  147. package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -1
  148. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +7 -1
  149. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  150. package/dist/hooks/magic-context/hook-handlers.d.ts +3 -1
  151. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  152. package/dist/hooks/magic-context/hook.d.ts +13 -4
  153. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  154. package/dist/hooks/magic-context/inject-compartments.d.ts +14 -1
  155. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  156. package/dist/hooks/magic-context/live-session-state.d.ts +1 -1
  157. package/dist/hooks/magic-context/lkg-slot.d.ts +34 -0
  158. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  159. package/dist/hooks/magic-context/module-state-sync-timing.d.ts +21 -0
  160. package/dist/hooks/magic-context/module-state-sync-timing.d.ts.map +1 -0
  161. package/dist/hooks/magic-context/module-state-sync.d.ts +21 -7
  162. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  163. package/dist/hooks/magic-context/module-transport.d.ts +12 -0
  164. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  165. package/dist/hooks/magic-context/module-wire.d.ts +8 -0
  166. package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
  167. package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
  168. package/dist/hooks/magic-context/persist-filtered-noise.d.ts +5 -0
  169. package/dist/hooks/magic-context/persist-filtered-noise.d.ts.map +1 -0
  170. package/dist/hooks/magic-context/protected-tail-boundary.d.ts +6 -0
  171. package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
  172. package/dist/hooks/magic-context/read-session-chunk.d.ts +18 -18
  173. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  174. package/dist/hooks/magic-context/read-session-db.d.ts +22 -12
  175. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  176. package/dist/hooks/magic-context/read-session-formatting.d.ts +8 -0
  177. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  178. package/dist/hooks/magic-context/read-session-raw.d.ts +10 -1
  179. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  180. package/dist/hooks/magic-context/reference-retrieval.d.ts.map +1 -1
  181. package/dist/hooks/magic-context/rust-mode-transform.d.ts +74 -9
  182. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  183. package/dist/hooks/magic-context/send-session-notification.d.ts +11 -10
  184. package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
  185. package/dist/hooks/magic-context/strip-content.d.ts +7 -0
  186. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  187. package/dist/hooks/magic-context/supersession-reclaim.d.ts +13 -0
  188. package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
  189. package/dist/hooks/magic-context/system-prompt-hash.d.ts +2 -3
  190. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  191. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +8 -3
  192. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
  193. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  194. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  195. package/dist/hooks/magic-context/transform-context-state.d.ts +7 -1
  196. package/dist/hooks/magic-context/transform-context-state.d.ts.map +1 -1
  197. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +11 -4
  198. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  199. package/dist/hooks/magic-context/transform.d.ts +7 -2
  200. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  201. package/dist/hooks/magic-context/upgrade-reminder.d.ts +7 -9
  202. package/dist/hooks/magic-context/upgrade-reminder.d.ts.map +1 -1
  203. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  204. package/dist/index-1974jvn3.js +783 -0
  205. package/dist/{index-4avmwfp5.js → index-36wd1d6k.js} +6 -29
  206. package/dist/{index-0y2mcg5y.js → index-c9hamh36.js} +18332 -16226
  207. package/dist/{index-fz104x1j.js → index-jmtjh2d3.js} +3 -3
  208. package/dist/{index-q264erdp.js → index-nqgjwfzn.js} +70 -38
  209. package/dist/{index-mjv9je4e.js → index-xdtge3y2.js} +45 -7
  210. package/dist/index.d.ts.map +1 -1
  211. package/dist/index.js +6199 -5088
  212. package/dist/plugin/conflict-warning-hook.d.ts +5 -6
  213. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  214. package/dist/plugin/dream-timer.d.ts +1 -0
  215. package/dist/plugin/dream-timer.d.ts.map +1 -1
  216. package/dist/plugin/embedding-routing.d.ts +4 -1
  217. package/dist/plugin/embedding-routing.d.ts.map +1 -1
  218. package/dist/plugin/hooks/create-session-hooks.d.ts +16 -2
  219. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  220. package/dist/plugin/rpc-handlers.d.ts +31 -1
  221. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  222. package/dist/plugin/rust-tool-backends.d.ts +1 -0
  223. package/dist/plugin/rust-tool-backends.d.ts.map +1 -1
  224. package/dist/plugin/tool-registry.d.ts.map +1 -1
  225. package/dist/{recomp-orchestrator-q98r6rga.js → recomp-orchestrator-3atsasjy.js} +5 -5
  226. package/dist/{send-session-notification-hdh02wy9.js → send-session-notification-1qeqkswb.js} +6 -4
  227. package/dist/shared/child-session-teardown.d.ts +34 -0
  228. package/dist/shared/child-session-teardown.d.ts.map +1 -0
  229. package/dist/shared/keep-subagents.d.ts +1 -1
  230. package/dist/shared/keep-subagents.d.ts.map +1 -1
  231. package/dist/shared/model-suggestion-retry.d.ts +1 -1
  232. package/dist/shared/opencode-db-path.d.ts +24 -0
  233. package/dist/shared/opencode-db-path.d.ts.map +1 -0
  234. package/dist/shared/prompt-surface-runtime.d.ts +10 -5
  235. package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
  236. package/dist/shared/resolve-fallbacks.d.ts +1 -1
  237. package/dist/shared/rpc-types.d.ts +111 -0
  238. package/dist/shared/rpc-types.d.ts.map +1 -1
  239. package/dist/shared/sqlite.d.ts +42 -0
  240. package/dist/shared/sqlite.d.ts.map +1 -1
  241. package/dist/shared/status-detail-text.d.ts.map +1 -1
  242. package/dist/shared/subagent-runner.d.ts +17 -10
  243. package/dist/shared/subagent-runner.d.ts.map +1 -1
  244. package/dist/shared/tag-transcript.d.ts.map +1 -1
  245. package/dist/shared/transcript.d.ts +6 -0
  246. package/dist/shared/transcript.d.ts.map +1 -1
  247. package/dist/shared/window-geometry.d.ts +23 -0
  248. package/dist/shared/window-geometry.d.ts.map +1 -1
  249. package/dist/shared/write-transaction-timing.d.ts +1 -1
  250. package/dist/shared/write-transaction-timing.d.ts.map +1 -1
  251. package/dist/{storage-meta-session-dvwfw778.js → storage-meta-session-e6g0mmec.js} +2 -2
  252. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  253. package/dist/tools/ctx-memory/verification-recording.d.ts.map +1 -1
  254. package/dist/tools/ctx-note/constants.d.ts +1 -1
  255. package/dist/tools/ctx-note/constants.d.ts.map +1 -1
  256. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  257. package/dist/tools/ctx-note/types.d.ts +1 -0
  258. package/dist/tools/ctx-note/types.d.ts.map +1 -1
  259. package/dist/tools/ctx-reduce/tools.d.ts +10 -1
  260. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  261. package/dist/tools/ctx-search/constants.d.ts +1 -1
  262. package/dist/tools/ctx-search/constants.d.ts.map +1 -1
  263. package/dist/tools/light-descriptions.d.ts +2 -2
  264. package/dist/tools/light-descriptions.d.ts.map +1 -1
  265. package/package.json +2 -3
  266. package/src/agents/hidden-agent-registrations.ts +3 -15
  267. package/src/agents/magic-context-prompt.test.ts +7 -5
  268. package/src/agents/magic-context-prompt.ts +22 -30
  269. package/src/agents/permissions.test.ts +1 -42
  270. package/src/agents/permissions.ts +1 -24
  271. package/src/config/agent-disable.ts +5 -22
  272. package/src/config/index.test.ts +126 -15
  273. package/src/config/index.ts +41 -6
  274. package/src/config/latch-permanence-guard.test.ts +4 -0
  275. package/src/config/migrate-config-location.test.ts +39 -6
  276. package/src/config/migrate-config-location.ts +18 -31
  277. package/src/config/profiles.ts +1 -1
  278. package/src/config/project-security.hostile-matrix.test.ts +0 -8
  279. package/src/config/project-security.test.ts +11 -22
  280. package/src/config/project-security.ts +103 -9
  281. package/src/config/protected-tokens.test.ts +235 -0
  282. package/src/config/removed-agent-config.ts +45 -0
  283. package/src/config/removed-agent-source-fence.test.ts +56 -0
  284. package/src/config/schema/magic-context.test.ts +21 -42
  285. package/src/config/schema/magic-context.ts +46 -37
  286. package/src/shared/child-session-teardown.test.ts +218 -0
  287. package/src/shared/child-session-teardown.ts +66 -0
  288. package/src/shared/keep-subagents.ts +9 -14
  289. package/src/shared/model-suggestion-retry.ts +1 -1
  290. package/src/shared/opencode-db-path.test.ts +196 -0
  291. package/src/shared/opencode-db-path.ts +215 -0
  292. package/src/shared/prompt-surface-a1-golden.md +50 -35
  293. package/src/shared/prompt-surface-runtime.test.ts +28 -0
  294. package/src/shared/prompt-surface-runtime.ts +22 -5
  295. package/src/shared/resolve-fallbacks.ts +1 -1
  296. package/src/shared/rpc-types.ts +118 -0
  297. package/src/shared/session-messages-bounded.test.ts +1 -1
  298. package/src/shared/sqlite-import-fence.test.ts +19 -0
  299. package/src/shared/sqlite.test.ts +27 -1
  300. package/src/shared/sqlite.ts +204 -1
  301. package/src/shared/status-detail-text.test.ts +6 -3
  302. package/src/shared/status-detail-text.ts +15 -0
  303. package/src/shared/subagent-runner.ts +17 -10
  304. package/src/shared/tag-transcript.ts +33 -21
  305. package/src/shared/transcript.ts +7 -0
  306. package/src/shared/window-geometry.test.ts +44 -0
  307. package/src/shared/window-geometry.ts +89 -11
  308. package/src/shared/write-transaction-attribution.test.ts +241 -0
  309. package/src/shared/write-transaction-timing.ts +2 -1
  310. package/src/tui/index.tsx +3 -3
  311. package/src/tui/slots/sidebar-content.tsx +12 -7
  312. package/src/tui-compiled/index.tsx +3 -3
  313. package/src/tui-compiled/slots/sidebar-content.tsx +301 -305
  314. package/dist/agents/sidekick.d.ts +0 -2
  315. package/dist/agents/sidekick.d.ts.map +0 -1
  316. package/dist/features/magic-context/defaults.d.ts +0 -2
  317. package/dist/features/magic-context/defaults.d.ts.map +0 -1
  318. package/dist/features/magic-context/sidekick/agent.d.ts +0 -14
  319. package/dist/features/magic-context/sidekick/agent.d.ts.map +0 -1
  320. package/dist/features/magic-context/sidekick/core.d.ts +0 -38
  321. package/dist/features/magic-context/sidekick/core.d.ts.map +0 -1
  322. package/dist/features/magic-context/sidekick/index.d.ts +0 -3
  323. package/dist/features/magic-context/sidekick/index.d.ts.map +0 -1
  324. package/dist/hooks/magic-context/boundary-execution.d.ts +0 -27
  325. package/dist/hooks/magic-context/boundary-execution.d.ts.map +0 -1
  326. package/dist/index-raatkvw5.js +0 -360
  327. package/src/agents/sidekick.ts +0 -1
  328. package/src/features/magic-context/defaults.ts +0 -1
@@ -39,12 +39,28 @@
39
39
  * `run()` → {changes,lastInsertRowid}) is identical and was verified directly.
40
40
  */
41
41
 
42
+ import { statSync } from "node:fs";
42
43
  // Type import only — runtime is loaded dynamically below. @types/better-sqlite3
43
44
  // has the richest definitions and is a structural superset of the API surface
44
45
  // we use, so calls typed against BetterSqlite3 work under bun:sqlite and
45
46
  // node:sqlite at runtime (both expose prepare/run/get/all/exec/close).
46
47
  import type BetterSqlite3 from "better-sqlite3";
47
48
 
49
+ /**
50
+ * Slow-write reporting is injected rather than imported: this module is also
51
+ * executed directly by Node (the `node:sqlite` smoke in CI, Pi, Desktop), and
52
+ * Node's type-stripping loader does not resolve extensionless imports, so any
53
+ * static import of the plugin's logging chain here breaks that path. The
54
+ * storage bootstrap registers the real reporter; until then slow privileged
55
+ * writes are simply not logged.
56
+ */
57
+ type SlowWriteReporter = (site: string, transactionStartedAt: number) => void;
58
+ let reportSlowPrivilegedWrite: SlowWriteReporter | undefined;
59
+
60
+ export function registerSlowWriteReporter(reporter: SlowWriteReporter): void {
61
+ reportSlowPrivilegedWrite = reporter;
62
+ }
63
+
48
64
  export type SqliteRuntime = "Bun" | "Node.js";
49
65
 
50
66
  type SqliteModule = {
@@ -158,6 +174,88 @@ const DatabaseImpl: typeof BetterSqlite3 = isBun
158
174
  ? (sqliteModule.Database as typeof BetterSqlite3)
159
175
  : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
160
176
 
177
+ interface TrackedSqliteConnection {
178
+ sequence: number;
179
+ filename: string;
180
+ readonly: boolean;
181
+ reference: WeakRef<BetterSqlite3.Database>;
182
+ }
183
+
184
+ export interface SqliteConnectionMemoryStats {
185
+ sequence: number;
186
+ filename: string;
187
+ readonly: boolean;
188
+ pageSize: number | null;
189
+ pageCount: number | null;
190
+ freelistCount: number | null;
191
+ cacheSize: number | null;
192
+ cacheSizeUnit: "pages" | "kib" | null;
193
+ cacheUpperBoundBytes: number | null;
194
+ mmapSizeBytes: number | null;
195
+ walFileBytes: number | null;
196
+ shmFileBytes: number | null;
197
+ fts5TableCount: number | null;
198
+ journalMode: string | null;
199
+ }
200
+
201
+ export interface SqliteMemoryStats {
202
+ connectionCount: number;
203
+ cacheUpperBoundBytes: number;
204
+ mmapUpperBoundBytes: number;
205
+ walFileBytes: number;
206
+ shmFileBytes: number;
207
+ sqliteStatusApi: "unavailable";
208
+ connections: SqliteConnectionMemoryStats[];
209
+ }
210
+
211
+ const trackedSqliteConnections = new Map<number, TrackedSqliteConnection>();
212
+ let nextSqliteConnectionSequence = 1;
213
+
214
+ function trackSqliteConnection(
215
+ db: BetterSqlite3.Database,
216
+ filename: unknown,
217
+ options: unknown,
218
+ ): BetterSqlite3.Database {
219
+ const originalClose = db.close.bind(db) as (...args: unknown[]) => unknown;
220
+ const sequence = nextSqliteConnectionSequence++;
221
+ const metadata = {
222
+ sequence,
223
+ filename:
224
+ typeof filename === "string"
225
+ ? filename
226
+ : Buffer.isBuffer(filename)
227
+ ? "<buffer>"
228
+ : ":memory:",
229
+ readonly:
230
+ Boolean(options) &&
231
+ typeof options === "object" &&
232
+ ((options as { readonly?: unknown }).readonly === true ||
233
+ (options as { readOnly?: unknown }).readOnly === true),
234
+ };
235
+ Object.defineProperty(db, "close", {
236
+ configurable: true,
237
+ value: (...args: unknown[]) => {
238
+ try {
239
+ return originalClose(...args);
240
+ } finally {
241
+ trackedSqliteConnections.delete(sequence);
242
+ }
243
+ },
244
+ });
245
+ trackedSqliteConnections.set(sequence, {
246
+ ...metadata,
247
+ reference: new WeakRef(db),
248
+ });
249
+ return db;
250
+ }
251
+
252
+ const TrackedDatabase = new Proxy(DatabaseImpl, {
253
+ construct(target, args) {
254
+ const db = Reflect.construct(target, args, target) as BetterSqlite3.Database;
255
+ return trackSqliteConnection(db, args[0], args[1]);
256
+ },
257
+ }) as typeof BetterSqlite3;
258
+
161
259
  /**
162
260
  * Wrap node:sqlite's `DatabaseSync` so it presents the better-sqlite3/bun
163
261
  * surface the rest of the codebase calls:
@@ -259,7 +357,108 @@ function buildNodeSqliteDatabaseClass(DatabaseSync: any): typeof BetterSqlite3 {
259
357
  return NodeSqliteDatabase as unknown as typeof BetterSqlite3;
260
358
  }
261
359
 
262
- export const Database: typeof BetterSqlite3 = DatabaseImpl;
360
+ export const Database: typeof BetterSqlite3 = TrackedDatabase;
361
+
362
+ function pragmaValue(db: BetterSqlite3.Database, name: string): unknown {
363
+ const row = db.prepare(`PRAGMA ${name}`).get() as Record<string, unknown> | undefined;
364
+ if (!row) return undefined;
365
+ return row[name] ?? Object.values(row)[0];
366
+ }
367
+
368
+ function pragmaNumber(db: BetterSqlite3.Database, name: string): number | null {
369
+ try {
370
+ const value = pragmaValue(db, name);
371
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
372
+ } catch {
373
+ return null;
374
+ }
375
+ }
376
+
377
+ function pragmaString(db: BetterSqlite3.Database, name: string): string | null {
378
+ try {
379
+ const value = pragmaValue(db, name);
380
+ return typeof value === "string" ? value : null;
381
+ } catch {
382
+ return null;
383
+ }
384
+ }
385
+
386
+ function sqliteSidecarBytes(filename: string, suffix: "-wal" | "-shm"): number | null {
387
+ if (filename === ":memory:" || filename === "<buffer>") return 0;
388
+ try {
389
+ return statSync(`${filename}${suffix}`).size;
390
+ } catch (error) {
391
+ return (error as NodeJS.ErrnoException).code === "ENOENT" ? 0 : null;
392
+ }
393
+ }
394
+
395
+ function fts5TableCount(db: BetterSqlite3.Database): number | null {
396
+ try {
397
+ const row = db
398
+ .prepare(
399
+ "SELECT COUNT(*) AS count FROM sqlite_master WHERE type = 'table' AND sql LIKE '%USING fts5%'",
400
+ )
401
+ .get() as { count?: unknown } | undefined;
402
+ return typeof row?.count === "number" ? row.count : null;
403
+ } catch {
404
+ return null;
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Read the SQLite memory bounds that Bun/node:sqlite expose without native FFI.
410
+ * SQLite's sqlite3_status()/sqlite3_db_status() counters are not surfaced by
411
+ * either runtime, so cache and mmap values are configured upper bounds rather
412
+ * than resident-byte measurements.
413
+ */
414
+ export function getSqliteMemoryStats(): SqliteMemoryStats {
415
+ const connections: SqliteConnectionMemoryStats[] = [];
416
+ for (const [sequence, tracked] of trackedSqliteConnections) {
417
+ const db = tracked.reference.deref();
418
+ if (!db) {
419
+ trackedSqliteConnections.delete(sequence);
420
+ continue;
421
+ }
422
+ const pageSize = pragmaNumber(db, "page_size");
423
+ const cacheSize = pragmaNumber(db, "cache_size");
424
+ const cacheSizeUnit = cacheSize === null ? null : cacheSize < 0 ? "kib" : "pages";
425
+ const cacheUpperBoundBytes =
426
+ cacheSize === null
427
+ ? null
428
+ : cacheSize < 0
429
+ ? Math.abs(cacheSize) * 1024
430
+ : pageSize === null
431
+ ? null
432
+ : cacheSize * pageSize;
433
+ connections.push({
434
+ sequence: tracked.sequence,
435
+ filename: tracked.filename,
436
+ readonly: tracked.readonly,
437
+ pageSize,
438
+ pageCount: pragmaNumber(db, "page_count"),
439
+ freelistCount: pragmaNumber(db, "freelist_count"),
440
+ cacheSize,
441
+ cacheSizeUnit,
442
+ cacheUpperBoundBytes,
443
+ mmapSizeBytes: pragmaNumber(db, "mmap_size"),
444
+ walFileBytes: sqliteSidecarBytes(tracked.filename, "-wal"),
445
+ shmFileBytes: sqliteSidecarBytes(tracked.filename, "-shm"),
446
+ fts5TableCount: fts5TableCount(db),
447
+ journalMode: pragmaString(db, "journal_mode"),
448
+ });
449
+ }
450
+ const sumKnown = (select: (connection: SqliteConnectionMemoryStats) => number | null): number =>
451
+ connections.reduce((sum, connection) => sum + (select(connection) ?? 0), 0);
452
+ return {
453
+ connectionCount: connections.length,
454
+ cacheUpperBoundBytes: sumKnown((connection) => connection.cacheUpperBoundBytes),
455
+ mmapUpperBoundBytes: sumKnown((connection) => connection.mmapSizeBytes),
456
+ walFileBytes: sumKnown((connection) => connection.walFileBytes),
457
+ shmFileBytes: sumKnown((connection) => connection.shmFileBytes),
458
+ sqliteStatusApi: "unavailable",
459
+ connections,
460
+ };
461
+ }
263
462
 
264
463
  /** Instance type alias used by helpers and storage modules. */
265
464
  export type Database = BetterSqlite3.Database;
@@ -298,6 +497,7 @@ export function withPrivilegedWriter<T>(db: Database, operation: () => T): T {
298
497
  const previousDepth = privilegeDepth.get(db) ?? 0;
299
498
  const nested = isInTransaction(db);
300
499
  const savepoint = "mc_privilege_scope";
500
+ const transactionStartedAt = nested ? undefined : performance.now();
301
501
  if (nested) {
302
502
  db.exec(`SAVEPOINT ${savepoint}`);
303
503
  } else {
@@ -316,6 +516,9 @@ export function withPrivilegedWriter<T>(db: Database, operation: () => T): T {
316
516
  db.exec(`RELEASE ${savepoint}`);
317
517
  } else {
318
518
  db.exec("COMMIT");
519
+ if (transactionStartedAt !== undefined) {
520
+ reportSlowPrivilegedWrite?.("privileged_writer", transactionStartedAt);
521
+ }
319
522
  }
320
523
  if (previousDepth > 0) privilegeDepth.set(db, previousDepth);
321
524
  else privilegeDepth.delete(db);
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { formatOpenCodeDbMissingStatusLine, resolveOpenCodeDbPath } from "./opencode-db-path";
2
3
  import type { StatusDetail } from "./rpc-types";
3
4
  import { formatStatusDetailMarkdown } from "./status-detail-text";
4
5
 
@@ -68,8 +69,9 @@ const STATUS_FIXTURE: StatusDetail = {
68
69
  };
69
70
 
70
71
  describe("formatStatusDetailMarkdown", () => {
71
- test("renders the fixed TUI status payload as compact markdown", () => {
72
- expect(formatStatusDetailMarkdown(STATUS_FIXTURE)).toBe(`## Magic Context Status
72
+ test("renders a missing-store condition before the fixed TUI status payload", () => {
73
+ expect(formatStatusDetailMarkdown(STATUS_FIXTURE)).toBe(
74
+ `${formatOpenCodeDbMissingStatusLine(resolveOpenCodeDbPath())}\n\n## Magic Context Status
73
75
 
74
76
  - **Mode:** Magic Context compaction
75
77
  - **Active profile:** work
@@ -78,7 +80,8 @@ describe("formatStatusDetailMarkdown", () => {
78
80
  - **Historian:** running; boundary present; coverage 12
79
81
  - **Memory:** 8 active; 3 injected
80
82
  - **Tags:** 4 active, 1 dropped; 2 pending drops
81
- - **Execute threshold:** 65.0%`);
83
+ - **Execute threshold:** 65.0%`,
84
+ );
82
85
  });
83
86
 
84
87
  test("shows module-routed host paths only in Rust-mode chat fallback", () => {
@@ -1,5 +1,12 @@
1
1
  import { formatCacheTtlDisplay } from "./cache-ttl-display";
2
2
  import { formatConfigParseStatusLine } from "./config-diagnostics";
3
+ import {
4
+ formatOpenCodeDbMissingStatusLine,
5
+ formatOpenCodeDbReadFailureStatusLine,
6
+ getOpenCodeDbReadFailure,
7
+ openCodeDbPathExists,
8
+ resolveOpenCodeDbPath,
9
+ } from "./opencode-db-path";
3
10
  import type { StatusDetail } from "./rpc-types";
4
11
  import { RUST_MODE_HOST_PATHS_LINE } from "./rust-mode-status";
5
12
 
@@ -29,12 +36,20 @@ export function formatStatusDetailMarkdown(detail: StatusDetail): string {
29
36
  ? undefined
30
37
  : `coverage ${detail.coverageOrdinal === null ? "none" : detail.coverageOrdinal}`,
31
38
  ].filter((value): value is string => value !== undefined);
39
+ const openCodeDbResolution = resolveOpenCodeDbPath();
40
+ const openCodeDbReadFailure = getOpenCodeDbReadFailure();
41
+ const openCodeDbStatusLine = !openCodeDbPathExists(openCodeDbResolution)
42
+ ? formatOpenCodeDbMissingStatusLine(openCodeDbResolution)
43
+ : openCodeDbReadFailure?.path === openCodeDbResolution.path
44
+ ? formatOpenCodeDbReadFailureStatusLine(openCodeDbReadFailure)
45
+ : null;
32
46
  const mode =
33
47
  detail.compaction_enabled === false
34
48
  ? "native compaction (Magic Context history compaction disabled)"
35
49
  : "Magic Context compaction";
36
50
 
37
51
  const lines = [
52
+ ...(openCodeDbStatusLine ? [openCodeDbStatusLine, ""] : []),
38
53
  ...(detail.configParseFailures ?? []).map(formatConfigParseStatusLine),
39
54
  ...((detail.configParseFailures?.length ?? 0) > 0 ? [""] : []),
40
55
  "## Magic Context Status",
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Cross-harness subagent runner abstraction.
3
3
  *
4
- * Magic Context spawns three kinds of subagents — historian, dreamer, sidekick —
4
+ * Magic Context spawns historian and Dreamer subagents
5
5
  * each as a child "session" with its own model/prompt/tools. OpenCode and Pi
6
6
  * have very different APIs for this:
7
7
  *
@@ -15,15 +15,15 @@
15
15
  * disk, optionally addressed via `--session <path>`.
16
16
  *
17
17
  * The runner interface below normalizes both into the same shape so the
18
- * actual subagent business logic (historian XML parsing, dreamer task loop,
19
- * sidekick augmentation) can stay harness-agnostic. Each harness ships its
18
+ * actual subagent business logic (historian XML parsing and Dreamer task loops)
19
+ * can stay harness-agnostic. Each harness ships its
20
20
  * own runner implementation; agents take a `SubagentRunner` as a dep instead
21
21
  * of reaching for `client.session.*` directly.
22
22
  *
23
23
  * Step 5a (this commit) defines the contract and ships `PiSubagentRunner`.
24
24
  * Step 5b will refactor the OpenCode-side spawn paths in
25
- * `compartment-runner-historian.ts`, `dreamer/runner.ts`, and
26
- * `sidekick/agent.ts` onto an `OpenCodeSubagentRunner` so both harnesses
25
+ * `compartment-runner-historian.ts` and `dreamer/runner.ts` onto an
26
+ * `OpenCodeSubagentRunner` so both harnesses
27
27
  * share the agent business logic instead of duplicating it. Until 5b lands,
28
28
  * OpenCode keeps its existing direct `client.session.*` calls untouched —
29
29
  * the runner contract is purely additive on the OpenCode side.
@@ -36,11 +36,11 @@ import type { ModelInput } from "./model-resolution";
36
36
  *
37
37
  * Mirrors the union of OpenCode's `session.create` + `session.prompt` body
38
38
  * fields and Pi's `--print` CLI flags, picking the shared subset that all
39
- * three subagent kinds (historian, dreamer, sidekick) actually use today.
39
+ * the historian and Dreamer subagents actually use today.
40
40
  *
41
41
  * Fields:
42
42
  * - `agent`: harness-specific agent name. OpenCode looks this up in its
43
- * agent registry (`HISTORIAN_AGENT`, `DREAMER_AGENT`, `SIDEKICK_AGENT`).
43
+ * agent registry (`HISTORIAN_AGENT`, `DREAMER_AGENT`).
44
44
  * Pi has no concept of "agent name" beyond config, so this is ignored
45
45
  * on the Pi side and used only by `OpenCodeSubagentRunner`.
46
46
  * - `systemPrompt`: full system prompt for this child run. Replaces (not
@@ -88,7 +88,7 @@ export interface SubagentRunOptions {
88
88
  /**
89
89
  * Optional progress callback. The runner invokes it for milestone events
90
90
  * during the run: spawn, first event received, terminal stop reason
91
- * detected, child exit. Used by historian/dreamer/sidekick to write
91
+ * detected, child exit. Used by historian/dreamer to write
92
92
  * lifecycle entries to the magic-context.log without polluting the
93
93
  * normal stdout stream.
94
94
  *
@@ -104,7 +104,6 @@ export interface SubagentRunOptions {
104
104
  | "historian_editor"
105
105
  | "compressor"
106
106
  | "dreamer"
107
- | "sidekick"
108
107
  | "user_memory_review"
109
108
  | "recomp"
110
109
  | undefined;
@@ -182,6 +181,11 @@ export type SubagentProgressEvent =
182
181
  * here so the OpenCode runner can surface the child session ID for log
183
182
  * correlation when Step 5b lands.
184
183
  */
184
+ export interface CompletedSubagentToolCall {
185
+ name: string;
186
+ arguments: Record<string, unknown>;
187
+ }
188
+
185
189
  export type SubagentRunResult =
186
190
  | {
187
191
  ok: true;
@@ -196,6 +200,9 @@ export type SubagentRunResult =
196
200
  * the real session messages.
197
201
  */
198
202
  toolCallCount?: number;
203
+ /** Completed, non-error tool results preserved by runners that can
204
+ * reconstruct invocation/result pairs from their transcript. */
205
+ completedToolCalls?: CompletedSubagentToolCall[];
199
206
  meta?: Record<string, unknown>;
200
207
  }
201
208
  | {
@@ -222,7 +229,7 @@ export type SubagentRunResult =
222
229
  *
223
230
  * Each harness ships a single instance — the OpenCode plugin wires
224
231
  * `OpenCodeSubagentRunner` and the Pi plugin wires `PiSubagentRunner` in
225
- * its `extension` boot path. Agent code (historian, dreamer, sidekick)
232
+ * its `extension` boot path. Agent code (historian and dreamer)
226
233
  * receives the runner as a dep and never reaches for harness-specific
227
234
  * client APIs directly.
228
235
  */
@@ -55,6 +55,7 @@ import {
55
55
  updateTagTokenCount,
56
56
  } from "../features/magic-context/storage-tags";
57
57
  import { makeToolCompositeKey, type Tagger } from "../features/magic-context/tagger";
58
+ import { droppedInputMarker } from "../hooks/magic-context/dropped-input-guard";
58
59
  import { applyEditMarkerToInput } from "../hooks/magic-context/edit-marker";
59
60
  import { estimateImageTokensFromDataUrl } from "../hooks/magic-context/image-token-estimate";
60
61
  import { estimateTokens } from "../hooks/magic-context/read-session-formatting";
@@ -247,7 +248,8 @@ export function tagTranscript(
247
248
  let toolResultOrdinal = 0;
248
249
  const parts = message.parts;
249
250
  const messageHasNativeReasoning =
250
- message.info.role === "assistant" && parts.some((part) => part.kind === "thinking");
251
+ message.requiresToolArcSkeleton ??
252
+ (message.info.role === "assistant" && parts.some((part) => part.kind === "thinking"));
251
253
  const contentDerivedTextIds =
252
254
  messageId !== undefined && options.textIdentityDriftMessageIds?.has(messageId) === true
253
255
  ? buildContentDerivedTextIds(messageId, parts)
@@ -1056,8 +1058,20 @@ function buildAggregateTarget(
1056
1058
  }
1057
1059
  return occurrences[0]?.part.getText() ?? null;
1058
1060
  },
1059
- drop(): "removed" | "absent" {
1060
- // Replace BOTH halves with the dropped sentinel.
1061
+ drop(): "removed" | "absent" | "incomplete" {
1062
+ const complete =
1063
+ occurrences.some((occ) => occ.kind === "tool_use") &&
1064
+ occurrences.some((occ) => occ.kind === "tool_result");
1065
+ if (!complete) return "incomplete";
1066
+ if (
1067
+ !requiresToolArcSkeleton &&
1068
+ occurrences.every((occ) => occ.part.remove && occ.part.canRemove?.() !== false)
1069
+ ) {
1070
+ let removed = false;
1071
+ for (const occ of occurrences) removed = occ.part.remove?.() || removed;
1072
+ return removed ? "removed" : "absent";
1073
+ }
1074
+ // Adapters without structural removal retain paired sentinels.
1061
1075
  const sentinel = `[dropped \u00a7${tagId}\u00a7]`;
1062
1076
  let any = false;
1063
1077
  for (const occ of occurrences) {
@@ -1066,14 +1080,14 @@ function buildAggregateTarget(
1066
1080
  return any ? "removed" : "absent";
1067
1081
  },
1068
1082
  truncate(): "truncated" | "absent" {
1069
- // Skeleton-drop: replace BOTH halves' content with the one
1070
- // canonical `[dropped §N§]` placeholder (byte-identical to a full
1071
- // drop and to OpenCode). Frozen by the dropMode column → replays
1072
- // the same string every pass. The tool_use call survives intact.
1083
+ // Keep the paired call shell, but replace its arguments with one
1084
+ // non-executable marker; result halves carry the matching sentinel.
1073
1085
  const sentinel = `[dropped \u00a7${tagId}\u00a7]`;
1074
1086
  let any = false;
1075
1087
  for (const occ of occurrences) {
1076
- if (setToolContentOrText(occ.part, sentinel)) {
1088
+ if (occ.kind === "tool_use" && occ.part.setToolInput) {
1089
+ if (occ.part.setToolInput(droppedInputMarker(tagId))) any = true;
1090
+ } else if (setToolContentOrText(occ.part, sentinel)) {
1077
1091
  any = true;
1078
1092
  }
1079
1093
  }
@@ -1101,12 +1115,12 @@ function buildAggregateTarget(
1101
1115
  }
1102
1116
  return any ? "truncated" : "absent";
1103
1117
  },
1104
- // Non-mutating reclaim predicate (Pi parity with OpenCode's canDrop).
1105
- // Pi sentinelizes BOTH halves, so unlike OpenCode there's no
1106
- // result-part requirement — a target reclaims as long as it still has
1107
- // at least one live occurrence to sentinelize.
1118
+ // Open invocations still belong to the current turn; only complete arcs reclaim.
1108
1119
  canDrop(): boolean {
1109
- return occurrences.length > 0;
1120
+ return (
1121
+ occurrences.some((occ) => occ.kind === "tool_use") &&
1122
+ occurrences.some((occ) => occ.kind === "tool_result")
1123
+ );
1110
1124
  },
1111
1125
  requiresToolArcSkeleton,
1112
1126
  // Non-mutating read of the invocation input (the tool_use occurrence
@@ -1162,8 +1176,8 @@ function buildTextTarget(
1162
1176
  * TagTarget for a tag-eligible tool part. Tool parts get full-drop or
1163
1177
  * skeleton-drop treatment from `applyFlushedStatuses` based on the stored
1164
1178
  * `drop_mode` column. Both render the SAME canonical `[dropped §N§]`
1165
- * placeholder — full-drop replaces the whole pair, skeleton-drop keeps the
1166
- * tool_use call and replaces only its output. One placeholder string,
1179
+ * placeholder — full-drop replaces the whole pair, while skeleton-drop keeps
1180
+ * the tool_use call with only a dropped-input marker. Marker bytes are
1167
1181
  * byte-identical across passes and across harnesses.
1168
1182
  */
1169
1183
  function buildToolTarget(
@@ -1190,12 +1204,10 @@ function buildToolTarget(
1190
1204
  return replaced ? "removed" : "absent";
1191
1205
  },
1192
1206
  truncate(): "truncated" | "absent" {
1193
- // Skeleton-drop: replace the tool output with the one canonical
1194
- // `[dropped §N§]` placeholder (byte-identical to a full drop and to
1195
- // OpenCode). Frozen by the dropMode column, so it replays the same
1196
- // string every pass. The tool_use call itself survives intact.
1197
- const ok = setToolContentOrText(part, `[dropped \u00a7${tagId}\u00a7]`);
1198
- return ok ? "truncated" : "absent";
1207
+ const changed = part.setToolInput
1208
+ ? part.setToolInput(droppedInputMarker(tagId))
1209
+ : setToolContentOrText(part, `[dropped \u00a7${tagId}\u00a7]`);
1210
+ return changed ? "truncated" : "absent";
1199
1211
  },
1200
1212
  message: {
1201
1213
  info: { id: message.info.id, role: message.info.role },
@@ -167,6 +167,11 @@ export interface TranscriptPart {
167
167
  */
168
168
  replaceWithSentinel(sentinelText: string): boolean;
169
169
 
170
+ /** Remove one half of a complete tool arc. Callers must remove both halves together. */
171
+ remove?(): boolean;
172
+ /** Refuse structural deletion when the adapter cannot match provider-native identity safely. */
173
+ canRemove?(): boolean;
174
+
170
175
  /**
171
176
  * Optional: serialized byte size of the part's REAL payload, including
172
177
  * non-text content (images, structured data) that `getText()` can't
@@ -187,6 +192,8 @@ export interface TranscriptPart {
187
192
  * use `info.id` for cross-pass correlation, never the message reference.
188
193
  */
189
194
  export interface TranscriptMessage {
195
+ /** Override when the receiving protocol does not merge adjacent assistant reasoning turns. */
196
+ requiresToolArcSkeleton?: boolean;
190
197
  /**
191
198
  * Lightweight metadata exposed for tagging, sentinel persistence, and
192
199
  * cross-pass correlation. Adapters fill these from harness-native
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { resolveLimit, resolveOutputReserve } from "./models-dev-cache";
6
6
  import {
7
+ applyProvenInputFloor,
7
8
  deriveWindowGeometry,
8
9
  formatWindowDerivationLine,
9
10
  parseWindowOverlay,
@@ -298,6 +299,49 @@ describe("window geometry", () => {
298
299
  expect(hooked?.derivation.reserve).toBe(30_000);
299
300
  });
300
301
 
302
+ test("keeps shared-upfront emergency headroom below the separate absolute wall", () => {
303
+ const result = deriveWindowGeometry(
304
+ "openai-codex",
305
+ "gpt-5.6-sol",
306
+ { context: 272_000, output: 128_000 },
307
+ { providerLimit: { context: 272_000 }, harness: "pi" },
308
+ );
309
+
310
+ expect(result?.usableSoft).toBe(204_000);
311
+ expect(result?.usableHard).toBe(204_000);
312
+ expect(result?.usableHard).toBeLessThan(result?.derivation.absoluteWall ?? 0);
313
+ expect(result?.derivation.absoluteWall).toBe(272_000);
314
+ expect((result?.derivation.absoluteWall ?? 0) - (result?.usableHard ?? 0)).toBe(
315
+ result?.derivation.reserve,
316
+ );
317
+ expect(result?.derivation.windowSource).toBe("provider");
318
+ });
319
+
320
+ test("bounds proof at trusted absolute walls but retains the static-catalog escape hatch", () => {
321
+ const trusted = deriveWindowGeometry(
322
+ "openai-codex",
323
+ "gpt-5.6-sol",
324
+ { context: 272_000, output: 128_000 },
325
+ { providerLimit: { context: 272_000 }, harness: "pi" },
326
+ );
327
+ expect(trusted).toBeDefined();
328
+ const raisedWithinWall = applyProvenInputFloor(trusted!, 255_834);
329
+ expect(raisedWithinWall.geometry.usableSoft).toBe(255_834);
330
+ expect(raisedWithinWall.geometry.usableHard).toBe(204_000);
331
+ const refused = applyProvenInputFloor(trusted!, 593_717);
332
+ expect(refused.refused).toEqual({ reading: 593_717, absoluteWall: 272_000 });
333
+ expect(refused.geometry.usableSoft).toBe(204_000);
334
+ expect(refused.geometry.usableHard).toBe(204_000);
335
+
336
+ const staticFallback = deriveWindowGeometry("custom", "model", { context: 30_000 });
337
+ expect(staticFallback).toBeDefined();
338
+ const escaped = applyProvenInputFloor(staticFallback!, 90_000);
339
+ expect(escaped.refused).toBeUndefined();
340
+ expect(escaped.geometry.usableSoft).toBe(90_000);
341
+ expect(escaped.geometry.usableHard).toBe(90_000);
342
+ expect(escaped.geometry.derivation.absoluteWall).toBe(90_000);
343
+ });
344
+
301
345
  test("clamps an inversion and logs it", () => {
302
346
  const logs: string[] = [];
303
347
  const result = deriveWindowGeometry(