@caupulican/pi-adaptative 0.80.86 → 0.80.89

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 (353) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/dist/core/agent-session.d.ts +412 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +2053 -41
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/autonomy/approval-gate.d.ts +4 -0
  7. package/dist/core/autonomy/approval-gate.d.ts.map +1 -0
  8. package/dist/core/autonomy/approval-gate.js +27 -0
  9. package/dist/core/autonomy/approval-gate.js.map +1 -0
  10. package/dist/core/autonomy/bounded-completion.d.ts +27 -0
  11. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -0
  12. package/dist/core/autonomy/bounded-completion.js +44 -0
  13. package/dist/core/autonomy/bounded-completion.js.map +1 -0
  14. package/dist/core/autonomy/contracts.d.ts +129 -0
  15. package/dist/core/autonomy/contracts.d.ts.map +1 -0
  16. package/dist/core/autonomy/contracts.js +2 -0
  17. package/dist/core/autonomy/contracts.js.map +1 -0
  18. package/dist/core/autonomy/gates.d.ts +15 -0
  19. package/dist/core/autonomy/gates.d.ts.map +1 -0
  20. package/dist/core/autonomy/gates.js +205 -0
  21. package/dist/core/autonomy/gates.js.map +1 -0
  22. package/dist/core/autonomy/lane-tracker.d.ts +48 -0
  23. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -0
  24. package/dist/core/autonomy/lane-tracker.js +125 -0
  25. package/dist/core/autonomy/lane-tracker.js.map +1 -0
  26. package/dist/core/autonomy/path-scope.d.ts +9 -0
  27. package/dist/core/autonomy/path-scope.d.ts.map +1 -0
  28. package/dist/core/autonomy/path-scope.js +122 -0
  29. package/dist/core/autonomy/path-scope.js.map +1 -0
  30. package/dist/core/autonomy/risk-assessment.d.ts +3 -0
  31. package/dist/core/autonomy/risk-assessment.d.ts.map +1 -0
  32. package/dist/core/autonomy/risk-assessment.js +122 -0
  33. package/dist/core/autonomy/risk-assessment.js.map +1 -0
  34. package/dist/core/autonomy/session-lane-record.d.ts +10 -0
  35. package/dist/core/autonomy/session-lane-record.d.ts.map +1 -0
  36. package/dist/core/autonomy/session-lane-record.js +36 -0
  37. package/dist/core/autonomy/session-lane-record.js.map +1 -0
  38. package/dist/core/autonomy/status.d.ts +40 -0
  39. package/dist/core/autonomy/status.d.ts.map +1 -0
  40. package/dist/core/autonomy/status.js +107 -0
  41. package/dist/core/autonomy/status.js.map +1 -0
  42. package/dist/core/autonomy/subagent-prompt.d.ts +21 -0
  43. package/dist/core/autonomy/subagent-prompt.d.ts.map +1 -0
  44. package/dist/core/autonomy/subagent-prompt.js +28 -0
  45. package/dist/core/autonomy/subagent-prompt.js.map +1 -0
  46. package/dist/core/autonomy/telemetry-events.d.ts +18 -0
  47. package/dist/core/autonomy/telemetry-events.d.ts.map +1 -0
  48. package/dist/core/autonomy/telemetry-events.js +60 -0
  49. package/dist/core/autonomy/telemetry-events.js.map +1 -0
  50. package/dist/core/context/artifact-retrieval.d.ts +49 -0
  51. package/dist/core/context/artifact-retrieval.d.ts.map +1 -0
  52. package/dist/core/context/artifact-retrieval.js +49 -0
  53. package/dist/core/context/artifact-retrieval.js.map +1 -0
  54. package/dist/core/context/brain-curator.d.ts +88 -0
  55. package/dist/core/context/brain-curator.d.ts.map +1 -0
  56. package/dist/core/context/brain-curator.js +192 -0
  57. package/dist/core/context/brain-curator.js.map +1 -0
  58. package/dist/core/context/context-artifacts.d.ts +94 -0
  59. package/dist/core/context/context-artifacts.d.ts.map +1 -0
  60. package/dist/core/context/context-artifacts.js +307 -0
  61. package/dist/core/context/context-artifacts.js.map +1 -0
  62. package/dist/core/context/context-audit.d.ts +66 -0
  63. package/dist/core/context/context-audit.d.ts.map +1 -0
  64. package/dist/core/context/context-audit.js +173 -0
  65. package/dist/core/context/context-audit.js.map +1 -0
  66. package/dist/core/context/context-composition.d.ts +122 -0
  67. package/dist/core/context/context-composition.d.ts.map +1 -0
  68. package/dist/core/context/context-composition.js +163 -0
  69. package/dist/core/context/context-composition.js.map +1 -0
  70. package/dist/core/context/context-item.d.ts +117 -0
  71. package/dist/core/context/context-item.d.ts.map +1 -0
  72. package/dist/core/context/context-item.js +36 -0
  73. package/dist/core/context/context-item.js.map +1 -0
  74. package/dist/core/context/context-prompt-enforcement.d.ts +86 -0
  75. package/dist/core/context/context-prompt-enforcement.d.ts.map +1 -0
  76. package/dist/core/context/context-prompt-enforcement.js +168 -0
  77. package/dist/core/context/context-prompt-enforcement.js.map +1 -0
  78. package/dist/core/context/context-prompt-policy.d.ts +90 -0
  79. package/dist/core/context/context-prompt-policy.d.ts.map +1 -0
  80. package/dist/core/context/context-prompt-policy.js +73 -0
  81. package/dist/core/context/context-prompt-policy.js.map +1 -0
  82. package/dist/core/context/context-retention.d.ts +36 -0
  83. package/dist/core/context/context-retention.d.ts.map +1 -0
  84. package/dist/core/context/context-retention.js +108 -0
  85. package/dist/core/context/context-retention.js.map +1 -0
  86. package/dist/core/context/context-store.d.ts +37 -0
  87. package/dist/core/context/context-store.d.ts.map +1 -0
  88. package/dist/core/context/context-store.js +45 -0
  89. package/dist/core/context/context-store.js.map +1 -0
  90. package/dist/core/context/memory-diagnostics.d.ts +50 -0
  91. package/dist/core/context/memory-diagnostics.d.ts.map +1 -0
  92. package/dist/core/context/memory-diagnostics.js +43 -0
  93. package/dist/core/context/memory-diagnostics.js.map +1 -0
  94. package/dist/core/context/memory-index-store.d.ts +28 -0
  95. package/dist/core/context/memory-index-store.d.ts.map +1 -0
  96. package/dist/core/context/memory-index-store.js +38 -0
  97. package/dist/core/context/memory-index-store.js.map +1 -0
  98. package/dist/core/context/memory-prompt-block.d.ts +34 -0
  99. package/dist/core/context/memory-prompt-block.d.ts.map +1 -0
  100. package/dist/core/context/memory-prompt-block.js +58 -0
  101. package/dist/core/context/memory-prompt-block.js.map +1 -0
  102. package/dist/core/context/memory-provider-contract.d.ts +114 -0
  103. package/dist/core/context/memory-provider-contract.d.ts.map +1 -0
  104. package/dist/core/context/memory-provider-contract.js +121 -0
  105. package/dist/core/context/memory-provider-contract.js.map +1 -0
  106. package/dist/core/context/memory-retrieval.d.ts +27 -0
  107. package/dist/core/context/memory-retrieval.d.ts.map +1 -0
  108. package/dist/core/context/memory-retrieval.js +91 -0
  109. package/dist/core/context/memory-retrieval.js.map +1 -0
  110. package/dist/core/context/okf-memory-provider.d.ts +26 -0
  111. package/dist/core/context/okf-memory-provider.d.ts.map +1 -0
  112. package/dist/core/context/okf-memory-provider.js +154 -0
  113. package/dist/core/context/okf-memory-provider.js.map +1 -0
  114. package/dist/core/context/okf-memory.d.ts +42 -0
  115. package/dist/core/context/okf-memory.d.ts.map +1 -0
  116. package/dist/core/context/okf-memory.js +175 -0
  117. package/dist/core/context/okf-memory.js.map +1 -0
  118. package/dist/core/context/policy-engine.d.ts +66 -0
  119. package/dist/core/context/policy-engine.d.ts.map +1 -0
  120. package/dist/core/context/policy-engine.js +171 -0
  121. package/dist/core/context/policy-engine.js.map +1 -0
  122. package/dist/core/context/policy-types.d.ts +102 -0
  123. package/dist/core/context/policy-types.d.ts.map +1 -0
  124. package/dist/core/context/policy-types.js +7 -0
  125. package/dist/core/context/policy-types.js.map +1 -0
  126. package/dist/core/context/sqlite-runtime-index.d.ts +19 -0
  127. package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -0
  128. package/dist/core/context/sqlite-runtime-index.js +344 -0
  129. package/dist/core/context/sqlite-runtime-index.js.map +1 -0
  130. package/dist/core/context/storage-authority.d.ts +20 -0
  131. package/dist/core/context/storage-authority.d.ts.map +1 -0
  132. package/dist/core/context/storage-authority.js +51 -0
  133. package/dist/core/context/storage-authority.js.map +1 -0
  134. package/dist/core/context/tool-output-packer.d.ts +75 -0
  135. package/dist/core/context/tool-output-packer.d.ts.map +1 -0
  136. package/dist/core/context/tool-output-packer.js +77 -0
  137. package/dist/core/context/tool-output-packer.js.map +1 -0
  138. package/dist/core/context-gc.d.ts +13 -0
  139. package/dist/core/context-gc.d.ts.map +1 -1
  140. package/dist/core/context-gc.js +6 -0
  141. package/dist/core/context-gc.js.map +1 -1
  142. package/dist/core/cost/session-usage.d.ts +20 -0
  143. package/dist/core/cost/session-usage.d.ts.map +1 -0
  144. package/dist/core/cost/session-usage.js +164 -0
  145. package/dist/core/cost/session-usage.js.map +1 -0
  146. package/dist/core/delegation/session-worker-result.d.ts +10 -0
  147. package/dist/core/delegation/session-worker-result.d.ts.map +1 -0
  148. package/dist/core/delegation/session-worker-result.js +36 -0
  149. package/dist/core/delegation/session-worker-result.js.map +1 -0
  150. package/dist/core/delegation/worker-result.d.ts +9 -0
  151. package/dist/core/delegation/worker-result.d.ts.map +1 -0
  152. package/dist/core/delegation/worker-result.js +152 -0
  153. package/dist/core/delegation/worker-result.js.map +1 -0
  154. package/dist/core/delegation/worker-runner.d.ts +58 -0
  155. package/dist/core/delegation/worker-runner.d.ts.map +1 -0
  156. package/dist/core/delegation/worker-runner.js +188 -0
  157. package/dist/core/delegation/worker-runner.js.map +1 -0
  158. package/dist/core/extensions/builtin.d.ts +5 -1
  159. package/dist/core/extensions/builtin.d.ts.map +1 -1
  160. package/dist/core/extensions/builtin.js +23 -1
  161. package/dist/core/extensions/builtin.js.map +1 -1
  162. package/dist/core/footer-data-provider.d.ts +5 -1
  163. package/dist/core/footer-data-provider.d.ts.map +1 -1
  164. package/dist/core/footer-data-provider.js +13 -0
  165. package/dist/core/footer-data-provider.js.map +1 -1
  166. package/dist/core/goals/goal-continuation-controller.d.ts +22 -0
  167. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -0
  168. package/dist/core/goals/goal-continuation-controller.js +88 -0
  169. package/dist/core/goals/goal-continuation-controller.js.map +1 -0
  170. package/dist/core/goals/goal-continuation-defaults.d.ts +10 -0
  171. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -0
  172. package/dist/core/goals/goal-continuation-defaults.js +10 -0
  173. package/dist/core/goals/goal-continuation-defaults.js.map +1 -0
  174. package/dist/core/goals/goal-continuation-prompt.d.ts +18 -0
  175. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -0
  176. package/dist/core/goals/goal-continuation-prompt.js +141 -0
  177. package/dist/core/goals/goal-continuation-prompt.js.map +1 -0
  178. package/dist/core/goals/goal-runtime-snapshot.d.ts +19 -0
  179. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -0
  180. package/dist/core/goals/goal-runtime-snapshot.js +23 -0
  181. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -0
  182. package/dist/core/goals/goal-state.d.ts +87 -0
  183. package/dist/core/goals/goal-state.d.ts.map +1 -0
  184. package/dist/core/goals/goal-state.js +259 -0
  185. package/dist/core/goals/goal-state.js.map +1 -0
  186. package/dist/core/goals/goal-tool-core.d.ts +66 -0
  187. package/dist/core/goals/goal-tool-core.d.ts.map +1 -0
  188. package/dist/core/goals/goal-tool-core.js +146 -0
  189. package/dist/core/goals/goal-tool-core.js.map +1 -0
  190. package/dist/core/goals/session-goal-state.d.ts +10 -0
  191. package/dist/core/goals/session-goal-state.d.ts.map +1 -0
  192. package/dist/core/goals/session-goal-state.js +35 -0
  193. package/dist/core/goals/session-goal-state.js.map +1 -0
  194. package/dist/core/learning/learning-audit.d.ts +45 -0
  195. package/dist/core/learning/learning-audit.d.ts.map +1 -0
  196. package/dist/core/learning/learning-audit.js +139 -0
  197. package/dist/core/learning/learning-audit.js.map +1 -0
  198. package/dist/core/learning/learning-gate.d.ts +29 -0
  199. package/dist/core/learning/learning-gate.d.ts.map +1 -0
  200. package/dist/core/learning/learning-gate.js +150 -0
  201. package/dist/core/learning/learning-gate.js.map +1 -0
  202. package/dist/core/learning/session-learning-decision.d.ts +10 -0
  203. package/dist/core/learning/session-learning-decision.d.ts.map +1 -0
  204. package/dist/core/learning/session-learning-decision.js +36 -0
  205. package/dist/core/learning/session-learning-decision.js.map +1 -0
  206. package/dist/core/model-capability.d.ts +41 -0
  207. package/dist/core/model-capability.d.ts.map +1 -0
  208. package/dist/core/model-capability.js +101 -0
  209. package/dist/core/model-capability.js.map +1 -0
  210. package/dist/core/model-router/config-diagnostics.d.ts.map +1 -1
  211. package/dist/core/model-router/config-diagnostics.js +1 -0
  212. package/dist/core/model-router/config-diagnostics.js.map +1 -1
  213. package/dist/core/model-router/intent-classifier.d.ts +2 -0
  214. package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
  215. package/dist/core/model-router/intent-classifier.js +154 -9
  216. package/dist/core/model-router/intent-classifier.js.map +1 -1
  217. package/dist/core/model-router/route-judge.d.ts +54 -0
  218. package/dist/core/model-router/route-judge.d.ts.map +1 -0
  219. package/dist/core/model-router/route-judge.js +128 -0
  220. package/dist/core/model-router/route-judge.js.map +1 -0
  221. package/dist/core/model-router/status.d.ts +4 -1
  222. package/dist/core/model-router/status.d.ts.map +1 -1
  223. package/dist/core/model-router/status.js +30 -6
  224. package/dist/core/model-router/status.js.map +1 -1
  225. package/dist/core/model-router/tool-escalation.d.ts +4 -6
  226. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  227. package/dist/core/model-router/tool-escalation.js +1 -1
  228. package/dist/core/model-router/tool-escalation.js.map +1 -1
  229. package/dist/core/models/fitness-store.d.ts +40 -0
  230. package/dist/core/models/fitness-store.d.ts.map +1 -0
  231. package/dist/core/models/fitness-store.js +61 -0
  232. package/dist/core/models/fitness-store.js.map +1 -0
  233. package/dist/core/profile-registry.d.ts.map +1 -1
  234. package/dist/core/profile-registry.js +1 -1
  235. package/dist/core/profile-registry.js.map +1 -1
  236. package/dist/core/prompt-templates.d.ts +2 -0
  237. package/dist/core/prompt-templates.d.ts.map +1 -1
  238. package/dist/core/prompt-templates.js +12 -4
  239. package/dist/core/prompt-templates.js.map +1 -1
  240. package/dist/core/research/automata-provider.d.ts +5 -0
  241. package/dist/core/research/automata-provider.d.ts.map +1 -0
  242. package/dist/core/research/automata-provider.js +15 -0
  243. package/dist/core/research/automata-provider.js.map +1 -0
  244. package/dist/core/research/evidence-bundle.d.ts +10 -0
  245. package/dist/core/research/evidence-bundle.d.ts.map +1 -0
  246. package/dist/core/research/evidence-bundle.js +116 -0
  247. package/dist/core/research/evidence-bundle.js.map +1 -0
  248. package/dist/core/research/model-fitness.d.ts +82 -0
  249. package/dist/core/research/model-fitness.d.ts.map +1 -0
  250. package/dist/core/research/model-fitness.js +308 -0
  251. package/dist/core/research/model-fitness.js.map +1 -0
  252. package/dist/core/research/research-gate.d.ts +11 -0
  253. package/dist/core/research/research-gate.d.ts.map +1 -0
  254. package/dist/core/research/research-gate.js +82 -0
  255. package/dist/core/research/research-gate.js.map +1 -0
  256. package/dist/core/research/research-runner.d.ts +59 -0
  257. package/dist/core/research/research-runner.d.ts.map +1 -0
  258. package/dist/core/research/research-runner.js +155 -0
  259. package/dist/core/research/research-runner.js.map +1 -0
  260. package/dist/core/research/session-evidence-bundle.d.ts +11 -0
  261. package/dist/core/research/session-evidence-bundle.d.ts.map +1 -0
  262. package/dist/core/research/session-evidence-bundle.js +55 -0
  263. package/dist/core/research/session-evidence-bundle.js.map +1 -0
  264. package/dist/core/resource-loader.d.ts.map +1 -1
  265. package/dist/core/resource-loader.js +4 -0
  266. package/dist/core/resource-loader.js.map +1 -1
  267. package/dist/core/settings-manager.d.ts +160 -4
  268. package/dist/core/settings-manager.d.ts.map +1 -1
  269. package/dist/core/settings-manager.js +304 -9
  270. package/dist/core/settings-manager.js.map +1 -1
  271. package/dist/core/skills.d.ts +4 -0
  272. package/dist/core/skills.d.ts.map +1 -1
  273. package/dist/core/skills.js +18 -6
  274. package/dist/core/skills.js.map +1 -1
  275. package/dist/core/slash-commands.d.ts.map +1 -1
  276. package/dist/core/slash-commands.js +10 -1
  277. package/dist/core/slash-commands.js.map +1 -1
  278. package/dist/core/toolkit/script-registry.d.ts +34 -0
  279. package/dist/core/toolkit/script-registry.d.ts.map +1 -0
  280. package/dist/core/toolkit/script-registry.js +71 -0
  281. package/dist/core/toolkit/script-registry.js.map +1 -0
  282. package/dist/core/toolkit/script-runner.d.ts +28 -0
  283. package/dist/core/toolkit/script-runner.d.ts.map +1 -0
  284. package/dist/core/toolkit/script-runner.js +48 -0
  285. package/dist/core/toolkit/script-runner.js.map +1 -0
  286. package/dist/core/tools/artifact-retrieve.d.ts +23 -0
  287. package/dist/core/tools/artifact-retrieve.d.ts.map +1 -0
  288. package/dist/core/tools/artifact-retrieve.js +110 -0
  289. package/dist/core/tools/artifact-retrieve.js.map +1 -0
  290. package/dist/core/tools/delegate.d.ts +32 -0
  291. package/dist/core/tools/delegate.d.ts.map +1 -0
  292. package/dist/core/tools/delegate.js +60 -0
  293. package/dist/core/tools/delegate.js.map +1 -0
  294. package/dist/core/tools/fff-search-backend.d.ts +103 -0
  295. package/dist/core/tools/fff-search-backend.d.ts.map +1 -0
  296. package/dist/core/tools/fff-search-backend.js +151 -0
  297. package/dist/core/tools/fff-search-backend.js.map +1 -0
  298. package/dist/core/tools/find.d.ts +21 -1
  299. package/dist/core/tools/find.d.ts.map +1 -1
  300. package/dist/core/tools/find.js +183 -10
  301. package/dist/core/tools/find.js.map +1 -1
  302. package/dist/core/tools/goal.d.ts +35 -0
  303. package/dist/core/tools/goal.d.ts.map +1 -0
  304. package/dist/core/tools/goal.js +122 -0
  305. package/dist/core/tools/goal.js.map +1 -0
  306. package/dist/core/tools/grep.d.ts +21 -1
  307. package/dist/core/tools/grep.d.ts.map +1 -1
  308. package/dist/core/tools/grep.js +272 -27
  309. package/dist/core/tools/grep.js.map +1 -1
  310. package/dist/core/tools/index.d.ts +4 -1
  311. package/dist/core/tools/index.d.ts.map +1 -1
  312. package/dist/core/tools/index.js +9 -0
  313. package/dist/core/tools/index.js.map +1 -1
  314. package/dist/core/tools/model-fitness.d.ts +30 -0
  315. package/dist/core/tools/model-fitness.d.ts.map +1 -0
  316. package/dist/core/tools/model-fitness.js +38 -0
  317. package/dist/core/tools/model-fitness.js.map +1 -0
  318. package/dist/core/tools/run-toolkit-script.d.ts +24 -0
  319. package/dist/core/tools/run-toolkit-script.d.ts.map +1 -0
  320. package/dist/core/tools/run-toolkit-script.js +103 -0
  321. package/dist/core/tools/run-toolkit-script.js.map +1 -0
  322. package/dist/core/tools/search-router.d.ts +75 -0
  323. package/dist/core/tools/search-router.d.ts.map +1 -0
  324. package/dist/core/tools/search-router.js +85 -0
  325. package/dist/core/tools/search-router.js.map +1 -0
  326. package/dist/modes/interactive/components/fitness-role-selector.d.ts +13 -0
  327. package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -0
  328. package/dist/modes/interactive/components/fitness-role-selector.js +65 -0
  329. package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -0
  330. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  331. package/dist/modes/interactive/components/footer.js +18 -16
  332. package/dist/modes/interactive/components/footer.js.map +1 -1
  333. package/dist/modes/interactive/components/settings-selector.d.ts +16 -1
  334. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  335. package/dist/modes/interactive/components/settings-selector.js +555 -11
  336. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  337. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  338. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  339. package/dist/modes/interactive/interactive-mode.js +308 -39
  340. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  341. package/dist/utils/tools-manager.d.ts +2 -0
  342. package/dist/utils/tools-manager.d.ts.map +1 -1
  343. package/dist/utils/tools-manager.js +154 -2
  344. package/dist/utils/tools-manager.js.map +1 -1
  345. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  346. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  347. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  348. package/examples/extensions/sandbox/package-lock.json +2 -2
  349. package/examples/extensions/sandbox/package.json +1 -1
  350. package/examples/extensions/with-deps/package-lock.json +2 -2
  351. package/examples/extensions/with-deps/package.json +1 -1
  352. package/npm-shrinkwrap.json +368 -12
  353. package/package.json +5 -4
