@caupulican/pi-adaptative 0.81.39 → 0.81.40

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 (667) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +2 -0
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +2 -0
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -1
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +2 -0
  6. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -1
  7. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +3 -0
  8. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +9 -0
  9. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  10. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +290 -107
  11. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  12. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +357 -122
  13. package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +2 -2
  14. package/dist/cli/args.d.ts +2 -0
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +9 -0
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/core/agent-directory-layout.d.ts +15 -0
  19. package/dist/core/agent-directory-layout.d.ts.map +1 -0
  20. package/dist/core/agent-directory-layout.js +72 -0
  21. package/dist/core/agent-directory-layout.js.map +1 -0
  22. package/dist/core/agent-paths.d.ts +19 -2
  23. package/dist/core/agent-paths.d.ts.map +1 -1
  24. package/dist/core/agent-paths.js +67 -4
  25. package/dist/core/agent-paths.js.map +1 -1
  26. package/dist/core/agent-session-runtime.d.ts +52 -8
  27. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  28. package/dist/core/agent-session-runtime.js +397 -79
  29. package/dist/core/agent-session-runtime.js.map +1 -1
  30. package/dist/core/agent-session-services.d.ts +1 -0
  31. package/dist/core/agent-session-services.d.ts.map +1 -1
  32. package/dist/core/agent-session-services.js +1 -0
  33. package/dist/core/agent-session-services.js.map +1 -1
  34. package/dist/core/agent-session.d.ts +71 -136
  35. package/dist/core/agent-session.d.ts.map +1 -1
  36. package/dist/core/agent-session.js +477 -1318
  37. package/dist/core/agent-session.js.map +1 -1
  38. package/dist/core/autonomy/approval-gate.d.ts +3 -2
  39. package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
  40. package/dist/core/autonomy/approval-gate.js +6 -28
  41. package/dist/core/autonomy/approval-gate.js.map +1 -1
  42. package/dist/core/autonomy/bounded-completion.d.ts +1 -1
  43. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  44. package/dist/core/autonomy/bounded-completion.js +1 -1
  45. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  46. package/dist/core/autonomy/contracts.d.ts +20 -12
  47. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  48. package/dist/core/autonomy/contracts.js.map +1 -1
  49. package/dist/core/autonomy/foreground-envelope.d.ts +8 -2
  50. package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
  51. package/dist/core/autonomy/foreground-envelope.js +8 -25
  52. package/dist/core/autonomy/foreground-envelope.js.map +1 -1
  53. package/dist/core/autonomy/gates.d.ts.map +1 -1
  54. package/dist/core/autonomy/gates.js +8 -10
  55. package/dist/core/autonomy/gates.js.map +1 -1
  56. package/dist/core/autonomy/lane-private-paths.d.ts +2 -0
  57. package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -0
  58. package/dist/core/autonomy/lane-private-paths.js +16 -0
  59. package/dist/core/autonomy/lane-private-paths.js.map +1 -0
  60. package/dist/core/autonomy/lane-tool-surface.d.ts +10 -0
  61. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  62. package/dist/core/autonomy/lane-tool-surface.js +46 -8
  63. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  64. package/dist/core/autonomy/lane-tracker.d.ts +14 -0
  65. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  66. package/dist/core/autonomy/lane-tracker.js +38 -0
  67. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  68. package/dist/core/autonomy/risk-assessment.d.ts +1 -2
  69. package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
  70. package/dist/core/autonomy/risk-assessment.js +1 -123
  71. package/dist/core/autonomy/risk-assessment.js.map +1 -1
  72. package/dist/core/autonomy/session-lane-record.d.ts +4 -4
  73. package/dist/core/autonomy/session-lane-record.d.ts.map +1 -1
  74. package/dist/core/autonomy/session-lane-record.js +16 -29
  75. package/dist/core/autonomy/session-lane-record.js.map +1 -1
  76. package/dist/core/autonomy/telemetry-events.d.ts +1 -1
  77. package/dist/core/autonomy/telemetry-events.d.ts.map +1 -1
  78. package/dist/core/autonomy/telemetry-events.js +1 -1
  79. package/dist/core/autonomy/telemetry-events.js.map +1 -1
  80. package/dist/core/autonomy-telemetry.d.ts +7 -7
  81. package/dist/core/autonomy-telemetry.d.ts.map +1 -1
  82. package/dist/core/autonomy-telemetry.js +9 -9
  83. package/dist/core/autonomy-telemetry.js.map +1 -1
  84. package/dist/core/background-lane-controller.d.ts +54 -236
  85. package/dist/core/background-lane-controller.d.ts.map +1 -1
  86. package/dist/core/background-lane-controller.js +140 -1273
  87. package/dist/core/background-lane-controller.js.map +1 -1
  88. package/dist/core/capability-contract.d.ts +4 -0
  89. package/dist/core/capability-contract.d.ts.map +1 -0
  90. package/dist/core/capability-contract.js +28 -0
  91. package/dist/core/capability-contract.js.map +1 -0
  92. package/dist/core/compaction-controller.d.ts +90 -0
  93. package/dist/core/compaction-controller.d.ts.map +1 -0
  94. package/dist/core/compaction-controller.js +452 -0
  95. package/dist/core/compaction-controller.js.map +1 -0
  96. package/dist/core/compaction-support.d.ts +12 -11
  97. package/dist/core/compaction-support.d.ts.map +1 -1
  98. package/dist/core/compaction-support.js.map +1 -1
  99. package/dist/core/context/context-artifacts.d.ts +6 -2
  100. package/dist/core/context/context-artifacts.d.ts.map +1 -1
  101. package/dist/core/context/context-artifacts.js +34 -2
  102. package/dist/core/context/context-artifacts.js.map +1 -1
  103. package/dist/core/context/context-store-retention.d.ts +8 -2
  104. package/dist/core/context/context-store-retention.d.ts.map +1 -1
  105. package/dist/core/context/context-store-retention.js +158 -35
  106. package/dist/core/context/context-store-retention.js.map +1 -1
  107. package/dist/core/context/okf-memory.d.ts.map +1 -1
  108. package/dist/core/context/okf-memory.js +9 -11
  109. package/dist/core/context/okf-memory.js.map +1 -1
  110. package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
  111. package/dist/core/context/sqlite-runtime-index.js +6 -8
  112. package/dist/core/context/sqlite-runtime-index.js.map +1 -1
  113. package/dist/core/context-gc.d.ts +3 -0
  114. package/dist/core/context-gc.d.ts.map +1 -1
  115. package/dist/core/context-gc.js +10 -5
  116. package/dist/core/context-gc.js.map +1 -1
  117. package/dist/core/context-pipeline.d.ts.map +1 -1
  118. package/dist/core/context-pipeline.js +9 -4
  119. package/dist/core/context-pipeline.js.map +1 -1
  120. package/dist/core/default-tool-surface.d.ts.map +1 -1
  121. package/dist/core/default-tool-surface.js +1 -0
  122. package/dist/core/default-tool-surface.js.map +1 -1
  123. package/dist/core/delegation/managed-lane-controller.d.ts +34 -0
  124. package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -0
  125. package/dist/core/delegation/managed-lane-controller.js +217 -0
  126. package/dist/core/delegation/managed-lane-controller.js.map +1 -0
  127. package/dist/core/delegation/session-worker-claim.d.ts +44 -0
  128. package/dist/core/delegation/session-worker-claim.d.ts.map +1 -0
  129. package/dist/core/delegation/session-worker-claim.js +98 -0
  130. package/dist/core/delegation/session-worker-claim.js.map +1 -0
  131. package/dist/core/delegation/worker-actions.d.ts +7 -5
  132. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  133. package/dist/core/delegation/worker-actions.js +20 -4
  134. package/dist/core/delegation/worker-actions.js.map +1 -1
  135. package/dist/core/delegation/{worker-result.d.ts → worker-claim.d.ts} +13 -13
  136. package/dist/core/delegation/worker-claim.d.ts.map +1 -0
  137. package/dist/core/delegation/{worker-result.js → worker-claim.js} +61 -51
  138. package/dist/core/delegation/worker-claim.js.map +1 -0
  139. package/dist/core/delegation/worker-delegation-controller.d.ts +110 -0
  140. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -0
  141. package/dist/core/delegation/worker-delegation-controller.js +861 -0
  142. package/dist/core/delegation/worker-delegation-controller.js.map +1 -0
  143. package/dist/core/delegation/worker-delegation-request.d.ts +11 -0
  144. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -0
  145. package/dist/core/delegation/worker-delegation-request.js +2 -0
  146. package/dist/core/delegation/worker-delegation-request.js.map +1 -0
  147. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +40 -0
  148. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -0
  149. package/dist/core/delegation/worker-dispatch-scheduler.js +93 -0
  150. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -0
  151. package/dist/core/delegation/worker-execution-policy.d.ts +62 -0
  152. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -0
  153. package/dist/core/delegation/worker-execution-policy.js +160 -0
  154. package/dist/core/delegation/worker-execution-policy.js.map +1 -0
  155. package/dist/core/delegation/worker-lane-projection.d.ts +14 -0
  156. package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -0
  157. package/dist/core/delegation/worker-lane-projection.js +100 -0
  158. package/dist/core/delegation/worker-lane-projection.js.map +1 -0
  159. package/dist/core/delegation/worker-lifecycle.d.ts +97 -0
  160. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -0
  161. package/dist/core/delegation/worker-lifecycle.js +339 -0
  162. package/dist/core/delegation/worker-lifecycle.js.map +1 -0
  163. package/dist/core/delegation/worker-notification-coordinator.d.ts +36 -0
  164. package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -0
  165. package/dist/core/delegation/worker-notification-coordinator.js +90 -0
  166. package/dist/core/delegation/worker-notification-coordinator.js.map +1 -0
  167. package/dist/core/delegation/worker-profile-resolver.d.ts +55 -0
  168. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -0
  169. package/dist/core/delegation/worker-profile-resolver.js +113 -0
  170. package/dist/core/delegation/worker-profile-resolver.js.map +1 -0
  171. package/dist/core/delegation/worker-runner.d.ts +25 -14
  172. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  173. package/dist/core/delegation/worker-runner.js +122 -53
  174. package/dist/core/delegation/worker-runner.js.map +1 -1
  175. package/dist/core/delegation/worker-task-label.d.ts +3 -0
  176. package/dist/core/delegation/worker-task-label.d.ts.map +1 -0
  177. package/dist/core/delegation/worker-task-label.js +11 -0
  178. package/dist/core/delegation/worker-task-label.js.map +1 -0
  179. package/dist/core/doctor.d.ts +8 -0
  180. package/dist/core/doctor.d.ts.map +1 -1
  181. package/dist/core/doctor.js +32 -1
  182. package/dist/core/doctor.js.map +1 -1
  183. package/dist/core/extensions/index.d.ts +1 -1
  184. package/dist/core/extensions/index.d.ts.map +1 -1
  185. package/dist/core/extensions/index.js.map +1 -1
  186. package/dist/core/extensions/loader.d.ts +5 -1
  187. package/dist/core/extensions/loader.d.ts.map +1 -1
  188. package/dist/core/extensions/loader.js +50 -15
  189. package/dist/core/extensions/loader.js.map +1 -1
  190. package/dist/core/extensions/runner.d.ts.map +1 -1
  191. package/dist/core/extensions/runner.js +6 -0
  192. package/dist/core/extensions/runner.js.map +1 -1
  193. package/dist/core/extensions/storage.d.ts +8 -0
  194. package/dist/core/extensions/storage.d.ts.map +1 -0
  195. package/dist/core/extensions/storage.js +28 -0
  196. package/dist/core/extensions/storage.js.map +1 -0
  197. package/dist/core/extensions/types.d.ts +70 -21
  198. package/dist/core/extensions/types.d.ts.map +1 -1
  199. package/dist/core/extensions/types.js.map +1 -1
  200. package/dist/core/failure-corpus.d.ts +1 -15
  201. package/dist/core/failure-corpus.d.ts.map +1 -1
  202. package/dist/core/failure-corpus.js +9 -37
  203. package/dist/core/failure-corpus.js.map +1 -1
  204. package/dist/core/foreground-recovery-controller.d.ts +52 -0
  205. package/dist/core/foreground-recovery-controller.d.ts.map +1 -0
  206. package/dist/core/foreground-recovery-controller.js +111 -0
  207. package/dist/core/foreground-recovery-controller.js.map +1 -0
  208. package/dist/core/gateways/channel-provider.d.ts +3 -0
  209. package/dist/core/gateways/channel-provider.d.ts.map +1 -1
  210. package/dist/core/gateways/channel-provider.js +31 -6
  211. package/dist/core/gateways/channel-provider.js.map +1 -1
  212. package/dist/core/goals/goal-acceptance.d.ts +11 -0
  213. package/dist/core/goals/goal-acceptance.d.ts.map +1 -0
  214. package/dist/core/goals/goal-acceptance.js +25 -0
  215. package/dist/core/goals/goal-acceptance.js.map +1 -0
  216. package/dist/core/goals/goal-auto-continue-controller.d.ts +27 -0
  217. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -0
  218. package/dist/core/goals/goal-auto-continue-controller.js +93 -0
  219. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -0
  220. package/dist/core/goals/goal-continuation-controller.d.ts +1 -1
  221. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  222. package/dist/core/goals/goal-continuation-controller.js +10 -0
  223. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  224. package/dist/core/goals/goal-continuation-prompt.d.ts +1 -1
  225. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  226. package/dist/core/goals/goal-continuation-prompt.js +6 -6
  227. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  228. package/dist/core/goals/goal-lifecycle.d.ts +20 -0
  229. package/dist/core/goals/goal-lifecycle.d.ts.map +1 -0
  230. package/dist/core/goals/goal-lifecycle.js +17 -0
  231. package/dist/core/goals/goal-lifecycle.js.map +1 -0
  232. package/dist/core/goals/goal-runtime-snapshot.d.ts +11 -9
  233. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
  234. package/dist/core/goals/goal-runtime-snapshot.js +10 -8
  235. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
  236. package/dist/core/goals/goal-state.d.ts +1 -0
  237. package/dist/core/goals/goal-state.d.ts.map +1 -1
  238. package/dist/core/goals/goal-state.js +7 -10
  239. package/dist/core/goals/goal-state.js.map +1 -1
  240. package/dist/core/goals/goal-tool-core.d.ts +8 -6
  241. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  242. package/dist/core/goals/goal-tool-core.js +11 -41
  243. package/dist/core/goals/goal-tool-core.js.map +1 -1
  244. package/dist/core/goals/session-goal-state.d.ts +2 -4
  245. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  246. package/dist/core/goals/session-goal-state.js +10 -33
  247. package/dist/core/goals/session-goal-state.js.map +1 -1
  248. package/dist/core/human-input.d.ts +100 -0
  249. package/dist/core/human-input.d.ts.map +1 -0
  250. package/dist/core/human-input.js +362 -0
  251. package/dist/core/human-input.js.map +1 -0
  252. package/dist/core/index.d.ts +9 -1
  253. package/dist/core/index.d.ts.map +1 -1
  254. package/dist/core/index.js +9 -1
  255. package/dist/core/index.js.map +1 -1
  256. package/dist/core/{telemetry.d.ts → install-telemetry.d.ts} +1 -1
  257. package/dist/core/install-telemetry.d.ts.map +1 -0
  258. package/dist/core/{telemetry.js → install-telemetry.js} +1 -1
  259. package/dist/core/install-telemetry.js.map +1 -0
  260. package/dist/core/keybindings.d.ts +17 -1
  261. package/dist/core/keybindings.d.ts.map +1 -1
  262. package/dist/core/keybindings.js +24 -6
  263. package/dist/core/keybindings.js.map +1 -1
  264. package/dist/core/learning/learning-audit.d.ts +2 -4
  265. package/dist/core/learning/learning-audit.d.ts.map +1 -1
  266. package/dist/core/learning/learning-audit.js +9 -29
  267. package/dist/core/learning/learning-audit.js.map +1 -1
  268. package/dist/core/learning/learning-gate.d.ts.map +1 -1
  269. package/dist/core/learning/learning-gate.js +1 -6
  270. package/dist/core/learning/learning-gate.js.map +1 -1
  271. package/dist/core/learning/session-learning-decision.d.ts +2 -4
  272. package/dist/core/learning/session-learning-decision.d.ts.map +1 -1
  273. package/dist/core/learning/session-learning-decision.js +9 -29
  274. package/dist/core/learning/session-learning-decision.js.map +1 -1
  275. package/dist/core/memory-controller.d.ts.map +1 -1
  276. package/dist/core/memory-controller.js +4 -4
  277. package/dist/core/memory-controller.js.map +1 -1
  278. package/dist/core/model-capability.d.ts.map +1 -1
  279. package/dist/core/model-capability.js +1 -0
  280. package/dist/core/model-capability.js.map +1 -1
  281. package/dist/core/model-resolver.d.ts +1 -1
  282. package/dist/core/model-resolver.d.ts.map +1 -1
  283. package/dist/core/model-resolver.js +6 -9
  284. package/dist/core/model-resolver.js.map +1 -1
  285. package/dist/core/model-router/intent-classifier.d.ts +1 -4
  286. package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
  287. package/dist/core/model-router/intent-classifier.js +1 -156
  288. package/dist/core/model-router/intent-classifier.js.map +1 -1
  289. package/dist/core/model-router-controller.d.ts.map +1 -1
  290. package/dist/core/model-router-controller.js +2 -2
  291. package/dist/core/model-router-controller.js.map +1 -1
  292. package/dist/core/model-selection-controller.d.ts +4 -4
  293. package/dist/core/model-selection-controller.d.ts.map +1 -1
  294. package/dist/core/model-selection-controller.js.map +1 -1
  295. package/dist/core/model-tool-protocol.d.ts +23 -0
  296. package/dist/core/model-tool-protocol.d.ts.map +1 -0
  297. package/dist/core/model-tool-protocol.js +43 -0
  298. package/dist/core/model-tool-protocol.js.map +1 -0
  299. package/dist/core/models/adaptation-store.d.ts +4 -6
  300. package/dist/core/models/adaptation-store.d.ts.map +1 -1
  301. package/dist/core/models/adaptation-store.js +95 -83
  302. package/dist/core/models/adaptation-store.js.map +1 -1
  303. package/dist/core/models/fitness-store.d.ts +2 -12
  304. package/dist/core/models/fitness-store.d.ts.map +1 -1
  305. package/dist/core/models/fitness-store.js +84 -57
  306. package/dist/core/models/fitness-store.js.map +1 -1
  307. package/dist/core/models/host-state-store.d.ts +34 -0
  308. package/dist/core/models/host-state-store.d.ts.map +1 -0
  309. package/dist/core/models/host-state-store.js +86 -0
  310. package/dist/core/models/host-state-store.js.map +1 -0
  311. package/dist/core/orchestration/agent-resume.d.ts +24 -0
  312. package/dist/core/orchestration/agent-resume.d.ts.map +1 -0
  313. package/dist/core/orchestration/agent-resume.js +90 -0
  314. package/dist/core/orchestration/agent-resume.js.map +1 -0
  315. package/dist/core/orchestration/capability-gateway.d.ts +61 -0
  316. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -0
  317. package/dist/core/orchestration/capability-gateway.js +152 -0
  318. package/dist/core/orchestration/capability-gateway.js.map +1 -0
  319. package/dist/core/orchestration/contracts.d.ts +343 -0
  320. package/dist/core/orchestration/contracts.d.ts.map +1 -0
  321. package/dist/core/orchestration/contracts.js +135 -0
  322. package/dist/core/orchestration/contracts.js.map +1 -0
  323. package/dist/core/orchestration/delegation-ledger.d.ts +56 -0
  324. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -0
  325. package/dist/core/orchestration/delegation-ledger.js +197 -0
  326. package/dist/core/orchestration/delegation-ledger.js.map +1 -0
  327. package/dist/core/orchestration/event-store.d.ts +83 -0
  328. package/dist/core/orchestration/event-store.d.ts.map +1 -0
  329. package/dist/core/orchestration/event-store.js +560 -0
  330. package/dist/core/orchestration/event-store.js.map +1 -0
  331. package/dist/core/orchestration/lane-tool-manifests.d.ts +9 -0
  332. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -0
  333. package/dist/core/orchestration/lane-tool-manifests.js +37 -0
  334. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -0
  335. package/dist/core/orchestration/model-binding.d.ts +15 -0
  336. package/dist/core/orchestration/model-binding.d.ts.map +1 -0
  337. package/dist/core/orchestration/model-binding.js +29 -0
  338. package/dist/core/orchestration/model-binding.js.map +1 -0
  339. package/dist/core/orchestration/policy-compiler.d.ts +49 -0
  340. package/dist/core/orchestration/policy-compiler.d.ts.map +1 -0
  341. package/dist/core/orchestration/policy-compiler.js +191 -0
  342. package/dist/core/orchestration/policy-compiler.js.map +1 -0
  343. package/dist/core/orchestration/policy-gate.d.ts +18 -0
  344. package/dist/core/orchestration/policy-gate.d.ts.map +1 -0
  345. package/dist/core/orchestration/policy-gate.js +24 -0
  346. package/dist/core/orchestration/policy-gate.js.map +1 -0
  347. package/dist/core/orchestration/profile-registry.d.ts +51 -0
  348. package/dist/core/orchestration/profile-registry.d.ts.map +1 -0
  349. package/dist/core/orchestration/profile-registry.js +393 -0
  350. package/dist/core/orchestration/profile-registry.js.map +1 -0
  351. package/dist/core/orchestration/profile-store.d.ts +31 -0
  352. package/dist/core/orchestration/profile-store.d.ts.map +1 -0
  353. package/dist/core/orchestration/profile-store.js +136 -0
  354. package/dist/core/orchestration/profile-store.js.map +1 -0
  355. package/dist/core/orchestration/risk-budget.d.ts +9 -0
  356. package/dist/core/orchestration/risk-budget.d.ts.map +1 -0
  357. package/dist/core/orchestration/risk-budget.js +48 -0
  358. package/dist/core/orchestration/risk-budget.js.map +1 -0
  359. package/dist/core/orchestration/task-runtime.d.ts +168 -0
  360. package/dist/core/orchestration/task-runtime.d.ts.map +1 -0
  361. package/dist/core/orchestration/task-runtime.js +1600 -0
  362. package/dist/core/orchestration/task-runtime.js.map +1 -0
  363. package/dist/core/orchestration/work-state-projection.d.ts +32 -0
  364. package/dist/core/orchestration/work-state-projection.d.ts.map +1 -0
  365. package/dist/core/orchestration/work-state-projection.js +116 -0
  366. package/dist/core/orchestration/work-state-projection.js.map +1 -0
  367. package/dist/core/orchestration/worker-execution-contract.d.ts +21 -0
  368. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -0
  369. package/dist/core/orchestration/worker-execution-contract.js +179 -0
  370. package/dist/core/orchestration/worker-execution-contract.js.map +1 -0
  371. package/dist/core/orchestration/worker-result-adapter.d.ts +21 -0
  372. package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -0
  373. package/dist/core/orchestration/worker-result-adapter.js +109 -0
  374. package/dist/core/orchestration/worker-result-adapter.js.map +1 -0
  375. package/dist/core/process-identity.d.ts +11 -0
  376. package/dist/core/process-identity.d.ts.map +1 -0
  377. package/dist/core/process-identity.js +26 -0
  378. package/dist/core/process-identity.js.map +1 -0
  379. package/dist/core/process-matrix/codes.d.ts +14 -3
  380. package/dist/core/process-matrix/codes.d.ts.map +1 -1
  381. package/dist/core/process-matrix/codes.js.map +1 -1
  382. package/dist/core/process-matrix/resume-launcher.d.ts +26 -0
  383. package/dist/core/process-matrix/resume-launcher.d.ts.map +1 -0
  384. package/dist/core/process-matrix/resume-launcher.js +62 -0
  385. package/dist/core/process-matrix/resume-launcher.js.map +1 -0
  386. package/dist/core/process-matrix/runtime.d.ts +37 -22
  387. package/dist/core/process-matrix/runtime.d.ts.map +1 -1
  388. package/dist/core/process-matrix/runtime.js +399 -76
  389. package/dist/core/process-matrix/runtime.js.map +1 -1
  390. package/dist/core/process-matrix/store.d.ts +5 -2
  391. package/dist/core/process-matrix/store.d.ts.map +1 -1
  392. package/dist/core/process-matrix/store.js +123 -14
  393. package/dist/core/process-matrix/store.js.map +1 -1
  394. package/dist/core/process-matrix/supervisor.d.ts +10 -7
  395. package/dist/core/process-matrix/supervisor.d.ts.map +1 -1
  396. package/dist/core/process-matrix/supervisor.js +45 -14
  397. package/dist/core/process-matrix/supervisor.js.map +1 -1
  398. package/dist/core/profile-registry.d.ts.map +1 -1
  399. package/dist/core/profile-registry.js +6 -8
  400. package/dist/core/profile-registry.js.map +1 -1
  401. package/dist/core/reflection-controller.d.ts +4 -4
  402. package/dist/core/reflection-controller.d.ts.map +1 -1
  403. package/dist/core/reflection-controller.js +2 -1
  404. package/dist/core/reflection-controller.js.map +1 -1
  405. package/dist/core/reload-blockers.d.ts.map +1 -1
  406. package/dist/core/reload-blockers.js +6 -8
  407. package/dist/core/reload-blockers.js.map +1 -1
  408. package/dist/core/research/evidence-bundle.d.ts.map +1 -1
  409. package/dist/core/research/evidence-bundle.js +1 -6
  410. package/dist/core/research/evidence-bundle.js.map +1 -1
  411. package/dist/core/research/lane-model-resolver.d.ts +33 -0
  412. package/dist/core/research/lane-model-resolver.d.ts.map +1 -0
  413. package/dist/core/research/lane-model-resolver.js +65 -0
  414. package/dist/core/research/lane-model-resolver.js.map +1 -0
  415. package/dist/core/research/model-fitness-controller.d.ts +38 -0
  416. package/dist/core/research/model-fitness-controller.d.ts.map +1 -0
  417. package/dist/core/research/model-fitness-controller.js +106 -0
  418. package/dist/core/research/model-fitness-controller.js.map +1 -0
  419. package/dist/core/research/model-fitness.d.ts +1 -1
  420. package/dist/core/research/model-fitness.d.ts.map +1 -1
  421. package/dist/core/research/model-fitness.js +4 -4
  422. package/dist/core/research/model-fitness.js.map +1 -1
  423. package/dist/core/research/research-gate.d.ts.map +1 -1
  424. package/dist/core/research/research-gate.js +7 -7
  425. package/dist/core/research/research-gate.js.map +1 -1
  426. package/dist/core/research/research-lane-controller.d.ts +62 -0
  427. package/dist/core/research/research-lane-controller.d.ts.map +1 -0
  428. package/dist/core/research/research-lane-controller.js +292 -0
  429. package/dist/core/research/research-lane-controller.js.map +1 -0
  430. package/dist/core/research/session-evidence-bundle.d.ts +2 -4
  431. package/dist/core/research/session-evidence-bundle.d.ts.map +1 -1
  432. package/dist/core/research/session-evidence-bundle.js +10 -46
  433. package/dist/core/research/session-evidence-bundle.js.map +1 -1
  434. package/dist/core/resource-loader.d.ts.map +1 -1
  435. package/dist/core/resource-loader.js +7 -2
  436. package/dist/core/resource-loader.js.map +1 -1
  437. package/dist/core/risk-classifier.d.ts +6 -0
  438. package/dist/core/risk-classifier.d.ts.map +1 -0
  439. package/dist/core/risk-classifier.js +225 -0
  440. package/dist/core/risk-classifier.js.map +1 -0
  441. package/dist/core/runtime-builder.d.ts +26 -20
  442. package/dist/core/runtime-builder.d.ts.map +1 -1
  443. package/dist/core/runtime-builder.js +237 -178
  444. package/dist/core/runtime-builder.js.map +1 -1
  445. package/dist/core/sdk.d.ts +6 -3
  446. package/dist/core/sdk.d.ts.map +1 -1
  447. package/dist/core/sdk.js +61 -24
  448. package/dist/core/sdk.js.map +1 -1
  449. package/dist/core/session-analytics.d.ts.map +1 -1
  450. package/dist/core/session-analytics.js +28 -1
  451. package/dist/core/session-analytics.js.map +1 -1
  452. package/dist/core/session-image-store.d.ts +33 -0
  453. package/dist/core/session-image-store.d.ts.map +1 -0
  454. package/dist/core/session-image-store.js +283 -0
  455. package/dist/core/session-image-store.js.map +1 -0
  456. package/dist/core/session-role.d.ts +6 -4
  457. package/dist/core/session-role.d.ts.map +1 -1
  458. package/dist/core/session-role.js +10 -4
  459. package/dist/core/session-role.js.map +1 -1
  460. package/dist/core/session-snapshot.d.ts +34 -0
  461. package/dist/core/session-snapshot.d.ts.map +1 -0
  462. package/dist/core/session-snapshot.js +68 -0
  463. package/dist/core/session-snapshot.js.map +1 -0
  464. package/dist/core/session-supervision-runtime.d.ts +31 -0
  465. package/dist/core/session-supervision-runtime.d.ts.map +1 -0
  466. package/dist/core/session-supervision-runtime.js +97 -0
  467. package/dist/core/session-supervision-runtime.js.map +1 -0
  468. package/dist/core/settings-manager.d.ts +9 -4
  469. package/dist/core/settings-manager.d.ts.map +1 -1
  470. package/dist/core/settings-manager.js +80 -28
  471. package/dist/core/settings-manager.js.map +1 -1
  472. package/dist/core/tasks/session-task-state.d.ts +2 -4
  473. package/dist/core/tasks/session-task-state.d.ts.map +1 -1
  474. package/dist/core/tasks/session-task-state.js +10 -28
  475. package/dist/core/tasks/session-task-state.js.map +1 -1
  476. package/dist/core/tasks/task-projection.d.ts +20 -0
  477. package/dist/core/tasks/task-projection.d.ts.map +1 -0
  478. package/dist/core/tasks/task-projection.js +47 -0
  479. package/dist/core/tasks/task-projection.js.map +1 -0
  480. package/dist/core/tasks/task-state.d.ts +4 -0
  481. package/dist/core/tasks/task-state.d.ts.map +1 -1
  482. package/dist/core/tasks/task-state.js +28 -11
  483. package/dist/core/tasks/task-state.js.map +1 -1
  484. package/dist/core/tool-capability-policy.d.ts +13 -0
  485. package/dist/core/tool-capability-policy.d.ts.map +1 -0
  486. package/dist/core/tool-capability-policy.js +85 -0
  487. package/dist/core/tool-capability-policy.js.map +1 -0
  488. package/dist/core/tool-protocol-controller.d.ts +91 -0
  489. package/dist/core/tool-protocol-controller.d.ts.map +1 -0
  490. package/dist/core/tool-protocol-controller.js +512 -0
  491. package/dist/core/tool-protocol-controller.js.map +1 -0
  492. package/dist/core/tool-recovery-log-records.d.ts +2 -3
  493. package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
  494. package/dist/core/tool-recovery-log-records.js +9 -35
  495. package/dist/core/tool-recovery-log-records.js.map +1 -1
  496. package/dist/core/tool-selection/tool-performance-store.d.ts +3 -7
  497. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
  498. package/dist/core/tool-selection/tool-performance-store.js +97 -138
  499. package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
  500. package/dist/core/tools/ask-question.d.ts +124 -0
  501. package/dist/core/tools/ask-question.d.ts.map +1 -0
  502. package/dist/core/tools/ask-question.js +693 -0
  503. package/dist/core/tools/ask-question.js.map +1 -0
  504. package/dist/core/tools/delegate-status.d.ts +31 -3
  505. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  506. package/dist/core/tools/delegate-status.js +149 -29
  507. package/dist/core/tools/delegate-status.js.map +1 -1
  508. package/dist/core/tools/delegate.d.ts +15 -13
  509. package/dist/core/tools/delegate.d.ts.map +1 -1
  510. package/dist/core/tools/delegate.js +108 -23
  511. package/dist/core/tools/delegate.js.map +1 -1
  512. package/dist/core/tools/extensionify.d.ts.map +1 -1
  513. package/dist/core/tools/extensionify.js +1 -0
  514. package/dist/core/tools/extensionify.js.map +1 -1
  515. package/dist/core/tools/goal.d.ts +5 -5
  516. package/dist/core/tools/goal.d.ts.map +1 -1
  517. package/dist/core/tools/goal.js +10 -10
  518. package/dist/core/tools/goal.js.map +1 -1
  519. package/dist/core/tools/model-fitness.d.ts.map +1 -1
  520. package/dist/core/tools/model-fitness.js +2 -2
  521. package/dist/core/tools/model-fitness.js.map +1 -1
  522. package/dist/core/tools/orchestration-panel.d.ts +43 -0
  523. package/dist/core/tools/orchestration-panel.d.ts.map +1 -0
  524. package/dist/core/tools/orchestration-panel.js +168 -0
  525. package/dist/core/tools/orchestration-panel.js.map +1 -0
  526. package/dist/core/tools/run-process.d.ts +26 -0
  527. package/dist/core/tools/run-process.d.ts.map +1 -0
  528. package/dist/core/tools/run-process.js +138 -0
  529. package/dist/core/tools/run-process.js.map +1 -0
  530. package/dist/core/tools/task-steps.d.ts +2 -0
  531. package/dist/core/tools/task-steps.d.ts.map +1 -1
  532. package/dist/core/tools/task-steps.js +102 -0
  533. package/dist/core/tools/task-steps.js.map +1 -1
  534. package/dist/core/tools/tmux-dispatch.d.ts +3 -5
  535. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -1
  536. package/dist/core/tools/tmux-dispatch.js +4 -4
  537. package/dist/core/tools/tmux-dispatch.js.map +1 -1
  538. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -1
  539. package/dist/core/tools/windows-shell-engine.js +4 -6
  540. package/dist/core/tools/windows-shell-engine.js.map +1 -1
  541. package/dist/core/util/bounded-jsonl.d.ts +10 -0
  542. package/dist/core/util/bounded-jsonl.d.ts.map +1 -0
  543. package/dist/core/util/bounded-jsonl.js +75 -0
  544. package/dist/core/util/bounded-jsonl.js.map +1 -0
  545. package/dist/core/util/value-guards.d.ts +5 -0
  546. package/dist/core/util/value-guards.d.ts.map +1 -0
  547. package/dist/core/util/value-guards.js +12 -0
  548. package/dist/core/util/value-guards.js.map +1 -0
  549. package/dist/core/worktree-sync/store.d.ts +3 -3
  550. package/dist/core/worktree-sync/store.d.ts.map +1 -1
  551. package/dist/core/worktree-sync/store.js +10 -5
  552. package/dist/core/worktree-sync/store.js.map +1 -1
  553. package/dist/index.d.ts +13 -3
  554. package/dist/index.d.ts.map +1 -1
  555. package/dist/index.js +12 -2
  556. package/dist/index.js.map +1 -1
  557. package/dist/main.d.ts.map +1 -1
  558. package/dist/main.js +128 -44
  559. package/dist/main.js.map +1 -1
  560. package/dist/migrations.d.ts +4 -6
  561. package/dist/migrations.d.ts.map +1 -1
  562. package/dist/migrations.js +186 -28
  563. package/dist/migrations.js.map +1 -1
  564. package/dist/modes/interactive/clipboard-input.d.ts +7 -1
  565. package/dist/modes/interactive/clipboard-input.d.ts.map +1 -1
  566. package/dist/modes/interactive/clipboard-input.js +44 -6
  567. package/dist/modes/interactive/clipboard-input.js.map +1 -1
  568. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  569. package/dist/modes/interactive/components/settings-selector.js +2 -2
  570. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  571. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  572. package/dist/modes/interactive/config-backup.js +5 -4
  573. package/dist/modes/interactive/config-backup.js.map +1 -1
  574. package/dist/modes/interactive/extension-ui-host.d.ts +14 -0
  575. package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
  576. package/dist/modes/interactive/extension-ui-host.js +76 -15
  577. package/dist/modes/interactive/extension-ui-host.js.map +1 -1
  578. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  579. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  580. package/dist/modes/interactive/interactive-mode.js +63 -1
  581. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  582. package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
  583. package/dist/modes/interactive/key-handlers.js +1 -1
  584. package/dist/modes/interactive/key-handlers.js.map +1 -1
  585. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  586. package/dist/modes/interactive/profile-menu-controller.js +7 -7
  587. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  588. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  589. package/dist/modes/interactive/report-commands.js +21 -0
  590. package/dist/modes/interactive/report-commands.js.map +1 -1
  591. package/dist/modes/interactive/session-flow-commands.d.ts +17 -2
  592. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  593. package/dist/modes/interactive/session-flow-commands.js +50 -17
  594. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  595. package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
  596. package/dist/modes/interactive/session-io-commands.js +16 -6
  597. package/dist/modes/interactive/session-io-commands.js.map +1 -1
  598. package/dist/modes/interactive/session-replacement-errors.d.ts +7 -0
  599. package/dist/modes/interactive/session-replacement-errors.d.ts.map +1 -0
  600. package/dist/modes/interactive/session-replacement-errors.js +15 -0
  601. package/dist/modes/interactive/session-replacement-errors.js.map +1 -0
  602. package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
  603. package/dist/modes/interactive/startup-checks.js +1 -1
  604. package/dist/modes/interactive/startup-checks.js.map +1 -1
  605. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  606. package/dist/modes/rpc/rpc-mode.js +82 -23
  607. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  608. package/dist/modes/rpc/rpc-types.d.ts +15 -0
  609. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  610. package/dist/modes/rpc/rpc-types.js +138 -1
  611. package/dist/modes/rpc/rpc-types.js.map +1 -1
  612. package/dist/utils/clipboard-image.d.ts.map +1 -1
  613. package/dist/utils/clipboard-image.js +2 -13
  614. package/dist/utils/clipboard-image.js.map +1 -1
  615. package/dist/utils/clipboard-native.d.ts +1 -0
  616. package/dist/utils/clipboard-native.d.ts.map +1 -1
  617. package/dist/utils/clipboard-native.js.map +1 -1
  618. package/dist/utils/clipboard.d.ts +1 -0
  619. package/dist/utils/clipboard.d.ts.map +1 -1
  620. package/dist/utils/clipboard.js +52 -1
  621. package/dist/utils/clipboard.js.map +1 -1
  622. package/dist/utils/platform.d.ts +3 -0
  623. package/dist/utils/platform.d.ts.map +1 -0
  624. package/dist/utils/platform.js +8 -0
  625. package/dist/utils/platform.js.map +1 -0
  626. package/dist/utils/work-directory.d.ts +11 -0
  627. package/dist/utils/work-directory.d.ts.map +1 -1
  628. package/dist/utils/work-directory.js +30 -9
  629. package/dist/utils/work-directory.js.map +1 -1
  630. package/docs/UI_POLISH_ORCHESTRATION.md +44 -0
  631. package/docs/UI_QUALITY_ORCHESTRATION.md +30 -0
  632. package/docs/docs.json +4 -0
  633. package/docs/extensions.md +62 -2
  634. package/docs/harness-architecture.md +156 -0
  635. package/docs/keybindings.md +11 -1
  636. package/docs/process-matrix.md +59 -26
  637. package/docs/rpc.md +45 -2
  638. package/docs/sessions.md +15 -0
  639. package/docs/settings.md +125 -11
  640. package/docs/task-steps.md +8 -0
  641. package/docs/telemetry.md +19 -0
  642. package/docs/tmux-agent-manager.md +10 -3
  643. package/docs/ui-reference/README.md +9 -0
  644. package/docs/ui-reference/orchestration-after.txt +7 -0
  645. package/docs/ui-reference/orchestration-before.txt +7 -0
  646. package/docs/ui-reference/orchestration-reference.txt +6 -0
  647. package/docs/usage.md +14 -2
  648. package/docs/work-directory.md +19 -8
  649. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  650. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  651. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  652. package/examples/extensions/question.ts +34 -245
  653. package/examples/extensions/questionnaire.ts +41 -407
  654. package/examples/extensions/sandbox/package-lock.json +2 -2
  655. package/examples/extensions/sandbox/package.json +1 -1
  656. package/examples/extensions/with-deps/package-lock.json +2 -2
  657. package/examples/extensions/with-deps/package.json +1 -1
  658. package/npm-shrinkwrap.json +12 -12
  659. package/package.json +4 -4
  660. package/dist/core/delegation/session-worker-result.d.ts +0 -46
  661. package/dist/core/delegation/session-worker-result.d.ts.map +0 -1
  662. package/dist/core/delegation/session-worker-result.js +0 -113
  663. package/dist/core/delegation/session-worker-result.js.map +0 -1
  664. package/dist/core/delegation/worker-result.d.ts.map +0 -1
  665. package/dist/core/delegation/worker-result.js.map +0 -1
  666. package/dist/core/telemetry.d.ts.map +0 -1
  667. package/dist/core/telemetry.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-support.d.ts","sourceRoot":"","sources":["../../src/core/compaction-support.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Compaction support: summarizer model selection (#30 cost guard), request-auth resolution\n * with session-model fallback, and window-adapted compaction settings.\n *\n * Extracted verbatim from AgentSession (agent-session.ts) as a narrow-deps collaborator; the\n * session keeps same-signature private delegations at every call-in point.\n */\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { type CompactionSettings, summarizerCanIngest } from \"@caupulican/pi-agent-core/node\";\nimport type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { evaluateSurfaceFitness } from \"./model-router/fitness-gate.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nexport interface CompactionSupportDeps {\n\tgetModel(): Model<any> | undefined;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelRegistry(): ModelRegistry;\n\t/** True when the agent's streamFn is (or wraps) the raw streamSimple — auth must be explicit then. */\n\tisRawStream(): boolean;\n\t/** Host auth resolution that THROWS with a user-actionable message when no key exists. */\n\tgetRequiredRequestAuth(model: Model<any>): Promise<{ apiKey?: string; headers?: Record<string, string> }>;\n\tisModelExhausted(ref: string): boolean;\n\tgetStoredFitnessReport(ref: string): ModelFitnessReport | undefined;\n\t/** Estimated tokens of the summarization input (live context; over-estimates, which is safe). */\n\testimateSummarizationInputTokens(): number;\n\temitWarning(message: string): void;\n\t/**\n\t * The same readiness/residency gate every other isolated consumer uses\n\t * (LocalRuntimeController.ensureIsolatedModelReady) before this model is used for an\n\t * out-of-band call. No-ops for a non-managed-local model (checked internally by the gate);\n\t * throws with a user-actionable reason when a managed-local model cannot be made ready.\n\t */\n\tensureModelReady(model: Model<any>): Promise<void>;\n}\n\nexport class CompactionSupport {\n\tprivate readonly deps: CompactionSupportDeps;\n\tprivate lastSelectionReason: string | undefined;\n\n\tconstructor(deps: CompactionSupportDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tgetLastSelectionReason(): string | undefined {\n\t\treturn this.lastSelectionReason;\n\t}\n\n\tgetAdaptedSettings(): CompactionSettings {\n\t\tconst settings = this.deps.getSettingsManager().getCompactionSettings();\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return settings;\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) return settings;\n\n\t\t// Adapt reserveTokens: at most 25% of context window\n\t\tconst maxReserve = Math.floor(contextWindow * 0.25);\n\t\tconst reserveTokens = Math.min(settings.reserveTokens, maxReserve);\n\n\t\t// Adapt keepRecentTokens: at most 50% of context window\n\t\tconst maxKeepRecent = Math.floor(contextWindow * 0.5);\n\t\tconst keepRecentTokens = Math.min(settings.keepRecentTokens, maxKeepRecent);\n\n\t\treturn {\n\t\t\t...settings,\n\t\t\treserveTokens,\n\t\t\tkeepRecentTokens,\n\t\t};\n\t}\n\n\tasync getRequestAuth(model: Model<any>): Promise<{\n\t\tapiKey?: string;\n\t\theaders?: Record<string, string>;\n\t}> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\treturn this.deps.getRequiredRequestAuth(model);\n\t\t}\n\n\t\tconst result = await this.deps.getModelRegistry().getApiKeyAndHeaders(model);\n\t\treturn result.ok ? { apiKey: result.apiKey, headers: result.headers } : {};\n\t}\n\n\t/**\n\t * Readiness/residency gate a candidate summarizer model right before it is handed back for\n\t * use. A no-op for a non-managed-local model (checked internally by\n\t * {@link CompactionSupportDeps.ensureModelReady}); a managed-local model that cannot be made\n\t * ready (server down, model missing, residency refused) returns its failure reason instead of\n\t * throwing, so callers can still try the next candidate in the existing auth-fallback ladder\n\t * rather than aborting resolution outright.\n\t */\n\tprivate async readinessFailure(model: Model<any>): Promise<string | undefined> {\n\t\ttry {\n\t\t\tawait this.deps.ensureModelReady(model);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\treturn error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the summarizer model AND its request auth for auto-compaction. The cheap auxiliary\n\t * model (#30) can be nominally available yet fail key resolution at request time (expired\n\t * OAuth, revoked key) — or, for a managed-local model, fail the readiness/residency gate (\n\t * server down, model not installed, residency refused). Falling back to the session model keeps\n\t * auto-compaction working in exactly the situations where manual /compact works; only when\n\t * neither resolves do we fail — and then with a concrete message instead of a silent no-op or a\n\t * bypass of the readiness gate.\n\t */\n\tasync resolveModelAndAuth(\n\t\tcompactionModel: Model<any>,\n\t\tsessionModel: Model<any>,\n\t): Promise<{ model: Model<any>; apiKey?: string; headers?: Record<string, string>; failure?: string }> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\tconst registry = this.deps.getModelRegistry();\n\t\t\tlet auth = await registry.getApiKeyAndHeaders(compactionModel);\n\t\t\tlet readiness: string | undefined;\n\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\treadiness = await this.readinessFailure(compactionModel);\n\t\t\t\tif (!readiness) return { model: compactionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t}\n\t\t\tconst isSameModel =\n\t\t\t\tcompactionModel.provider === sessionModel.provider && compactionModel.id === sessionModel.id;\n\t\t\tif (!isSameModel) {\n\t\t\t\tauth = await registry.getApiKeyAndHeaders(sessionModel);\n\t\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\t\treadiness = await this.readinessFailure(sessionModel);\n\t\t\t\t\tif (!readiness) return { model: sessionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmodel: sessionModel,\n\t\t\t\tfailure: readiness\n\t\t\t\t\t? `summarizer ${isSameModel ? compactionModel.id : sessionModel.id} not ready: ${readiness}`\n\t\t\t\t\t: `no usable API key for the summarizer (tried ${compactionModel.id}${isSameModel ? \"\" : ` and ${sessionModel.id}`})`,\n\t\t\t};\n\t\t}\n\n\t\t// Custom streamFn owns auth injection (CLI path) — resolve best-effort, never fail on auth\n\t\t// here; a managed-local summarizer must still pass the readiness gate before compact() runs.\n\t\tconst { apiKey, headers } = await this.getRequestAuth(compactionModel);\n\t\tconst readiness = await this.readinessFailure(compactionModel);\n\t\tif (readiness) {\n\t\t\treturn {\n\t\t\t\tmodel: compactionModel,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tfailure: `summarizer ${compactionModel.id} not ready: ${readiness}`,\n\t\t\t};\n\t\t}\n\t\treturn { model: compactionModel, apiKey, headers };\n\t}\n\n\tprivate getExplicitCompactionModelSetting(): string | undefined {\n\t\tconst setting = this.deps.getSettingsManager().getCompactionModel().trim();\n\t\treturn setting && setting !== \"auto\" ? setting : undefined;\n\t}\n\n\tprivate modelRef(model: Model<any>): string {\n\t\treturn `${model.provider}/${model.id}`;\n\t}\n\n\tprivate resolveConfiguredModel(pattern: string): { model?: Model<any>; cause?: \"unresolved\" | \"unauthed\" } {\n\t\tconst registry = this.deps.getModelRegistry();\n\t\tconst resolved = resolveCliModel({ cliModel: pattern, modelRegistry: registry });\n\t\tif (!resolved.model) return { cause: \"unresolved\" };\n\t\tif (!registry.hasConfiguredAuth(resolved.model)) return { cause: \"unauthed\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate selectConfiguredModel(pattern: string): { model?: Model<any>; cause?: string } {\n\t\tconst resolved = this.resolveConfiguredModel(pattern);\n\t\tif (!resolved.model) return { cause: resolved.cause };\n\t\tif (this.deps.isModelExhausted(this.modelRef(resolved.model))) return { cause: \"exhausted\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate effectiveContextWindow(model: Model<any>): number {\n\t\tconst registered = model.contextWindow > 0 ? model.contextWindow : Number.POSITIVE_INFINITY;\n\t\tconst served = this.deps.getStoredFitnessReport(this.modelRef(model))?.capacity?.servedContextWindow;\n\t\treturn served && served > 0 ? Math.min(registered, served) : registered;\n\t}\n\n\tprivate modelWithEffectiveWindow(model: Model<any>): Model<any> {\n\t\tconst contextWindow = this.effectiveContextWindow(model);\n\t\treturn contextWindow === model.contextWindow ? model : { ...model, contextWindow };\n\t}\n\n\tprivate resolveDefaultModel(sessionModel: Model<any>): Model<any> {\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (!router.enabled || !router.cheapModel) {\n\t\t\tthis.lastSelectionReason = \"session_default\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst selected = this.selectConfiguredModel(router.cheapModel);\n\t\tif (!selected.model) {\n\t\t\tthis.lastSelectionReason = `fallback:${selected.cause}`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\t// Capacity is a hard constraint, independent of the fitness doctrine: a summarizer whose\n\t\t// window cannot hold the actual span produces recall-empty checkpoints (local servers\n\t\t// silently truncate over-window prompts instead of erroring), and the verification gate\n\t\t// then fails deterministically.\n\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\tthis.lastSelectionReason = `fallback:window_too_small(~${Math.ceil(estimatedInputTokens / 1000)}k input vs ${effectiveWindow} window)`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst fitness = this.deps.getStoredFitnessReport(this.modelRef(selected.model));\n\t\tconst verdict = evaluateSurfaceFitness(\"compaction\", fitness);\n\t\tif (!verdict.fit) {\n\t\t\tthis.lastSelectionReason =\n\t\t\t\tverdict.reason === \"lane_failed\"\n\t\t\t\t\t? `fallback:digest_unfit(${verdict.succeeded}/${verdict.total})`\n\t\t\t\t\t: \"fallback:unprobed\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tthis.lastSelectionReason = \"router_cheap\";\n\t\treturn selected.model;\n\t}\n\n\tprivate modelsAreEqual(left: Model<any>, right: Model<any>): boolean {\n\t\treturn left.provider === right.provider && left.id === right.id;\n\t}\n\n\t/**\n\t * Resolve the model used to SUMMARIZE during compaction. Selection:\n\t * - an explicit `compaction.model` setting wins, but only if its provider is authed (else fall back);\n\t * - `\"auto\"`/unset follows the model router's configured cheap model when the router is enabled;\n\t * - otherwise the session model is used (safe default).\n\t */\n\tresolveModel(sessionModel: Model<any>): Model<any> {\n\t\tconst explicitSetting = this.getExplicitCompactionModelSetting();\n\t\tif (explicitSetting) {\n\t\t\tconst selected = this.selectConfiguredModel(explicitSetting);\n\t\t\tthis.lastSelectionReason = selected.model ? \"explicit\" : `fallback:${selected.cause}`;\n\t\t\tif (!selected.model) this.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t\t// An explicit user choice is honored (Class C doctrine), but silently sending an\n\t\t\t// over-window prompt yields a recall-empty summary — warn with the numbers.\n\t\t\tif (selected.model) {\n\t\t\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\t\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\t\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\t\t\tthis.deps.emitWarning(\n\t\t\t\t\t\t`Compaction summarizer (explicit setting) likely cannot ingest the current context: ~${Math.ceil(estimatedInputTokens / 1000)}k input tokens vs a ${effectiveWindow}-token window`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn selected.model ?? sessionModel;\n\t\t}\n\t\tconst model = this.resolveDefaultModel(sessionModel);\n\t\tif (this.lastSelectionReason?.startsWith(\"fallback:\")) {\n\t\t\tthis.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t}\n\t\treturn model;\n\t}\n\n\t/** Default compaction should never inherit expensive session thinking. */\n\tresolveThinkingLevel(\n\t\tsessionThinkingLevel: ThinkingLevel | undefined,\n\t\tcompactionModel: Model<any>,\n\t\tsessionModel: Model<any>,\n\t): ThinkingLevel | undefined {\n\t\tif (this.getExplicitCompactionModelSetting()) return sessionThinkingLevel;\n\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (router.enabled && router.cheapModel) {\n\t\t\tconst routerModel = this.resolveConfiguredModel(router.cheapModel).model;\n\t\t\tif (routerModel && this.modelsAreEqual(routerModel, compactionModel)) {\n\t\t\t\treturn router.cheapThinking ?? \"low\";\n\t\t\t}\n\t\t}\n\n\t\treturn this.modelsAreEqual(compactionModel, sessionModel) ? \"low\" : sessionThinkingLevel;\n\t}\n}\n"]}
1
+ {"version":3,"file":"compaction-support.d.ts","sourceRoot":"","sources":["../../src/core/compaction-support.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Compaction support: summarizer model selection (#30 cost guard), request-auth resolution\n * with session-model fallback, and window-adapted compaction settings.\n *\n * This is the policy half of compaction. CompactionController owns detection, execution, retry,\n * persistence, notification, and cancellation; AgentSession keeps compatibility delegations at\n * established regression seams.\n */\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { type CompactionSettings, summarizerCanIngest } from \"@caupulican/pi-agent-core/node\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { evaluateSurfaceFitness } from \"./model-router/fitness-gate.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nexport interface CompactionSupportDeps {\n\tgetModel(): Model<Api> | undefined;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelRegistry(): ModelRegistry;\n\t/** True when the agent's streamFn is (or wraps) the raw streamSimple — auth must be explicit then. */\n\tisRawStream(): boolean;\n\t/** Host auth resolution that THROWS with a user-actionable message when no key exists. */\n\tgetRequiredRequestAuth(model: Model<Api>): Promise<{ apiKey?: string; headers?: Record<string, string> }>;\n\tisModelExhausted(ref: string): boolean;\n\tgetStoredFitnessReport(ref: string): ModelFitnessReport | undefined;\n\t/** Estimated tokens of the summarization input (live context; over-estimates, which is safe). */\n\testimateSummarizationInputTokens(): number;\n\temitWarning(message: string): void;\n\t/**\n\t * The same readiness/residency gate every other isolated consumer uses\n\t * (LocalRuntimeController.ensureIsolatedModelReady) before this model is used for an\n\t * out-of-band call. No-ops for a non-managed-local model (checked internally by the gate);\n\t * throws with a user-actionable reason when a managed-local model cannot be made ready.\n\t */\n\tensureModelReady(model: Model<Api>): Promise<void>;\n}\n\nexport class CompactionSupport {\n\tprivate readonly deps: CompactionSupportDeps;\n\tprivate lastSelectionReason: string | undefined;\n\n\tconstructor(deps: CompactionSupportDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tgetLastSelectionReason(): string | undefined {\n\t\treturn this.lastSelectionReason;\n\t}\n\n\tgetAdaptedSettings(): CompactionSettings {\n\t\tconst settings = this.deps.getSettingsManager().getCompactionSettings();\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return settings;\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) return settings;\n\n\t\t// Adapt reserveTokens: at most 25% of context window\n\t\tconst maxReserve = Math.floor(contextWindow * 0.25);\n\t\tconst reserveTokens = Math.min(settings.reserveTokens, maxReserve);\n\n\t\t// Adapt keepRecentTokens: at most 50% of context window\n\t\tconst maxKeepRecent = Math.floor(contextWindow * 0.5);\n\t\tconst keepRecentTokens = Math.min(settings.keepRecentTokens, maxKeepRecent);\n\n\t\treturn {\n\t\t\t...settings,\n\t\t\treserveTokens,\n\t\t\tkeepRecentTokens,\n\t\t};\n\t}\n\n\tasync getRequestAuth(model: Model<Api>): Promise<{\n\t\tapiKey?: string;\n\t\theaders?: Record<string, string>;\n\t}> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\treturn this.deps.getRequiredRequestAuth(model);\n\t\t}\n\n\t\tconst result = await this.deps.getModelRegistry().getApiKeyAndHeaders(model);\n\t\treturn result.ok ? { apiKey: result.apiKey, headers: result.headers } : {};\n\t}\n\n\t/**\n\t * Readiness/residency gate a candidate summarizer model right before it is handed back for\n\t * use. A no-op for a non-managed-local model (checked internally by\n\t * {@link CompactionSupportDeps.ensureModelReady}); a managed-local model that cannot be made\n\t * ready (server down, model missing, residency refused) returns its failure reason instead of\n\t * throwing, so callers can still try the next candidate in the existing auth-fallback ladder\n\t * rather than aborting resolution outright.\n\t */\n\tprivate async readinessFailure(model: Model<Api>): Promise<string | undefined> {\n\t\ttry {\n\t\t\tawait this.deps.ensureModelReady(model);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\treturn error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the summarizer model AND its request auth for auto-compaction. The cheap auxiliary\n\t * model (#30) can be nominally available yet fail key resolution at request time (expired\n\t * OAuth, revoked key) — or, for a managed-local model, fail the readiness/residency gate (\n\t * server down, model not installed, residency refused). Falling back to the session model keeps\n\t * auto-compaction working in exactly the situations where manual /compact works; only when\n\t * neither resolves do we fail — and then with a concrete message instead of a silent no-op or a\n\t * bypass of the readiness gate.\n\t */\n\tasync resolveModelAndAuth(\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): Promise<{ model: Model<Api>; apiKey?: string; headers?: Record<string, string>; failure?: string }> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\tconst registry = this.deps.getModelRegistry();\n\t\t\tlet auth = await registry.getApiKeyAndHeaders(compactionModel);\n\t\t\tlet readiness: string | undefined;\n\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\treadiness = await this.readinessFailure(compactionModel);\n\t\t\t\tif (!readiness) return { model: compactionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t}\n\t\t\tconst isSameModel =\n\t\t\t\tcompactionModel.provider === sessionModel.provider && compactionModel.id === sessionModel.id;\n\t\t\tif (!isSameModel) {\n\t\t\t\tauth = await registry.getApiKeyAndHeaders(sessionModel);\n\t\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\t\treadiness = await this.readinessFailure(sessionModel);\n\t\t\t\t\tif (!readiness) return { model: sessionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmodel: sessionModel,\n\t\t\t\tfailure: readiness\n\t\t\t\t\t? `summarizer ${isSameModel ? compactionModel.id : sessionModel.id} not ready: ${readiness}`\n\t\t\t\t\t: `no usable API key for the summarizer (tried ${compactionModel.id}${isSameModel ? \"\" : ` and ${sessionModel.id}`})`,\n\t\t\t};\n\t\t}\n\n\t\t// Custom streamFn owns auth injection (CLI path) — resolve best-effort, never fail on auth\n\t\t// here; a managed-local summarizer must still pass the readiness gate before compact() runs.\n\t\tconst { apiKey, headers } = await this.getRequestAuth(compactionModel);\n\t\tconst readiness = await this.readinessFailure(compactionModel);\n\t\tif (readiness) {\n\t\t\treturn {\n\t\t\t\tmodel: compactionModel,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tfailure: `summarizer ${compactionModel.id} not ready: ${readiness}`,\n\t\t\t};\n\t\t}\n\t\treturn { model: compactionModel, apiKey, headers };\n\t}\n\n\tprivate getExplicitCompactionModelSetting(): string | undefined {\n\t\tconst setting = this.deps.getSettingsManager().getCompactionModel().trim();\n\t\treturn setting && setting !== \"auto\" ? setting : undefined;\n\t}\n\n\tprivate modelRef(model: Model<Api>): string {\n\t\treturn `${model.provider}/${model.id}`;\n\t}\n\n\tprivate resolveConfiguredModel(pattern: string): { model?: Model<Api>; cause?: \"unresolved\" | \"unauthed\" } {\n\t\tconst registry = this.deps.getModelRegistry();\n\t\tconst resolved = resolveCliModel({ cliModel: pattern, modelRegistry: registry });\n\t\tif (!resolved.model) return { cause: \"unresolved\" };\n\t\tif (!registry.hasConfiguredAuth(resolved.model)) return { cause: \"unauthed\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate selectConfiguredModel(pattern: string): { model?: Model<Api>; cause?: string } {\n\t\tconst resolved = this.resolveConfiguredModel(pattern);\n\t\tif (!resolved.model) return { cause: resolved.cause };\n\t\tif (this.deps.isModelExhausted(this.modelRef(resolved.model))) return { cause: \"exhausted\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate effectiveContextWindow(model: Model<Api>): number {\n\t\tconst registered = model.contextWindow > 0 ? model.contextWindow : Number.POSITIVE_INFINITY;\n\t\tconst served = this.deps.getStoredFitnessReport(this.modelRef(model))?.capacity?.servedContextWindow;\n\t\treturn served && served > 0 ? Math.min(registered, served) : registered;\n\t}\n\n\tprivate modelWithEffectiveWindow(model: Model<Api>): Model<Api> {\n\t\tconst contextWindow = this.effectiveContextWindow(model);\n\t\treturn contextWindow === model.contextWindow ? model : { ...model, contextWindow };\n\t}\n\n\tprivate resolveDefaultModel(sessionModel: Model<Api>): Model<Api> {\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (!router.enabled || !router.cheapModel) {\n\t\t\tthis.lastSelectionReason = \"session_default\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst selected = this.selectConfiguredModel(router.cheapModel);\n\t\tif (!selected.model) {\n\t\t\tthis.lastSelectionReason = `fallback:${selected.cause}`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\t// Capacity is a hard constraint, independent of the fitness doctrine: a summarizer whose\n\t\t// window cannot hold the actual span produces recall-empty checkpoints (local servers\n\t\t// silently truncate over-window prompts instead of erroring), and the verification gate\n\t\t// then fails deterministically.\n\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\tthis.lastSelectionReason = `fallback:window_too_small(~${Math.ceil(estimatedInputTokens / 1000)}k input vs ${effectiveWindow} window)`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst fitness = this.deps.getStoredFitnessReport(this.modelRef(selected.model));\n\t\tconst verdict = evaluateSurfaceFitness(\"compaction\", fitness);\n\t\tif (!verdict.fit) {\n\t\t\tthis.lastSelectionReason =\n\t\t\t\tverdict.reason === \"lane_failed\"\n\t\t\t\t\t? `fallback:digest_unfit(${verdict.succeeded}/${verdict.total})`\n\t\t\t\t\t: \"fallback:unprobed\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tthis.lastSelectionReason = \"router_cheap\";\n\t\treturn selected.model;\n\t}\n\n\tprivate modelsAreEqual(left: Model<Api>, right: Model<Api>): boolean {\n\t\treturn left.provider === right.provider && left.id === right.id;\n\t}\n\n\t/**\n\t * Resolve the model used to SUMMARIZE during compaction. Selection:\n\t * - an explicit `compaction.model` setting wins, but only if its provider is authed (else fall back);\n\t * - `\"auto\"`/unset follows the model router's configured cheap model when the router is enabled;\n\t * - otherwise the session model is used (safe default).\n\t */\n\tresolveModel(sessionModel: Model<Api>): Model<Api> {\n\t\tconst explicitSetting = this.getExplicitCompactionModelSetting();\n\t\tif (explicitSetting) {\n\t\t\tconst selected = this.selectConfiguredModel(explicitSetting);\n\t\t\tthis.lastSelectionReason = selected.model ? \"explicit\" : `fallback:${selected.cause}`;\n\t\t\tif (!selected.model) this.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t\t// An explicit user choice is honored (Class C doctrine), but silently sending an\n\t\t\t// over-window prompt yields a recall-empty summary — warn with the numbers.\n\t\t\tif (selected.model) {\n\t\t\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\t\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\t\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\t\t\tthis.deps.emitWarning(\n\t\t\t\t\t\t`Compaction summarizer (explicit setting) likely cannot ingest the current context: ~${Math.ceil(estimatedInputTokens / 1000)}k input tokens vs a ${effectiveWindow}-token window`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn selected.model ?? sessionModel;\n\t\t}\n\t\tconst model = this.resolveDefaultModel(sessionModel);\n\t\tif (this.lastSelectionReason?.startsWith(\"fallback:\")) {\n\t\t\tthis.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t}\n\t\treturn model;\n\t}\n\n\t/** Default compaction should never inherit expensive session thinking. */\n\tresolveThinkingLevel(\n\t\tsessionThinkingLevel: ThinkingLevel | undefined,\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): ThinkingLevel | undefined {\n\t\tif (this.getExplicitCompactionModelSetting()) return sessionThinkingLevel;\n\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (router.enabled && router.cheapModel) {\n\t\t\tconst routerModel = this.resolveConfiguredModel(router.cheapModel).model;\n\t\t\tif (routerModel && this.modelsAreEqual(routerModel, compactionModel)) {\n\t\t\t\treturn router.cheapThinking ?? \"low\";\n\t\t\t}\n\t\t}\n\n\t\treturn this.modelsAreEqual(compactionModel, sessionModel) ? \"low\" : sessionThinkingLevel;\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-support.js","sourceRoot":"","sources":["../../src/core/compaction-support.ts"],"names":[],"mappings":"AAQA,OAAO,EAA2B,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG9F,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AA0BxE,MAAM,OAAO,iBAAiB;IACZ,IAAI,CAAwB;IACrC,mBAAmB,CAAqB;IAEhD,YAAY,IAA2B,EAAE;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;IAED,sBAAsB,GAAuB;QAC5C,OAAO,IAAI,CAAC,mBAAmB,CAAC;IAAA,CAChC;IAED,kBAAkB,GAAuB;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,qBAAqB,EAAE,CAAC;QACxE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO,QAAQ,CAAC;QAC5B,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;QAC/C,IAAI,aAAa,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC;QAExC,qDAAqD;QACrD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;QACpD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QAEnE,wDAAwD;QACxD,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,GAAG,CAAC,CAAC;QACtD,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAE5E,OAAO;YACN,GAAG,QAAQ;YACX,aAAa;YACb,gBAAgB;SAChB,CAAC;IAAA,CACF;IAED,KAAK,CAAC,cAAc,CAAC,KAAiB,EAGnC;QACF,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC7E,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAAA,CAC3E;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,gBAAgB,CAAC,KAAiB,EAA+B;QAC9E,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;YACxC,OAAO,SAAS,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/D,CAAC;IAAA,CACD;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,mBAAmB,CACxB,eAA2B,EAC3B,YAAwB,EAC8E;QACtG,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC9C,IAAI,IAAI,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC;YAC/D,IAAI,SAA6B,CAAC;YAClC,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC5B,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC;gBACzD,IAAI,CAAC,SAAS;oBAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/F,CAAC;YACD,MAAM,WAAW,GAChB,eAAe,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ,IAAI,eAAe,CAAC,EAAE,KAAK,YAAY,CAAC,EAAE,CAAC;YAC9F,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,IAAI,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;gBACxD,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAC5B,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;oBACtD,IAAI,CAAC,SAAS;wBAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC5F,CAAC;YACF,CAAC;YACD,OAAO;gBACN,KAAK,EAAE,YAAY;gBACnB,OAAO,EAAE,SAAS;oBACjB,CAAC,CAAC,cAAc,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,eAAe,SAAS,EAAE;oBAC5F,CAAC,CAAC,+CAA+C,eAAe,CAAC,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,YAAY,CAAC,EAAE,EAAE,GAAG;aACtH,CAAC;QACH,CAAC;QAED,6FAA2F;QAC3F,6FAA6F;QAC7F,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC;QAC/D,IAAI,SAAS,EAAE,CAAC;YACf,OAAO;gBACN,KAAK,EAAE,eAAe;gBACtB,MAAM;gBACN,OAAO;gBACP,OAAO,EAAE,cAAc,eAAe,CAAC,EAAE,eAAe,SAAS,EAAE;aACnE,CAAC;QACH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAAA,CACnD;IAEO,iCAAiC,GAAuB;QAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,CAAC;QAC3E,OAAO,OAAO,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAAA,CAC3D;IAEO,QAAQ,CAAC,KAAiB,EAAU;QAC3C,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;IAAA,CACvC;IAEO,sBAAsB,CAAC,OAAe,EAA6D;QAC1G,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;QACjF,IAAI,CAAC,QAAQ,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAC9E,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAAA,CACjC;IAEO,qBAAqB,CAAC,OAAe,EAA0C;QACtF,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtD,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAAA,CACjC;IAEO,sBAAsB,CAAC,KAAiB,EAAU;QACzD,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAC5F,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC;QACrG,OAAO,MAAM,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IAAA,CACxE;IAEO,wBAAwB,CAAC,KAAiB,EAAc;QAC/D,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACzD,OAAO,aAAa,KAAK,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,CAAC;IAAA,CACnF;IAEO,mBAAmB,CAAC,YAAwB,EAAc;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,sBAAsB,EAAE,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC3C,IAAI,CAAC,mBAAmB,GAAG,iBAAiB,CAAC;YAC7C,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC/D,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,mBAAmB,GAAG,YAAY,QAAQ,CAAC,KAAK,EAAE,CAAC;YACxD,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,yFAAyF;QACzF,sFAAsF;QACtF,wFAAwF;QACxF,gCAAgC;QAChC,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,gCAAgC,EAAE,CAAC;QAC1E,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,wBAAwB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC,EAAE,CAAC;YAC/F,IAAI,CAAC,mBAAmB,GAAG,8BAA8B,IAAI,CAAC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,cAAc,eAAe,UAAU,CAAC;YACvI,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAChF,MAAM,OAAO,GAAG,sBAAsB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAClB,IAAI,CAAC,mBAAmB;gBACvB,OAAO,CAAC,MAAM,KAAK,aAAa;oBAC/B,CAAC,CAAC,yBAAyB,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,GAAG;oBAChE,CAAC,CAAC,mBAAmB,CAAC;YACxB,OAAO,YAAY,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,mBAAmB,GAAG,cAAc,CAAC;QAC1C,OAAO,QAAQ,CAAC,KAAK,CAAC;IAAA,CACtB;IAEO,cAAc,CAAC,IAAgB,EAAE,KAAiB,EAAW;QACpE,OAAO,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;IAAA,CAChE;IAED;;;;;OAKG;IACH,YAAY,CAAC,YAAwB,EAAc;QAClD,MAAM,eAAe,GAAG,IAAI,CAAC,iCAAiC,EAAE,CAAC;QACjE,IAAI,eAAe,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC;YAC7D,IAAI,CAAC,mBAAmB,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,QAAQ,CAAC,KAAK,EAAE,CAAC;YACtF,IAAI,CAAC,QAAQ,CAAC,KAAK;gBAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,yBAAyB,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;YAChG,iFAAiF;YACjF,8EAA4E;YAC5E,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACpB,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,gCAAgC,EAAE,CAAC;gBAC1E,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACpE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,wBAAwB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC,EAAE,CAAC;oBAC/F,IAAI,CAAC,IAAI,CAAC,WAAW,CACpB,uFAAuF,IAAI,CAAC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,uBAAuB,eAAe,eAAe,CAClL,CAAC;gBACH,CAAC;YACF,CAAC;YACD,OAAO,QAAQ,CAAC,KAAK,IAAI,YAAY,CAAC;QACvC,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,yBAAyB,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,KAAK,CAAC;IAAA,CACb;IAED,0EAA0E;IAC1E,oBAAoB,CACnB,oBAA+C,EAC/C,eAA2B,EAC3B,YAAwB,EACI;QAC5B,IAAI,IAAI,CAAC,iCAAiC,EAAE;YAAE,OAAO,oBAAoB,CAAC;QAE1E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,sBAAsB,EAAE,CAAC;QACvE,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACzC,MAAM,WAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACzE,IAAI,WAAW,IAAI,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,CAAC;gBACtE,OAAO,MAAM,CAAC,aAAa,IAAI,KAAK,CAAC;YACtC,CAAC;QACF,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC;IAAA,CACzF;CACD","sourcesContent":["/**\n * Compaction support: summarizer model selection (#30 cost guard), request-auth resolution\n * with session-model fallback, and window-adapted compaction settings.\n *\n * Extracted verbatim from AgentSession (agent-session.ts) as a narrow-deps collaborator; the\n * session keeps same-signature private delegations at every call-in point.\n */\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { type CompactionSettings, summarizerCanIngest } from \"@caupulican/pi-agent-core/node\";\nimport type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { evaluateSurfaceFitness } from \"./model-router/fitness-gate.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nexport interface CompactionSupportDeps {\n\tgetModel(): Model<any> | undefined;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelRegistry(): ModelRegistry;\n\t/** True when the agent's streamFn is (or wraps) the raw streamSimple — auth must be explicit then. */\n\tisRawStream(): boolean;\n\t/** Host auth resolution that THROWS with a user-actionable message when no key exists. */\n\tgetRequiredRequestAuth(model: Model<any>): Promise<{ apiKey?: string; headers?: Record<string, string> }>;\n\tisModelExhausted(ref: string): boolean;\n\tgetStoredFitnessReport(ref: string): ModelFitnessReport | undefined;\n\t/** Estimated tokens of the summarization input (live context; over-estimates, which is safe). */\n\testimateSummarizationInputTokens(): number;\n\temitWarning(message: string): void;\n\t/**\n\t * The same readiness/residency gate every other isolated consumer uses\n\t * (LocalRuntimeController.ensureIsolatedModelReady) before this model is used for an\n\t * out-of-band call. No-ops for a non-managed-local model (checked internally by the gate);\n\t * throws with a user-actionable reason when a managed-local model cannot be made ready.\n\t */\n\tensureModelReady(model: Model<any>): Promise<void>;\n}\n\nexport class CompactionSupport {\n\tprivate readonly deps: CompactionSupportDeps;\n\tprivate lastSelectionReason: string | undefined;\n\n\tconstructor(deps: CompactionSupportDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tgetLastSelectionReason(): string | undefined {\n\t\treturn this.lastSelectionReason;\n\t}\n\n\tgetAdaptedSettings(): CompactionSettings {\n\t\tconst settings = this.deps.getSettingsManager().getCompactionSettings();\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return settings;\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) return settings;\n\n\t\t// Adapt reserveTokens: at most 25% of context window\n\t\tconst maxReserve = Math.floor(contextWindow * 0.25);\n\t\tconst reserveTokens = Math.min(settings.reserveTokens, maxReserve);\n\n\t\t// Adapt keepRecentTokens: at most 50% of context window\n\t\tconst maxKeepRecent = Math.floor(contextWindow * 0.5);\n\t\tconst keepRecentTokens = Math.min(settings.keepRecentTokens, maxKeepRecent);\n\n\t\treturn {\n\t\t\t...settings,\n\t\t\treserveTokens,\n\t\t\tkeepRecentTokens,\n\t\t};\n\t}\n\n\tasync getRequestAuth(model: Model<any>): Promise<{\n\t\tapiKey?: string;\n\t\theaders?: Record<string, string>;\n\t}> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\treturn this.deps.getRequiredRequestAuth(model);\n\t\t}\n\n\t\tconst result = await this.deps.getModelRegistry().getApiKeyAndHeaders(model);\n\t\treturn result.ok ? { apiKey: result.apiKey, headers: result.headers } : {};\n\t}\n\n\t/**\n\t * Readiness/residency gate a candidate summarizer model right before it is handed back for\n\t * use. A no-op for a non-managed-local model (checked internally by\n\t * {@link CompactionSupportDeps.ensureModelReady}); a managed-local model that cannot be made\n\t * ready (server down, model missing, residency refused) returns its failure reason instead of\n\t * throwing, so callers can still try the next candidate in the existing auth-fallback ladder\n\t * rather than aborting resolution outright.\n\t */\n\tprivate async readinessFailure(model: Model<any>): Promise<string | undefined> {\n\t\ttry {\n\t\t\tawait this.deps.ensureModelReady(model);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\treturn error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the summarizer model AND its request auth for auto-compaction. The cheap auxiliary\n\t * model (#30) can be nominally available yet fail key resolution at request time (expired\n\t * OAuth, revoked key) — or, for a managed-local model, fail the readiness/residency gate (\n\t * server down, model not installed, residency refused). Falling back to the session model keeps\n\t * auto-compaction working in exactly the situations where manual /compact works; only when\n\t * neither resolves do we fail — and then with a concrete message instead of a silent no-op or a\n\t * bypass of the readiness gate.\n\t */\n\tasync resolveModelAndAuth(\n\t\tcompactionModel: Model<any>,\n\t\tsessionModel: Model<any>,\n\t): Promise<{ model: Model<any>; apiKey?: string; headers?: Record<string, string>; failure?: string }> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\tconst registry = this.deps.getModelRegistry();\n\t\t\tlet auth = await registry.getApiKeyAndHeaders(compactionModel);\n\t\t\tlet readiness: string | undefined;\n\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\treadiness = await this.readinessFailure(compactionModel);\n\t\t\t\tif (!readiness) return { model: compactionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t}\n\t\t\tconst isSameModel =\n\t\t\t\tcompactionModel.provider === sessionModel.provider && compactionModel.id === sessionModel.id;\n\t\t\tif (!isSameModel) {\n\t\t\t\tauth = await registry.getApiKeyAndHeaders(sessionModel);\n\t\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\t\treadiness = await this.readinessFailure(sessionModel);\n\t\t\t\t\tif (!readiness) return { model: sessionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmodel: sessionModel,\n\t\t\t\tfailure: readiness\n\t\t\t\t\t? `summarizer ${isSameModel ? compactionModel.id : sessionModel.id} not ready: ${readiness}`\n\t\t\t\t\t: `no usable API key for the summarizer (tried ${compactionModel.id}${isSameModel ? \"\" : ` and ${sessionModel.id}`})`,\n\t\t\t};\n\t\t}\n\n\t\t// Custom streamFn owns auth injection (CLI path) — resolve best-effort, never fail on auth\n\t\t// here; a managed-local summarizer must still pass the readiness gate before compact() runs.\n\t\tconst { apiKey, headers } = await this.getRequestAuth(compactionModel);\n\t\tconst readiness = await this.readinessFailure(compactionModel);\n\t\tif (readiness) {\n\t\t\treturn {\n\t\t\t\tmodel: compactionModel,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tfailure: `summarizer ${compactionModel.id} not ready: ${readiness}`,\n\t\t\t};\n\t\t}\n\t\treturn { model: compactionModel, apiKey, headers };\n\t}\n\n\tprivate getExplicitCompactionModelSetting(): string | undefined {\n\t\tconst setting = this.deps.getSettingsManager().getCompactionModel().trim();\n\t\treturn setting && setting !== \"auto\" ? setting : undefined;\n\t}\n\n\tprivate modelRef(model: Model<any>): string {\n\t\treturn `${model.provider}/${model.id}`;\n\t}\n\n\tprivate resolveConfiguredModel(pattern: string): { model?: Model<any>; cause?: \"unresolved\" | \"unauthed\" } {\n\t\tconst registry = this.deps.getModelRegistry();\n\t\tconst resolved = resolveCliModel({ cliModel: pattern, modelRegistry: registry });\n\t\tif (!resolved.model) return { cause: \"unresolved\" };\n\t\tif (!registry.hasConfiguredAuth(resolved.model)) return { cause: \"unauthed\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate selectConfiguredModel(pattern: string): { model?: Model<any>; cause?: string } {\n\t\tconst resolved = this.resolveConfiguredModel(pattern);\n\t\tif (!resolved.model) return { cause: resolved.cause };\n\t\tif (this.deps.isModelExhausted(this.modelRef(resolved.model))) return { cause: \"exhausted\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate effectiveContextWindow(model: Model<any>): number {\n\t\tconst registered = model.contextWindow > 0 ? model.contextWindow : Number.POSITIVE_INFINITY;\n\t\tconst served = this.deps.getStoredFitnessReport(this.modelRef(model))?.capacity?.servedContextWindow;\n\t\treturn served && served > 0 ? Math.min(registered, served) : registered;\n\t}\n\n\tprivate modelWithEffectiveWindow(model: Model<any>): Model<any> {\n\t\tconst contextWindow = this.effectiveContextWindow(model);\n\t\treturn contextWindow === model.contextWindow ? model : { ...model, contextWindow };\n\t}\n\n\tprivate resolveDefaultModel(sessionModel: Model<any>): Model<any> {\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (!router.enabled || !router.cheapModel) {\n\t\t\tthis.lastSelectionReason = \"session_default\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst selected = this.selectConfiguredModel(router.cheapModel);\n\t\tif (!selected.model) {\n\t\t\tthis.lastSelectionReason = `fallback:${selected.cause}`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\t// Capacity is a hard constraint, independent of the fitness doctrine: a summarizer whose\n\t\t// window cannot hold the actual span produces recall-empty checkpoints (local servers\n\t\t// silently truncate over-window prompts instead of erroring), and the verification gate\n\t\t// then fails deterministically.\n\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\tthis.lastSelectionReason = `fallback:window_too_small(~${Math.ceil(estimatedInputTokens / 1000)}k input vs ${effectiveWindow} window)`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst fitness = this.deps.getStoredFitnessReport(this.modelRef(selected.model));\n\t\tconst verdict = evaluateSurfaceFitness(\"compaction\", fitness);\n\t\tif (!verdict.fit) {\n\t\t\tthis.lastSelectionReason =\n\t\t\t\tverdict.reason === \"lane_failed\"\n\t\t\t\t\t? `fallback:digest_unfit(${verdict.succeeded}/${verdict.total})`\n\t\t\t\t\t: \"fallback:unprobed\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tthis.lastSelectionReason = \"router_cheap\";\n\t\treturn selected.model;\n\t}\n\n\tprivate modelsAreEqual(left: Model<any>, right: Model<any>): boolean {\n\t\treturn left.provider === right.provider && left.id === right.id;\n\t}\n\n\t/**\n\t * Resolve the model used to SUMMARIZE during compaction. Selection:\n\t * - an explicit `compaction.model` setting wins, but only if its provider is authed (else fall back);\n\t * - `\"auto\"`/unset follows the model router's configured cheap model when the router is enabled;\n\t * - otherwise the session model is used (safe default).\n\t */\n\tresolveModel(sessionModel: Model<any>): Model<any> {\n\t\tconst explicitSetting = this.getExplicitCompactionModelSetting();\n\t\tif (explicitSetting) {\n\t\t\tconst selected = this.selectConfiguredModel(explicitSetting);\n\t\t\tthis.lastSelectionReason = selected.model ? \"explicit\" : `fallback:${selected.cause}`;\n\t\t\tif (!selected.model) this.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t\t// An explicit user choice is honored (Class C doctrine), but silently sending an\n\t\t\t// over-window prompt yields a recall-empty summary — warn with the numbers.\n\t\t\tif (selected.model) {\n\t\t\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\t\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\t\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\t\t\tthis.deps.emitWarning(\n\t\t\t\t\t\t`Compaction summarizer (explicit setting) likely cannot ingest the current context: ~${Math.ceil(estimatedInputTokens / 1000)}k input tokens vs a ${effectiveWindow}-token window`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn selected.model ?? sessionModel;\n\t\t}\n\t\tconst model = this.resolveDefaultModel(sessionModel);\n\t\tif (this.lastSelectionReason?.startsWith(\"fallback:\")) {\n\t\t\tthis.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t}\n\t\treturn model;\n\t}\n\n\t/** Default compaction should never inherit expensive session thinking. */\n\tresolveThinkingLevel(\n\t\tsessionThinkingLevel: ThinkingLevel | undefined,\n\t\tcompactionModel: Model<any>,\n\t\tsessionModel: Model<any>,\n\t): ThinkingLevel | undefined {\n\t\tif (this.getExplicitCompactionModelSetting()) return sessionThinkingLevel;\n\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (router.enabled && router.cheapModel) {\n\t\t\tconst routerModel = this.resolveConfiguredModel(router.cheapModel).model;\n\t\t\tif (routerModel && this.modelsAreEqual(routerModel, compactionModel)) {\n\t\t\t\treturn router.cheapThinking ?? \"low\";\n\t\t\t}\n\t\t}\n\n\t\treturn this.modelsAreEqual(compactionModel, sessionModel) ? \"low\" : sessionThinkingLevel;\n\t}\n}\n"]}
1
+ {"version":3,"file":"compaction-support.js","sourceRoot":"","sources":["../../src/core/compaction-support.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Compaction support: summarizer model selection (#30 cost guard), request-auth resolution\n * with session-model fallback, and window-adapted compaction settings.\n *\n * This is the policy half of compaction. CompactionController owns detection, execution, retry,\n * persistence, notification, and cancellation; AgentSession keeps compatibility delegations at\n * established regression seams.\n */\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { type CompactionSettings, summarizerCanIngest } from \"@caupulican/pi-agent-core/node\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { evaluateSurfaceFitness } from \"./model-router/fitness-gate.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nexport interface CompactionSupportDeps {\n\tgetModel(): Model<Api> | undefined;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelRegistry(): ModelRegistry;\n\t/** True when the agent's streamFn is (or wraps) the raw streamSimple — auth must be explicit then. */\n\tisRawStream(): boolean;\n\t/** Host auth resolution that THROWS with a user-actionable message when no key exists. */\n\tgetRequiredRequestAuth(model: Model<Api>): Promise<{ apiKey?: string; headers?: Record<string, string> }>;\n\tisModelExhausted(ref: string): boolean;\n\tgetStoredFitnessReport(ref: string): ModelFitnessReport | undefined;\n\t/** Estimated tokens of the summarization input (live context; over-estimates, which is safe). */\n\testimateSummarizationInputTokens(): number;\n\temitWarning(message: string): void;\n\t/**\n\t * The same readiness/residency gate every other isolated consumer uses\n\t * (LocalRuntimeController.ensureIsolatedModelReady) before this model is used for an\n\t * out-of-band call. No-ops for a non-managed-local model (checked internally by the gate);\n\t * throws with a user-actionable reason when a managed-local model cannot be made ready.\n\t */\n\tensureModelReady(model: Model<Api>): Promise<void>;\n}\n\nexport class CompactionSupport {\n\tprivate readonly deps: CompactionSupportDeps;\n\tprivate lastSelectionReason: string | undefined;\n\n\tconstructor(deps: CompactionSupportDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tgetLastSelectionReason(): string | undefined {\n\t\treturn this.lastSelectionReason;\n\t}\n\n\tgetAdaptedSettings(): CompactionSettings {\n\t\tconst settings = this.deps.getSettingsManager().getCompactionSettings();\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return settings;\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) return settings;\n\n\t\t// Adapt reserveTokens: at most 25% of context window\n\t\tconst maxReserve = Math.floor(contextWindow * 0.25);\n\t\tconst reserveTokens = Math.min(settings.reserveTokens, maxReserve);\n\n\t\t// Adapt keepRecentTokens: at most 50% of context window\n\t\tconst maxKeepRecent = Math.floor(contextWindow * 0.5);\n\t\tconst keepRecentTokens = Math.min(settings.keepRecentTokens, maxKeepRecent);\n\n\t\treturn {\n\t\t\t...settings,\n\t\t\treserveTokens,\n\t\t\tkeepRecentTokens,\n\t\t};\n\t}\n\n\tasync getRequestAuth(model: Model<Api>): Promise<{\n\t\tapiKey?: string;\n\t\theaders?: Record<string, string>;\n\t}> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\treturn this.deps.getRequiredRequestAuth(model);\n\t\t}\n\n\t\tconst result = await this.deps.getModelRegistry().getApiKeyAndHeaders(model);\n\t\treturn result.ok ? { apiKey: result.apiKey, headers: result.headers } : {};\n\t}\n\n\t/**\n\t * Readiness/residency gate a candidate summarizer model right before it is handed back for\n\t * use. A no-op for a non-managed-local model (checked internally by\n\t * {@link CompactionSupportDeps.ensureModelReady}); a managed-local model that cannot be made\n\t * ready (server down, model missing, residency refused) returns its failure reason instead of\n\t * throwing, so callers can still try the next candidate in the existing auth-fallback ladder\n\t * rather than aborting resolution outright.\n\t */\n\tprivate async readinessFailure(model: Model<Api>): Promise<string | undefined> {\n\t\ttry {\n\t\t\tawait this.deps.ensureModelReady(model);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\treturn error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the summarizer model AND its request auth for auto-compaction. The cheap auxiliary\n\t * model (#30) can be nominally available yet fail key resolution at request time (expired\n\t * OAuth, revoked key) — or, for a managed-local model, fail the readiness/residency gate (\n\t * server down, model not installed, residency refused). Falling back to the session model keeps\n\t * auto-compaction working in exactly the situations where manual /compact works; only when\n\t * neither resolves do we fail — and then with a concrete message instead of a silent no-op or a\n\t * bypass of the readiness gate.\n\t */\n\tasync resolveModelAndAuth(\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): Promise<{ model: Model<Api>; apiKey?: string; headers?: Record<string, string>; failure?: string }> {\n\t\tif (this.deps.isRawStream()) {\n\t\t\tconst registry = this.deps.getModelRegistry();\n\t\t\tlet auth = await registry.getApiKeyAndHeaders(compactionModel);\n\t\t\tlet readiness: string | undefined;\n\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\treadiness = await this.readinessFailure(compactionModel);\n\t\t\t\tif (!readiness) return { model: compactionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t}\n\t\t\tconst isSameModel =\n\t\t\t\tcompactionModel.provider === sessionModel.provider && compactionModel.id === sessionModel.id;\n\t\t\tif (!isSameModel) {\n\t\t\t\tauth = await registry.getApiKeyAndHeaders(sessionModel);\n\t\t\t\tif (auth.ok && auth.apiKey) {\n\t\t\t\t\treadiness = await this.readinessFailure(sessionModel);\n\t\t\t\t\tif (!readiness) return { model: sessionModel, apiKey: auth.apiKey, headers: auth.headers };\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmodel: sessionModel,\n\t\t\t\tfailure: readiness\n\t\t\t\t\t? `summarizer ${isSameModel ? compactionModel.id : sessionModel.id} not ready: ${readiness}`\n\t\t\t\t\t: `no usable API key for the summarizer (tried ${compactionModel.id}${isSameModel ? \"\" : ` and ${sessionModel.id}`})`,\n\t\t\t};\n\t\t}\n\n\t\t// Custom streamFn owns auth injection (CLI path) — resolve best-effort, never fail on auth\n\t\t// here; a managed-local summarizer must still pass the readiness gate before compact() runs.\n\t\tconst { apiKey, headers } = await this.getRequestAuth(compactionModel);\n\t\tconst readiness = await this.readinessFailure(compactionModel);\n\t\tif (readiness) {\n\t\t\treturn {\n\t\t\t\tmodel: compactionModel,\n\t\t\t\tapiKey,\n\t\t\t\theaders,\n\t\t\t\tfailure: `summarizer ${compactionModel.id} not ready: ${readiness}`,\n\t\t\t};\n\t\t}\n\t\treturn { model: compactionModel, apiKey, headers };\n\t}\n\n\tprivate getExplicitCompactionModelSetting(): string | undefined {\n\t\tconst setting = this.deps.getSettingsManager().getCompactionModel().trim();\n\t\treturn setting && setting !== \"auto\" ? setting : undefined;\n\t}\n\n\tprivate modelRef(model: Model<Api>): string {\n\t\treturn `${model.provider}/${model.id}`;\n\t}\n\n\tprivate resolveConfiguredModel(pattern: string): { model?: Model<Api>; cause?: \"unresolved\" | \"unauthed\" } {\n\t\tconst registry = this.deps.getModelRegistry();\n\t\tconst resolved = resolveCliModel({ cliModel: pattern, modelRegistry: registry });\n\t\tif (!resolved.model) return { cause: \"unresolved\" };\n\t\tif (!registry.hasConfiguredAuth(resolved.model)) return { cause: \"unauthed\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate selectConfiguredModel(pattern: string): { model?: Model<Api>; cause?: string } {\n\t\tconst resolved = this.resolveConfiguredModel(pattern);\n\t\tif (!resolved.model) return { cause: resolved.cause };\n\t\tif (this.deps.isModelExhausted(this.modelRef(resolved.model))) return { cause: \"exhausted\" };\n\t\treturn { model: resolved.model };\n\t}\n\n\tprivate effectiveContextWindow(model: Model<Api>): number {\n\t\tconst registered = model.contextWindow > 0 ? model.contextWindow : Number.POSITIVE_INFINITY;\n\t\tconst served = this.deps.getStoredFitnessReport(this.modelRef(model))?.capacity?.servedContextWindow;\n\t\treturn served && served > 0 ? Math.min(registered, served) : registered;\n\t}\n\n\tprivate modelWithEffectiveWindow(model: Model<Api>): Model<Api> {\n\t\tconst contextWindow = this.effectiveContextWindow(model);\n\t\treturn contextWindow === model.contextWindow ? model : { ...model, contextWindow };\n\t}\n\n\tprivate resolveDefaultModel(sessionModel: Model<Api>): Model<Api> {\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (!router.enabled || !router.cheapModel) {\n\t\t\tthis.lastSelectionReason = \"session_default\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst selected = this.selectConfiguredModel(router.cheapModel);\n\t\tif (!selected.model) {\n\t\t\tthis.lastSelectionReason = `fallback:${selected.cause}`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\t// Capacity is a hard constraint, independent of the fitness doctrine: a summarizer whose\n\t\t// window cannot hold the actual span produces recall-empty checkpoints (local servers\n\t\t// silently truncate over-window prompts instead of erroring), and the verification gate\n\t\t// then fails deterministically.\n\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\tthis.lastSelectionReason = `fallback:window_too_small(~${Math.ceil(estimatedInputTokens / 1000)}k input vs ${effectiveWindow} window)`;\n\t\t\treturn sessionModel;\n\t\t}\n\t\tconst fitness = this.deps.getStoredFitnessReport(this.modelRef(selected.model));\n\t\tconst verdict = evaluateSurfaceFitness(\"compaction\", fitness);\n\t\tif (!verdict.fit) {\n\t\t\tthis.lastSelectionReason =\n\t\t\t\tverdict.reason === \"lane_failed\"\n\t\t\t\t\t? `fallback:digest_unfit(${verdict.succeeded}/${verdict.total})`\n\t\t\t\t\t: \"fallback:unprobed\";\n\t\t\treturn sessionModel;\n\t\t}\n\t\tthis.lastSelectionReason = \"router_cheap\";\n\t\treturn selected.model;\n\t}\n\n\tprivate modelsAreEqual(left: Model<Api>, right: Model<Api>): boolean {\n\t\treturn left.provider === right.provider && left.id === right.id;\n\t}\n\n\t/**\n\t * Resolve the model used to SUMMARIZE during compaction. Selection:\n\t * - an explicit `compaction.model` setting wins, but only if its provider is authed (else fall back);\n\t * - `\"auto\"`/unset follows the model router's configured cheap model when the router is enabled;\n\t * - otherwise the session model is used (safe default).\n\t */\n\tresolveModel(sessionModel: Model<Api>): Model<Api> {\n\t\tconst explicitSetting = this.getExplicitCompactionModelSetting();\n\t\tif (explicitSetting) {\n\t\t\tconst selected = this.selectConfiguredModel(explicitSetting);\n\t\t\tthis.lastSelectionReason = selected.model ? \"explicit\" : `fallback:${selected.cause}`;\n\t\t\tif (!selected.model) this.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t\t// An explicit user choice is honored (Class C doctrine), but silently sending an\n\t\t\t// over-window prompt yields a recall-empty summary — warn with the numbers.\n\t\t\tif (selected.model) {\n\t\t\t\tconst estimatedInputTokens = this.deps.estimateSummarizationInputTokens();\n\t\t\t\tconst effectiveWindow = this.effectiveContextWindow(selected.model);\n\t\t\t\tif (!summarizerCanIngest(this.modelWithEffectiveWindow(selected.model), estimatedInputTokens)) {\n\t\t\t\t\tthis.deps.emitWarning(\n\t\t\t\t\t\t`Compaction summarizer (explicit setting) likely cannot ingest the current context: ~${Math.ceil(estimatedInputTokens / 1000)}k input tokens vs a ${effectiveWindow}-token window`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn selected.model ?? sessionModel;\n\t\t}\n\t\tconst model = this.resolveDefaultModel(sessionModel);\n\t\tif (this.lastSelectionReason?.startsWith(\"fallback:\")) {\n\t\t\tthis.deps.emitWarning(`Compaction summarizer ${this.lastSelectionReason}`);\n\t\t}\n\t\treturn model;\n\t}\n\n\t/** Default compaction should never inherit expensive session thinking. */\n\tresolveThinkingLevel(\n\t\tsessionThinkingLevel: ThinkingLevel | undefined,\n\t\tcompactionModel: Model<Api>,\n\t\tsessionModel: Model<Api>,\n\t): ThinkingLevel | undefined {\n\t\tif (this.getExplicitCompactionModelSetting()) return sessionThinkingLevel;\n\n\t\tconst router = this.deps.getSettingsManager().getModelRouterSettings();\n\t\tif (router.enabled && router.cheapModel) {\n\t\t\tconst routerModel = this.resolveConfiguredModel(router.cheapModel).model;\n\t\t\tif (routerModel && this.modelsAreEqual(routerModel, compactionModel)) {\n\t\t\t\treturn router.cheapThinking ?? \"low\";\n\t\t\t}\n\t\t}\n\n\t\treturn this.modelsAreEqual(compactionModel, sessionModel) ? \"low\" : sessionThinkingLevel;\n\t}\n}\n"]}
@@ -7,7 +7,7 @@
7
7
  * docs/context-management-rework/memory-architecture.md).
8
8
  *
9
9
  * `createFileArtifactStore` is wired into live grep/find tool construction in
10
- * agent-session.ts (session-scoped under `<agentDir>/context-artifacts/<sessionId>/`).
10
+ * agent-session.ts (session-scoped under `<agentDir>/work/context/sessions/<sessionId>/artifacts/`).
11
11
  * References are registered at pack time and released when context-gc evicts the
12
12
  * corresponding grep/find tool result (opportunistic, conservative cleanup), with a
13
13
  * best-effort dispose-time sweep for zero-reference artifacts. Payloads are retrievable
@@ -71,8 +71,12 @@ export interface ArtifactStore {
71
71
  }
72
72
  export declare function createInMemoryArtifactStore(): ArtifactStore;
73
73
  export interface FileArtifactStoreOptions {
74
- /** Directory the store persists artifact payloads and metadata under. Created if missing. */
74
+ /** Leased session work directory where payloads and metadata live. Created on first use. */
75
75
  baseDir: string;
76
+ /** Migrate an existing store before probing it; must remain zero-write when no legacy data exists. */
77
+ prepareBaseDir?: () => void;
78
+ /** Acquire the owning lease immediately before the first real store access. */
79
+ acquireBaseDir?: () => string;
76
80
  }
77
81
  /**
78
82
  * Filesystem-backed `ArtifactStore`: payload and metadata (including reference holder ids)
@@ -1 +1 @@
1
- {"version":3,"file":"context-artifacts.d.ts","sourceRoot":"","sources":["../../../src/core/context/context-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,KAAK,kBAAkB,EAAyC,MAAM,mBAAmB,CAAC;AAEnG,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,kBAAkB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG,YAAY,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,qBAAqB,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,GAAG,qBAAqB,GAAG,KAAK,IAAI,qBAAqB,CAErH;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,IAAI,CACZ,oBAAoB,EACpB,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,gBAAgB,GAAG,eAAe,GAAG,cAAc,CAC1G,GACC,MAAM,CAeR;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,CAAC,OAAO,EAAE,oBAAoB,GAAG,cAAc,CAAC;IACrD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,qBAAqB,CAAC;IACzD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAC;IACpD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACpD,yGAAyG;IACzG,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACvD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,2FAA2F;IAC3F,OAAO,IAAI,MAAM,EAAE,CAAC;CACpB;AAQD,wBAAgB,2BAA2B,IAAI,aAAa,CA2E3D;AAED,MAAM,WAAW,wBAAwB;IACxC,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;CAChB;AA+ED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CA0GxF","sourcesContent":["/**\n * Artifact store abstraction (Phase 2): stable refs for raw large payloads, kept out of\n * band from prompt context. This module defines the `ArtifactStore` interface plus two\n * implementations: an in-memory one for tests, and `createFileArtifactStore` (session-\n * scoped, filesystem-backed). A SQLite-backed implementation waits until the Phase M0\n * storage-authority/location/concurrency decisions are accepted (see\n * docs/context-management-rework/memory-architecture.md).\n *\n * `createFileArtifactStore` is wired into live grep/find tool construction in\n * agent-session.ts (session-scoped under `<agentDir>/context-artifacts/<sessionId>/`).\n * References are registered at pack time and released when context-gc evicts the\n * corresponding grep/find tool result (opportunistic, conservative cleanup), with a\n * best-effort dispose-time sweep for zero-reference artifacts. Payloads are retrievable\n * out of band via the artifact_retrieve tool (context/artifact-retrieval.ts).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { type ContextArtifactRef, estimateByteLength, estimateLineCount } from \"./context-item.ts\";\n\nexport interface ArtifactWriteRequest {\n\tkind: ContextArtifactRef[\"kind\"];\n\tcontent: string;\n\ttoolName?: string;\n\tcommand?: string;\n\tpath?: string;\n\tsessionEntryId?: string;\n\tcreatedAtTurn: number;\n\treproducible: boolean;\n}\n\nexport interface ArtifactRecord {\n\tref: ContextArtifactRef;\n\tcontent: string;\n}\n\nexport type MissingArtifactReason = \"not_found\" | \"cleaned_up\";\n\nexport interface MissingArtifactMarker {\n\tid: string;\n\tmissing: true;\n\treason: MissingArtifactReason;\n}\n\nexport function isMissingArtifactMarker(value: ArtifactRecord | MissingArtifactMarker): value is MissingArtifactMarker {\n\treturn (value as MissingArtifactMarker).missing === true;\n}\n\n/**\n * Artifact id for a capture event, not merely a payload: it hashes every ref-defining\n * field (kind, tool/command/path, content, sessionEntryId, createdAtTurn, reproducible).\n * A repeat write with identical content but a different turn or session entry is a\n * distinct capture and must get a distinct id -- otherwise the later capture's metadata\n * would be silently discarded in favor of the first write. Only a truly identical\n * request (same capture, re-submitted) is idempotent under this id.\n */\nexport function generateArtifactId(\n\trequest: Pick<\n\t\tArtifactWriteRequest,\n\t\t\"kind\" | \"content\" | \"toolName\" | \"command\" | \"path\" | \"sessionEntryId\" | \"createdAtTurn\" | \"reproducible\"\n\t>,\n): string {\n\tconst hash = createHash(\"sha256\");\n\tfor (const field of [\n\t\trequest.kind,\n\t\trequest.toolName ?? \"\",\n\t\trequest.command ?? \"\",\n\t\trequest.path ?? \"\",\n\t\trequest.sessionEntryId ?? \"\",\n\t\tString(request.createdAtTurn),\n\t\tString(request.reproducible),\n\t]) {\n\t\thash.update(field).update(\"\\0\");\n\t}\n\thash.update(request.content);\n\treturn hash.digest(\"hex\").slice(0, 24);\n}\n\nexport interface ArtifactStore {\n\twrite(request: ArtifactWriteRequest): ArtifactRecord;\n\tread(id: string): ArtifactRecord | MissingArtifactMarker;\n\t/**\n\t * Metadata-only lookup: the ref if `id` resolves to a live artifact, `undefined`\n\t * otherwise. Never loads the payload -- for the file store this must not touch the\n\t * payload file at all beyond an existence check, so a caller that only needs to know\n\t * \"does this still exist, and what are its ref fields\" (e.g. a per-turn audit pass)\n\t * never pays the cost of reading potentially large content off disk.\n\t */\n\treadRef(id: string): ContextArtifactRef | undefined;\n\thas(id: string): boolean;\n\t/**\n\t * Register that `holderId` (a context item id, session entry id, etc.) depends on this\n\t * artifact. Returns false if `id` does not exist (never written, or already cleaned\n\t * up) so a caller cannot believe it protected an artifact that was never registered.\n\t * Callers must fail closed (treat the artifact as unprotected) on a false return.\n\t */\n\taddReference(id: string, holderId: string): boolean;\n\t/** Release a previously registered dependency. Returns true only if a reference was actually removed. */\n\tremoveReference(id: string, holderId: string): boolean;\n\treferenceCount(id: string): number;\n\t/** Delete only artifacts with zero active references. Returns the ids actually deleted. */\n\tcleanup(): string[];\n}\n\ninterface StoredArtifact {\n\tref: ContextArtifactRef;\n\tcontent: string;\n\treferences: Set<string>;\n}\n\nexport function createInMemoryArtifactStore(): ArtifactStore {\n\tconst artifacts = new Map<string, StoredArtifact>();\n\tconst cleanedUp = new Set<string>();\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existing = artifacts.get(id);\n\t\t\tif (existing) {\n\t\t\t\tcleanedUp.delete(id);\n\t\t\t\treturn { ref: existing.ref, content: existing.content };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\tartifacts.set(id, { ref, content: request.content, references: new Set() });\n\t\t\tcleanedUp.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUp.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: stored.ref, content: stored.content };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\treturn artifacts.get(id)?.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\treturn artifacts.has(id);\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\tstored.references.add(holderId);\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\treturn stored.references.delete(holderId);\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\treturn artifacts.get(id)?.references.size ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tfor (const [id, stored] of artifacts) {\n\t\t\t\tif (stored.references.size === 0) {\n\t\t\t\t\tartifacts.delete(id);\n\t\t\t\t\tcleanedUp.add(id);\n\t\t\t\t\tdeleted.push(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n\nexport interface FileArtifactStoreOptions {\n\t/** Directory the store persists artifact payloads and metadata under. Created if missing. */\n\tbaseDir: string;\n}\n\ninterface PersistedArtifactMeta {\n\tref: ContextArtifactRef;\n\treferences: string[];\n}\n\nconst META_SUFFIX = \".meta.json\";\nconst PAYLOAD_SUFFIX = \".payload\";\n\n/**\n * Artifact ids are generated by `generateArtifactId` as a lowercase hex digest. Reject\n * anything else so a caller-supplied id (including one echoed back from model output)\n * can never be used as a path-traversal vector into `baseDir`.\n */\nfunction isSafeArtifactId(id: string): boolean {\n\treturn /^[0-9a-f]{1,64}$/.test(id);\n}\n\nfunction payloadPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${PAYLOAD_SUFFIX}`);\n}\n\nfunction metaPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${META_SUFFIX}`);\n}\n\nconst VALID_ARTIFACT_KINDS: ReadonlySet<ContextArtifactRef[\"kind\"]> = new Set([\n\t\"tool_output\",\n\t\"file_snapshot\",\n\t\"test_output\",\n\t\"diff\",\n\t\"transcript_slice\",\n]);\n\nfunction isValidArtifactRefShape(value: unknown): value is ContextArtifactRef {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst ref = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof ref.id === \"string\" &&\n\t\ttypeof ref.kind === \"string\" &&\n\t\tVALID_ARTIFACT_KINDS.has(ref.kind as ContextArtifactRef[\"kind\"]) &&\n\t\ttypeof ref.byteLength === \"number\" &&\n\t\ttypeof ref.createdAtTurn === \"number\" &&\n\t\ttypeof ref.reproducible === \"boolean\"\n\t);\n}\n\n/**\n * A parsed JSON value can be syntactically valid but semantically garbage (truncated\n * write, hand-edited file, future/incompatible format). Validate shape, not just parse\n * success, so a malformed sidecar can never produce an invalid ref or crash `cleanup()` --\n * it is treated as unusable/missing, the same as a sidecar that doesn't exist.\n */\nfunction isValidPersistedArtifactMeta(value: unknown): value is PersistedArtifactMeta {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst meta = value as Record<string, unknown>;\n\treturn (\n\t\tisValidArtifactRefShape(meta.ref) &&\n\t\tArray.isArray(meta.references) &&\n\t\tmeta.references.every((entry) => typeof entry === \"string\")\n\t);\n}\n\nfunction readMeta(baseDir: string, id: string): PersistedArtifactMeta | undefined {\n\tconst path = metaPath(baseDir, id);\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"));\n\t\treturn isValidPersistedArtifactMeta(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeMeta(baseDir: string, id: string, meta: PersistedArtifactMeta): void {\n\twriteFileSync(metaPath(baseDir, id), JSON.stringify(meta), \"utf8\");\n}\n\n/**\n * Filesystem-backed `ArtifactStore`: payload and metadata (including reference holder ids)\n * are written to `baseDir` so content, ref fields, and cleanup-protecting references all\n * survive recreating the store (e.g. across a process restart against the same\n * directory) -- unlike the in-memory store, which loses everything when the instance is\n * dropped. No SQLite or other index is used; each artifact's metadata is a small sidecar\n * JSON file next to its payload file, per the \"keep SQLite out of scope unless a minimal\n * metadata shape is unavoidable\" constraint for this slice.\n *\n * The one thing that does NOT survive recreation: the missing-artifact reason\n * distinction. A fresh instance has no in-memory record of which ids it personally\n * cleaned up, so a previously-cleaned-up id reads back as \"not_found\" rather than\n * \"cleaned_up\" after a restart. This still always returns an explicit missing marker,\n * never fabricated or empty content -- it only affects which of the two reason codes is\n * reported.\n */\nexport function createFileArtifactStore(options: FileArtifactStoreOptions): ArtifactStore {\n\tconst baseDir = options.baseDir;\n\tmkdirSync(baseDir, { recursive: true });\n\tconst cleanedUpThisInstance = new Set<string>();\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existingMeta = readMeta(baseDir, id);\n\t\t\tconst existingPayloadPath = payloadPath(baseDir, id);\n\t\t\tif (existingMeta && existsSync(existingPayloadPath)) {\n\t\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\t\treturn { ref: existingMeta.ref, content: readFileSync(existingPayloadPath, \"utf8\") };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\twriteFileSync(payloadPath(baseDir, id), request.content, \"utf8\");\n\t\t\twriteMeta(baseDir, id, { ref, references: [] });\n\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tif (!isSafeArtifactId(id)) return { id, missing: true, reason: \"not_found\" };\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tconst pPath = payloadPath(baseDir, id);\n\t\t\tif (!meta || !existsSync(pPath)) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUpThisInstance.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: meta.ref, content: readFileSync(pPath, \"utf8\") };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\tif (!isSafeArtifactId(id)) return undefined;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return undefined;\n\t\t\treturn meta.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\treturn readMeta(baseDir, id) !== undefined && existsSync(payloadPath(baseDir, id));\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return false;\n\t\t\tif (!meta.references.includes(holderId)) {\n\t\t\t\tmeta.references.push(holderId);\n\t\t\t\twriteMeta(baseDir, id, meta);\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta) return false;\n\t\t\tconst index = meta.references.indexOf(holderId);\n\t\t\tif (index === -1) return false;\n\t\t\tmeta.references.splice(index, 1);\n\t\t\twriteMeta(baseDir, id, meta);\n\t\t\treturn true;\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\tif (!isSafeArtifactId(id)) return 0;\n\t\t\treturn readMeta(baseDir, id)?.references.length ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tfor (const entry of readdirSync(baseDir)) {\n\t\t\t\tif (!entry.endsWith(META_SUFFIX)) continue;\n\t\t\t\tconst id = entry.slice(0, -META_SUFFIX.length);\n\t\t\t\tif (!isSafeArtifactId(id)) continue;\n\t\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\t\tif (!meta || meta.references.length > 0) continue;\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(metaPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(payloadPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\t// Payload already gone; metadata removal above is what matters for reachability.\n\t\t\t\t}\n\t\t\t\tcleanedUpThisInstance.add(id);\n\t\t\t\tdeleted.push(id);\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"context-artifacts.d.ts","sourceRoot":"","sources":["../../../src/core/context/context-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,KAAK,kBAAkB,EAAyC,MAAM,mBAAmB,CAAC;AAEnG,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,kBAAkB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG,YAAY,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,qBAAqB,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,GAAG,qBAAqB,GAAG,KAAK,IAAI,qBAAqB,CAErH;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,IAAI,CACZ,oBAAoB,EACpB,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,gBAAgB,GAAG,eAAe,GAAG,cAAc,CAC1G,GACC,MAAM,CAeR;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,CAAC,OAAO,EAAE,oBAAoB,GAAG,cAAc,CAAC;IACrD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,qBAAqB,CAAC;IACzD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAC;IACpD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACpD,yGAAyG;IACzG,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACvD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,2FAA2F;IAC3F,OAAO,IAAI,MAAM,EAAE,CAAC;CACpB;AAQD,wBAAgB,2BAA2B,IAAI,aAAa,CA2E3D;AAED,MAAM,WAAW,wBAAwB;IACxC,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,sGAAsG;IACtG,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;CAC9B;AA+ED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CAkIxF","sourcesContent":["/**\n * Artifact store abstraction (Phase 2): stable refs for raw large payloads, kept out of\n * band from prompt context. This module defines the `ArtifactStore` interface plus two\n * implementations: an in-memory one for tests, and `createFileArtifactStore` (session-\n * scoped, filesystem-backed). A SQLite-backed implementation waits until the Phase M0\n * storage-authority/location/concurrency decisions are accepted (see\n * docs/context-management-rework/memory-architecture.md).\n *\n * `createFileArtifactStore` is wired into live grep/find tool construction in\n * agent-session.ts (session-scoped under `<agentDir>/work/context/sessions/<sessionId>/artifacts/`).\n * References are registered at pack time and released when context-gc evicts the\n * corresponding grep/find tool result (opportunistic, conservative cleanup), with a\n * best-effort dispose-time sweep for zero-reference artifacts. Payloads are retrievable\n * out of band via the artifact_retrieve tool (context/artifact-retrieval.ts).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { type ContextArtifactRef, estimateByteLength, estimateLineCount } from \"./context-item.ts\";\n\nexport interface ArtifactWriteRequest {\n\tkind: ContextArtifactRef[\"kind\"];\n\tcontent: string;\n\ttoolName?: string;\n\tcommand?: string;\n\tpath?: string;\n\tsessionEntryId?: string;\n\tcreatedAtTurn: number;\n\treproducible: boolean;\n}\n\nexport interface ArtifactRecord {\n\tref: ContextArtifactRef;\n\tcontent: string;\n}\n\nexport type MissingArtifactReason = \"not_found\" | \"cleaned_up\";\n\nexport interface MissingArtifactMarker {\n\tid: string;\n\tmissing: true;\n\treason: MissingArtifactReason;\n}\n\nexport function isMissingArtifactMarker(value: ArtifactRecord | MissingArtifactMarker): value is MissingArtifactMarker {\n\treturn (value as MissingArtifactMarker).missing === true;\n}\n\n/**\n * Artifact id for a capture event, not merely a payload: it hashes every ref-defining\n * field (kind, tool/command/path, content, sessionEntryId, createdAtTurn, reproducible).\n * A repeat write with identical content but a different turn or session entry is a\n * distinct capture and must get a distinct id -- otherwise the later capture's metadata\n * would be silently discarded in favor of the first write. Only a truly identical\n * request (same capture, re-submitted) is idempotent under this id.\n */\nexport function generateArtifactId(\n\trequest: Pick<\n\t\tArtifactWriteRequest,\n\t\t\"kind\" | \"content\" | \"toolName\" | \"command\" | \"path\" | \"sessionEntryId\" | \"createdAtTurn\" | \"reproducible\"\n\t>,\n): string {\n\tconst hash = createHash(\"sha256\");\n\tfor (const field of [\n\t\trequest.kind,\n\t\trequest.toolName ?? \"\",\n\t\trequest.command ?? \"\",\n\t\trequest.path ?? \"\",\n\t\trequest.sessionEntryId ?? \"\",\n\t\tString(request.createdAtTurn),\n\t\tString(request.reproducible),\n\t]) {\n\t\thash.update(field).update(\"\\0\");\n\t}\n\thash.update(request.content);\n\treturn hash.digest(\"hex\").slice(0, 24);\n}\n\nexport interface ArtifactStore {\n\twrite(request: ArtifactWriteRequest): ArtifactRecord;\n\tread(id: string): ArtifactRecord | MissingArtifactMarker;\n\t/**\n\t * Metadata-only lookup: the ref if `id` resolves to a live artifact, `undefined`\n\t * otherwise. Never loads the payload -- for the file store this must not touch the\n\t * payload file at all beyond an existence check, so a caller that only needs to know\n\t * \"does this still exist, and what are its ref fields\" (e.g. a per-turn audit pass)\n\t * never pays the cost of reading potentially large content off disk.\n\t */\n\treadRef(id: string): ContextArtifactRef | undefined;\n\thas(id: string): boolean;\n\t/**\n\t * Register that `holderId` (a context item id, session entry id, etc.) depends on this\n\t * artifact. Returns false if `id` does not exist (never written, or already cleaned\n\t * up) so a caller cannot believe it protected an artifact that was never registered.\n\t * Callers must fail closed (treat the artifact as unprotected) on a false return.\n\t */\n\taddReference(id: string, holderId: string): boolean;\n\t/** Release a previously registered dependency. Returns true only if a reference was actually removed. */\n\tremoveReference(id: string, holderId: string): boolean;\n\treferenceCount(id: string): number;\n\t/** Delete only artifacts with zero active references. Returns the ids actually deleted. */\n\tcleanup(): string[];\n}\n\ninterface StoredArtifact {\n\tref: ContextArtifactRef;\n\tcontent: string;\n\treferences: Set<string>;\n}\n\nexport function createInMemoryArtifactStore(): ArtifactStore {\n\tconst artifacts = new Map<string, StoredArtifact>();\n\tconst cleanedUp = new Set<string>();\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existing = artifacts.get(id);\n\t\t\tif (existing) {\n\t\t\t\tcleanedUp.delete(id);\n\t\t\t\treturn { ref: existing.ref, content: existing.content };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\tartifacts.set(id, { ref, content: request.content, references: new Set() });\n\t\t\tcleanedUp.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUp.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: stored.ref, content: stored.content };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\treturn artifacts.get(id)?.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\treturn artifacts.has(id);\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\tstored.references.add(holderId);\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\treturn stored.references.delete(holderId);\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\treturn artifacts.get(id)?.references.size ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tfor (const [id, stored] of artifacts) {\n\t\t\t\tif (stored.references.size === 0) {\n\t\t\t\t\tartifacts.delete(id);\n\t\t\t\t\tcleanedUp.add(id);\n\t\t\t\t\tdeleted.push(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n\nexport interface FileArtifactStoreOptions {\n\t/** Leased session work directory where payloads and metadata live. Created on first use. */\n\tbaseDir: string;\n\t/** Migrate an existing store before probing it; must remain zero-write when no legacy data exists. */\n\tprepareBaseDir?: () => void;\n\t/** Acquire the owning lease immediately before the first real store access. */\n\tacquireBaseDir?: () => string;\n}\n\ninterface PersistedArtifactMeta {\n\tref: ContextArtifactRef;\n\treferences: string[];\n}\n\nconst META_SUFFIX = \".meta.json\";\nconst PAYLOAD_SUFFIX = \".payload\";\n\n/**\n * Artifact ids are generated by `generateArtifactId` as a lowercase hex digest. Reject\n * anything else so a caller-supplied id (including one echoed back from model output)\n * can never be used as a path-traversal vector into `baseDir`.\n */\nfunction isSafeArtifactId(id: string): boolean {\n\treturn /^[0-9a-f]{1,64}$/.test(id);\n}\n\nfunction payloadPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${PAYLOAD_SUFFIX}`);\n}\n\nfunction metaPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${META_SUFFIX}`);\n}\n\nconst VALID_ARTIFACT_KINDS: ReadonlySet<ContextArtifactRef[\"kind\"]> = new Set([\n\t\"tool_output\",\n\t\"file_snapshot\",\n\t\"test_output\",\n\t\"diff\",\n\t\"transcript_slice\",\n]);\n\nfunction isValidArtifactRefShape(value: unknown): value is ContextArtifactRef {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst ref = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof ref.id === \"string\" &&\n\t\ttypeof ref.kind === \"string\" &&\n\t\tVALID_ARTIFACT_KINDS.has(ref.kind as ContextArtifactRef[\"kind\"]) &&\n\t\ttypeof ref.byteLength === \"number\" &&\n\t\ttypeof ref.createdAtTurn === \"number\" &&\n\t\ttypeof ref.reproducible === \"boolean\"\n\t);\n}\n\n/**\n * A parsed JSON value can be syntactically valid but semantically garbage (truncated\n * write, hand-edited file, future/incompatible format). Validate shape, not just parse\n * success, so a malformed sidecar can never produce an invalid ref or crash `cleanup()` --\n * it is treated as unusable/missing, the same as a sidecar that doesn't exist.\n */\nfunction isValidPersistedArtifactMeta(value: unknown): value is PersistedArtifactMeta {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst meta = value as Record<string, unknown>;\n\treturn (\n\t\tisValidArtifactRefShape(meta.ref) &&\n\t\tArray.isArray(meta.references) &&\n\t\tmeta.references.every((entry) => typeof entry === \"string\")\n\t);\n}\n\nfunction readMeta(baseDir: string, id: string): PersistedArtifactMeta | undefined {\n\tconst path = metaPath(baseDir, id);\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"));\n\t\treturn isValidPersistedArtifactMeta(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeMeta(baseDir: string, id: string, meta: PersistedArtifactMeta): void {\n\twriteFileSync(metaPath(baseDir, id), JSON.stringify(meta), \"utf8\");\n}\n\n/**\n * Filesystem-backed `ArtifactStore`: payload and metadata (including reference holder ids)\n * are written to `baseDir` so content, ref fields, and cleanup-protecting references all\n * survive recreating the store (e.g. across a process restart against the same\n * directory) -- unlike the in-memory store, which loses everything when the instance is\n * dropped. No SQLite or other index is used; each artifact's metadata is a small sidecar\n * JSON file next to its payload file, per the \"keep SQLite out of scope unless a minimal\n * metadata shape is unavoidable\" constraint for this slice.\n *\n * The one thing that does NOT survive recreation: the missing-artifact reason\n * distinction. A fresh instance has no in-memory record of which ids it personally\n * cleaned up, so a previously-cleaned-up id reads back as \"not_found\" rather than\n * \"cleaned_up\" after a restart. This still always returns an explicit missing marker,\n * never fabricated or empty content -- it only affects which of the two reason codes is\n * reported.\n */\nexport function createFileArtifactStore(options: FileArtifactStoreOptions): ArtifactStore {\n\tconst baseDir = options.baseDir;\n\tconst cleanedUpThisInstance = new Set<string>();\n\tlet acquiredBaseDir: string | undefined;\n\tlet preparedBaseDir = false;\n\tconst ensureBaseDir = (): string => {\n\t\tpreparedBaseDir = true;\n\t\tacquiredBaseDir ??= options.acquireBaseDir?.() ?? baseDir;\n\t\tmkdirSync(acquiredBaseDir, { recursive: true });\n\t\treturn acquiredBaseDir;\n\t};\n\tconst ensureExistingBaseDir = (): boolean => {\n\t\tif (!preparedBaseDir) {\n\t\t\toptions.prepareBaseDir?.();\n\t\t\tpreparedBaseDir = true;\n\t\t}\n\t\tif (!existsSync(baseDir)) return false;\n\t\tensureBaseDir();\n\t\treturn true;\n\t};\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tensureBaseDir();\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existingMeta = readMeta(baseDir, id);\n\t\t\tconst existingPayloadPath = payloadPath(baseDir, id);\n\t\t\tif (existingMeta && existsSync(existingPayloadPath)) {\n\t\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\t\treturn { ref: existingMeta.ref, content: readFileSync(existingPayloadPath, \"utf8\") };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\twriteFileSync(payloadPath(baseDir, id), request.content, \"utf8\");\n\t\t\twriteMeta(baseDir, id, { ref, references: [] });\n\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tif (!isSafeArtifactId(id)) return { id, missing: true, reason: \"not_found\" };\n\t\t\tif (!ensureExistingBaseDir()) return { id, missing: true, reason: \"not_found\" };\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tconst pPath = payloadPath(baseDir, id);\n\t\t\tif (!meta || !existsSync(pPath)) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUpThisInstance.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: meta.ref, content: readFileSync(pPath, \"utf8\") };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\tif (!isSafeArtifactId(id)) return undefined;\n\t\t\tif (!ensureExistingBaseDir()) return undefined;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return undefined;\n\t\t\treturn meta.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tif (!ensureExistingBaseDir()) return false;\n\t\t\treturn readMeta(baseDir, id) !== undefined && existsSync(payloadPath(baseDir, id));\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tif (!ensureExistingBaseDir()) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return false;\n\t\t\tif (!meta.references.includes(holderId)) {\n\t\t\t\tmeta.references.push(holderId);\n\t\t\t\twriteMeta(baseDir, id, meta);\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tif (!ensureExistingBaseDir()) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta) return false;\n\t\t\tconst index = meta.references.indexOf(holderId);\n\t\t\tif (index === -1) return false;\n\t\t\tmeta.references.splice(index, 1);\n\t\t\twriteMeta(baseDir, id, meta);\n\t\t\treturn true;\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\tif (!isSafeArtifactId(id)) return 0;\n\t\t\tif (!ensureExistingBaseDir()) return 0;\n\t\t\treturn readMeta(baseDir, id)?.references.length ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tif (!ensureExistingBaseDir()) return deleted;\n\t\t\tfor (const entry of readdirSync(baseDir)) {\n\t\t\t\tif (!entry.endsWith(META_SUFFIX)) continue;\n\t\t\t\tconst id = entry.slice(0, -META_SUFFIX.length);\n\t\t\t\tif (!isSafeArtifactId(id)) continue;\n\t\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\t\tif (!meta || meta.references.length > 0) continue;\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(metaPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(payloadPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\t// Payload already gone; metadata removal above is what matters for reachability.\n\t\t\t\t}\n\t\t\t\tcleanedUpThisInstance.add(id);\n\t\t\t\tdeleted.push(id);\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n"]}
@@ -7,7 +7,7 @@
7
7
  * docs/context-management-rework/memory-architecture.md).
8
8
  *
9
9
  * `createFileArtifactStore` is wired into live grep/find tool construction in
10
- * agent-session.ts (session-scoped under `<agentDir>/context-artifacts/<sessionId>/`).
10
+ * agent-session.ts (session-scoped under `<agentDir>/work/context/sessions/<sessionId>/artifacts/`).
11
11
  * References are registered at pack time and released when context-gc evicts the
12
12
  * corresponding grep/find tool result (opportunistic, conservative cleanup), with a
13
13
  * best-effort dispose-time sweep for zero-reference artifacts. Payloads are retrievable
@@ -194,10 +194,28 @@ function writeMeta(baseDir, id, meta) {
194
194
  */
195
195
  export function createFileArtifactStore(options) {
196
196
  const baseDir = options.baseDir;
197
- mkdirSync(baseDir, { recursive: true });
198
197
  const cleanedUpThisInstance = new Set();
198
+ let acquiredBaseDir;
199
+ let preparedBaseDir = false;
200
+ const ensureBaseDir = () => {
201
+ preparedBaseDir = true;
202
+ acquiredBaseDir ??= options.acquireBaseDir?.() ?? baseDir;
203
+ mkdirSync(acquiredBaseDir, { recursive: true });
204
+ return acquiredBaseDir;
205
+ };
206
+ const ensureExistingBaseDir = () => {
207
+ if (!preparedBaseDir) {
208
+ options.prepareBaseDir?.();
209
+ preparedBaseDir = true;
210
+ }
211
+ if (!existsSync(baseDir))
212
+ return false;
213
+ ensureBaseDir();
214
+ return true;
215
+ };
199
216
  return {
200
217
  write(request) {
218
+ ensureBaseDir();
201
219
  const id = generateArtifactId(request);
202
220
  const existingMeta = readMeta(baseDir, id);
203
221
  const existingPayloadPath = payloadPath(baseDir, id);
@@ -225,6 +243,8 @@ export function createFileArtifactStore(options) {
225
243
  read(id) {
226
244
  if (!isSafeArtifactId(id))
227
245
  return { id, missing: true, reason: "not_found" };
246
+ if (!ensureExistingBaseDir())
247
+ return { id, missing: true, reason: "not_found" };
228
248
  const meta = readMeta(baseDir, id);
229
249
  const pPath = payloadPath(baseDir, id);
230
250
  if (!meta || !existsSync(pPath)) {
@@ -235,6 +255,8 @@ export function createFileArtifactStore(options) {
235
255
  readRef(id) {
236
256
  if (!isSafeArtifactId(id))
237
257
  return undefined;
258
+ if (!ensureExistingBaseDir())
259
+ return undefined;
238
260
  const meta = readMeta(baseDir, id);
239
261
  if (!meta || !existsSync(payloadPath(baseDir, id)))
240
262
  return undefined;
@@ -243,11 +265,15 @@ export function createFileArtifactStore(options) {
243
265
  has(id) {
244
266
  if (!isSafeArtifactId(id))
245
267
  return false;
268
+ if (!ensureExistingBaseDir())
269
+ return false;
246
270
  return readMeta(baseDir, id) !== undefined && existsSync(payloadPath(baseDir, id));
247
271
  },
248
272
  addReference(id, holderId) {
249
273
  if (!isSafeArtifactId(id))
250
274
  return false;
275
+ if (!ensureExistingBaseDir())
276
+ return false;
251
277
  const meta = readMeta(baseDir, id);
252
278
  if (!meta || !existsSync(payloadPath(baseDir, id)))
253
279
  return false;
@@ -260,6 +286,8 @@ export function createFileArtifactStore(options) {
260
286
  removeReference(id, holderId) {
261
287
  if (!isSafeArtifactId(id))
262
288
  return false;
289
+ if (!ensureExistingBaseDir())
290
+ return false;
263
291
  const meta = readMeta(baseDir, id);
264
292
  if (!meta)
265
293
  return false;
@@ -273,10 +301,14 @@ export function createFileArtifactStore(options) {
273
301
  referenceCount(id) {
274
302
  if (!isSafeArtifactId(id))
275
303
  return 0;
304
+ if (!ensureExistingBaseDir())
305
+ return 0;
276
306
  return readMeta(baseDir, id)?.references.length ?? 0;
277
307
  },
278
308
  cleanup() {
279
309
  const deleted = [];
310
+ if (!ensureExistingBaseDir())
311
+ return deleted;
280
312
  for (const entry of readdirSync(baseDir)) {
281
313
  if (!entry.endsWith(META_SUFFIX))
282
314
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"context-artifacts.js","sourceRoot":"","sources":["../../../src/core/context/context-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAA2B,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA0BnG,MAAM,UAAU,uBAAuB,CAAC,KAA6C,EAAkC;IACtH,OAAQ,KAA+B,CAAC,OAAO,KAAK,IAAI,CAAC;AAAA,CACzD;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CACjC,OAGC,EACQ;IACT,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI;QACnB,OAAO,CAAC,IAAI;QACZ,OAAO,CAAC,QAAQ,IAAI,EAAE;QACtB,OAAO,CAAC,OAAO,IAAI,EAAE;QACrB,OAAO,CAAC,IAAI,IAAI,EAAE;QAClB,OAAO,CAAC,cAAc,IAAI,EAAE;QAC5B,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;QAC7B,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC;KAC5B,EAAE,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAAA,CACvC;AAkCD,MAAM,UAAU,2BAA2B,GAAkB;IAC5D,MAAM,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;IACpD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAEpC,OAAO;QACN,KAAK,CAAC,OAA6B,EAAkB;YACpD,MAAM,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,QAAQ,EAAE,CAAC;gBACd,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrB,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;YACzD,CAAC;YAED,MAAM,GAAG,GAAuB;gBAC/B,EAAE;gBACF,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,UAAU,EAAE,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC/C,SAAS,EAAE,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC7C,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,YAAY,EAAE,OAAO,CAAC,YAAY;aAClC,CAAC;YACF,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;YAC5E,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACrB,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QAAA,CACzC;QAED,IAAI,CAAC,EAAU,EAA0C;YACxD,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YACtF,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QAAA,CACpD;QAED,OAAO,CAAC,EAAU,EAAkC;YACnD,OAAO,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC;QAAA,CAC9B;QAED,GAAG,CAAC,EAAU,EAAW;YACxB,OAAO,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAAA,CACzB;QAED,YAAY,CAAC,EAAU,EAAE,QAAgB,EAAW;YACnD,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1B,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAChC,OAAO,IAAI,CAAC;QAAA,CACZ;QAED,eAAe,CAAC,EAAU,EAAE,QAAgB,EAAW;YACtD,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1B,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAAA,CAC1C;QAED,cAAc,CAAC,EAAU,EAAU;YAClC,OAAO,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC;QAAA,CAC/C;QAED,OAAO,GAAa;YACnB,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;gBACtC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBAClC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBACrB,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAClB,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClB,CAAC;YACF,CAAC;YACD,OAAO,OAAO,CAAC;QAAA,CACf;KACD,CAAC;AAAA,CACF;AAYD,MAAM,WAAW,GAAG,YAAY,CAAC;AACjC,MAAM,cAAc,GAAG,UAAU,CAAC;AAElC;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,EAAU,EAAW;IAC9C,OAAO,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,CACnC;AAED,SAAS,WAAW,CAAC,OAAe,EAAE,EAAU,EAAU;IACzD,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC,CAAC;AAAA,CAC/C;AAED,SAAS,QAAQ,CAAC,OAAe,EAAE,EAAU,EAAU;IACtD,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC,CAAC;AAAA,CAC5C;AAED,MAAM,oBAAoB,GAA4C,IAAI,GAAG,CAAC;IAC7E,aAAa;IACb,eAAe;IACf,aAAa;IACb,MAAM;IACN,kBAAkB;CAClB,CAAC,CAAC;AAEH,SAAS,uBAAuB,CAAC,KAAc,EAA+B;IAC7E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,OAAO,CACN,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;QAC1B,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC5B,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAkC,CAAC;QAChE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ;QAClC,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ;QACrC,OAAO,GAAG,CAAC,YAAY,KAAK,SAAS,CACrC,CAAC;AAAA,CACF;AAED;;;;;GAKG;AACH,SAAS,4BAA4B,CAAC,KAAc,EAAkC;IACrF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,IAAI,GAAG,KAAgC,CAAC;IAC9C,OAAO,CACN,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC;QACjC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;QAC9B,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAC3D,CAAC;AAAA,CACF;AAED,SAAS,QAAQ,CAAC,OAAe,EAAE,EAAU,EAAqC;IACjF,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACJ,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/D,OAAO,4BAA4B,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AAAA,CACD;AAED,SAAS,SAAS,CAAC,OAAe,EAAE,EAAU,EAAE,IAA2B,EAAQ;IAClF,aAAa,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;AAAA,CACnE;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAiC,EAAiB;IACzF,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhD,OAAO;QACN,KAAK,CAAC,OAA6B,EAAkB;YACpD,MAAM,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC3C,MAAM,mBAAmB,GAAG,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACrD,IAAI,YAAY,IAAI,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACrD,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACjC,OAAO,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,CAAC,mBAAmB,EAAE,MAAM,CAAC,EAAE,CAAC;YACtF,CAAC;YAED,MAAM,GAAG,GAAuB;gBAC/B,EAAE;gBACF,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,UAAU,EAAE,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC/C,SAAS,EAAE,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC7C,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,YAAY,EAAE,OAAO,CAAC,YAAY;aAClC,CAAC;YACF,aAAa,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACjE,SAAS,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;YAChD,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACjC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QAAA,CACzC;QAED,IAAI,CAAC,EAAU,EAA0C;YACxD,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YAC7E,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACnC,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,qBAAqB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAClG,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QAAA,CAC/D;QAED,OAAO,CAAC,EAAU,EAAkC;YACnD,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAAE,OAAO,SAAS,CAAC;YACrE,OAAO,IAAI,CAAC,GAAG,CAAC;QAAA,CAChB;QAED,GAAG,CAAC,EAAU,EAAW;YACxB,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxC,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,SAAS,IAAI,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;QAAA,CACnF;QAED,YAAY,CAAC,EAAU,EAAE,QAAgB,EAAW;YACnD,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YACjE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC/B,SAAS,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;YAC9B,CAAC;YACD,OAAO,IAAI,CAAC;QAAA,CACZ;QAED,eAAe,CAAC,EAAU,EAAE,QAAgB,EAAW;YACtD,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI;gBAAE,OAAO,KAAK,CAAC;YACxB,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAChD,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC/B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACjC,SAAS,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC;QAAA,CACZ;QAED,cAAc,CAAC,EAAU,EAAU;YAClC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAAE,OAAO,CAAC,CAAC;YACpC,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC;QAAA,CACrD;QAED,OAAO,GAAa;YACnB,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1C,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;oBAAE,SAAS;gBAC3C,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBAC/C,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;oBAAE,SAAS;gBACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBACnC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;oBAAE,SAAS;gBAClD,IAAI,CAAC;oBACJ,UAAU,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;gBACnC,CAAC;gBAAC,MAAM,CAAC;oBACR,SAAS;gBACV,CAAC;gBACD,IAAI,CAAC;oBACJ,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;gBACtC,CAAC;gBAAC,MAAM,CAAC;oBACR,iFAAiF;gBAClF,CAAC;gBACD,qBAAqB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;YACD,OAAO,OAAO,CAAC;QAAA,CACf;KACD,CAAC;AAAA,CACF","sourcesContent":["/**\n * Artifact store abstraction (Phase 2): stable refs for raw large payloads, kept out of\n * band from prompt context. This module defines the `ArtifactStore` interface plus two\n * implementations: an in-memory one for tests, and `createFileArtifactStore` (session-\n * scoped, filesystem-backed). A SQLite-backed implementation waits until the Phase M0\n * storage-authority/location/concurrency decisions are accepted (see\n * docs/context-management-rework/memory-architecture.md).\n *\n * `createFileArtifactStore` is wired into live grep/find tool construction in\n * agent-session.ts (session-scoped under `<agentDir>/context-artifacts/<sessionId>/`).\n * References are registered at pack time and released when context-gc evicts the\n * corresponding grep/find tool result (opportunistic, conservative cleanup), with a\n * best-effort dispose-time sweep for zero-reference artifacts. Payloads are retrievable\n * out of band via the artifact_retrieve tool (context/artifact-retrieval.ts).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { type ContextArtifactRef, estimateByteLength, estimateLineCount } from \"./context-item.ts\";\n\nexport interface ArtifactWriteRequest {\n\tkind: ContextArtifactRef[\"kind\"];\n\tcontent: string;\n\ttoolName?: string;\n\tcommand?: string;\n\tpath?: string;\n\tsessionEntryId?: string;\n\tcreatedAtTurn: number;\n\treproducible: boolean;\n}\n\nexport interface ArtifactRecord {\n\tref: ContextArtifactRef;\n\tcontent: string;\n}\n\nexport type MissingArtifactReason = \"not_found\" | \"cleaned_up\";\n\nexport interface MissingArtifactMarker {\n\tid: string;\n\tmissing: true;\n\treason: MissingArtifactReason;\n}\n\nexport function isMissingArtifactMarker(value: ArtifactRecord | MissingArtifactMarker): value is MissingArtifactMarker {\n\treturn (value as MissingArtifactMarker).missing === true;\n}\n\n/**\n * Artifact id for a capture event, not merely a payload: it hashes every ref-defining\n * field (kind, tool/command/path, content, sessionEntryId, createdAtTurn, reproducible).\n * A repeat write with identical content but a different turn or session entry is a\n * distinct capture and must get a distinct id -- otherwise the later capture's metadata\n * would be silently discarded in favor of the first write. Only a truly identical\n * request (same capture, re-submitted) is idempotent under this id.\n */\nexport function generateArtifactId(\n\trequest: Pick<\n\t\tArtifactWriteRequest,\n\t\t\"kind\" | \"content\" | \"toolName\" | \"command\" | \"path\" | \"sessionEntryId\" | \"createdAtTurn\" | \"reproducible\"\n\t>,\n): string {\n\tconst hash = createHash(\"sha256\");\n\tfor (const field of [\n\t\trequest.kind,\n\t\trequest.toolName ?? \"\",\n\t\trequest.command ?? \"\",\n\t\trequest.path ?? \"\",\n\t\trequest.sessionEntryId ?? \"\",\n\t\tString(request.createdAtTurn),\n\t\tString(request.reproducible),\n\t]) {\n\t\thash.update(field).update(\"\\0\");\n\t}\n\thash.update(request.content);\n\treturn hash.digest(\"hex\").slice(0, 24);\n}\n\nexport interface ArtifactStore {\n\twrite(request: ArtifactWriteRequest): ArtifactRecord;\n\tread(id: string): ArtifactRecord | MissingArtifactMarker;\n\t/**\n\t * Metadata-only lookup: the ref if `id` resolves to a live artifact, `undefined`\n\t * otherwise. Never loads the payload -- for the file store this must not touch the\n\t * payload file at all beyond an existence check, so a caller that only needs to know\n\t * \"does this still exist, and what are its ref fields\" (e.g. a per-turn audit pass)\n\t * never pays the cost of reading potentially large content off disk.\n\t */\n\treadRef(id: string): ContextArtifactRef | undefined;\n\thas(id: string): boolean;\n\t/**\n\t * Register that `holderId` (a context item id, session entry id, etc.) depends on this\n\t * artifact. Returns false if `id` does not exist (never written, or already cleaned\n\t * up) so a caller cannot believe it protected an artifact that was never registered.\n\t * Callers must fail closed (treat the artifact as unprotected) on a false return.\n\t */\n\taddReference(id: string, holderId: string): boolean;\n\t/** Release a previously registered dependency. Returns true only if a reference was actually removed. */\n\tremoveReference(id: string, holderId: string): boolean;\n\treferenceCount(id: string): number;\n\t/** Delete only artifacts with zero active references. Returns the ids actually deleted. */\n\tcleanup(): string[];\n}\n\ninterface StoredArtifact {\n\tref: ContextArtifactRef;\n\tcontent: string;\n\treferences: Set<string>;\n}\n\nexport function createInMemoryArtifactStore(): ArtifactStore {\n\tconst artifacts = new Map<string, StoredArtifact>();\n\tconst cleanedUp = new Set<string>();\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existing = artifacts.get(id);\n\t\t\tif (existing) {\n\t\t\t\tcleanedUp.delete(id);\n\t\t\t\treturn { ref: existing.ref, content: existing.content };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\tartifacts.set(id, { ref, content: request.content, references: new Set() });\n\t\t\tcleanedUp.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUp.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: stored.ref, content: stored.content };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\treturn artifacts.get(id)?.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\treturn artifacts.has(id);\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\tstored.references.add(holderId);\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\treturn stored.references.delete(holderId);\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\treturn artifacts.get(id)?.references.size ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tfor (const [id, stored] of artifacts) {\n\t\t\t\tif (stored.references.size === 0) {\n\t\t\t\t\tartifacts.delete(id);\n\t\t\t\t\tcleanedUp.add(id);\n\t\t\t\t\tdeleted.push(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n\nexport interface FileArtifactStoreOptions {\n\t/** Directory the store persists artifact payloads and metadata under. Created if missing. */\n\tbaseDir: string;\n}\n\ninterface PersistedArtifactMeta {\n\tref: ContextArtifactRef;\n\treferences: string[];\n}\n\nconst META_SUFFIX = \".meta.json\";\nconst PAYLOAD_SUFFIX = \".payload\";\n\n/**\n * Artifact ids are generated by `generateArtifactId` as a lowercase hex digest. Reject\n * anything else so a caller-supplied id (including one echoed back from model output)\n * can never be used as a path-traversal vector into `baseDir`.\n */\nfunction isSafeArtifactId(id: string): boolean {\n\treturn /^[0-9a-f]{1,64}$/.test(id);\n}\n\nfunction payloadPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${PAYLOAD_SUFFIX}`);\n}\n\nfunction metaPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${META_SUFFIX}`);\n}\n\nconst VALID_ARTIFACT_KINDS: ReadonlySet<ContextArtifactRef[\"kind\"]> = new Set([\n\t\"tool_output\",\n\t\"file_snapshot\",\n\t\"test_output\",\n\t\"diff\",\n\t\"transcript_slice\",\n]);\n\nfunction isValidArtifactRefShape(value: unknown): value is ContextArtifactRef {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst ref = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof ref.id === \"string\" &&\n\t\ttypeof ref.kind === \"string\" &&\n\t\tVALID_ARTIFACT_KINDS.has(ref.kind as ContextArtifactRef[\"kind\"]) &&\n\t\ttypeof ref.byteLength === \"number\" &&\n\t\ttypeof ref.createdAtTurn === \"number\" &&\n\t\ttypeof ref.reproducible === \"boolean\"\n\t);\n}\n\n/**\n * A parsed JSON value can be syntactically valid but semantically garbage (truncated\n * write, hand-edited file, future/incompatible format). Validate shape, not just parse\n * success, so a malformed sidecar can never produce an invalid ref or crash `cleanup()` --\n * it is treated as unusable/missing, the same as a sidecar that doesn't exist.\n */\nfunction isValidPersistedArtifactMeta(value: unknown): value is PersistedArtifactMeta {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst meta = value as Record<string, unknown>;\n\treturn (\n\t\tisValidArtifactRefShape(meta.ref) &&\n\t\tArray.isArray(meta.references) &&\n\t\tmeta.references.every((entry) => typeof entry === \"string\")\n\t);\n}\n\nfunction readMeta(baseDir: string, id: string): PersistedArtifactMeta | undefined {\n\tconst path = metaPath(baseDir, id);\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"));\n\t\treturn isValidPersistedArtifactMeta(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeMeta(baseDir: string, id: string, meta: PersistedArtifactMeta): void {\n\twriteFileSync(metaPath(baseDir, id), JSON.stringify(meta), \"utf8\");\n}\n\n/**\n * Filesystem-backed `ArtifactStore`: payload and metadata (including reference holder ids)\n * are written to `baseDir` so content, ref fields, and cleanup-protecting references all\n * survive recreating the store (e.g. across a process restart against the same\n * directory) -- unlike the in-memory store, which loses everything when the instance is\n * dropped. No SQLite or other index is used; each artifact's metadata is a small sidecar\n * JSON file next to its payload file, per the \"keep SQLite out of scope unless a minimal\n * metadata shape is unavoidable\" constraint for this slice.\n *\n * The one thing that does NOT survive recreation: the missing-artifact reason\n * distinction. A fresh instance has no in-memory record of which ids it personally\n * cleaned up, so a previously-cleaned-up id reads back as \"not_found\" rather than\n * \"cleaned_up\" after a restart. This still always returns an explicit missing marker,\n * never fabricated or empty content -- it only affects which of the two reason codes is\n * reported.\n */\nexport function createFileArtifactStore(options: FileArtifactStoreOptions): ArtifactStore {\n\tconst baseDir = options.baseDir;\n\tmkdirSync(baseDir, { recursive: true });\n\tconst cleanedUpThisInstance = new Set<string>();\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existingMeta = readMeta(baseDir, id);\n\t\t\tconst existingPayloadPath = payloadPath(baseDir, id);\n\t\t\tif (existingMeta && existsSync(existingPayloadPath)) {\n\t\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\t\treturn { ref: existingMeta.ref, content: readFileSync(existingPayloadPath, \"utf8\") };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\twriteFileSync(payloadPath(baseDir, id), request.content, \"utf8\");\n\t\t\twriteMeta(baseDir, id, { ref, references: [] });\n\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tif (!isSafeArtifactId(id)) return { id, missing: true, reason: \"not_found\" };\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tconst pPath = payloadPath(baseDir, id);\n\t\t\tif (!meta || !existsSync(pPath)) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUpThisInstance.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: meta.ref, content: readFileSync(pPath, \"utf8\") };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\tif (!isSafeArtifactId(id)) return undefined;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return undefined;\n\t\t\treturn meta.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\treturn readMeta(baseDir, id) !== undefined && existsSync(payloadPath(baseDir, id));\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return false;\n\t\t\tif (!meta.references.includes(holderId)) {\n\t\t\t\tmeta.references.push(holderId);\n\t\t\t\twriteMeta(baseDir, id, meta);\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta) return false;\n\t\t\tconst index = meta.references.indexOf(holderId);\n\t\t\tif (index === -1) return false;\n\t\t\tmeta.references.splice(index, 1);\n\t\t\twriteMeta(baseDir, id, meta);\n\t\t\treturn true;\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\tif (!isSafeArtifactId(id)) return 0;\n\t\t\treturn readMeta(baseDir, id)?.references.length ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tfor (const entry of readdirSync(baseDir)) {\n\t\t\t\tif (!entry.endsWith(META_SUFFIX)) continue;\n\t\t\t\tconst id = entry.slice(0, -META_SUFFIX.length);\n\t\t\t\tif (!isSafeArtifactId(id)) continue;\n\t\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\t\tif (!meta || meta.references.length > 0) continue;\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(metaPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(payloadPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\t// Payload already gone; metadata removal above is what matters for reachability.\n\t\t\t\t}\n\t\t\t\tcleanedUpThisInstance.add(id);\n\t\t\t\tdeleted.push(id);\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"context-artifacts.js","sourceRoot":"","sources":["../../../src/core/context/context-artifacts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Artifact store abstraction (Phase 2): stable refs for raw large payloads, kept out of\n * band from prompt context. This module defines the `ArtifactStore` interface plus two\n * implementations: an in-memory one for tests, and `createFileArtifactStore` (session-\n * scoped, filesystem-backed). A SQLite-backed implementation waits until the Phase M0\n * storage-authority/location/concurrency decisions are accepted (see\n * docs/context-management-rework/memory-architecture.md).\n *\n * `createFileArtifactStore` is wired into live grep/find tool construction in\n * agent-session.ts (session-scoped under `<agentDir>/work/context/sessions/<sessionId>/artifacts/`).\n * References are registered at pack time and released when context-gc evicts the\n * corresponding grep/find tool result (opportunistic, conservative cleanup), with a\n * best-effort dispose-time sweep for zero-reference artifacts. Payloads are retrievable\n * out of band via the artifact_retrieve tool (context/artifact-retrieval.ts).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { type ContextArtifactRef, estimateByteLength, estimateLineCount } from \"./context-item.ts\";\n\nexport interface ArtifactWriteRequest {\n\tkind: ContextArtifactRef[\"kind\"];\n\tcontent: string;\n\ttoolName?: string;\n\tcommand?: string;\n\tpath?: string;\n\tsessionEntryId?: string;\n\tcreatedAtTurn: number;\n\treproducible: boolean;\n}\n\nexport interface ArtifactRecord {\n\tref: ContextArtifactRef;\n\tcontent: string;\n}\n\nexport type MissingArtifactReason = \"not_found\" | \"cleaned_up\";\n\nexport interface MissingArtifactMarker {\n\tid: string;\n\tmissing: true;\n\treason: MissingArtifactReason;\n}\n\nexport function isMissingArtifactMarker(value: ArtifactRecord | MissingArtifactMarker): value is MissingArtifactMarker {\n\treturn (value as MissingArtifactMarker).missing === true;\n}\n\n/**\n * Artifact id for a capture event, not merely a payload: it hashes every ref-defining\n * field (kind, tool/command/path, content, sessionEntryId, createdAtTurn, reproducible).\n * A repeat write with identical content but a different turn or session entry is a\n * distinct capture and must get a distinct id -- otherwise the later capture's metadata\n * would be silently discarded in favor of the first write. Only a truly identical\n * request (same capture, re-submitted) is idempotent under this id.\n */\nexport function generateArtifactId(\n\trequest: Pick<\n\t\tArtifactWriteRequest,\n\t\t\"kind\" | \"content\" | \"toolName\" | \"command\" | \"path\" | \"sessionEntryId\" | \"createdAtTurn\" | \"reproducible\"\n\t>,\n): string {\n\tconst hash = createHash(\"sha256\");\n\tfor (const field of [\n\t\trequest.kind,\n\t\trequest.toolName ?? \"\",\n\t\trequest.command ?? \"\",\n\t\trequest.path ?? \"\",\n\t\trequest.sessionEntryId ?? \"\",\n\t\tString(request.createdAtTurn),\n\t\tString(request.reproducible),\n\t]) {\n\t\thash.update(field).update(\"\\0\");\n\t}\n\thash.update(request.content);\n\treturn hash.digest(\"hex\").slice(0, 24);\n}\n\nexport interface ArtifactStore {\n\twrite(request: ArtifactWriteRequest): ArtifactRecord;\n\tread(id: string): ArtifactRecord | MissingArtifactMarker;\n\t/**\n\t * Metadata-only lookup: the ref if `id` resolves to a live artifact, `undefined`\n\t * otherwise. Never loads the payload -- for the file store this must not touch the\n\t * payload file at all beyond an existence check, so a caller that only needs to know\n\t * \"does this still exist, and what are its ref fields\" (e.g. a per-turn audit pass)\n\t * never pays the cost of reading potentially large content off disk.\n\t */\n\treadRef(id: string): ContextArtifactRef | undefined;\n\thas(id: string): boolean;\n\t/**\n\t * Register that `holderId` (a context item id, session entry id, etc.) depends on this\n\t * artifact. Returns false if `id` does not exist (never written, or already cleaned\n\t * up) so a caller cannot believe it protected an artifact that was never registered.\n\t * Callers must fail closed (treat the artifact as unprotected) on a false return.\n\t */\n\taddReference(id: string, holderId: string): boolean;\n\t/** Release a previously registered dependency. Returns true only if a reference was actually removed. */\n\tremoveReference(id: string, holderId: string): boolean;\n\treferenceCount(id: string): number;\n\t/** Delete only artifacts with zero active references. Returns the ids actually deleted. */\n\tcleanup(): string[];\n}\n\ninterface StoredArtifact {\n\tref: ContextArtifactRef;\n\tcontent: string;\n\treferences: Set<string>;\n}\n\nexport function createInMemoryArtifactStore(): ArtifactStore {\n\tconst artifacts = new Map<string, StoredArtifact>();\n\tconst cleanedUp = new Set<string>();\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existing = artifacts.get(id);\n\t\t\tif (existing) {\n\t\t\t\tcleanedUp.delete(id);\n\t\t\t\treturn { ref: existing.ref, content: existing.content };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\tartifacts.set(id, { ref, content: request.content, references: new Set() });\n\t\t\tcleanedUp.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUp.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: stored.ref, content: stored.content };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\treturn artifacts.get(id)?.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\treturn artifacts.has(id);\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\tstored.references.add(holderId);\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tconst stored = artifacts.get(id);\n\t\t\tif (!stored) return false;\n\t\t\treturn stored.references.delete(holderId);\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\treturn artifacts.get(id)?.references.size ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tfor (const [id, stored] of artifacts) {\n\t\t\t\tif (stored.references.size === 0) {\n\t\t\t\t\tartifacts.delete(id);\n\t\t\t\t\tcleanedUp.add(id);\n\t\t\t\t\tdeleted.push(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n\nexport interface FileArtifactStoreOptions {\n\t/** Leased session work directory where payloads and metadata live. Created on first use. */\n\tbaseDir: string;\n\t/** Migrate an existing store before probing it; must remain zero-write when no legacy data exists. */\n\tprepareBaseDir?: () => void;\n\t/** Acquire the owning lease immediately before the first real store access. */\n\tacquireBaseDir?: () => string;\n}\n\ninterface PersistedArtifactMeta {\n\tref: ContextArtifactRef;\n\treferences: string[];\n}\n\nconst META_SUFFIX = \".meta.json\";\nconst PAYLOAD_SUFFIX = \".payload\";\n\n/**\n * Artifact ids are generated by `generateArtifactId` as a lowercase hex digest. Reject\n * anything else so a caller-supplied id (including one echoed back from model output)\n * can never be used as a path-traversal vector into `baseDir`.\n */\nfunction isSafeArtifactId(id: string): boolean {\n\treturn /^[0-9a-f]{1,64}$/.test(id);\n}\n\nfunction payloadPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${PAYLOAD_SUFFIX}`);\n}\n\nfunction metaPath(baseDir: string, id: string): string {\n\treturn join(baseDir, `${id}${META_SUFFIX}`);\n}\n\nconst VALID_ARTIFACT_KINDS: ReadonlySet<ContextArtifactRef[\"kind\"]> = new Set([\n\t\"tool_output\",\n\t\"file_snapshot\",\n\t\"test_output\",\n\t\"diff\",\n\t\"transcript_slice\",\n]);\n\nfunction isValidArtifactRefShape(value: unknown): value is ContextArtifactRef {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst ref = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof ref.id === \"string\" &&\n\t\ttypeof ref.kind === \"string\" &&\n\t\tVALID_ARTIFACT_KINDS.has(ref.kind as ContextArtifactRef[\"kind\"]) &&\n\t\ttypeof ref.byteLength === \"number\" &&\n\t\ttypeof ref.createdAtTurn === \"number\" &&\n\t\ttypeof ref.reproducible === \"boolean\"\n\t);\n}\n\n/**\n * A parsed JSON value can be syntactically valid but semantically garbage (truncated\n * write, hand-edited file, future/incompatible format). Validate shape, not just parse\n * success, so a malformed sidecar can never produce an invalid ref or crash `cleanup()` --\n * it is treated as unusable/missing, the same as a sidecar that doesn't exist.\n */\nfunction isValidPersistedArtifactMeta(value: unknown): value is PersistedArtifactMeta {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst meta = value as Record<string, unknown>;\n\treturn (\n\t\tisValidArtifactRefShape(meta.ref) &&\n\t\tArray.isArray(meta.references) &&\n\t\tmeta.references.every((entry) => typeof entry === \"string\")\n\t);\n}\n\nfunction readMeta(baseDir: string, id: string): PersistedArtifactMeta | undefined {\n\tconst path = metaPath(baseDir, id);\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf8\"));\n\t\treturn isValidPersistedArtifactMeta(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeMeta(baseDir: string, id: string, meta: PersistedArtifactMeta): void {\n\twriteFileSync(metaPath(baseDir, id), JSON.stringify(meta), \"utf8\");\n}\n\n/**\n * Filesystem-backed `ArtifactStore`: payload and metadata (including reference holder ids)\n * are written to `baseDir` so content, ref fields, and cleanup-protecting references all\n * survive recreating the store (e.g. across a process restart against the same\n * directory) -- unlike the in-memory store, which loses everything when the instance is\n * dropped. No SQLite or other index is used; each artifact's metadata is a small sidecar\n * JSON file next to its payload file, per the \"keep SQLite out of scope unless a minimal\n * metadata shape is unavoidable\" constraint for this slice.\n *\n * The one thing that does NOT survive recreation: the missing-artifact reason\n * distinction. A fresh instance has no in-memory record of which ids it personally\n * cleaned up, so a previously-cleaned-up id reads back as \"not_found\" rather than\n * \"cleaned_up\" after a restart. This still always returns an explicit missing marker,\n * never fabricated or empty content -- it only affects which of the two reason codes is\n * reported.\n */\nexport function createFileArtifactStore(options: FileArtifactStoreOptions): ArtifactStore {\n\tconst baseDir = options.baseDir;\n\tconst cleanedUpThisInstance = new Set<string>();\n\tlet acquiredBaseDir: string | undefined;\n\tlet preparedBaseDir = false;\n\tconst ensureBaseDir = (): string => {\n\t\tpreparedBaseDir = true;\n\t\tacquiredBaseDir ??= options.acquireBaseDir?.() ?? baseDir;\n\t\tmkdirSync(acquiredBaseDir, { recursive: true });\n\t\treturn acquiredBaseDir;\n\t};\n\tconst ensureExistingBaseDir = (): boolean => {\n\t\tif (!preparedBaseDir) {\n\t\t\toptions.prepareBaseDir?.();\n\t\t\tpreparedBaseDir = true;\n\t\t}\n\t\tif (!existsSync(baseDir)) return false;\n\t\tensureBaseDir();\n\t\treturn true;\n\t};\n\n\treturn {\n\t\twrite(request: ArtifactWriteRequest): ArtifactRecord {\n\t\t\tensureBaseDir();\n\t\t\tconst id = generateArtifactId(request);\n\t\t\tconst existingMeta = readMeta(baseDir, id);\n\t\t\tconst existingPayloadPath = payloadPath(baseDir, id);\n\t\t\tif (existingMeta && existsSync(existingPayloadPath)) {\n\t\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\t\treturn { ref: existingMeta.ref, content: readFileSync(existingPayloadPath, \"utf8\") };\n\t\t\t}\n\n\t\t\tconst ref: ContextArtifactRef = {\n\t\t\t\tid,\n\t\t\t\tkind: request.kind,\n\t\t\t\tsessionEntryId: request.sessionEntryId,\n\t\t\t\ttoolName: request.toolName,\n\t\t\t\tcommand: request.command,\n\t\t\t\tpath: request.path,\n\t\t\t\tbyteLength: estimateByteLength(request.content),\n\t\t\t\tlineCount: estimateLineCount(request.content),\n\t\t\t\tcreatedAtTurn: request.createdAtTurn,\n\t\t\t\treproducible: request.reproducible,\n\t\t\t};\n\t\t\twriteFileSync(payloadPath(baseDir, id), request.content, \"utf8\");\n\t\t\twriteMeta(baseDir, id, { ref, references: [] });\n\t\t\tcleanedUpThisInstance.delete(id);\n\t\t\treturn { ref, content: request.content };\n\t\t},\n\n\t\tread(id: string): ArtifactRecord | MissingArtifactMarker {\n\t\t\tif (!isSafeArtifactId(id)) return { id, missing: true, reason: \"not_found\" };\n\t\t\tif (!ensureExistingBaseDir()) return { id, missing: true, reason: \"not_found\" };\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tconst pPath = payloadPath(baseDir, id);\n\t\t\tif (!meta || !existsSync(pPath)) {\n\t\t\t\treturn { id, missing: true, reason: cleanedUpThisInstance.has(id) ? \"cleaned_up\" : \"not_found\" };\n\t\t\t}\n\t\t\treturn { ref: meta.ref, content: readFileSync(pPath, \"utf8\") };\n\t\t},\n\n\t\treadRef(id: string): ContextArtifactRef | undefined {\n\t\t\tif (!isSafeArtifactId(id)) return undefined;\n\t\t\tif (!ensureExistingBaseDir()) return undefined;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return undefined;\n\t\t\treturn meta.ref;\n\t\t},\n\n\t\thas(id: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tif (!ensureExistingBaseDir()) return false;\n\t\t\treturn readMeta(baseDir, id) !== undefined && existsSync(payloadPath(baseDir, id));\n\t\t},\n\n\t\taddReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tif (!ensureExistingBaseDir()) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta || !existsSync(payloadPath(baseDir, id))) return false;\n\t\t\tif (!meta.references.includes(holderId)) {\n\t\t\t\tmeta.references.push(holderId);\n\t\t\t\twriteMeta(baseDir, id, meta);\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\tremoveReference(id: string, holderId: string): boolean {\n\t\t\tif (!isSafeArtifactId(id)) return false;\n\t\t\tif (!ensureExistingBaseDir()) return false;\n\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\tif (!meta) return false;\n\t\t\tconst index = meta.references.indexOf(holderId);\n\t\t\tif (index === -1) return false;\n\t\t\tmeta.references.splice(index, 1);\n\t\t\twriteMeta(baseDir, id, meta);\n\t\t\treturn true;\n\t\t},\n\n\t\treferenceCount(id: string): number {\n\t\t\tif (!isSafeArtifactId(id)) return 0;\n\t\t\tif (!ensureExistingBaseDir()) return 0;\n\t\t\treturn readMeta(baseDir, id)?.references.length ?? 0;\n\t\t},\n\n\t\tcleanup(): string[] {\n\t\t\tconst deleted: string[] = [];\n\t\t\tif (!ensureExistingBaseDir()) return deleted;\n\t\t\tfor (const entry of readdirSync(baseDir)) {\n\t\t\t\tif (!entry.endsWith(META_SUFFIX)) continue;\n\t\t\t\tconst id = entry.slice(0, -META_SUFFIX.length);\n\t\t\t\tif (!isSafeArtifactId(id)) continue;\n\t\t\t\tconst meta = readMeta(baseDir, id);\n\t\t\t\tif (!meta || meta.references.length > 0) continue;\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(metaPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(payloadPath(baseDir, id));\n\t\t\t\t} catch {\n\t\t\t\t\t// Payload already gone; metadata removal above is what matters for reachability.\n\t\t\t\t}\n\t\t\t\tcleanedUpThisInstance.add(id);\n\t\t\t\tdeleted.push(id);\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export type ContextStoreKind = "gc" | "artifacts";
1
2
  export interface ContextStoreRetentionOptions {
2
3
  maxAgeMs?: number;
3
4
  maxSessionDirs?: number;
@@ -9,7 +10,12 @@ export interface ContextStoreRetentionLease {
9
10
  readonly artifactsDir: string;
10
11
  release(): void;
11
12
  }
12
- export declare function pruneContextStores(agentDir: string, tenant: "gc" | "artifacts", options?: ContextStoreRetentionOptions): string[];
13
- export declare function getContextStoreDir(agentDir: string, tenant: "gc" | "artifacts", sessionId: string): string;
13
+ /**
14
+ * Move recognized context payloads from both historical layouts into the single leased session
15
+ * namespace. Unknown files, symlinks, active runs, and conflicting destinations are left untouched.
16
+ */
17
+ export declare function migrateLegacyContextStores(agentDir: string, onlySessionId?: string): void;
18
+ export declare function pruneContextStores(agentDir: string, options?: ContextStoreRetentionOptions): string[];
19
+ export declare function getContextStoreDir(agentDir: string, kind: ContextStoreKind, sessionId: string): string;
14
20
  export declare function acquireContextStoreRetention(agentDir: string, sessionId: string, options?: ContextStoreRetentionOptions): ContextStoreRetentionLease;
15
21
  //# sourceMappingURL=context-store-retention.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-store-retention.d.ts","sourceRoot":"","sources":["../../../src/core/context/context-store-retention.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CAChB;AAWD,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,GAAG,WAAW,EAC1B,OAAO,GAAE,4BAAiC,GACxC,MAAM,EAAE,CAEV;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1G;AAED,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,4BAAiC,GACxC,0BAA0B,CA4B5B","sourcesContent":["import {\n\tacquireWorkRun,\n\tgetWorkRunDir,\n\tpruneWorkTenant,\n\ttype WorkRetentionOptions,\n\ttype WorkRunLease,\n} from \"../../utils/work-directory.ts\";\n\nconst DEFAULT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MAX_SESSION_DIRS = 64;\nconst DEFAULT_MAX_TOTAL_BYTES = 512 * 1024 * 1024;\n\nexport interface ContextStoreRetentionOptions {\n\tmaxAgeMs?: number;\n\tmaxSessionDirs?: number;\n\tmaxTotalBytes?: number;\n\tnow?: number;\n}\n\nexport interface ContextStoreRetentionLease {\n\treadonly gcDir: string;\n\treadonly artifactsDir: string;\n\trelease(): void;\n}\n\nfunction toWorkRetention(options: ContextStoreRetentionOptions): WorkRetentionOptions {\n\treturn {\n\t\tmaxAgeMs: options.maxAgeMs ?? DEFAULT_MAX_AGE_MS,\n\t\tmaxRuns: options.maxSessionDirs ?? DEFAULT_MAX_SESSION_DIRS,\n\t\tmaxTotalBytes: options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES,\n\t\tnow: options.now,\n\t};\n}\n\nexport function pruneContextStores(\n\tagentDir: string,\n\ttenant: \"gc\" | \"artifacts\",\n\toptions: ContextStoreRetentionOptions = {},\n): string[] {\n\treturn pruneWorkTenant(agentDir, \"context\", tenant, toWorkRetention(options));\n}\n\nexport function getContextStoreDir(agentDir: string, tenant: \"gc\" | \"artifacts\", sessionId: string): string {\n\treturn getWorkRunDir(agentDir, \"context\", tenant, sessionId);\n}\n\nexport function acquireContextStoreRetention(\n\tagentDir: string,\n\tsessionId: string,\n\toptions: ContextStoreRetentionOptions = {},\n): ContextStoreRetentionLease {\n\tconst retention = toWorkRetention(options);\n\tconst leases: WorkRunLease[] = [];\n\ttry {\n\t\tconst gc = acquireWorkRun({ agentDir, category: \"context\", tenant: \"gc\", runId: sessionId, retention });\n\t\tleases.push(gc);\n\t\tconst artifacts = acquireWorkRun({\n\t\t\tagentDir,\n\t\t\tcategory: \"context\",\n\t\t\ttenant: \"artifacts\",\n\t\t\trunId: sessionId,\n\t\t\tretention,\n\t\t});\n\t\tleases.push(artifacts);\n\t\tlet released = false;\n\t\treturn {\n\t\t\tgcDir: gc.path,\n\t\t\tartifactsDir: artifacts.path,\n\t\t\trelease(): void {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tfor (const lease of leases) lease.release();\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tfor (const lease of leases) lease.release();\n\t\tthrow error;\n\t}\n}\n"]}
1
+ {"version":3,"file":"context-store-retention.d.ts","sourceRoot":"","sources":["../../../src/core/context/context-store-retention.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,WAAW,CAAC;AAElD,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CAChB;AA0GD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAyBzF;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,4BAAiC,GAAG,MAAM,EAAE,CAEzG;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtG;AAED,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,4BAAiC,GACxC,0BAA0B,CAmB5B","sourcesContent":["import { existsSync, lstatSync, mkdirSync, readdirSync, renameSync, rmdirSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tacquireWorkRun,\n\tgetWorkRunDir,\n\tgetWorkTenantDir,\n\thasActiveWorkRunLease,\n\tpruneWorkTenant,\n\ttype WorkRetentionOptions,\n} from \"../../utils/work-directory.ts\";\n\nconst DEFAULT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MAX_SESSION_DIRS = 64;\nconst DEFAULT_MAX_TOTAL_BYTES = 512 * 1024 * 1024;\nconst MAX_LEGACY_SESSION_DIRS = 10_000;\nconst MAX_LEGACY_FILES_PER_SESSION = 100_000;\nconst CONTEXT_WORK_TENANT = \"sessions\";\nconst WORK_RUN_MANIFEST_FILE = \".pi-work-run.json\";\n\nexport type ContextStoreKind = \"gc\" | \"artifacts\";\n\nexport interface ContextStoreRetentionOptions {\n\tmaxAgeMs?: number;\n\tmaxSessionDirs?: number;\n\tmaxTotalBytes?: number;\n\tnow?: number;\n}\n\nexport interface ContextStoreRetentionLease {\n\treadonly gcDir: string;\n\treadonly artifactsDir: string;\n\trelease(): void;\n}\n\ninterface LegacyContextStore {\n\troot: string;\n\tkind: ContextStoreKind;\n\tmanagedRun: boolean;\n}\n\nfunction toWorkRetention(options: ContextStoreRetentionOptions): WorkRetentionOptions {\n\treturn {\n\t\tmaxAgeMs: options.maxAgeMs ?? DEFAULT_MAX_AGE_MS,\n\t\tmaxRuns: options.maxSessionDirs ?? DEFAULT_MAX_SESSION_DIRS,\n\t\tmaxTotalBytes: options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES,\n\t\tnow: options.now,\n\t};\n}\n\nfunction legacyContextStores(agentDir: string): LegacyContextStore[] {\n\treturn [\n\t\t{ root: join(agentDir, \"context-gc\"), kind: \"gc\", managedRun: false },\n\t\t{ root: join(agentDir, \"context-artifacts\"), kind: \"artifacts\", managedRun: false },\n\t\t{ root: getWorkTenantDir(agentDir, \"context\", \"gc\"), kind: \"gc\", managedRun: true },\n\t\t{ root: getWorkTenantDir(agentDir, \"context\", \"artifacts\"), kind: \"artifacts\", managedRun: true },\n\t];\n}\n\nfunction isRecognizedPayload(kind: ContextStoreKind, name: string): boolean {\n\tif (kind === \"gc\") return /^[0-9a-f]{1,64}\\.txt$/.test(name);\n\treturn /^[0-9a-f]{1,64}(?:\\.payload|\\.meta\\.json)$/.test(name);\n}\n\nfunction isRealDirectory(path: string): boolean {\n\ttry {\n\t\tconst stats = lstatSync(path);\n\t\treturn stats.isDirectory() && !stats.isSymbolicLink();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction recognizedPayloads(sourceDir: string, kind: ContextStoreKind): string[] | undefined {\n\ttry {\n\t\tconst entries = readdirSync(sourceDir, { withFileTypes: true });\n\t\tif (entries.length > MAX_LEGACY_FILES_PER_SESSION) return undefined;\n\t\treturn entries\n\t\t\t.filter((entry) => entry.isFile() && !entry.isSymbolicLink() && isRecognizedPayload(kind, entry.name))\n\t\t\t.map((entry) => entry.name);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction removeLegacyRunIfEmpty(sourceDir: string, managedRun: boolean): void {\n\ttry {\n\t\trmdirSync(join(sourceDir, \".leases\"));\n\t} catch {}\n\tif (managedRun) {\n\t\ttry {\n\t\t\tconst entries = readdirSync(sourceDir);\n\t\t\tif (entries.length === 1 && entries[0] === WORK_RUN_MANIFEST_FILE) {\n\t\t\t\tunlinkSync(join(sourceDir, WORK_RUN_MANIFEST_FILE));\n\t\t\t}\n\t\t} catch {}\n\t}\n\ttry {\n\t\trmdirSync(sourceDir);\n\t} catch {}\n}\n\nfunction migrateLegacySessionStore(agentDir: string, sessionId: string, store: LegacyContextStore): void {\n\tconst sourceDir = join(store.root, sessionId);\n\tif (!isRealDirectory(sourceDir)) return;\n\tif (store.managedRun && hasActiveWorkRunLease(sourceDir)) return;\n\tconst payloads = recognizedPayloads(sourceDir, store.kind);\n\tif (!payloads) return;\n\n\tif (payloads.length > 0) {\n\t\tlet lease: ReturnType<typeof acquireWorkRun> | undefined;\n\t\ttry {\n\t\t\tlease = acquireWorkRun({\n\t\t\t\tagentDir,\n\t\t\t\tcategory: \"context\",\n\t\t\t\ttenant: CONTEXT_WORK_TENANT,\n\t\t\t\trunId: sessionId,\n\t\t\t\tretention: false,\n\t\t\t});\n\t\t\tconst targetDir = join(lease.path, store.kind);\n\t\t\tmkdirSync(targetDir, { recursive: true });\n\t\t\tfor (const name of payloads) {\n\t\t\t\tconst sourcePath = join(sourceDir, name);\n\t\t\t\tconst targetPath = join(targetDir, name);\n\t\t\t\tif (existsSync(targetPath)) continue;\n\t\t\t\ttry {\n\t\t\t\t\trenameSync(sourcePath, targetPath);\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {\n\t\t\treturn;\n\t\t} finally {\n\t\t\tlease?.release();\n\t\t}\n\t}\n\n\tremoveLegacyRunIfEmpty(sourceDir, store.managedRun);\n}\n\n/**\n * Move recognized context payloads from both historical layouts into the single leased session\n * namespace. Unknown files, symlinks, active runs, and conflicting destinations are left untouched.\n */\nexport function migrateLegacyContextStores(agentDir: string, onlySessionId?: string): void {\n\tfor (const store of legacyContextStores(agentDir)) {\n\t\tlet sessionIds: string[];\n\t\tif (onlySessionId) {\n\t\t\tsessionIds = [onlySessionId];\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tsessionIds = readdirSync(store.root, { withFileTypes: true })\n\t\t\t\t\t.slice(0, MAX_LEGACY_SESSION_DIRS)\n\t\t\t\t\t.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink())\n\t\t\t\t\t.map((entry) => entry.name);\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tfor (const sessionId of sessionIds) {\n\t\t\tmigrateLegacySessionStore(agentDir, sessionId, store);\n\t\t}\n\t\ttry {\n\t\t\trmdirSync(store.root);\n\t\t} catch {}\n\t}\n\ttry {\n\t\trmdirSync(join(agentDir, \"work\", \"context\"));\n\t} catch {}\n}\n\nexport function pruneContextStores(agentDir: string, options: ContextStoreRetentionOptions = {}): string[] {\n\treturn pruneWorkTenant(agentDir, \"context\", CONTEXT_WORK_TENANT, toWorkRetention(options));\n}\n\nexport function getContextStoreDir(agentDir: string, kind: ContextStoreKind, sessionId: string): string {\n\treturn join(getWorkRunDir(agentDir, \"context\", CONTEXT_WORK_TENANT, sessionId), kind);\n}\n\nexport function acquireContextStoreRetention(\n\tagentDir: string,\n\tsessionId: string,\n\toptions: ContextStoreRetentionOptions = {},\n): ContextStoreRetentionLease {\n\tmigrateLegacyContextStores(agentDir, sessionId);\n\tconst lease = acquireWorkRun({\n\t\tagentDir,\n\t\tcategory: \"context\",\n\t\ttenant: CONTEXT_WORK_TENANT,\n\t\trunId: sessionId,\n\t\tretention: toWorkRetention(options),\n\t});\n\tlet released = false;\n\treturn {\n\t\tgcDir: join(lease.path, \"gc\"),\n\t\tartifactsDir: join(lease.path, \"artifacts\"),\n\t\trelease(): void {\n\t\t\tif (released) return;\n\t\t\treleased = true;\n\t\t\tlease.release();\n\t\t},\n\t};\n}\n"]}