@@ -0,0 +1,86 @@
1
+ /**
2
+ * First enforcement pilot for the context-policy layer (opt-in, default disabled). Unlike
3
+ * context-audit.ts/context-prompt-policy.ts (both strictly observe-only), this module CAN
4
+ * change the provider-visible message array -- but only ever via stub-in-place on
5
+ * artifact-backed tool_output results, never by removing a message or breaking
6
+ * assistant/toolResult pairing. It never touches the transcript, never releases/reclaims
7
+ * artifact references, and never writes a new artifact -- it only replaces the visible
8
+ * text of an already artifact-backed message with a bounded pointer to the existing
9
+ * artifact, retrievable via the `artifact_retrieve` tool.
10
+ *
11
+ * Eligibility for stubbing is deliberately conservative (see `enforcePromptPolicy`): the
12
+ * setting must be enabled, the item must be outside the recent-message safety window, not
13
+ * an errored tool result, not already stubbed by this module or already packed by legacy
14
+ * context-gc this turn, must have a resolvable artifact id, the `artifact_retrieve` tool
15
+ * must actually be active this turn, and must clear `hardConstraints.dropFromPrompt` (see
16
+ * below for why that specific action, not `pack_to_artifact`).
17
+ *
18
+ * Why `dropFromPrompt`, not `packToArtifact`: this operation does not create a new
19
+ * artifact -- it reuses the ref an earlier `pack_to_artifact` capture already produced (see
20
+ * tool-output-artifacts.md's "measure -> digest/preview/artifact -> prompt item" pipeline).
21
+ * `drop_from_prompt` requires an existing retrieval path and is exactly the operation being
22
+ * performed (evicting raw content from the live prompt in favor of that existing path);
23
+ * `pack_to_artifact` is the distinct first-capture operation, which we never invoke here.
24
+ *
25
+ * Why `retrievalToolAvailable` is checked separately from `hasAvailableRetrievalPath`: the
26
+ * latter only proves the artifact still exists in the store; it says nothing about whether
27
+ * the model can currently act on the stub's instruction to call `artifact_retrieve`.
28
+ * `artifact_retrieve` is a companion affordance (auto-activated alongside grep/find, not a
29
+ * default/global tool -- see agent-session.ts's companion-activation enforcement), so active
30
+ * tools can differ turn to turn. Stubbing content with an unactionable pointer would be
31
+ * strictly worse than leaving the raw content in place.
32
+ */
33
+ import type { AgentMessage } from "@caupulican/pi-agent-core";
34
+ import type { PromptPolicyShadowReport } from "./context-prompt-policy.ts";
35
+ export interface ContextPromptEnforcementSettings {
36
+ enabled: boolean;
37
+ preserveRecentMessages: number;
38
+ minChars: number;
39
+ /**
40
+ * Whether the `artifact_retrieve` tool is actually active this turn -- a runtime fact,
41
+ * not a persisted setting. Callers must derive this from the live active-tool set (e.g.
42
+ * `AgentSession.getActiveToolNames().includes("artifact_retrieve")`), never assume it.
43
+ */
44
+ retrievalToolAvailable: boolean;
45
+ /**
46
+ * Brain-curator relevance lookup (runtime fact, like `retrievalToolAvailable`; never
47
+ * persisted). ASYMMETRIC by design: an explicit high-confidence irrelevance verdict may
48
+ * evict an otherwise-eligible item from within the recent window (never past the absolute
49
+ * floor), but an advisory can never keep an item the policy wants gone, never stub a
50
+ * hard-constraint-protected item, and its absence is byte-for-byte today's behavior.
51
+ */
52
+ brainRelevance?: (itemId: string) => {
53
+ relevant: boolean;
54
+ confidence: number;
55
+ } | undefined;
56
+ }
57
+ export type PromptEnforcementSkipReason = "message_mismatch" | "within_recent_window" | "errored_tool_result" | "already_stubbed_or_packed" | "not_artifact_backed" | "retrieval_tool_unavailable" | "hard_constraint_rejected" | "missing_artifact_id" | "below_min_chars";
58
+ export interface PromptEnforcementItemReport {
59
+ itemId: string;
60
+ toolCallId: string;
61
+ messageIndex: number;
62
+ enforced: boolean;
63
+ action?: "artifact_stub";
64
+ artifactId?: string;
65
+ originalChars?: number;
66
+ skipReason?: PromptEnforcementSkipReason;
67
+ /** Set when a brain-curator irrelevance verdict allowed eviction inside the recent window. */
68
+ advisory?: "brain_irrelevant";
69
+ }
70
+ export interface PromptEnforcementReport {
71
+ turnIndex: number;
72
+ items: PromptEnforcementItemReport[];
73
+ }
74
+ export interface EnforcePromptPolicyResult {
75
+ messages: AgentMessage[];
76
+ report: PromptEnforcementReport;
77
+ }
78
+ /**
79
+ * Apply the first enforcement pilot to `messages` (expected to be the provider-visible
80
+ * array after existing context-gc has already run). Returns a new array only when at least
81
+ * one item was actually stubbed; otherwise returns the same `messages` reference unchanged
82
+ * (in particular, always true when `settings.enabled` is false). Never mutates `messages`
83
+ * or any message object within it -- every stubbed entry is a fresh object.
84
+ */
85
+ export declare function enforcePromptPolicy(messages: AgentMessage[], shadowReport: PromptPolicyShadowReport, settings: ContextPromptEnforcementSettings): EnforcePromptPolicyResult;
86
+ //# sourceMappingURL=context-prompt-enforcement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-prompt-enforcement.d.ts","sourceRoot":"","sources":["../../../src/core/context/context-prompt-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAE3E,MAAM,WAAW,gCAAgC;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,sBAAsB,EAAE,OAAO,CAAC;IAChC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC3F;AAED,MAAM,MAAM,2BAA2B,GACpC,kBAAkB,GAClB,sBAAsB,GACtB,qBAAqB,GACrB,2BAA2B,GAC3B,qBAAqB,GACrB,4BAA4B,GAC5B,0BAA0B,GAC1B,qBAAqB,GACrB,iBAAiB,CAAC;AAErB,MAAM,WAAW,2BAA2B;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,2BAA2B,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACzC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,uBAAuB,CAAC;CAChC;AA4CD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,YAAY,EAAE,EACxB,YAAY,EAAE,wBAAwB,EACtC,QAAQ,EAAE,gCAAgC,GACxC,yBAAyB,CA+F3B","sourcesContent":["/**\n * First enforcement pilot for the context-policy layer (opt-in, default disabled). Unlike\n * context-audit.ts/context-prompt-policy.ts (both strictly observe-only), this module CAN\n * change the provider-visible message array -- but only ever via stub-in-place on\n * artifact-backed tool_output results, never by removing a message or breaking\n * assistant/toolResult pairing. It never touches the transcript, never releases/reclaims\n * artifact references, and never writes a new artifact -- it only replaces the visible\n * text of an already artifact-backed message with a bounded pointer to the existing\n * artifact, retrievable via the `artifact_retrieve` tool.\n *\n * Eligibility for stubbing is deliberately conservative (see `enforcePromptPolicy`): the\n * setting must be enabled, the item must be outside the recent-message safety window, not\n * an errored tool result, not already stubbed by this module or already packed by legacy\n * context-gc this turn, must have a resolvable artifact id, the `artifact_retrieve` tool\n * must actually be active this turn, and must clear `hardConstraints.dropFromPrompt` (see\n * below for why that specific action, not `pack_to_artifact`).\n *\n * Why `dropFromPrompt`, not `packToArtifact`: this operation does not create a new\n * artifact -- it reuses the ref an earlier `pack_to_artifact` capture already produced (see\n * tool-output-artifacts.md's \"measure -> digest/preview/artifact -> prompt item\" pipeline).\n * `drop_from_prompt` requires an existing retrieval path and is exactly the operation being\n * performed (evicting raw content from the live prompt in favor of that existing path);\n * `pack_to_artifact` is the distinct first-capture operation, which we never invoke here.\n *\n * Why `retrievalToolAvailable` is checked separately from `hasAvailableRetrievalPath`: the\n * latter only proves the artifact still exists in the store; it says nothing about whether\n * the model can currently act on the stub's instruction to call `artifact_retrieve`.\n * `artifact_retrieve` is a companion affordance (auto-activated alongside grep/find, not a\n * default/global tool -- see agent-session.ts's companion-activation enforcement), so active\n * tools can differ turn to turn. Stubbing content with an unactionable pointer would be\n * strictly worse than leaving the raw content in place.\n */\n\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport type { ToolResultMessage } from \"@caupulican/pi-ai\";\nimport { CURATION_RELEVANCE_MIN_CONFIDENCE } from \"./brain-curator.ts\";\nimport type { PromptPolicyShadowReport } from \"./context-prompt-policy.ts\";\n\nexport interface ContextPromptEnforcementSettings {\n\tenabled: boolean;\n\tpreserveRecentMessages: number;\n\tminChars: number;\n\t/**\n\t * Whether the `artifact_retrieve` tool is actually active this turn -- a runtime fact,\n\t * not a persisted setting. Callers must derive this from the live active-tool set (e.g.\n\t * `AgentSession.getActiveToolNames().includes(\"artifact_retrieve\")`), never assume it.\n\t */\n\tretrievalToolAvailable: boolean;\n\t/**\n\t * Brain-curator relevance lookup (runtime fact, like `retrievalToolAvailable`; never\n\t * persisted). ASYMMETRIC by design: an explicit high-confidence irrelevance verdict may\n\t * evict an otherwise-eligible item from within the recent window (never past the absolute\n\t * floor), but an advisory can never keep an item the policy wants gone, never stub a\n\t * hard-constraint-protected item, and its absence is byte-for-byte today's behavior.\n\t */\n\tbrainRelevance?: (itemId: string) => { relevant: boolean; confidence: number } | undefined;\n}\n\nexport type PromptEnforcementSkipReason =\n\t| \"message_mismatch\"\n\t| \"within_recent_window\"\n\t| \"errored_tool_result\"\n\t| \"already_stubbed_or_packed\"\n\t| \"not_artifact_backed\"\n\t| \"retrieval_tool_unavailable\"\n\t| \"hard_constraint_rejected\"\n\t| \"missing_artifact_id\"\n\t| \"below_min_chars\";\n\nexport interface PromptEnforcementItemReport {\n\titemId: string;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\tenforced: boolean;\n\taction?: \"artifact_stub\";\n\tartifactId?: string;\n\toriginalChars?: number;\n\tskipReason?: PromptEnforcementSkipReason;\n\t/** Set when a brain-curator irrelevance verdict allowed eviction inside the recent window. */\n\tadvisory?: \"brain_irrelevant\";\n}\n\nexport interface PromptEnforcementReport {\n\tturnIndex: number;\n\titems: PromptEnforcementItemReport[];\n}\n\nexport interface EnforcePromptPolicyResult {\n\tmessages: AgentMessage[];\n\treport: PromptEnforcementReport;\n}\n\nconst ENFORCEMENT_ABSOLUTE_RECENT_FLOOR = 4;\n\nfunction extractDetailsArtifactId(details: unknown): string | undefined {\n\tif (typeof details !== \"object\" || details === null) return undefined;\n\tconst artifactId = (details as { artifactId?: unknown }).artifactId;\n\treturn typeof artifactId === \"string\" ? artifactId : undefined;\n}\n\n/** True if legacy context-gc already packed this message this turn, or this module already stubbed it. */\nfunction isAlreadyStubbedOrPacked(details: unknown): boolean {\n\tif (typeof details !== \"object\" || details === null) return false;\n\tconst record = details as { promptPolicy?: { enforced?: unknown }; contextGc?: { packed?: unknown } };\n\treturn record.promptPolicy?.enforced === true || record.contextGc?.packed === true;\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") parts.push(part.text);\n\t}\n\treturn parts.join(\"\\n\");\n}\n\nfunction buildStubText(toolName: string, originalChars: number, artifactId: string): string {\n\treturn `[content replaced by prompt-policy: originally ${originalChars} chars from a stale ${toolName} tool result. Retrieve the full output with artifact_retrieve using artifactId \"${artifactId}\".]`;\n}\n\nfunction skip(\n\titem: { itemId: string; toolCallId: string; messageIndex: number },\n\tskipReason: PromptEnforcementSkipReason,\n\textra?: { artifactId?: string; originalChars?: number },\n): PromptEnforcementItemReport {\n\treturn {\n\t\titemId: item.itemId,\n\t\ttoolCallId: item.toolCallId,\n\t\tmessageIndex: item.messageIndex,\n\t\tenforced: false,\n\t\tskipReason,\n\t\t...extra,\n\t};\n}\n\n/**\n * Apply the first enforcement pilot to `messages` (expected to be the provider-visible\n * array after existing context-gc has already run). Returns a new array only when at least\n * one item was actually stubbed; otherwise returns the same `messages` reference unchanged\n * (in particular, always true when `settings.enabled` is false). Never mutates `messages`\n * or any message object within it -- every stubbed entry is a fresh object.\n */\nexport function enforcePromptPolicy(\n\tmessages: AgentMessage[],\n\tshadowReport: PromptPolicyShadowReport,\n\tsettings: ContextPromptEnforcementSettings,\n): EnforcePromptPolicyResult {\n\tif (!settings.enabled) {\n\t\treturn { messages, report: { turnIndex: shadowReport.turnIndex, items: [] } };\n\t}\n\n\tconst recentCutoffIndex = Math.max(0, messages.length - settings.preserveRecentMessages);\n\t// Advisory evictions may reach inside the recent window but NEVER past this absolute floor:\n\t// the last few messages are what the model is actively reasoning over.\n\tconst absoluteFloorIndex = Math.max(0, messages.length - ENFORCEMENT_ABSOLUTE_RECENT_FLOOR);\n\tconst nextMessages = messages.slice();\n\tlet changed = false;\n\tconst items: PromptEnforcementItemReport[] = [];\n\n\tfor (const planItem of shadowReport.items) {\n\t\tconst message = messages[planItem.messageIndex];\n\t\tif (!message || message.role !== \"toolResult\" || message.toolCallId !== planItem.toolCallId) {\n\t\t\titems.push(skip(planItem, \"message_mismatch\"));\n\t\t\tcontinue;\n\t\t}\n\t\tlet advisoryEviction = false;\n\t\tif (planItem.messageIndex >= recentCutoffIndex) {\n\t\t\tconst advisory = settings.brainRelevance?.(planItem.itemId);\n\t\t\tadvisoryEviction =\n\t\t\t\tadvisory !== undefined &&\n\t\t\t\t!advisory.relevant &&\n\t\t\t\tadvisory.confidence >= CURATION_RELEVANCE_MIN_CONFIDENCE &&\n\t\t\t\tplanItem.messageIndex < absoluteFloorIndex;\n\t\t\tif (!advisoryEviction) {\n\t\t\t\titems.push(skip(planItem, \"within_recent_window\"));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (message.isError) {\n\t\t\titems.push(skip(planItem, \"errored_tool_result\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (isAlreadyStubbedOrPacked(message.details)) {\n\t\t\titems.push(skip(planItem, \"already_stubbed_or_packed\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (!planItem.hasAvailableRetrievalPath) {\n\t\t\titems.push(skip(planItem, \"not_artifact_backed\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (!settings.retrievalToolAvailable) {\n\t\t\titems.push(skip(planItem, \"retrieval_tool_unavailable\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (planItem.hardConstraints.dropFromPrompt.length > 0) {\n\t\t\titems.push(skip(planItem, \"hard_constraint_rejected\"));\n\t\t\tcontinue;\n\t\t}\n\t\tconst artifactId = extractDetailsArtifactId(message.details);\n\t\tif (!artifactId) {\n\t\t\titems.push(skip(planItem, \"missing_artifact_id\"));\n\t\t\tcontinue;\n\t\t}\n\t\tconst originalChars = toolResultText(message).length;\n\t\tif (originalChars < settings.minChars) {\n\t\t\titems.push(skip(planItem, \"below_min_chars\", { artifactId, originalChars }));\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst existingDetails = typeof message.details === \"object\" && message.details !== null ? message.details : {};\n\t\tnextMessages[planItem.messageIndex] = {\n\t\t\t...message,\n\t\t\tcontent: [{ type: \"text\", text: buildStubText(message.toolName, originalChars, artifactId) }],\n\t\t\tdetails: {\n\t\t\t\t...existingDetails,\n\t\t\t\tpromptPolicy: {\n\t\t\t\t\tenforced: true,\n\t\t\t\t\taction: \"artifact_stub\",\n\t\t\t\t\tartifactId,\n\t\t\t\t\toriginalChars,\n\t\t\t\t\treason: \"stale_artifact_backed_tool_output\",\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tchanged = true;\n\t\titems.push({\n\t\t\titemId: planItem.itemId,\n\t\t\ttoolCallId: planItem.toolCallId,\n\t\t\tmessageIndex: planItem.messageIndex,\n\t\t\tenforced: true,\n\t\t\taction: \"artifact_stub\",\n\t\t\tartifactId,\n\t\t\toriginalChars,\n\t\t\t...(advisoryEviction ? { advisory: \"brain_irrelevant\" as const } : {}),\n\t\t});\n\t}\n\n\treturn {\n\t\tmessages: changed ? nextMessages : messages,\n\t\treport: { turnIndex: shadowReport.turnIndex, items },\n\t};\n}\n"]}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * First enforcement pilot for the context-policy layer (opt-in, default disabled). Unlike
3
+ * context-audit.ts/context-prompt-policy.ts (both strictly observe-only), this module CAN
4
+ * change the provider-visible message array -- but only ever via stub-in-place on
5
+ * artifact-backed tool_output results, never by removing a message or breaking
6
+ * assistant/toolResult pairing. It never touches the transcript, never releases/reclaims
7
+ * artifact references, and never writes a new artifact -- it only replaces the visible
8
+ * text of an already artifact-backed message with a bounded pointer to the existing
9
+ * artifact, retrievable via the `artifact_retrieve` tool.
10
+ *
11
+ * Eligibility for stubbing is deliberately conservative (see `enforcePromptPolicy`): the
12
+ * setting must be enabled, the item must be outside the recent-message safety window, not
13
+ * an errored tool result, not already stubbed by this module or already packed by legacy
14
+ * context-gc this turn, must have a resolvable artifact id, the `artifact_retrieve` tool
15
+ * must actually be active this turn, and must clear `hardConstraints.dropFromPrompt` (see
16
+ * below for why that specific action, not `pack_to_artifact`).
17
+ *
18
+ * Why `dropFromPrompt`, not `packToArtifact`: this operation does not create a new
19
+ * artifact -- it reuses the ref an earlier `pack_to_artifact` capture already produced (see
20
+ * tool-output-artifacts.md's "measure -> digest/preview/artifact -> prompt item" pipeline).
21
+ * `drop_from_prompt` requires an existing retrieval path and is exactly the operation being
22
+ * performed (evicting raw content from the live prompt in favor of that existing path);
23
+ * `pack_to_artifact` is the distinct first-capture operation, which we never invoke here.
24
+ *
25
+ * Why `retrievalToolAvailable` is checked separately from `hasAvailableRetrievalPath`: the
26
+ * latter only proves the artifact still exists in the store; it says nothing about whether
27
+ * the model can currently act on the stub's instruction to call `artifact_retrieve`.
28
+ * `artifact_retrieve` is a companion affordance (auto-activated alongside grep/find, not a
29
+ * default/global tool -- see agent-session.ts's companion-activation enforcement), so active
30
+ * tools can differ turn to turn. Stubbing content with an unactionable pointer would be
31
+ * strictly worse than leaving the raw content in place.
32
+ */
33
+ import { CURATION_RELEVANCE_MIN_CONFIDENCE } from "./brain-curator.js";
34
+ const ENFORCEMENT_ABSOLUTE_RECENT_FLOOR = 4;
35
+ function extractDetailsArtifactId(details) {
36
+ if (typeof details !== "object" || details === null)
37
+ return undefined;
38
+ const artifactId = details.artifactId;
39
+ return typeof artifactId === "string" ? artifactId : undefined;
40
+ }
41
+ /** True if legacy context-gc already packed this message this turn, or this module already stubbed it. */
42
+ function isAlreadyStubbedOrPacked(details) {
43
+ if (typeof details !== "object" || details === null)
44
+ return false;
45
+ const record = details;
46
+ return record.promptPolicy?.enforced === true || record.contextGc?.packed === true;
47
+ }
48
+ function toolResultText(message) {
49
+ const parts = [];
50
+ for (const part of message.content) {
51
+ if (part.type === "text")
52
+ parts.push(part.text);
53
+ }
54
+ return parts.join("\n");
55
+ }
56
+ function buildStubText(toolName, originalChars, artifactId) {
57
+ return `[content replaced by prompt-policy: originally ${originalChars} chars from a stale ${toolName} tool result. Retrieve the full output with artifact_retrieve using artifactId "${artifactId}".]`;
58
+ }
59
+ function skip(item, skipReason, extra) {
60
+ return {
61
+ itemId: item.itemId,
62
+ toolCallId: item.toolCallId,
63
+ messageIndex: item.messageIndex,
64
+ enforced: false,
65
+ skipReason,
66
+ ...extra,
67
+ };
68
+ }
69
+ /**
70
+ * Apply the first enforcement pilot to `messages` (expected to be the provider-visible
71
+ * array after existing context-gc has already run). Returns a new array only when at least
72
+ * one item was actually stubbed; otherwise returns the same `messages` reference unchanged
73
+ * (in particular, always true when `settings.enabled` is false). Never mutates `messages`
74
+ * or any message object within it -- every stubbed entry is a fresh object.
75
+ */
76
+ export function enforcePromptPolicy(messages, shadowReport, settings) {
77
+ if (!settings.enabled) {
78
+ return { messages, report: { turnIndex: shadowReport.turnIndex, items: [] } };
79
+ }
80
+ const recentCutoffIndex = Math.max(0, messages.length - settings.preserveRecentMessages);
81
+ // Advisory evictions may reach inside the recent window but NEVER past this absolute floor:
82
+ // the last few messages are what the model is actively reasoning over.
83
+ const absoluteFloorIndex = Math.max(0, messages.length - ENFORCEMENT_ABSOLUTE_RECENT_FLOOR);
84
+ const nextMessages = messages.slice();
85
+ let changed = false;
86
+ const items = [];
87
+ for (const planItem of shadowReport.items) {
88
+ const message = messages[planItem.messageIndex];
89
+ if (!message || message.role !== "toolResult" || message.toolCallId !== planItem.toolCallId) {
90
+ items.push(skip(planItem, "message_mismatch"));
91
+ continue;
92
+ }
93
+ let advisoryEviction = false;
94
+ if (planItem.messageIndex >= recentCutoffIndex) {
95
+ const advisory = settings.brainRelevance?.(planItem.itemId);
96
+ advisoryEviction =
97
+ advisory !== undefined &&
98
+ !advisory.relevant &&
99
+ advisory.confidence >= CURATION_RELEVANCE_MIN_CONFIDENCE &&
100
+ planItem.messageIndex < absoluteFloorIndex;
101
+ if (!advisoryEviction) {
102
+ items.push(skip(planItem, "within_recent_window"));
103
+ continue;
104
+ }
105
+ }
106
+ if (message.isError) {
107
+ items.push(skip(planItem, "errored_tool_result"));
108
+ continue;
109
+ }
110
+ if (isAlreadyStubbedOrPacked(message.details)) {
111
+ items.push(skip(planItem, "already_stubbed_or_packed"));
112
+ continue;
113
+ }
114
+ if (!planItem.hasAvailableRetrievalPath) {
115
+ items.push(skip(planItem, "not_artifact_backed"));
116
+ continue;
117
+ }
118
+ if (!settings.retrievalToolAvailable) {
119
+ items.push(skip(planItem, "retrieval_tool_unavailable"));
120
+ continue;
121
+ }
122
+ if (planItem.hardConstraints.dropFromPrompt.length > 0) {
123
+ items.push(skip(planItem, "hard_constraint_rejected"));
124
+ continue;
125
+ }
126
+ const artifactId = extractDetailsArtifactId(message.details);
127
+ if (!artifactId) {
128
+ items.push(skip(planItem, "missing_artifact_id"));
129
+ continue;
130
+ }
131
+ const originalChars = toolResultText(message).length;
132
+ if (originalChars < settings.minChars) {
133
+ items.push(skip(planItem, "below_min_chars", { artifactId, originalChars }));
134
+ continue;
135
+ }
136
+ const existingDetails = typeof message.details === "object" && message.details !== null ? message.details : {};
137
+ nextMessages[planItem.messageIndex] = {
138
+ ...message,
139
+ content: [{ type: "text", text: buildStubText(message.toolName, originalChars, artifactId) }],
140
+ details: {
141
+ ...existingDetails,
142
+ promptPolicy: {
143
+ enforced: true,
144
+ action: "artifact_stub",
145
+ artifactId,
146
+ originalChars,
147
+ reason: "stale_artifact_backed_tool_output",
148
+ },
149
+ },
150
+ };
151
+ changed = true;
152
+ items.push({
153
+ itemId: planItem.itemId,
154
+ toolCallId: planItem.toolCallId,
155
+ messageIndex: planItem.messageIndex,
156
+ enforced: true,
157
+ action: "artifact_stub",
158
+ artifactId,
159
+ originalChars,
160
+ ...(advisoryEviction ? { advisory: "brain_irrelevant" } : {}),
161
+ });
162
+ }
163
+ return {
164
+ messages: changed ? nextMessages : messages,
165
+ report: { turnIndex: shadowReport.turnIndex, items },
166
+ };
167
+ }
168
+ //# sourceMappingURL=context-prompt-enforcement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-prompt-enforcement.js","sourceRoot":"","sources":["../../../src/core/context/context-prompt-enforcement.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * First enforcement pilot for the context-policy layer (opt-in, default disabled). Unlike\n * context-audit.ts/context-prompt-policy.ts (both strictly observe-only), this module CAN\n * change the provider-visible message array -- but only ever via stub-in-place on\n * artifact-backed tool_output results, never by removing a message or breaking\n * assistant/toolResult pairing. It never touches the transcript, never releases/reclaims\n * artifact references, and never writes a new artifact -- it only replaces the visible\n * text of an already artifact-backed message with a bounded pointer to the existing\n * artifact, retrievable via the `artifact_retrieve` tool.\n *\n * Eligibility for stubbing is deliberately conservative (see `enforcePromptPolicy`): the\n * setting must be enabled, the item must be outside the recent-message safety window, not\n * an errored tool result, not already stubbed by this module or already packed by legacy\n * context-gc this turn, must have a resolvable artifact id, the `artifact_retrieve` tool\n * must actually be active this turn, and must clear `hardConstraints.dropFromPrompt` (see\n * below for why that specific action, not `pack_to_artifact`).\n *\n * Why `dropFromPrompt`, not `packToArtifact`: this operation does not create a new\n * artifact -- it reuses the ref an earlier `pack_to_artifact` capture already produced (see\n * tool-output-artifacts.md's \"measure -> digest/preview/artifact -> prompt item\" pipeline).\n * `drop_from_prompt` requires an existing retrieval path and is exactly the operation being\n * performed (evicting raw content from the live prompt in favor of that existing path);\n * `pack_to_artifact` is the distinct first-capture operation, which we never invoke here.\n *\n * Why `retrievalToolAvailable` is checked separately from `hasAvailableRetrievalPath`: the\n * latter only proves the artifact still exists in the store; it says nothing about whether\n * the model can currently act on the stub's instruction to call `artifact_retrieve`.\n * `artifact_retrieve` is a companion affordance (auto-activated alongside grep/find, not a\n * default/global tool -- see agent-session.ts's companion-activation enforcement), so active\n * tools can differ turn to turn. Stubbing content with an unactionable pointer would be\n * strictly worse than leaving the raw content in place.\n */\n\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport type { ToolResultMessage } from \"@caupulican/pi-ai\";\nimport { CURATION_RELEVANCE_MIN_CONFIDENCE } from \"./brain-curator.ts\";\nimport type { PromptPolicyShadowReport } from \"./context-prompt-policy.ts\";\n\nexport interface ContextPromptEnforcementSettings {\n\tenabled: boolean;\n\tpreserveRecentMessages: number;\n\tminChars: number;\n\t/**\n\t * Whether the `artifact_retrieve` tool is actually active this turn -- a runtime fact,\n\t * not a persisted setting. Callers must derive this from the live active-tool set (e.g.\n\t * `AgentSession.getActiveToolNames().includes(\"artifact_retrieve\")`), never assume it.\n\t */\n\tretrievalToolAvailable: boolean;\n\t/**\n\t * Brain-curator relevance lookup (runtime fact, like `retrievalToolAvailable`; never\n\t * persisted). ASYMMETRIC by design: an explicit high-confidence irrelevance verdict may\n\t * evict an otherwise-eligible item from within the recent window (never past the absolute\n\t * floor), but an advisory can never keep an item the policy wants gone, never stub a\n\t * hard-constraint-protected item, and its absence is byte-for-byte today's behavior.\n\t */\n\tbrainRelevance?: (itemId: string) => { relevant: boolean; confidence: number } | undefined;\n}\n\nexport type PromptEnforcementSkipReason =\n\t| \"message_mismatch\"\n\t| \"within_recent_window\"\n\t| \"errored_tool_result\"\n\t| \"already_stubbed_or_packed\"\n\t| \"not_artifact_backed\"\n\t| \"retrieval_tool_unavailable\"\n\t| \"hard_constraint_rejected\"\n\t| \"missing_artifact_id\"\n\t| \"below_min_chars\";\n\nexport interface PromptEnforcementItemReport {\n\titemId: string;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\tenforced: boolean;\n\taction?: \"artifact_stub\";\n\tartifactId?: string;\n\toriginalChars?: number;\n\tskipReason?: PromptEnforcementSkipReason;\n\t/** Set when a brain-curator irrelevance verdict allowed eviction inside the recent window. */\n\tadvisory?: \"brain_irrelevant\";\n}\n\nexport interface PromptEnforcementReport {\n\tturnIndex: number;\n\titems: PromptEnforcementItemReport[];\n}\n\nexport interface EnforcePromptPolicyResult {\n\tmessages: AgentMessage[];\n\treport: PromptEnforcementReport;\n}\n\nconst ENFORCEMENT_ABSOLUTE_RECENT_FLOOR = 4;\n\nfunction extractDetailsArtifactId(details: unknown): string | undefined {\n\tif (typeof details !== \"object\" || details === null) return undefined;\n\tconst artifactId = (details as { artifactId?: unknown }).artifactId;\n\treturn typeof artifactId === \"string\" ? artifactId : undefined;\n}\n\n/** True if legacy context-gc already packed this message this turn, or this module already stubbed it. */\nfunction isAlreadyStubbedOrPacked(details: unknown): boolean {\n\tif (typeof details !== \"object\" || details === null) return false;\n\tconst record = details as { promptPolicy?: { enforced?: unknown }; contextGc?: { packed?: unknown } };\n\treturn record.promptPolicy?.enforced === true || record.contextGc?.packed === true;\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") parts.push(part.text);\n\t}\n\treturn parts.join(\"\\n\");\n}\n\nfunction buildStubText(toolName: string, originalChars: number, artifactId: string): string {\n\treturn `[content replaced by prompt-policy: originally ${originalChars} chars from a stale ${toolName} tool result. Retrieve the full output with artifact_retrieve using artifactId \"${artifactId}\".]`;\n}\n\nfunction skip(\n\titem: { itemId: string; toolCallId: string; messageIndex: number },\n\tskipReason: PromptEnforcementSkipReason,\n\textra?: { artifactId?: string; originalChars?: number },\n): PromptEnforcementItemReport {\n\treturn {\n\t\titemId: item.itemId,\n\t\ttoolCallId: item.toolCallId,\n\t\tmessageIndex: item.messageIndex,\n\t\tenforced: false,\n\t\tskipReason,\n\t\t...extra,\n\t};\n}\n\n/**\n * Apply the first enforcement pilot to `messages` (expected to be the provider-visible\n * array after existing context-gc has already run). Returns a new array only when at least\n * one item was actually stubbed; otherwise returns the same `messages` reference unchanged\n * (in particular, always true when `settings.enabled` is false). Never mutates `messages`\n * or any message object within it -- every stubbed entry is a fresh object.\n */\nexport function enforcePromptPolicy(\n\tmessages: AgentMessage[],\n\tshadowReport: PromptPolicyShadowReport,\n\tsettings: ContextPromptEnforcementSettings,\n): EnforcePromptPolicyResult {\n\tif (!settings.enabled) {\n\t\treturn { messages, report: { turnIndex: shadowReport.turnIndex, items: [] } };\n\t}\n\n\tconst recentCutoffIndex = Math.max(0, messages.length - settings.preserveRecentMessages);\n\t// Advisory evictions may reach inside the recent window but NEVER past this absolute floor:\n\t// the last few messages are what the model is actively reasoning over.\n\tconst absoluteFloorIndex = Math.max(0, messages.length - ENFORCEMENT_ABSOLUTE_RECENT_FLOOR);\n\tconst nextMessages = messages.slice();\n\tlet changed = false;\n\tconst items: PromptEnforcementItemReport[] = [];\n\n\tfor (const planItem of shadowReport.items) {\n\t\tconst message = messages[planItem.messageIndex];\n\t\tif (!message || message.role !== \"toolResult\" || message.toolCallId !== planItem.toolCallId) {\n\t\t\titems.push(skip(planItem, \"message_mismatch\"));\n\t\t\tcontinue;\n\t\t}\n\t\tlet advisoryEviction = false;\n\t\tif (planItem.messageIndex >= recentCutoffIndex) {\n\t\t\tconst advisory = settings.brainRelevance?.(planItem.itemId);\n\t\t\tadvisoryEviction =\n\t\t\t\tadvisory !== undefined &&\n\t\t\t\t!advisory.relevant &&\n\t\t\t\tadvisory.confidence >= CURATION_RELEVANCE_MIN_CONFIDENCE &&\n\t\t\t\tplanItem.messageIndex < absoluteFloorIndex;\n\t\t\tif (!advisoryEviction) {\n\t\t\t\titems.push(skip(planItem, \"within_recent_window\"));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (message.isError) {\n\t\t\titems.push(skip(planItem, \"errored_tool_result\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (isAlreadyStubbedOrPacked(message.details)) {\n\t\t\titems.push(skip(planItem, \"already_stubbed_or_packed\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (!planItem.hasAvailableRetrievalPath) {\n\t\t\titems.push(skip(planItem, \"not_artifact_backed\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (!settings.retrievalToolAvailable) {\n\t\t\titems.push(skip(planItem, \"retrieval_tool_unavailable\"));\n\t\t\tcontinue;\n\t\t}\n\t\tif (planItem.hardConstraints.dropFromPrompt.length > 0) {\n\t\t\titems.push(skip(planItem, \"hard_constraint_rejected\"));\n\t\t\tcontinue;\n\t\t}\n\t\tconst artifactId = extractDetailsArtifactId(message.details);\n\t\tif (!artifactId) {\n\t\t\titems.push(skip(planItem, \"missing_artifact_id\"));\n\t\t\tcontinue;\n\t\t}\n\t\tconst originalChars = toolResultText(message).length;\n\t\tif (originalChars < settings.minChars) {\n\t\t\titems.push(skip(planItem, \"below_min_chars\", { artifactId, originalChars }));\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst existingDetails = typeof message.details === \"object\" && message.details !== null ? message.details : {};\n\t\tnextMessages[planItem.messageIndex] = {\n\t\t\t...message,\n\t\t\tcontent: [{ type: \"text\", text: buildStubText(message.toolName, originalChars, artifactId) }],\n\t\t\tdetails: {\n\t\t\t\t...existingDetails,\n\t\t\t\tpromptPolicy: {\n\t\t\t\t\tenforced: true,\n\t\t\t\t\taction: \"artifact_stub\",\n\t\t\t\t\tartifactId,\n\t\t\t\t\toriginalChars,\n\t\t\t\t\treason: \"stale_artifact_backed_tool_output\",\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tchanged = true;\n\t\titems.push({\n\t\t\titemId: planItem.itemId,\n\t\t\ttoolCallId: planItem.toolCallId,\n\t\t\tmessageIndex: planItem.messageIndex,\n\t\t\tenforced: true,\n\t\t\taction: \"artifact_stub\",\n\t\t\tartifactId,\n\t\t\toriginalChars,\n\t\t\t...(advisoryEviction ? { advisory: \"brain_irrelevant\" as const } : {}),\n\t\t});\n\t}\n\n\treturn {\n\t\tmessages: changed ? nextMessages : messages,\n\t\treport: { turnIndex: shadowReport.turnIndex, items },\n\t};\n}\n"]}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Observe-first shadow/planning layer, one step closer to prompt construction than
3
+ * context-audit.ts (Phase 1). This module re-shapes an already-computed `ContextAuditReport`
4
+ * into a deterministic per-item policy plan, and can correlate that plan against the
5
+ * legacy context-gc's actual packed records. It never enforces anything: `appliedAction` is
6
+ * a literal `"keep_raw"` on every item -- the type itself makes it impossible for this
7
+ * module to report having changed prompt behavior. Nothing here mutates messages, the
8
+ * transcript, or artifact references; it only reads an already-computed report.
9
+ *
10
+ * Single source of truth for feature/flag construction remains context-audit.ts
11
+ * (`buildPolicyFeatures`/`buildHardConstraintFlags`): this module reuses the hard-constraint
12
+ * codes already computed there rather than reconstructing them, so there is exactly one
13
+ * place that decides how a tool_output item's PolicyFeatures/HardConstraintFlags are built.
14
+ */
15
+ import type { ContextGcPackedRecord, ContextGcReport } from "../context-gc.ts";
16
+ import type { ContextAuditReport } from "./context-audit.ts";
17
+ import type { ContextEvidenceRef, ContextItemKind, ContextRetentionClass, ContextSource } from "./context-item.ts";
18
+ import type { RetentionAction } from "./context-retention.ts";
19
+ import type { PolicyHardConstraintCode } from "./policy-types.ts";
20
+ export interface PromptPolicyHardConstraints {
21
+ keepRaw: PolicyHardConstraintCode[];
22
+ packToArtifact: PolicyHardConstraintCode[];
23
+ dropFromPrompt: PolicyHardConstraintCode[];
24
+ summarize: PolicyHardConstraintCode[];
25
+ }
26
+ export interface PromptPolicyItemReport {
27
+ itemId: string;
28
+ kind: ContextItemKind;
29
+ retentionClass: ContextRetentionClass;
30
+ source: ContextSource;
31
+ toolCallId: string;
32
+ messageIndex: number;
33
+ primaryRefType: ContextEvidenceRef["type"] | undefined;
34
+ /**
35
+ * True only for a resolved artifact ref, never for a transcript ref -- the same
36
+ * fail-closed resolved-artifact-only rule context-audit.ts uses (a transcript ref is
37
+ * provenance, not a live retrieval mechanism; see that module's doc comment).
38
+ */
39
+ hasAvailableRetrievalPath: boolean;
40
+ /** Coarse, store-free eligibility from context-retention.ts (treats any ref as retrievable). */
41
+ allowedRetentionActions: RetentionAction[];
42
+ hardConstraints: PromptPolicyHardConstraints;
43
+ /**
44
+ * This shadow/planning pass never changes behavior: the applied action is always
45
+ * "keep_raw", regardless of what the hard-constraint/retention analysis above would
46
+ * otherwise allow. Typed as a literal so nothing here can ever report applying anything
47
+ * else.
48
+ */
49
+ appliedAction: "keep_raw";
50
+ }
51
+ export interface PromptPolicyShadowReport {
52
+ turnIndex: number;
53
+ items: PromptPolicyItemReport[];
54
+ }
55
+ /**
56
+ * Deterministic re-shaping of an audit report into the shadow policy plan shape. Pure: no
57
+ * I/O, no mutation, given the same audit report this always returns an equal result.
58
+ */
59
+ export declare function planPromptPolicy(auditReport: ContextAuditReport): PromptPolicyShadowReport;
60
+ export interface PromptPolicyGcCorrelationEntry {
61
+ itemId: string;
62
+ toolCallId: string;
63
+ /** True if the legacy context-gc pass actually packed this item's tool result this turn. */
64
+ actuallyPackedByLegacyGc: boolean;
65
+ gcPackReason?: ContextGcPackedRecord["reason"];
66
+ /** True if the shadow plan's pack_to_artifact hard constraints did not reject packing. */
67
+ policyWouldAllowPack: boolean;
68
+ /** True if the shadow plan's drop_from_prompt hard constraints did not reject dropping. */
69
+ policyWouldAllowDrop: boolean;
70
+ }
71
+ export interface PromptPolicyGcCorrelationReport {
72
+ turnIndex: number;
73
+ entries: PromptPolicyGcCorrelationEntry[];
74
+ }
75
+ /**
76
+ * Report-only correlation between the shadow plan and what the legacy context-gc pass
77
+ * actually did this turn. Does not influence context-gc in any way -- it runs after gc has
78
+ * already produced its report, purely to observe what each side did/would do.
79
+ *
80
+ * Deliberately does not derive an "agrees with legacy gc" boolean: legacy context-gc
81
+ * "packing" summarizes/stubs a tool result in place (provider-visible content shrinks in
82
+ * place), while the policy action `pack_to_artifact` means artifact-backed capture and
83
+ * out-of-band retrieval -- a different operation with different semantics, not a
84
+ * calibrated equivalent. Collapsing them into one "agreement" boolean would overclaim
85
+ * semantic agreement between the two and risk a later enforcement slice mistaking this
86
+ * observe-only diagnostic for a calibrated authority. The raw booleans below are reported
87
+ * as-is; comparing them is left to the reader.
88
+ */
89
+ export declare function correlateWithContextGc(shadowReport: PromptPolicyShadowReport, gcReport: ContextGcReport): PromptPolicyGcCorrelationReport;
90
+ //# sourceMappingURL=context-prompt-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-prompt-policy.d.ts","sourceRoot":"","sources":["../../../src/core/context/context-prompt-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,WAAW,2BAA2B;IAC3C,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,cAAc,EAAE,wBAAwB,EAAE,CAAC;IAC3C,cAAc,EAAE,wBAAwB,EAAE,CAAC;IAC3C,SAAS,EAAE,wBAAwB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,cAAc,EAAE,qBAAqB,CAAC;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,kBAAkB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IACvD;;;;OAIG;IACH,yBAAyB,EAAE,OAAO,CAAC;IACnC,gGAAgG;IAChG,uBAAuB,EAAE,eAAe,EAAE,CAAC;IAC3C,eAAe,EAAE,2BAA2B,CAAC;IAC7C;;;;;OAKG;IACH,aAAa,EAAE,UAAU,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,sBAAsB,EAAE,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,kBAAkB,GAAG,wBAAwB,CAsB1F;AAED,MAAM,WAAW,8BAA8B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,wBAAwB,EAAE,OAAO,CAAC;IAClC,YAAY,CAAC,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAC/C,0FAA0F;IAC1F,oBAAoB,EAAE,OAAO,CAAC;IAC9B,2FAA2F;IAC3F,oBAAoB,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,+BAA+B;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,8BAA8B,EAAE,CAAC;CAC1C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACrC,YAAY,EAAE,wBAAwB,EACtC,QAAQ,EAAE,eAAe,GACvB,+BAA+B,CAiBjC","sourcesContent":["/**\n * Observe-first shadow/planning layer, one step closer to prompt construction than\n * context-audit.ts (Phase 1). This module re-shapes an already-computed `ContextAuditReport`\n * into a deterministic per-item policy plan, and can correlate that plan against the\n * legacy context-gc's actual packed records. It never enforces anything: `appliedAction` is\n * a literal `\"keep_raw\"` on every item -- the type itself makes it impossible for this\n * module to report having changed prompt behavior. Nothing here mutates messages, the\n * transcript, or artifact references; it only reads an already-computed report.\n *\n * Single source of truth for feature/flag construction remains context-audit.ts\n * (`buildPolicyFeatures`/`buildHardConstraintFlags`): this module reuses the hard-constraint\n * codes already computed there rather than reconstructing them, so there is exactly one\n * place that decides how a tool_output item's PolicyFeatures/HardConstraintFlags are built.\n */\n\nimport type { ContextGcPackedRecord, ContextGcReport } from \"../context-gc.ts\";\nimport type { ContextAuditReport } from \"./context-audit.ts\";\nimport type { ContextEvidenceRef, ContextItemKind, ContextRetentionClass, ContextSource } from \"./context-item.ts\";\nimport type { RetentionAction } from \"./context-retention.ts\";\nimport type { PolicyHardConstraintCode } from \"./policy-types.ts\";\n\nexport interface PromptPolicyHardConstraints {\n\tkeepRaw: PolicyHardConstraintCode[];\n\tpackToArtifact: PolicyHardConstraintCode[];\n\tdropFromPrompt: PolicyHardConstraintCode[];\n\tsummarize: PolicyHardConstraintCode[];\n}\n\nexport interface PromptPolicyItemReport {\n\titemId: string;\n\tkind: ContextItemKind;\n\tretentionClass: ContextRetentionClass;\n\tsource: ContextSource;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\tprimaryRefType: ContextEvidenceRef[\"type\"] | undefined;\n\t/**\n\t * True only for a resolved artifact ref, never for a transcript ref -- the same\n\t * fail-closed resolved-artifact-only rule context-audit.ts uses (a transcript ref is\n\t * provenance, not a live retrieval mechanism; see that module's doc comment).\n\t */\n\thasAvailableRetrievalPath: boolean;\n\t/** Coarse, store-free eligibility from context-retention.ts (treats any ref as retrievable). */\n\tallowedRetentionActions: RetentionAction[];\n\thardConstraints: PromptPolicyHardConstraints;\n\t/**\n\t * This shadow/planning pass never changes behavior: the applied action is always\n\t * \"keep_raw\", regardless of what the hard-constraint/retention analysis above would\n\t * otherwise allow. Typed as a literal so nothing here can ever report applying anything\n\t * else.\n\t */\n\tappliedAction: \"keep_raw\";\n}\n\nexport interface PromptPolicyShadowReport {\n\tturnIndex: number;\n\titems: PromptPolicyItemReport[];\n}\n\n/**\n * Deterministic re-shaping of an audit report into the shadow policy plan shape. Pure: no\n * I/O, no mutation, given the same audit report this always returns an equal result.\n */\nexport function planPromptPolicy(auditReport: ContextAuditReport): PromptPolicyShadowReport {\n\treturn {\n\t\tturnIndex: auditReport.turnIndex,\n\t\titems: auditReport.items.map((entry) => ({\n\t\t\titemId: entry.item.id,\n\t\t\tkind: entry.item.kind,\n\t\t\tretentionClass: entry.item.retentionClass,\n\t\t\tsource: entry.item.source,\n\t\t\ttoolCallId: entry.toolCallId,\n\t\t\tmessageIndex: entry.messageIndex,\n\t\t\tprimaryRefType: entry.item.primaryRef?.type,\n\t\t\thasAvailableRetrievalPath: entry.item.primaryRef?.type === \"artifact\",\n\t\t\tallowedRetentionActions: entry.retention.allowedActions,\n\t\t\thardConstraints: {\n\t\t\t\tkeepRaw: entry.keepRawHardConstraints,\n\t\t\t\tpackToArtifact: entry.packToArtifactHardConstraints,\n\t\t\t\tdropFromPrompt: entry.dropFromPromptHardConstraints,\n\t\t\t\tsummarize: entry.summarizeHardConstraints,\n\t\t\t},\n\t\t\tappliedAction: \"keep_raw\",\n\t\t})),\n\t};\n}\n\nexport interface PromptPolicyGcCorrelationEntry {\n\titemId: string;\n\ttoolCallId: string;\n\t/** True if the legacy context-gc pass actually packed this item's tool result this turn. */\n\tactuallyPackedByLegacyGc: boolean;\n\tgcPackReason?: ContextGcPackedRecord[\"reason\"];\n\t/** True if the shadow plan's pack_to_artifact hard constraints did not reject packing. */\n\tpolicyWouldAllowPack: boolean;\n\t/** True if the shadow plan's drop_from_prompt hard constraints did not reject dropping. */\n\tpolicyWouldAllowDrop: boolean;\n}\n\nexport interface PromptPolicyGcCorrelationReport {\n\tturnIndex: number;\n\tentries: PromptPolicyGcCorrelationEntry[];\n}\n\n/**\n * Report-only correlation between the shadow plan and what the legacy context-gc pass\n * actually did this turn. Does not influence context-gc in any way -- it runs after gc has\n * already produced its report, purely to observe what each side did/would do.\n *\n * Deliberately does not derive an \"agrees with legacy gc\" boolean: legacy context-gc\n * \"packing\" summarizes/stubs a tool result in place (provider-visible content shrinks in\n * place), while the policy action `pack_to_artifact` means artifact-backed capture and\n * out-of-band retrieval -- a different operation with different semantics, not a\n * calibrated equivalent. Collapsing them into one \"agreement\" boolean would overclaim\n * semantic agreement between the two and risk a later enforcement slice mistaking this\n * observe-only diagnostic for a calibrated authority. The raw booleans below are reported\n * as-is; comparing them is left to the reader.\n */\nexport function correlateWithContextGc(\n\tshadowReport: PromptPolicyShadowReport,\n\tgcReport: ContextGcReport,\n): PromptPolicyGcCorrelationReport {\n\tconst packedByToolCallId = new Map<string, ContextGcPackedRecord>();\n\tfor (const record of gcReport.records) packedByToolCallId.set(record.toolCallId, record);\n\n\tconst entries: PromptPolicyGcCorrelationEntry[] = shadowReport.items.map((item) => {\n\t\tconst gcRecord = packedByToolCallId.get(item.toolCallId);\n\t\treturn {\n\t\t\titemId: item.itemId,\n\t\t\ttoolCallId: item.toolCallId,\n\t\t\tactuallyPackedByLegacyGc: gcRecord !== undefined,\n\t\t\tgcPackReason: gcRecord?.reason,\n\t\t\tpolicyWouldAllowPack: item.hardConstraints.packToArtifact.length === 0,\n\t\t\tpolicyWouldAllowDrop: item.hardConstraints.dropFromPrompt.length === 0,\n\t\t};\n\t});\n\n\treturn { turnIndex: shadowReport.turnIndex, entries };\n}\n"]}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Observe-first shadow/planning layer, one step closer to prompt construction than
3
+ * context-audit.ts (Phase 1). This module re-shapes an already-computed `ContextAuditReport`
4
+ * into a deterministic per-item policy plan, and can correlate that plan against the
5
+ * legacy context-gc's actual packed records. It never enforces anything: `appliedAction` is
6
+ * a literal `"keep_raw"` on every item -- the type itself makes it impossible for this
7
+ * module to report having changed prompt behavior. Nothing here mutates messages, the
8
+ * transcript, or artifact references; it only reads an already-computed report.
9
+ *
10
+ * Single source of truth for feature/flag construction remains context-audit.ts
11
+ * (`buildPolicyFeatures`/`buildHardConstraintFlags`): this module reuses the hard-constraint
12
+ * codes already computed there rather than reconstructing them, so there is exactly one
13
+ * place that decides how a tool_output item's PolicyFeatures/HardConstraintFlags are built.
14
+ */
15
+ /**
16
+ * Deterministic re-shaping of an audit report into the shadow policy plan shape. Pure: no
17
+ * I/O, no mutation, given the same audit report this always returns an equal result.
18
+ */
19
+ export function planPromptPolicy(auditReport) {
20
+ return {
21
+ turnIndex: auditReport.turnIndex,
22
+ items: auditReport.items.map((entry) => ({
23
+ itemId: entry.item.id,
24
+ kind: entry.item.kind,
25
+ retentionClass: entry.item.retentionClass,
26
+ source: entry.item.source,
27
+ toolCallId: entry.toolCallId,
28
+ messageIndex: entry.messageIndex,
29
+ primaryRefType: entry.item.primaryRef?.type,
30
+ hasAvailableRetrievalPath: entry.item.primaryRef?.type === "artifact",
31
+ allowedRetentionActions: entry.retention.allowedActions,
32
+ hardConstraints: {
33
+ keepRaw: entry.keepRawHardConstraints,
34
+ packToArtifact: entry.packToArtifactHardConstraints,
35
+ dropFromPrompt: entry.dropFromPromptHardConstraints,
36
+ summarize: entry.summarizeHardConstraints,
37
+ },
38
+ appliedAction: "keep_raw",
39
+ })),
40
+ };
41
+ }
42
+ /**
43
+ * Report-only correlation between the shadow plan and what the legacy context-gc pass
44
+ * actually did this turn. Does not influence context-gc in any way -- it runs after gc has
45
+ * already produced its report, purely to observe what each side did/would do.
46
+ *
47
+ * Deliberately does not derive an "agrees with legacy gc" boolean: legacy context-gc
48
+ * "packing" summarizes/stubs a tool result in place (provider-visible content shrinks in
49
+ * place), while the policy action `pack_to_artifact` means artifact-backed capture and
50
+ * out-of-band retrieval -- a different operation with different semantics, not a
51
+ * calibrated equivalent. Collapsing them into one "agreement" boolean would overclaim
52
+ * semantic agreement between the two and risk a later enforcement slice mistaking this
53
+ * observe-only diagnostic for a calibrated authority. The raw booleans below are reported
54
+ * as-is; comparing them is left to the reader.
55
+ */
56
+ export function correlateWithContextGc(shadowReport, gcReport) {
57
+ const packedByToolCallId = new Map();
58
+ for (const record of gcReport.records)
59
+ packedByToolCallId.set(record.toolCallId, record);
60
+ const entries = shadowReport.items.map((item) => {
61
+ const gcRecord = packedByToolCallId.get(item.toolCallId);
62
+ return {
63
+ itemId: item.itemId,
64
+ toolCallId: item.toolCallId,
65
+ actuallyPackedByLegacyGc: gcRecord !== undefined,
66
+ gcPackReason: gcRecord?.reason,
67
+ policyWouldAllowPack: item.hardConstraints.packToArtifact.length === 0,
68
+ policyWouldAllowDrop: item.hardConstraints.dropFromPrompt.length === 0,
69
+ };
70
+ });
71
+ return { turnIndex: shadowReport.turnIndex, entries };
72
+ }
73
+ //# sourceMappingURL=context-prompt-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-prompt-policy.js","sourceRoot":"","sources":["../../../src/core/context/context-prompt-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA8CH;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAA+B,EAA4B;IAC3F,OAAO;QACN,SAAS,EAAE,WAAW,CAAC,SAAS;QAChC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACxC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;YACrB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;YACrB,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,cAAc;YACzC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM;YACzB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI;YAC3C,yBAAyB,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,KAAK,UAAU;YACrE,uBAAuB,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc;YACvD,eAAe,EAAE;gBAChB,OAAO,EAAE,KAAK,CAAC,sBAAsB;gBACrC,cAAc,EAAE,KAAK,CAAC,6BAA6B;gBACnD,cAAc,EAAE,KAAK,CAAC,6BAA6B;gBACnD,SAAS,EAAE,KAAK,CAAC,wBAAwB;aACzC;YACD,aAAa,EAAE,UAAU;SACzB,CAAC,CAAC;KACH,CAAC;AAAA,CACF;AAmBD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CACrC,YAAsC,EACtC,QAAyB,EACS;IAClC,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAiC,CAAC;IACpE,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,OAAO;QAAE,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEzF,MAAM,OAAO,GAAqC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAClF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACzD,OAAO;YACN,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,wBAAwB,EAAE,QAAQ,KAAK,SAAS;YAChD,YAAY,EAAE,QAAQ,EAAE,MAAM;YAC9B,oBAAoB,EAAE,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;YACtE,oBAAoB,EAAE,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;SACtE,CAAC;IAAA,CACF,CAAC,CAAC;IAEH,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;AAAA,CACtD","sourcesContent":["/**\n * Observe-first shadow/planning layer, one step closer to prompt construction than\n * context-audit.ts (Phase 1). This module re-shapes an already-computed `ContextAuditReport`\n * into a deterministic per-item policy plan, and can correlate that plan against the\n * legacy context-gc's actual packed records. It never enforces anything: `appliedAction` is\n * a literal `\"keep_raw\"` on every item -- the type itself makes it impossible for this\n * module to report having changed prompt behavior. Nothing here mutates messages, the\n * transcript, or artifact references; it only reads an already-computed report.\n *\n * Single source of truth for feature/flag construction remains context-audit.ts\n * (`buildPolicyFeatures`/`buildHardConstraintFlags`): this module reuses the hard-constraint\n * codes already computed there rather than reconstructing them, so there is exactly one\n * place that decides how a tool_output item's PolicyFeatures/HardConstraintFlags are built.\n */\n\nimport type { ContextGcPackedRecord, ContextGcReport } from \"../context-gc.ts\";\nimport type { ContextAuditReport } from \"./context-audit.ts\";\nimport type { ContextEvidenceRef, ContextItemKind, ContextRetentionClass, ContextSource } from \"./context-item.ts\";\nimport type { RetentionAction } from \"./context-retention.ts\";\nimport type { PolicyHardConstraintCode } from \"./policy-types.ts\";\n\nexport interface PromptPolicyHardConstraints {\n\tkeepRaw: PolicyHardConstraintCode[];\n\tpackToArtifact: PolicyHardConstraintCode[];\n\tdropFromPrompt: PolicyHardConstraintCode[];\n\tsummarize: PolicyHardConstraintCode[];\n}\n\nexport interface PromptPolicyItemReport {\n\titemId: string;\n\tkind: ContextItemKind;\n\tretentionClass: ContextRetentionClass;\n\tsource: ContextSource;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\tprimaryRefType: ContextEvidenceRef[\"type\"] | undefined;\n\t/**\n\t * True only for a resolved artifact ref, never for a transcript ref -- the same\n\t * fail-closed resolved-artifact-only rule context-audit.ts uses (a transcript ref is\n\t * provenance, not a live retrieval mechanism; see that module's doc comment).\n\t */\n\thasAvailableRetrievalPath: boolean;\n\t/** Coarse, store-free eligibility from context-retention.ts (treats any ref as retrievable). */\n\tallowedRetentionActions: RetentionAction[];\n\thardConstraints: PromptPolicyHardConstraints;\n\t/**\n\t * This shadow/planning pass never changes behavior: the applied action is always\n\t * \"keep_raw\", regardless of what the hard-constraint/retention analysis above would\n\t * otherwise allow. Typed as a literal so nothing here can ever report applying anything\n\t * else.\n\t */\n\tappliedAction: \"keep_raw\";\n}\n\nexport interface PromptPolicyShadowReport {\n\tturnIndex: number;\n\titems: PromptPolicyItemReport[];\n}\n\n/**\n * Deterministic re-shaping of an audit report into the shadow policy plan shape. Pure: no\n * I/O, no mutation, given the same audit report this always returns an equal result.\n */\nexport function planPromptPolicy(auditReport: ContextAuditReport): PromptPolicyShadowReport {\n\treturn {\n\t\tturnIndex: auditReport.turnIndex,\n\t\titems: auditReport.items.map((entry) => ({\n\t\t\titemId: entry.item.id,\n\t\t\tkind: entry.item.kind,\n\t\t\tretentionClass: entry.item.retentionClass,\n\t\t\tsource: entry.item.source,\n\t\t\ttoolCallId: entry.toolCallId,\n\t\t\tmessageIndex: entry.messageIndex,\n\t\t\tprimaryRefType: entry.item.primaryRef?.type,\n\t\t\thasAvailableRetrievalPath: entry.item.primaryRef?.type === \"artifact\",\n\t\t\tallowedRetentionActions: entry.retention.allowedActions,\n\t\t\thardConstraints: {\n\t\t\t\tkeepRaw: entry.keepRawHardConstraints,\n\t\t\t\tpackToArtifact: entry.packToArtifactHardConstraints,\n\t\t\t\tdropFromPrompt: entry.dropFromPromptHardConstraints,\n\t\t\t\tsummarize: entry.summarizeHardConstraints,\n\t\t\t},\n\t\t\tappliedAction: \"keep_raw\",\n\t\t})),\n\t};\n}\n\nexport interface PromptPolicyGcCorrelationEntry {\n\titemId: string;\n\ttoolCallId: string;\n\t/** True if the legacy context-gc pass actually packed this item's tool result this turn. */\n\tactuallyPackedByLegacyGc: boolean;\n\tgcPackReason?: ContextGcPackedRecord[\"reason\"];\n\t/** True if the shadow plan's pack_to_artifact hard constraints did not reject packing. */\n\tpolicyWouldAllowPack: boolean;\n\t/** True if the shadow plan's drop_from_prompt hard constraints did not reject dropping. */\n\tpolicyWouldAllowDrop: boolean;\n}\n\nexport interface PromptPolicyGcCorrelationReport {\n\tturnIndex: number;\n\tentries: PromptPolicyGcCorrelationEntry[];\n}\n\n/**\n * Report-only correlation between the shadow plan and what the legacy context-gc pass\n * actually did this turn. Does not influence context-gc in any way -- it runs after gc has\n * already produced its report, purely to observe what each side did/would do.\n *\n * Deliberately does not derive an \"agrees with legacy gc\" boolean: legacy context-gc\n * \"packing\" summarizes/stubs a tool result in place (provider-visible content shrinks in\n * place), while the policy action `pack_to_artifact` means artifact-backed capture and\n * out-of-band retrieval -- a different operation with different semantics, not a\n * calibrated equivalent. Collapsing them into one \"agreement\" boolean would overclaim\n * semantic agreement between the two and risk a later enforcement slice mistaking this\n * observe-only diagnostic for a calibrated authority. The raw booleans below are reported\n * as-is; comparing them is left to the reader.\n */\nexport function correlateWithContextGc(\n\tshadowReport: PromptPolicyShadowReport,\n\tgcReport: ContextGcReport,\n): PromptPolicyGcCorrelationReport {\n\tconst packedByToolCallId = new Map<string, ContextGcPackedRecord>();\n\tfor (const record of gcReport.records) packedByToolCallId.set(record.toolCallId, record);\n\n\tconst entries: PromptPolicyGcCorrelationEntry[] = shadowReport.items.map((item) => {\n\t\tconst gcRecord = packedByToolCallId.get(item.toolCallId);\n\t\treturn {\n\t\t\titemId: item.itemId,\n\t\t\ttoolCallId: item.toolCallId,\n\t\t\tactuallyPackedByLegacyGc: gcRecord !== undefined,\n\t\t\tgcPackReason: gcRecord?.reason,\n\t\t\tpolicyWouldAllowPack: item.hardConstraints.packToArtifact.length === 0,\n\t\t\tpolicyWouldAllowDrop: item.hardConstraints.dropFromPrompt.length === 0,\n\t\t};\n\t});\n\n\treturn { turnIndex: shadowReport.turnIndex, entries };\n}\n"]}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure retention-eligibility helpers for ContextItem.
3
+ *
4
+ * These functions only classify what a runtime policy is *allowed* to do with an item;
5
+ * they do not mutate, evict, or summarize anything themselves, and they never touch the
6
+ * transcript or artifact stores. Per contracts-and-retention.md, dropping from model
7
+ * context must never mean deleting from storage: these helpers only ever describe prompt
8
+ * inclusion, not deletion.
9
+ */
10
+ import { type ContextItem } from "./context-item.ts";
11
+ export type RetentionAction = "keep_raw" | "summarize" | "pack_to_artifact" | "drop_from_prompt";
12
+ export interface RetentionEligibility {
13
+ /** Actions a runtime policy may choose between for this item, most-preserving first. */
14
+ allowedActions: RetentionAction[];
15
+ /** True if the item's semantics must stay present verbatim; only "keep_raw" is allowed. */
16
+ hardRetained: boolean;
17
+ reasonCodes: string[];
18
+ }
19
+ /**
20
+ * True if `kind`/`retentionClass` combination must never be dropped or summarized away,
21
+ * regardless of budget pressure. Mirrors the "Hard retention rules" in
22
+ * contracts-and-retention.md: pinned items and the explicitly hard-retained kinds
23
+ * (user instructions, approvals, denials, safety constraints).
24
+ */
25
+ export declare function isHardRetained(item: ContextItem): boolean;
26
+ /**
27
+ * Determine which prompt-retention actions are permitted for an item. This is the pure
28
+ * policy helper referenced by implementation-phases.md Phase 1: it must never allow
29
+ * dropping pinned/open/latest-failure items, and it must never allow summarizing
30
+ * decision-bearing content that has no evidence/retrieval path.
31
+ */
32
+ export declare function evaluateRetentionEligibility(item: ContextItem): RetentionEligibility;
33
+ export declare function canDropFromPrompt(item: ContextItem): boolean;
34
+ export declare function canSummarize(item: ContextItem): boolean;
35
+ export declare function canPackToArtifact(item: ContextItem): boolean;
36
+ //# sourceMappingURL=context-retention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-retention.d.ts","sourceRoot":"","sources":["../../../src/core/context/context-retention.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,WAAW,EAA+B,MAAM,mBAAmB,CAAC;AAElF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAEjG,MAAM,WAAW,oBAAoB;IACpC,wFAAwF;IACxF,cAAc,EAAE,eAAe,EAAE,CAAC;IAClC,2FAA2F;IAC3F,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAYD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,WAAW,GAAG,oBAAoB,CAiEpF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE5D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAEvD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE5D","sourcesContent":["/**\n * Pure retention-eligibility helpers for ContextItem.\n *\n * These functions only classify what a runtime policy is *allowed* to do with an item;\n * they do not mutate, evict, or summarize anything themselves, and they never touch the\n * transcript or artifact stores. Per contracts-and-retention.md, dropping from model\n * context must never mean deleting from storage: these helpers only ever describe prompt\n * inclusion, not deletion.\n */\n\nimport { type ContextItem, HARD_RETAINED_CONTEXT_KINDS } from \"./context-item.ts\";\n\nexport type RetentionAction = \"keep_raw\" | \"summarize\" | \"pack_to_artifact\" | \"drop_from_prompt\";\n\nexport interface RetentionEligibility {\n\t/** Actions a runtime policy may choose between for this item, most-preserving first. */\n\tallowedActions: RetentionAction[];\n\t/** True if the item's semantics must stay present verbatim; only \"keep_raw\" is allowed. */\n\thardRetained: boolean;\n\treasonCodes: string[];\n}\n\n/**\n * True if the item carries a ref at all. This is item-level and store-free by design: it\n * does not check whether an artifact ref is still resolvable in a live artifact store\n * (that check belongs to a store-aware layer, e.g. `HardConstraintFlags` in\n * policy-types.ts). Do not treat this as \"retrieval is available\" when wiring a real store.\n */\nfunction hasReferencedEvidence(item: ContextItem): boolean {\n\treturn item.primaryRef !== undefined || (item.evidenceRefs?.length ?? 0) > 0;\n}\n\n/**\n * True if `kind`/`retentionClass` combination must never be dropped or summarized away,\n * regardless of budget pressure. Mirrors the \"Hard retention rules\" in\n * contracts-and-retention.md: pinned items and the explicitly hard-retained kinds\n * (user instructions, approvals, denials, safety constraints).\n */\nexport function isHardRetained(item: ContextItem): boolean {\n\treturn item.retentionClass === \"pinned\" || HARD_RETAINED_CONTEXT_KINDS.has(item.kind);\n}\n\n/**\n * Determine which prompt-retention actions are permitted for an item. This is the pure\n * policy helper referenced by implementation-phases.md Phase 1: it must never allow\n * dropping pinned/open/latest-failure items, and it must never allow summarizing\n * decision-bearing content that has no evidence/retrieval path.\n */\nexport function evaluateRetentionEligibility(item: ContextItem): RetentionEligibility {\n\tif (isHardRetained(item)) {\n\t\treturn {\n\t\t\tallowedActions: [\"keep_raw\"],\n\t\t\thardRetained: true,\n\t\t\treasonCodes: [item.retentionClass === \"pinned\" ? \"pinned_hard_retained\" : \"hard_retained_kind\"],\n\t\t};\n\t}\n\n\tconst retrievable = hasReferencedEvidence(item);\n\n\tswitch (item.retentionClass) {\n\t\tcase \"active\":\n\t\t\t// Current working state (active goal, open requirement, plan, blocker):\n\t\t\t// must stay present while active. Summarizing requires evidence so the\n\t\t\t// summary does not silently lose decision-bearing facts.\n\t\t\treturn {\n\t\t\t\tallowedActions: retrievable ? [\"keep_raw\", \"summarize\"] : [\"keep_raw\"],\n\t\t\t\thardRetained: false,\n\t\t\t\treasonCodes: retrievable ? [\"active_summarizable\"] : [\"active_no_retrieval_path\"],\n\t\t\t};\n\t\tcase \"decision_bearing\":\n\t\t\t// Evidence for a current decision (latest failing check, current diff, etc.):\n\t\t\t// keep while the decision is pending. Only summarizable with an evidence ref\n\t\t\t// so exact identifiers remain recoverable.\n\t\t\treturn {\n\t\t\t\tallowedActions: retrievable ? [\"keep_raw\", \"summarize\"] : [\"keep_raw\"],\n\t\t\t\thardRetained: false,\n\t\t\t\treasonCodes: retrievable ? [\"decision_bearing_summarizable\"] : [\"decision_bearing_no_retrieval_path\"],\n\t\t\t};\n\t\tcase \"useful\":\n\t\t\t// Can be summarized or evicted if budget is tight AND retrieval is available.\n\t\t\t// With no retrieval/source ref, the conservative default is keep_raw only.\n\t\t\treturn {\n\t\t\t\tallowedActions: retrievable\n\t\t\t\t\t? [\"keep_raw\", \"summarize\", \"pack_to_artifact\", \"drop_from_prompt\"]\n\t\t\t\t\t: [\"keep_raw\"],\n\t\t\t\thardRetained: false,\n\t\t\t\treasonCodes: retrievable ? [\"useful_retrievable\"] : [\"useful_no_retrieval_path\"],\n\t\t\t};\n\t\tcase \"ephemeral\":\n\t\t\t// Should not be resent raw after immediate use (broad search output, command\n\t\t\t// logs, old file reads). Packing/dropping requires a retrieval path per the\n\t\t\t// \"retrieval precedes aggressive eviction\" ground rule.\n\t\t\treturn {\n\t\t\t\tallowedActions: retrievable\n\t\t\t\t\t? [\"keep_raw\", \"pack_to_artifact\", \"drop_from_prompt\"]\n\t\t\t\t\t: [\"keep_raw\", \"pack_to_artifact\"],\n\t\t\t\thardRetained: false,\n\t\t\t\treasonCodes: retrievable ? [\"ephemeral_retrievable\"] : [\"ephemeral_no_retrieval_path\"],\n\t\t\t};\n\t\tcase \"expired\":\n\t\t\t// Should not be included in prompt unless explicitly retrieved.\n\t\t\treturn {\n\t\t\t\tallowedActions: [\"drop_from_prompt\"],\n\t\t\t\thardRetained: false,\n\t\t\t\treasonCodes: [\"expired\"],\n\t\t\t};\n\t\tdefault:\n\t\t\treturn {\n\t\t\t\tallowedActions: [\"keep_raw\"],\n\t\t\t\thardRetained: false,\n\t\t\t\treasonCodes: [\"unknown_retention_class\"],\n\t\t\t};\n\t}\n}\n\nexport function canDropFromPrompt(item: ContextItem): boolean {\n\treturn evaluateRetentionEligibility(item).allowedActions.includes(\"drop_from_prompt\");\n}\n\nexport function canSummarize(item: ContextItem): boolean {\n\treturn evaluateRetentionEligibility(item).allowedActions.includes(\"summarize\");\n}\n\nexport function canPackToArtifact(item: ContextItem): boolean {\n\treturn evaluateRetentionEligibility(item).allowedActions.includes(\"pack_to_artifact\");\n}\n"]}