open-multi-agent-kit 1.0.0 → 1.2.2

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 (622) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/README.md +2 -2
  3. package/dist/approval-api.d.ts +5 -0
  4. package/dist/approval-api.d.ts.map +1 -0
  5. package/dist/approval-api.js +5 -0
  6. package/dist/approval-api.js.map +1 -0
  7. package/dist/cli/help.d.ts.map +1 -1
  8. package/dist/cli/help.js +1 -0
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/session-picker.d.ts +2 -2
  11. package/dist/cli/session-picker.d.ts.map +1 -1
  12. package/dist/cli/session-picker.js.map +1 -1
  13. package/dist/commands/verified-run-cli.d.ts.map +1 -1
  14. package/dist/commands/verified-run-cli.js +69 -22
  15. package/dist/commands/verified-run-cli.js.map +1 -1
  16. package/dist/commands/verified-run-signal.d.ts +3 -0
  17. package/dist/commands/verified-run-signal.d.ts.map +1 -0
  18. package/dist/commands/verified-run-signal.js +15 -0
  19. package/dist/commands/verified-run-signal.js.map +1 -0
  20. package/dist/coordination/awareness.d.ts +32 -0
  21. package/dist/coordination/awareness.d.ts.map +1 -0
  22. package/dist/coordination/awareness.js +55 -0
  23. package/dist/coordination/awareness.js.map +1 -0
  24. package/dist/coordination/broker.d.ts +79 -0
  25. package/dist/coordination/broker.d.ts.map +1 -0
  26. package/dist/coordination/broker.js +202 -0
  27. package/dist/coordination/broker.js.map +1 -0
  28. package/dist/coordination/index.d.ts +16 -0
  29. package/dist/coordination/index.d.ts.map +1 -0
  30. package/dist/coordination/index.js +16 -0
  31. package/dist/coordination/index.js.map +1 -0
  32. package/dist/coordination/integration.d.ts +59 -0
  33. package/dist/coordination/integration.d.ts.map +1 -0
  34. package/dist/coordination/integration.js +126 -0
  35. package/dist/coordination/integration.js.map +1 -0
  36. package/dist/coordination/operation.d.ts +107 -0
  37. package/dist/coordination/operation.d.ts.map +1 -0
  38. package/dist/coordination/operation.js +231 -0
  39. package/dist/coordination/operation.js.map +1 -0
  40. package/dist/coordination/resource.d.ts +33 -0
  41. package/dist/coordination/resource.d.ts.map +1 -0
  42. package/dist/coordination/resource.js +95 -0
  43. package/dist/coordination/resource.js.map +1 -0
  44. package/dist/coordination/session.d.ts +74 -0
  45. package/dist/coordination/session.d.ts.map +1 -0
  46. package/dist/coordination/session.js +143 -0
  47. package/dist/coordination/session.js.map +1 -0
  48. package/dist/coordination/types.d.ts +68 -0
  49. package/dist/coordination/types.d.ts.map +1 -0
  50. package/dist/coordination/types.js +26 -0
  51. package/dist/coordination/types.js.map +1 -0
  52. package/dist/core/adaptorch-bridge.d.ts +1 -1
  53. package/dist/core/adaptorch-bridge.d.ts.map +1 -1
  54. package/dist/core/adaptorch-bridge.js +1 -1
  55. package/dist/core/adaptorch-bridge.js.map +1 -1
  56. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  57. package/dist/core/agent-session-runtime.js +16 -16
  58. package/dist/core/agent-session-runtime.js.map +1 -1
  59. package/dist/core/agent-session.d.ts.map +1 -1
  60. package/dist/core/agent-session.js +35 -15
  61. package/dist/core/agent-session.js.map +1 -1
  62. package/dist/core/canonical-json.d.ts +3 -0
  63. package/dist/core/canonical-json.d.ts.map +1 -0
  64. package/dist/core/canonical-json.js +20 -0
  65. package/dist/core/canonical-json.js.map +1 -0
  66. package/dist/core/compaction/compaction.d.ts +7 -3
  67. package/dist/core/compaction/compaction.d.ts.map +1 -1
  68. package/dist/core/compaction/compaction.js +14 -29
  69. package/dist/core/compaction/compaction.js.map +1 -1
  70. package/dist/core/compaction/control-state.d.ts +41 -0
  71. package/dist/core/compaction/control-state.d.ts.map +1 -0
  72. package/dist/core/compaction/control-state.js +85 -0
  73. package/dist/core/compaction/control-state.js.map +1 -0
  74. package/dist/core/compaction/fallback.d.ts +71 -0
  75. package/dist/core/compaction/fallback.d.ts.map +1 -0
  76. package/dist/core/compaction/fallback.js +118 -0
  77. package/dist/core/compaction/fallback.js.map +1 -0
  78. package/dist/core/compaction/index.d.ts +2 -0
  79. package/dist/core/compaction/index.d.ts.map +1 -1
  80. package/dist/core/compaction/index.js +2 -0
  81. package/dist/core/compaction/index.js.map +1 -1
  82. package/dist/core/compaction/model-failover.d.ts +21 -0
  83. package/dist/core/compaction/model-failover.d.ts.map +1 -0
  84. package/dist/core/compaction/model-failover.js +46 -0
  85. package/dist/core/compaction/model-failover.js.map +1 -0
  86. package/dist/core/compaction/session-summary.d.ts +21 -0
  87. package/dist/core/compaction/session-summary.d.ts.map +1 -0
  88. package/dist/core/compaction/session-summary.js +27 -0
  89. package/dist/core/compaction/session-summary.js.map +1 -0
  90. package/dist/core/context-budget-headroom-candidates.d.ts +16 -1
  91. package/dist/core/context-budget-headroom-candidates.d.ts.map +1 -1
  92. package/dist/core/context-budget-headroom-candidates.js +31 -13
  93. package/dist/core/context-budget-headroom-candidates.js.map +1 -1
  94. package/dist/core/context-budget-headroom-types.d.ts +7 -1
  95. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  96. package/dist/core/context-budget-headroom-types.js +0 -1
  97. package/dist/core/context-budget-headroom-types.js.map +1 -1
  98. package/dist/core/context-budget-headroom.d.ts +10 -0
  99. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  100. package/dist/core/context-budget-headroom.js +11 -2
  101. package/dist/core/context-budget-headroom.js.map +1 -1
  102. package/dist/core/context-budget-v2-global-pass.d.ts +21 -0
  103. package/dist/core/context-budget-v2-global-pass.d.ts.map +1 -0
  104. package/dist/core/context-budget-v2-global-pass.js +203 -0
  105. package/dist/core/context-budget-v2-global-pass.js.map +1 -0
  106. package/dist/core/context-budget-v2-planned-items.d.ts +12 -0
  107. package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -1
  108. package/dist/core/context-budget-v2-planned-items.js +21 -2
  109. package/dist/core/context-budget-v2-planned-items.js.map +1 -1
  110. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  111. package/dist/core/context-budget-v2-planner.js +18 -19
  112. package/dist/core/context-budget-v2-planner.js.map +1 -1
  113. package/dist/core/context-budget-v2-scoring.d.ts +7 -1
  114. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  115. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  116. package/dist/core/context-budget-v2-selection.d.ts +3 -0
  117. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  118. package/dist/core/context-budget-v2-selection.js +6 -2
  119. package/dist/core/context-budget-v2-selection.js.map +1 -1
  120. package/dist/core/context-budget-v2-types.d.ts +6 -1
  121. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  122. package/dist/core/context-budget-v2-types.js +6 -1
  123. package/dist/core/context-budget-v2-types.js.map +1 -1
  124. package/dist/core/export-html/template.js +14 -0
  125. package/dist/core/extensions/index.d.ts +1 -1
  126. package/dist/core/extensions/index.d.ts.map +1 -1
  127. package/dist/core/extensions/index.js.map +1 -1
  128. package/dist/core/extensions/session-lifecycle-types.d.ts +14 -0
  129. package/dist/core/extensions/session-lifecycle-types.d.ts.map +1 -0
  130. package/dist/core/extensions/session-lifecycle-types.js +2 -0
  131. package/dist/core/extensions/session-lifecycle-types.js.map +1 -0
  132. package/dist/core/extensions/types.d.ts +4 -9
  133. package/dist/core/extensions/types.d.ts.map +1 -1
  134. package/dist/core/extensions/types.js.map +1 -1
  135. package/dist/core/http-dispatcher.d.ts.map +1 -1
  136. package/dist/core/http-dispatcher.js +10 -1
  137. package/dist/core/http-dispatcher.js.map +1 -1
  138. package/dist/core/keybindings.d.ts.map +1 -1
  139. package/dist/core/keybindings.js +3 -6
  140. package/dist/core/keybindings.js.map +1 -1
  141. package/dist/core/legacy-resource-policy.d.ts +7 -0
  142. package/dist/core/legacy-resource-policy.d.ts.map +1 -0
  143. package/dist/core/legacy-resource-policy.js +9 -0
  144. package/dist/core/legacy-resource-policy.js.map +1 -0
  145. package/dist/core/mcp/config.d.ts.map +1 -1
  146. package/dist/core/mcp/config.js +17 -3
  147. package/dist/core/mcp/config.js.map +1 -1
  148. package/dist/core/mcp/connection-queue.d.ts +12 -0
  149. package/dist/core/mcp/connection-queue.d.ts.map +1 -0
  150. package/dist/core/mcp/connection-queue.js +46 -0
  151. package/dist/core/mcp/connection-queue.js.map +1 -0
  152. package/dist/core/mcp/manager-runtime.d.ts +55 -0
  153. package/dist/core/mcp/manager-runtime.d.ts.map +1 -0
  154. package/dist/core/mcp/manager-runtime.js +66 -0
  155. package/dist/core/mcp/manager-runtime.js.map +1 -0
  156. package/dist/core/mcp/manager.d.ts +12 -68
  157. package/dist/core/mcp/manager.d.ts.map +1 -1
  158. package/dist/core/mcp/manager.js +33 -113
  159. package/dist/core/mcp/manager.js.map +1 -1
  160. package/dist/core/mcp/protocol.d.ts.map +1 -1
  161. package/dist/core/mcp/protocol.js +70 -40
  162. package/dist/core/mcp/protocol.js.map +1 -1
  163. package/dist/core/package-manager.d.ts +4 -0
  164. package/dist/core/package-manager.d.ts.map +1 -1
  165. package/dist/core/package-manager.js +59 -129
  166. package/dist/core/package-manager.js.map +1 -1
  167. package/dist/core/package-resource-patterns.d.ts +7 -0
  168. package/dist/core/package-resource-patterns.d.ts.map +1 -0
  169. package/dist/core/package-resource-patterns.js +99 -0
  170. package/dist/core/package-resource-patterns.js.map +1 -0
  171. package/dist/core/provider-default-models.d.ts +1 -0
  172. package/dist/core/provider-default-models.d.ts.map +1 -1
  173. package/dist/core/provider-default-models.js +1 -0
  174. package/dist/core/provider-default-models.js.map +1 -1
  175. package/dist/core/provider-display-names.d.ts.map +1 -1
  176. package/dist/core/provider-display-names.js +1 -0
  177. package/dist/core/provider-display-names.js.map +1 -1
  178. package/dist/core/provider-resilience.d.ts +9 -0
  179. package/dist/core/provider-resilience.d.ts.map +1 -1
  180. package/dist/core/provider-resilience.js +15 -0
  181. package/dist/core/provider-resilience.js.map +1 -1
  182. package/dist/core/reasoning-router-resolver.d.ts +7 -0
  183. package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
  184. package/dist/core/reasoning-router-resolver.js +21 -3
  185. package/dist/core/reasoning-router-resolver.js.map +1 -1
  186. package/dist/core/reasoning-router-v4.d.ts +13 -7
  187. package/dist/core/reasoning-router-v4.d.ts.map +1 -1
  188. package/dist/core/reasoning-router-v4.js +8 -5
  189. package/dist/core/reasoning-router-v4.js.map +1 -1
  190. package/dist/core/resource-loader.d.ts.map +1 -1
  191. package/dist/core/resource-loader.js +4 -8
  192. package/dist/core/resource-loader.js.map +1 -1
  193. package/dist/core/run-execution-api.d.ts +8 -1
  194. package/dist/core/run-execution-api.d.ts.map +1 -1
  195. package/dist/core/run-execution-api.js +4 -0
  196. package/dist/core/run-execution-api.js.map +1 -1
  197. package/dist/core/run-journal.d.ts +1 -2
  198. package/dist/core/run-journal.d.ts.map +1 -1
  199. package/dist/core/run-journal.js +2 -19
  200. package/dist/core/run-journal.js.map +1 -1
  201. package/dist/core/session-compaction-service.d.ts +15 -0
  202. package/dist/core/session-compaction-service.d.ts.map +1 -1
  203. package/dist/core/session-compaction-service.js +17 -4
  204. package/dist/core/session-compaction-service.js.map +1 -1
  205. package/dist/core/session-listing.d.ts +51 -0
  206. package/dist/core/session-listing.d.ts.map +1 -0
  207. package/dist/core/session-listing.js +111 -0
  208. package/dist/core/session-listing.js.map +1 -0
  209. package/dist/core/session-manager.d.ts +12 -37
  210. package/dist/core/session-manager.d.ts.map +1 -1
  211. package/dist/core/session-manager.js +12 -118
  212. package/dist/core/session-manager.js.map +1 -1
  213. package/dist/core/session-message-text.d.ts +5 -0
  214. package/dist/core/session-message-text.d.ts.map +1 -0
  215. package/dist/core/session-message-text.js +9 -0
  216. package/dist/core/session-message-text.js.map +1 -0
  217. package/dist/core/skills-catalog-cache.d.ts +2 -0
  218. package/dist/core/skills-catalog-cache.d.ts.map +1 -1
  219. package/dist/core/skills-catalog-cache.js +71 -17
  220. package/dist/core/skills-catalog-cache.js.map +1 -1
  221. package/dist/core/todo-runtime-state.d.ts +12 -0
  222. package/dist/core/todo-runtime-state.d.ts.map +1 -1
  223. package/dist/core/todo-runtime-state.js +23 -0
  224. package/dist/core/todo-runtime-state.js.map +1 -1
  225. package/dist/core/tools/read.d.ts.map +1 -1
  226. package/dist/core/tools/read.js +1 -1
  227. package/dist/core/tools/read.js.map +1 -1
  228. package/dist/core/verified-run/authority-clock.d.ts +3 -0
  229. package/dist/core/verified-run/authority-clock.d.ts.map +1 -0
  230. package/dist/core/verified-run/authority-clock.js +25 -0
  231. package/dist/core/verified-run/authority-clock.js.map +1 -0
  232. package/dist/core/verified-run/authority-errors.d.ts +10 -0
  233. package/dist/core/verified-run/authority-errors.d.ts.map +1 -0
  234. package/dist/core/verified-run/authority-errors.js +17 -0
  235. package/dist/core/verified-run/authority-errors.js.map +1 -0
  236. package/dist/core/verified-run/authority-events.d.ts +126 -0
  237. package/dist/core/verified-run/authority-events.d.ts.map +1 -0
  238. package/dist/core/verified-run/authority-events.js +482 -0
  239. package/dist/core/verified-run/authority-events.js.map +1 -0
  240. package/dist/core/verified-run/authority-journal.d.ts +27 -0
  241. package/dist/core/verified-run/authority-journal.d.ts.map +1 -0
  242. package/dist/core/verified-run/authority-journal.js +83 -0
  243. package/dist/core/verified-run/authority-journal.js.map +1 -0
  244. package/dist/core/verified-run/authority-meaning.d.ts +13 -0
  245. package/dist/core/verified-run/authority-meaning.d.ts.map +1 -0
  246. package/dist/core/verified-run/authority-meaning.js +40 -0
  247. package/dist/core/verified-run/authority-meaning.js.map +1 -0
  248. package/dist/core/verified-run/authority-runtime.d.ts +82 -0
  249. package/dist/core/verified-run/authority-runtime.d.ts.map +1 -0
  250. package/dist/core/verified-run/authority-runtime.js +169 -0
  251. package/dist/core/verified-run/authority-runtime.js.map +1 -0
  252. package/dist/core/verified-run/authority-store.d.ts +204 -0
  253. package/dist/core/verified-run/authority-store.d.ts.map +1 -0
  254. package/dist/core/verified-run/authority-store.js +852 -0
  255. package/dist/core/verified-run/authority-store.js.map +1 -0
  256. package/dist/core/verified-run/broker.d.ts +8 -1
  257. package/dist/core/verified-run/broker.d.ts.map +1 -1
  258. package/dist/core/verified-run/broker.js +94 -90
  259. package/dist/core/verified-run/broker.js.map +1 -1
  260. package/dist/core/verified-run/candidate-policy.d.ts +10 -0
  261. package/dist/core/verified-run/candidate-policy.d.ts.map +1 -0
  262. package/dist/core/verified-run/candidate-policy.js +33 -0
  263. package/dist/core/verified-run/candidate-policy.js.map +1 -0
  264. package/dist/core/verified-run/candidate.d.ts.map +1 -1
  265. package/dist/core/verified-run/candidate.js +5 -10
  266. package/dist/core/verified-run/candidate.js.map +1 -1
  267. package/dist/core/verified-run/coordinator.d.ts +35 -17
  268. package/dist/core/verified-run/coordinator.d.ts.map +1 -1
  269. package/dist/core/verified-run/coordinator.js +111 -57
  270. package/dist/core/verified-run/coordinator.js.map +1 -1
  271. package/dist/core/verified-run/dag-phase.d.ts.map +1 -1
  272. package/dist/core/verified-run/dag-phase.js +1 -1
  273. package/dist/core/verified-run/dag-phase.js.map +1 -1
  274. package/dist/core/verified-run/dag-recovery.d.ts +5 -1
  275. package/dist/core/verified-run/dag-recovery.d.ts.map +1 -1
  276. package/dist/core/verified-run/dag-recovery.js +10 -4
  277. package/dist/core/verified-run/dag-recovery.js.map +1 -1
  278. package/dist/core/verified-run/event-parser.d.ts.map +1 -1
  279. package/dist/core/verified-run/event-parser.js +33 -0
  280. package/dist/core/verified-run/event-parser.js.map +1 -1
  281. package/dist/core/verified-run/evidence-read-projection.d.ts +4 -0
  282. package/dist/core/verified-run/evidence-read-projection.d.ts.map +1 -0
  283. package/dist/core/verified-run/evidence-read-projection.js +27 -0
  284. package/dist/core/verified-run/evidence-read-projection.js.map +1 -0
  285. package/dist/core/verified-run/evidence.d.ts +12 -1
  286. package/dist/core/verified-run/evidence.d.ts.map +1 -1
  287. package/dist/core/verified-run/evidence.js +51 -2
  288. package/dist/core/verified-run/evidence.js.map +1 -1
  289. package/dist/core/verified-run/git-candidate-preflight.d.ts +4 -0
  290. package/dist/core/verified-run/git-candidate-preflight.d.ts.map +1 -0
  291. package/dist/core/verified-run/git-candidate-preflight.js +34 -0
  292. package/dist/core/verified-run/git-candidate-preflight.js.map +1 -0
  293. package/dist/core/verified-run/git-effect-supervisor.d.ts +24 -0
  294. package/dist/core/verified-run/git-effect-supervisor.d.ts.map +1 -0
  295. package/dist/core/verified-run/git-effect-supervisor.js +174 -0
  296. package/dist/core/verified-run/git-effect-supervisor.js.map +1 -0
  297. package/dist/core/verified-run/git-execution.d.ts +27 -0
  298. package/dist/core/verified-run/git-execution.d.ts.map +1 -0
  299. package/dist/core/verified-run/git-execution.js +118 -0
  300. package/dist/core/verified-run/git-execution.js.map +1 -0
  301. package/dist/core/verified-run/git-plumbing.d.ts +37 -0
  302. package/dist/core/verified-run/git-plumbing.d.ts.map +1 -0
  303. package/dist/core/verified-run/git-plumbing.js +171 -0
  304. package/dist/core/verified-run/git-plumbing.js.map +1 -0
  305. package/dist/core/verified-run/git-publication-worker.d.ts +2 -0
  306. package/dist/core/verified-run/git-publication-worker.d.ts.map +1 -0
  307. package/dist/core/verified-run/git-publication-worker.js +54 -0
  308. package/dist/core/verified-run/git-publication-worker.js.map +1 -0
  309. package/dist/core/verified-run/git-sandbox-layout.d.ts +4 -0
  310. package/dist/core/verified-run/git-sandbox-layout.d.ts.map +1 -0
  311. package/dist/core/verified-run/git-sandbox-layout.js +29 -0
  312. package/dist/core/verified-run/git-sandbox-layout.js.map +1 -0
  313. package/dist/core/verified-run/git-worker-protocol.d.ts +19 -0
  314. package/dist/core/verified-run/git-worker-protocol.d.ts.map +1 -0
  315. package/dist/core/verified-run/git-worker-protocol.js +109 -0
  316. package/dist/core/verified-run/git-worker-protocol.js.map +1 -0
  317. package/dist/core/verified-run/git-worker-runtime.d.ts +19 -0
  318. package/dist/core/verified-run/git-worker-runtime.d.ts.map +1 -0
  319. package/dist/core/verified-run/git-worker-runtime.js +74 -0
  320. package/dist/core/verified-run/git-worker-runtime.js.map +1 -0
  321. package/dist/core/verified-run/journal.d.ts +2 -3
  322. package/dist/core/verified-run/journal.d.ts.map +1 -1
  323. package/dist/core/verified-run/journal.js.map +1 -1
  324. package/dist/core/verified-run/namespace-identity.d.ts.map +1 -1
  325. package/dist/core/verified-run/namespace-identity.js +1 -1
  326. package/dist/core/verified-run/namespace-identity.js.map +1 -1
  327. package/dist/core/verified-run/owned-execution.d.ts +10 -1
  328. package/dist/core/verified-run/owned-execution.d.ts.map +1 -1
  329. package/dist/core/verified-run/owned-execution.js +124 -31
  330. package/dist/core/verified-run/owned-execution.js.map +1 -1
  331. package/dist/core/verified-run/phase-context.d.ts +2 -0
  332. package/dist/core/verified-run/phase-context.d.ts.map +1 -1
  333. package/dist/core/verified-run/phase-context.js.map +1 -1
  334. package/dist/core/verified-run/projection.d.ts.map +1 -1
  335. package/dist/core/verified-run/projection.js +51 -1
  336. package/dist/core/verified-run/projection.js.map +1 -1
  337. package/dist/core/verified-run/publish-preflight.d.ts +12 -0
  338. package/dist/core/verified-run/publish-preflight.d.ts.map +1 -0
  339. package/dist/core/verified-run/publish-preflight.js +38 -0
  340. package/dist/core/verified-run/publish-preflight.js.map +1 -0
  341. package/dist/core/verified-run/publish-start.d.ts +5 -0
  342. package/dist/core/verified-run/publish-start.d.ts.map +1 -0
  343. package/dist/core/verified-run/publish-start.js +7 -0
  344. package/dist/core/verified-run/publish-start.js.map +1 -0
  345. package/dist/core/verified-run/recovery-projection.d.ts.map +1 -1
  346. package/dist/core/verified-run/recovery-projection.js +5 -0
  347. package/dist/core/verified-run/recovery-projection.js.map +1 -1
  348. package/dist/core/verified-run/recovery.d.ts +5 -1
  349. package/dist/core/verified-run/recovery.d.ts.map +1 -1
  350. package/dist/core/verified-run/recovery.js +10 -4
  351. package/dist/core/verified-run/recovery.js.map +1 -1
  352. package/dist/core/verified-run/run-plan.d.ts +17 -0
  353. package/dist/core/verified-run/run-plan.d.ts.map +1 -0
  354. package/dist/core/verified-run/run-plan.js +18 -0
  355. package/dist/core/verified-run/run-plan.js.map +1 -0
  356. package/dist/core/verified-run/run-publish-owned.d.ts +6 -0
  357. package/dist/core/verified-run/run-publish-owned.d.ts.map +1 -0
  358. package/dist/core/verified-run/run-publish-owned.js +199 -0
  359. package/dist/core/verified-run/run-publish-owned.js.map +1 -0
  360. package/dist/core/verified-run/run-publish.d.ts +19 -0
  361. package/dist/core/verified-run/run-publish.d.ts.map +1 -0
  362. package/dist/core/verified-run/run-publish.js +210 -0
  363. package/dist/core/verified-run/run-publish.js.map +1 -0
  364. package/dist/core/verified-run/run-status-owned.d.ts +4 -0
  365. package/dist/core/verified-run/run-status-owned.d.ts.map +1 -0
  366. package/dist/core/verified-run/run-status-owned.js +14 -0
  367. package/dist/core/verified-run/run-status-owned.js.map +1 -0
  368. package/dist/core/verified-run/run-status.d.ts +133 -0
  369. package/dist/core/verified-run/run-status.d.ts.map +1 -0
  370. package/dist/core/verified-run/run-status.js +193 -0
  371. package/dist/core/verified-run/run-status.js.map +1 -0
  372. package/dist/core/verified-run/run-types.d.ts +31 -0
  373. package/dist/core/verified-run/run-types.d.ts.map +1 -1
  374. package/dist/core/verified-run/run-types.js.map +1 -1
  375. package/dist/core/verified-run/storage.d.ts.map +1 -1
  376. package/dist/core/verified-run/storage.js +1 -1
  377. package/dist/core/verified-run/storage.js.map +1 -1
  378. package/dist/core/verified-run/supervisor-adapter.d.ts +77 -0
  379. package/dist/core/verified-run/supervisor-adapter.d.ts.map +1 -0
  380. package/dist/core/verified-run/supervisor-adapter.js +164 -0
  381. package/dist/core/verified-run/supervisor-adapter.js.map +1 -0
  382. package/dist/core/verified-run/verification-phase.d.ts.map +1 -1
  383. package/dist/core/verified-run/verification-phase.js +1 -1
  384. package/dist/core/verified-run/verification-phase.js.map +1 -1
  385. package/dist/core/verified-run/writer-phase.d.ts.map +1 -1
  386. package/dist/core/verified-run/writer-phase.js +1 -0
  387. package/dist/core/verified-run/writer-phase.js.map +1 -1
  388. package/dist/core/verified-run/writer-recovery.d.ts +2 -0
  389. package/dist/core/verified-run/writer-recovery.d.ts.map +1 -1
  390. package/dist/core/verified-run/writer-recovery.js +1 -0
  391. package/dist/core/verified-run/writer-recovery.js.map +1 -1
  392. package/dist/core/workload-shard-execution-types.d.ts +4 -1
  393. package/dist/core/workload-shard-execution-types.d.ts.map +1 -1
  394. package/dist/core/workload-shard-execution-types.js.map +1 -1
  395. package/dist/core/workload-shard-executor.d.ts +2 -2
  396. package/dist/core/workload-shard-executor.d.ts.map +1 -1
  397. package/dist/core/workload-shard-executor.js +16 -37
  398. package/dist/core/workload-shard-executor.js.map +1 -1
  399. package/dist/core/workload-shard-frontier.d.ts +5 -0
  400. package/dist/core/workload-shard-frontier.d.ts.map +1 -0
  401. package/dist/core/workload-shard-frontier.js +47 -0
  402. package/dist/core/workload-shard-frontier.js.map +1 -0
  403. package/dist/core/workload-shard-runner.d.ts.map +1 -1
  404. package/dist/core/workload-shard-runner.js +28 -17
  405. package/dist/core/workload-shard-runner.js.map +1 -1
  406. package/dist/index.d.ts +4 -5
  407. package/dist/index.d.ts.map +1 -1
  408. package/dist/index.js +3 -4
  409. package/dist/index.js.map +1 -1
  410. package/dist/main.d.ts.map +1 -1
  411. package/dist/main.js +2 -1
  412. package/dist/main.js.map +1 -1
  413. package/dist/metacognition/bridge-result.d.ts +148 -0
  414. package/dist/metacognition/bridge-result.d.ts.map +1 -0
  415. package/dist/metacognition/bridge-result.js +46 -0
  416. package/dist/metacognition/bridge-result.js.map +1 -0
  417. package/dist/metacognition/calibration-selective.d.ts +78 -0
  418. package/dist/metacognition/calibration-selective.d.ts.map +1 -0
  419. package/dist/metacognition/calibration-selective.js +167 -0
  420. package/dist/metacognition/calibration-selective.js.map +1 -0
  421. package/dist/metacognition/calibration.d.ts +62 -0
  422. package/dist/metacognition/calibration.d.ts.map +1 -0
  423. package/dist/metacognition/calibration.js +122 -0
  424. package/dist/metacognition/calibration.js.map +1 -0
  425. package/dist/metacognition/checkpoint.d.ts +60 -0
  426. package/dist/metacognition/checkpoint.d.ts.map +1 -0
  427. package/dist/metacognition/checkpoint.js +57 -0
  428. package/dist/metacognition/checkpoint.js.map +1 -0
  429. package/dist/metacognition/context7.d.ts +48 -0
  430. package/dist/metacognition/context7.d.ts.map +1 -0
  431. package/dist/metacognition/context7.js +154 -0
  432. package/dist/metacognition/context7.js.map +1 -0
  433. package/dist/metacognition/decision.d.ts +44 -0
  434. package/dist/metacognition/decision.d.ts.map +1 -0
  435. package/dist/metacognition/decision.js +111 -0
  436. package/dist/metacognition/decision.js.map +1 -0
  437. package/dist/metacognition/evaluation.d.ts +73 -0
  438. package/dist/metacognition/evaluation.d.ts.map +1 -0
  439. package/dist/metacognition/evaluation.js +97 -0
  440. package/dist/metacognition/evaluation.js.map +1 -0
  441. package/dist/metacognition/index.d.ts +32 -0
  442. package/dist/metacognition/index.d.ts.map +1 -0
  443. package/dist/metacognition/index.js +32 -0
  444. package/dist/metacognition/index.js.map +1 -0
  445. package/dist/metacognition/knowledge-action.d.ts +43 -0
  446. package/dist/metacognition/knowledge-action.d.ts.map +1 -0
  447. package/dist/metacognition/knowledge-action.js +60 -0
  448. package/dist/metacognition/knowledge-action.js.map +1 -0
  449. package/dist/metacognition/knowledge.d.ts +84 -0
  450. package/dist/metacognition/knowledge.d.ts.map +1 -0
  451. package/dist/metacognition/knowledge.js +165 -0
  452. package/dist/metacognition/knowledge.js.map +1 -0
  453. package/dist/metacognition/obligations.d.ts +82 -0
  454. package/dist/metacognition/obligations.d.ts.map +1 -0
  455. package/dist/metacognition/obligations.js +139 -0
  456. package/dist/metacognition/obligations.js.map +1 -0
  457. package/dist/metacognition/observation-validity.d.ts +83 -0
  458. package/dist/metacognition/observation-validity.d.ts.map +1 -0
  459. package/dist/metacognition/observation-validity.js +118 -0
  460. package/dist/metacognition/observation-validity.js.map +1 -0
  461. package/dist/metacognition/observe.d.ts +45 -0
  462. package/dist/metacognition/observe.d.ts.map +1 -0
  463. package/dist/metacognition/observe.js +59 -0
  464. package/dist/metacognition/observe.js.map +1 -0
  465. package/dist/metacognition/policy.d.ts +53 -0
  466. package/dist/metacognition/policy.d.ts.map +1 -0
  467. package/dist/metacognition/policy.js +155 -0
  468. package/dist/metacognition/policy.js.map +1 -0
  469. package/dist/metacognition/predictions.d.ts +78 -0
  470. package/dist/metacognition/predictions.d.ts.map +1 -0
  471. package/dist/metacognition/predictions.js +181 -0
  472. package/dist/metacognition/predictions.js.map +1 -0
  473. package/dist/metacognition/retrieval.d.ts +53 -0
  474. package/dist/metacognition/retrieval.d.ts.map +1 -0
  475. package/dist/metacognition/retrieval.js +170 -0
  476. package/dist/metacognition/retrieval.js.map +1 -0
  477. package/dist/metacognition/risk.d.ts +48 -0
  478. package/dist/metacognition/risk.d.ts.map +1 -0
  479. package/dist/metacognition/risk.js +179 -0
  480. package/dist/metacognition/risk.js.map +1 -0
  481. package/dist/metacognition/route-economics.d.ts +95 -0
  482. package/dist/metacognition/route-economics.d.ts.map +1 -0
  483. package/dist/metacognition/route-economics.js +110 -0
  484. package/dist/metacognition/route-economics.js.map +1 -0
  485. package/dist/metacognition/runtime-bridge.d.ts +81 -0
  486. package/dist/metacognition/runtime-bridge.d.ts.map +1 -0
  487. package/dist/metacognition/runtime-bridge.js +458 -0
  488. package/dist/metacognition/runtime-bridge.js.map +1 -0
  489. package/dist/metacognition/skills.d.ts +42 -0
  490. package/dist/metacognition/skills.d.ts.map +1 -0
  491. package/dist/metacognition/skills.js +220 -0
  492. package/dist/metacognition/skills.js.map +1 -0
  493. package/dist/metacognition/state.d.ts +110 -0
  494. package/dist/metacognition/state.d.ts.map +1 -0
  495. package/dist/metacognition/state.js +52 -0
  496. package/dist/metacognition/state.js.map +1 -0
  497. package/dist/metacognition/validation.d.ts +19 -0
  498. package/dist/metacognition/validation.d.ts.map +1 -0
  499. package/dist/metacognition/validation.js +58 -0
  500. package/dist/metacognition/validation.js.map +1 -0
  501. package/dist/metacognition/verification.d.ts +93 -0
  502. package/dist/metacognition/verification.d.ts.map +1 -0
  503. package/dist/metacognition/verification.js +132 -0
  504. package/dist/metacognition/verification.js.map +1 -0
  505. package/dist/metacognition/verifier.d.ts +49 -0
  506. package/dist/metacognition/verifier.d.ts.map +1 -0
  507. package/dist/metacognition/verifier.js +88 -0
  508. package/dist/metacognition/verifier.js.map +1 -0
  509. package/dist/modes/interactive/components/assistant-message.d.ts +2 -0
  510. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  511. package/dist/modes/interactive/components/assistant-message.js +42 -26
  512. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  513. package/dist/modes/interactive/components/chat-container.d.ts +7 -0
  514. package/dist/modes/interactive/components/chat-container.d.ts.map +1 -0
  515. package/dist/modes/interactive/components/chat-container.js +14 -0
  516. package/dist/modes/interactive/components/chat-container.js.map +1 -0
  517. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  518. package/dist/modes/interactive/components/dynamic-border.js +4 -2
  519. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  520. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  521. package/dist/modes/interactive/components/login-dialog.js +0 -1
  522. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  523. package/dist/modes/interactive/components/session-selector-async-search.d.ts +33 -0
  524. package/dist/modes/interactive/components/session-selector-async-search.d.ts.map +1 -0
  525. package/dist/modes/interactive/components/session-selector-async-search.js +109 -0
  526. package/dist/modes/interactive/components/session-selector-async-search.js.map +1 -0
  527. package/dist/modes/interactive/components/session-selector-loaders.d.ts +6 -0
  528. package/dist/modes/interactive/components/session-selector-loaders.d.ts.map +1 -0
  529. package/dist/modes/interactive/components/session-selector-loaders.js +9 -0
  530. package/dist/modes/interactive/components/session-selector-loaders.js.map +1 -0
  531. package/dist/modes/interactive/components/session-selector-search.d.ts +1 -1
  532. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  533. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  534. package/dist/modes/interactive/components/session-selector-tree.d.ts +16 -0
  535. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -0
  536. package/dist/modes/interactive/components/session-selector-tree.js +46 -0
  537. package/dist/modes/interactive/components/session-selector-tree.js.map +1 -0
  538. package/dist/modes/interactive/components/session-selector.d.ts +6 -1
  539. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  540. package/dist/modes/interactive/components/session-selector.js +36 -76
  541. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  542. package/dist/modes/interactive/components/tool-execution-images.d.ts +32 -0
  543. package/dist/modes/interactive/components/tool-execution-images.d.ts.map +1 -0
  544. package/dist/modes/interactive/components/tool-execution-images.js +144 -0
  545. package/dist/modes/interactive/components/tool-execution-images.js.map +1 -0
  546. package/dist/modes/interactive/components/tool-execution.d.ts +3 -4
  547. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  548. package/dist/modes/interactive/components/tool-execution.js +22 -71
  549. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  550. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  551. package/dist/modes/interactive/interactive-mode.js +7 -9
  552. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  553. package/dist/modes/interactive/tui-diagnostics.d.ts +8 -4
  554. package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -1
  555. package/dist/modes/interactive/tui-diagnostics.js +35 -1
  556. package/dist/modes/interactive/tui-diagnostics.js.map +1 -1
  557. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  558. package/dist/modes/rpc/rpc-mode.js +14 -17
  559. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  560. package/dist/modes/rpc/rpc-response.d.ts +5 -0
  561. package/dist/modes/rpc/rpc-response.d.ts.map +1 -0
  562. package/dist/modes/rpc/rpc-response.js +17 -0
  563. package/dist/modes/rpc/rpc-response.js.map +1 -0
  564. package/dist/observation/identity.d.ts +14 -0
  565. package/dist/observation/identity.d.ts.map +1 -0
  566. package/dist/observation/identity.js +29 -0
  567. package/dist/observation/identity.js.map +1 -0
  568. package/dist/observation/index.d.ts +13 -0
  569. package/dist/observation/index.d.ts.map +1 -0
  570. package/dist/observation/index.js +13 -0
  571. package/dist/observation/index.js.map +1 -0
  572. package/dist/observation/observe-mode.d.ts +46 -0
  573. package/dist/observation/observe-mode.d.ts.map +1 -0
  574. package/dist/observation/observe-mode.js +83 -0
  575. package/dist/observation/observe-mode.js.map +1 -0
  576. package/dist/observation/store.d.ts +53 -0
  577. package/dist/observation/store.d.ts.map +1 -0
  578. package/dist/observation/store.js +126 -0
  579. package/dist/observation/store.js.map +1 -0
  580. package/dist/observation/types.d.ts +72 -0
  581. package/dist/observation/types.d.ts.map +1 -0
  582. package/dist/observation/types.js +9 -0
  583. package/dist/observation/types.js.map +1 -0
  584. package/dist/observation/view.d.ts +39 -0
  585. package/dist/observation/view.d.ts.map +1 -0
  586. package/dist/observation/view.js +173 -0
  587. package/dist/observation/view.js.map +1 -0
  588. package/dist/utils/exif-orientation.d.ts.map +1 -1
  589. package/dist/utils/exif-orientation.js +2 -3
  590. package/dist/utils/exif-orientation.js.map +1 -1
  591. package/dist/utils/tools-manager.d.ts.map +1 -1
  592. package/dist/utils/tools-manager.js +3 -5
  593. package/dist/utils/tools-manager.js.map +1 -1
  594. package/docs/adaptorch-preview-spec.md +7 -7
  595. package/docs/atomic-commit-planning.md +69 -0
  596. package/docs/compaction.md +26 -1
  597. package/docs/extensions.md +14 -0
  598. package/docs/mcp.md +22 -0
  599. package/docs/metacognition.md +130 -0
  600. package/docs/model-catalog-refresh.md +218 -1
  601. package/docs/models.md +1 -1
  602. package/docs/providers.md +53 -0
  603. package/docs/runtime-algorithms.md +156 -7
  604. package/docs/session-format.md +8 -0
  605. package/docs/sessions.md +19 -0
  606. package/docs/skills.md +19 -0
  607. package/docs/terminal-setup.md +15 -0
  608. package/docs/tui.md +41 -0
  609. package/docs/verified-run-safety.md +75 -0
  610. package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
  611. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  612. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  613. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  614. package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
  615. package/examples/extensions/gondolin/package-lock.json +2 -2
  616. package/examples/extensions/gondolin/package.json +1 -1
  617. package/examples/extensions/sandbox/package-lock.json +2 -2
  618. package/examples/extensions/sandbox/package.json +1 -1
  619. package/examples/extensions/with-deps/package-lock.json +2 -2
  620. package/examples/extensions/with-deps/package.json +1 -1
  621. package/npm-shrinkwrap.json +18 -18
  622. package/package.json +6 -8
@@ -0,0 +1,203 @@
1
+ import { chooseHeadroomRepresentation, deriveRepresentationCandidates, isRepresentationEligible, scoreRepresentationPreferenceV2, } from "./context-budget-headroom.js";
2
+ import { writeRepresentationCacheV2 } from "./context-budget-v2-cache.js";
3
+ import { selectOptionalItem, toSelected } from "./context-budget-v2-selection.js";
4
+ /**
5
+ * Everything that happens once every tier has spent its floor:
6
+ *
7
+ * 1. the global pass admits still-unselected optional items in rank order
8
+ * under their tier ceilings;
9
+ * 2. the exchange pass lets a still-omitted item pay for its admission by
10
+ * stepping selected items down to their cheapest admissible form (F04);
11
+ * 3. the promotion pass spends whatever is left on costlier representations
12
+ * the policy prefers (F03).
13
+ *
14
+ * Breadth is settled before quality: an exchange that admits another item
15
+ * cannot be undone by a promotion, because promotion only spends the budget
16
+ * the exchange left. Returns the final token usage.
17
+ */
18
+ export function runGlobalSelectionPass(optionalPlanned, state) {
19
+ let usedTokens = state.usedTokens;
20
+ for (const planned of optionalPlanned) {
21
+ if (state.selection.has(planned.item.id))
22
+ continue;
23
+ usedTokens = selectOptionalItem(planned, { ...state, usedTokens });
24
+ }
25
+ usedTokens = exchangeForOmittedItems(optionalPlanned, { ...state, usedTokens });
26
+ return promoteSelectedRepresentations(optionalPlanned, { ...state, usedTokens });
27
+ }
28
+ /** Representations the planner priced for this item, in the order selection saw them. */
29
+ function candidatesFor(planned, state) {
30
+ return (state.resolvedCandidates?.get(planned.item.id) ??
31
+ planned.candidates ??
32
+ planned.item.representations ??
33
+ deriveRepresentationCandidates(planned.item, state.qualityPolicy));
34
+ }
35
+ /** Cheapest representation the policy would allow at all, ignoring budget. */
36
+ function cheapestAdmissible(planned, state) {
37
+ let best;
38
+ for (const candidate of candidatesFor(planned, state)) {
39
+ if (candidate.kind === "omit")
40
+ continue;
41
+ if (!isRepresentationEligible(candidate, planned.item, state.qualityPolicy))
42
+ continue;
43
+ if (best === undefined || candidate.estimatedTokens < best.estimatedTokens)
44
+ best = candidate;
45
+ }
46
+ return best;
47
+ }
48
+ /**
49
+ * Plan the cheapest set of step-downs that makes room for `cost` tokens in
50
+ * `target`'s tier, or `undefined` when no admissible set exists.
51
+ *
52
+ * Donors are ordered by the preference the policy loses per token freed, so
53
+ * the item whose fidelity matters least to the policy yields first. A donor is
54
+ * skipped when stepping it down would drop its tier below the floor it is
55
+ * currently honouring: the floor is a reservation, not a budget to raid. An
56
+ * empty swap list is legitimate — an earlier exchange may already have freed
57
+ * enough for this item.
58
+ */
59
+ function planExchange(target, cost, optionalPlanned, state, usedTokens) {
60
+ const ceilingOf = (tier) => state.allocation.get(tier)?.ceiling ?? state.available;
61
+ const floorOf = (tier) => state.allocation.get(tier)?.floor ?? 0;
62
+ const projected = new Map();
63
+ const tierNow = (tier) => projected.get(tier) ?? state.tierUsed[tier];
64
+ const donors = [];
65
+ for (const donor of optionalPlanned) {
66
+ if (donor.item.id === target.item.id)
67
+ continue;
68
+ const current = state.selection.get(donor.item.id);
69
+ if (current === undefined || current.kind === "omit")
70
+ continue;
71
+ const cheap = cheapestAdmissible(donor, state);
72
+ if (cheap === undefined || cheap.estimatedTokens >= current.estimatedTokens)
73
+ continue;
74
+ const freed = current.estimatedTokens - cheap.estimatedTokens;
75
+ const from = toCandidate(current);
76
+ const loss = scoreRepresentationPreferenceV2(from, donor.item, state.qualityPolicy, false) -
77
+ scoreRepresentationPreferenceV2(cheap, donor.item, state.qualityPolicy, false);
78
+ donors.push({ donor, current, cheap, freed, lossPerToken: loss / freed });
79
+ }
80
+ donors.sort((a, b) => a.lossPerToken - b.lossPerToken || a.donor.item.id.localeCompare(b.donor.item.id));
81
+ const swaps = [];
82
+ let freed = 0;
83
+ const fits = () => cost <= state.available - (usedTokens - freed) && tierNow(target.item.tier) + cost <= ceilingOf(target.item.tier);
84
+ for (const entry of donors) {
85
+ if (fits())
86
+ break;
87
+ const after = tierNow(entry.donor.item.tier) - entry.freed;
88
+ if (tierNow(entry.donor.item.tier) >= floorOf(entry.donor.item.tier) && after < floorOf(entry.donor.item.tier)) {
89
+ continue;
90
+ }
91
+ projected.set(entry.donor.item.tier, after);
92
+ swaps.push({ donor: entry.donor, from: entry.current, to: entry.cheap });
93
+ freed += entry.freed;
94
+ }
95
+ return fits() ? { swaps, freed } : undefined;
96
+ }
97
+ /**
98
+ * Admit still-omitted items by exchanging fidelity for coverage (audit F04).
99
+ *
100
+ * Ranking prices an item by its cheapest admissible representation but the
101
+ * selector may admit it at full text, so one item ranked on a 10-token pointer
102
+ * can spend 100 and displace two 45-token items the policy would rather keep.
103
+ * After the global pass, each omitted item in rank order may buy its cheapest
104
+ * admissible form by stepping selected items down to theirs. The whole exchange
105
+ * is applied or none of it is; tier floors and ceilings hold throughout. This
106
+ * is a bounded repair, not joint `(item, representation)` optimization.
107
+ */
108
+ function exchangeForOmittedItems(optionalPlanned, state) {
109
+ let usedTokens = state.usedTokens;
110
+ for (const target of optionalPlanned) {
111
+ if (state.selection.has(target.item.id))
112
+ continue;
113
+ const admit = cheapestAdmissible(target, state);
114
+ if (admit === undefined)
115
+ continue;
116
+ const exchange = planExchange(target, admit.estimatedTokens, optionalPlanned, state, usedTokens);
117
+ if (exchange === undefined)
118
+ continue;
119
+ for (const swap of exchange.swaps) {
120
+ state.tierUsed[swap.donor.item.tier] += swap.to.estimatedTokens - swap.from.estimatedTokens;
121
+ state.selection.set(swap.donor.item.id, toSelected(swap.donor.item.id, swap.to));
122
+ writeSelected(swap.donor, swap.to, state);
123
+ }
124
+ state.tierUsed[target.item.tier] += admit.estimatedTokens;
125
+ state.selection.set(target.item.id, toSelected(target.item.id, admit));
126
+ writeSelected(target, admit, state);
127
+ // The global pass already recorded this item as omitted; an admitted item
128
+ // must leave that list, or the plan reports it as both included and
129
+ // omitted and bills its full text in omittedTokens.
130
+ const omittedIndex = state.omitted.findIndex((entry) => entry.id === target.item.id);
131
+ if (omittedIndex >= 0)
132
+ state.omitted.splice(omittedIndex, 1);
133
+ usedTokens = usedTokens - exchange.freed + admit.estimatedTokens;
134
+ }
135
+ return usedTokens;
136
+ }
137
+ function writeSelected(planned, selected, state) {
138
+ if (!state.cache)
139
+ return;
140
+ writeRepresentationCacheV2({
141
+ planned,
142
+ selected,
143
+ cache: state.cache,
144
+ materializedEnabled: planned.item.representations === undefined,
145
+ });
146
+ }
147
+ /**
148
+ * Re-offer each selected optional item its costlier representations against
149
+ * the leftover budget (audit F03). The floor pass admits an item at whatever
150
+ * fits the tier's remaining floor and the global pass then skips it by id,
151
+ * so a pointer chosen under a small floor stayed a pointer while most of the
152
+ * budget went unused. Runs once, in selection-rank order, after the global
153
+ * pass; a swap happens only when the selection policy itself prefers the
154
+ * costlier representation under the real remainder and it fits both the
155
+ * tier ceiling and the global budget. It never demotes, never touches hard
156
+ * items, and never spends beyond either cap. Returns the updated token usage.
157
+ */
158
+ function promoteSelectedRepresentations(optionalPlanned, state) {
159
+ let usedTokens = state.usedTokens;
160
+ for (const planned of optionalPlanned) {
161
+ const current = state.selection.get(planned.item.id);
162
+ if (current === undefined || current.kind === "omit")
163
+ continue;
164
+ const remaining = state.available - usedTokens;
165
+ if (remaining <= 0)
166
+ break;
167
+ const ceiling = state.allocation.get(planned.item.tier)?.ceiling ?? state.available;
168
+ const tierUsedWithout = state.tierUsed[planned.item.tier] - current.estimatedTokens;
169
+ const upgrades = candidatesFor(planned, state).filter((candidate) => candidate.kind !== "omit" &&
170
+ candidate.estimatedTokens > current.estimatedTokens &&
171
+ candidate.estimatedTokens <= remaining + current.estimatedTokens &&
172
+ tierUsedWithout + candidate.estimatedTokens <= ceiling);
173
+ if (upgrades.length === 0)
174
+ continue;
175
+ // Let the policy rank the current choice against every affordable upgrade
176
+ // under the real leftover; only a strictly costlier winner is a promotion.
177
+ const chosen = chooseHeadroomRepresentation({ ...planned.item, representations: [...upgrades, toCandidate(current)] }, {
178
+ tierUsedTokens: tierUsedWithout,
179
+ tierCeilingTokens: ceiling,
180
+ remainingGlobalTokens: remaining + current.estimatedTokens,
181
+ }, state.qualityPolicy);
182
+ if (chosen.kind === "omit" || chosen.estimatedTokens <= current.estimatedTokens)
183
+ continue;
184
+ writeSelected(planned, chosen, state);
185
+ state.tierUsed[planned.item.tier] = tierUsedWithout + chosen.estimatedTokens;
186
+ usedTokens += chosen.estimatedTokens - current.estimatedTokens;
187
+ state.selection.set(planned.item.id, toSelected(planned.item.id, chosen));
188
+ }
189
+ return usedTokens;
190
+ }
191
+ function toCandidate(selected) {
192
+ return {
193
+ kind: selected.kind,
194
+ text: selected.text,
195
+ estimatedTokens: selected.estimatedTokens,
196
+ fidelity: selected.fidelity,
197
+ sourceRef: selected.sourceRef,
198
+ summaryHash: selected.summaryHash,
199
+ compressorId: selected.compressorId,
200
+ cache: selected.cache,
201
+ };
202
+ }
203
+ //# sourceMappingURL=context-budget-v2-global-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-budget-v2-global-pass.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-global-pass.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ContextRepresentationCandidateV2,\n\tchooseHeadroomRepresentation,\n\tderiveRepresentationCandidates,\n\tisRepresentationEligible,\n\tscoreRepresentationPreferenceV2,\n} from \"./context-budget-headroom.ts\";\nimport { writeRepresentationCacheV2 } from \"./context-budget-v2-cache.ts\";\nimport type { PlannedItemV2 } from \"./context-budget-v2-scoring.ts\";\nimport { type OptionalSelectionState, selectOptionalItem, toSelected } from \"./context-budget-v2-selection.ts\";\nimport type { SelectedRepresentationV2 } from \"./context-budget-v2-types.ts\";\n\n/** Selection state after the floor-reservation passes; `usedTokens` is the running total so far. */\nexport type GlobalPassState = Omit<OptionalSelectionState, \"floorReservation\">;\n\nexport type PromotionState = Pick<\n\tGlobalPassState,\n\t| \"allocation\"\n\t| \"available\"\n\t| \"cache\"\n\t| \"qualityPolicy\"\n\t| \"resolvedCandidates\"\n\t| \"selection\"\n\t| \"tierUsed\"\n\t| \"usedTokens\"\n>;\n\n/**\n * Everything that happens once every tier has spent its floor:\n *\n * 1. the global pass admits still-unselected optional items in rank order\n * under their tier ceilings;\n * 2. the exchange pass lets a still-omitted item pay for its admission by\n * stepping selected items down to their cheapest admissible form (F04);\n * 3. the promotion pass spends whatever is left on costlier representations\n * the policy prefers (F03).\n *\n * Breadth is settled before quality: an exchange that admits another item\n * cannot be undone by a promotion, because promotion only spends the budget\n * the exchange left. Returns the final token usage.\n */\nexport function runGlobalSelectionPass(optionalPlanned: readonly PlannedItemV2[], state: GlobalPassState): number {\n\tlet usedTokens = state.usedTokens;\n\tfor (const planned of optionalPlanned) {\n\t\tif (state.selection.has(planned.item.id)) continue;\n\t\tusedTokens = selectOptionalItem(planned, { ...state, usedTokens });\n\t}\n\tusedTokens = exchangeForOmittedItems(optionalPlanned, { ...state, usedTokens });\n\treturn promoteSelectedRepresentations(optionalPlanned, { ...state, usedTokens });\n}\n\n/** Representations the planner priced for this item, in the order selection saw them. */\nfunction candidatesFor(planned: PlannedItemV2, state: PromotionState): readonly ContextRepresentationCandidateV2[] {\n\treturn (\n\t\tstate.resolvedCandidates?.get(planned.item.id) ??\n\t\tplanned.candidates ??\n\t\tplanned.item.representations ??\n\t\tderiveRepresentationCandidates(planned.item, state.qualityPolicy)\n\t);\n}\n\n/** Cheapest representation the policy would allow at all, ignoring budget. */\nfunction cheapestAdmissible(\n\tplanned: PlannedItemV2,\n\tstate: PromotionState,\n): ContextRepresentationCandidateV2 | undefined {\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tfor (const candidate of candidatesFor(planned, state)) {\n\t\tif (candidate.kind === \"omit\") continue;\n\t\tif (!isRepresentationEligible(candidate, planned.item, state.qualityPolicy)) continue;\n\t\tif (best === undefined || candidate.estimatedTokens < best.estimatedTokens) best = candidate;\n\t}\n\treturn best;\n}\n\ninterface PlannedExchange {\n\treadonly swaps: ReadonlyArray<{\n\t\treadonly donor: PlannedItemV2;\n\t\treadonly from: SelectedRepresentationV2;\n\t\treadonly to: ContextRepresentationCandidateV2;\n\t}>;\n\treadonly freed: number;\n}\n\n/**\n * Plan the cheapest set of step-downs that makes room for `cost` tokens in\n * `target`'s tier, or `undefined` when no admissible set exists.\n *\n * Donors are ordered by the preference the policy loses per token freed, so\n * the item whose fidelity matters least to the policy yields first. A donor is\n * skipped when stepping it down would drop its tier below the floor it is\n * currently honouring: the floor is a reservation, not a budget to raid. An\n * empty swap list is legitimate — an earlier exchange may already have freed\n * enough for this item.\n */\nfunction planExchange(\n\ttarget: PlannedItemV2,\n\tcost: number,\n\toptionalPlanned: readonly PlannedItemV2[],\n\tstate: PromotionState,\n\tusedTokens: number,\n): PlannedExchange | undefined {\n\tconst ceilingOf = (tier: PlannedItemV2[\"item\"][\"tier\"]): number =>\n\t\tstate.allocation.get(tier)?.ceiling ?? state.available;\n\tconst floorOf = (tier: PlannedItemV2[\"item\"][\"tier\"]): number => state.allocation.get(tier)?.floor ?? 0;\n\tconst projected = new Map<string, number>();\n\tconst tierNow = (tier: PlannedItemV2[\"item\"][\"tier\"]): number => projected.get(tier) ?? state.tierUsed[tier];\n\n\tconst donors = [];\n\tfor (const donor of optionalPlanned) {\n\t\tif (donor.item.id === target.item.id) continue;\n\t\tconst current = state.selection.get(donor.item.id);\n\t\tif (current === undefined || current.kind === \"omit\") continue;\n\t\tconst cheap = cheapestAdmissible(donor, state);\n\t\tif (cheap === undefined || cheap.estimatedTokens >= current.estimatedTokens) continue;\n\t\tconst freed = current.estimatedTokens - cheap.estimatedTokens;\n\t\tconst from = toCandidate(current);\n\t\tconst loss =\n\t\t\tscoreRepresentationPreferenceV2(from, donor.item, state.qualityPolicy, false) -\n\t\t\tscoreRepresentationPreferenceV2(cheap, donor.item, state.qualityPolicy, false);\n\t\tdonors.push({ donor, current, cheap, freed, lossPerToken: loss / freed });\n\t}\n\tdonors.sort((a, b) => a.lossPerToken - b.lossPerToken || a.donor.item.id.localeCompare(b.donor.item.id));\n\n\tconst swaps: Array<PlannedExchange[\"swaps\"][number]> = [];\n\tlet freed = 0;\n\tconst fits = (): boolean =>\n\t\tcost <= state.available - (usedTokens - freed) && tierNow(target.item.tier) + cost <= ceilingOf(target.item.tier);\n\tfor (const entry of donors) {\n\t\tif (fits()) break;\n\t\tconst after = tierNow(entry.donor.item.tier) - entry.freed;\n\t\tif (tierNow(entry.donor.item.tier) >= floorOf(entry.donor.item.tier) && after < floorOf(entry.donor.item.tier)) {\n\t\t\tcontinue;\n\t\t}\n\t\tprojected.set(entry.donor.item.tier, after);\n\t\tswaps.push({ donor: entry.donor, from: entry.current, to: entry.cheap });\n\t\tfreed += entry.freed;\n\t}\n\treturn fits() ? { swaps, freed } : undefined;\n}\n\n/**\n * Admit still-omitted items by exchanging fidelity for coverage (audit F04).\n *\n * Ranking prices an item by its cheapest admissible representation but the\n * selector may admit it at full text, so one item ranked on a 10-token pointer\n * can spend 100 and displace two 45-token items the policy would rather keep.\n * After the global pass, each omitted item in rank order may buy its cheapest\n * admissible form by stepping selected items down to theirs. The whole exchange\n * is applied or none of it is; tier floors and ceilings hold throughout. This\n * is a bounded repair, not joint `(item, representation)` optimization.\n */\nfunction exchangeForOmittedItems(optionalPlanned: readonly PlannedItemV2[], state: GlobalPassState): number {\n\tlet usedTokens = state.usedTokens;\n\tfor (const target of optionalPlanned) {\n\t\tif (state.selection.has(target.item.id)) continue;\n\t\tconst admit = cheapestAdmissible(target, state);\n\t\tif (admit === undefined) continue;\n\t\tconst exchange = planExchange(target, admit.estimatedTokens, optionalPlanned, state, usedTokens);\n\t\tif (exchange === undefined) continue;\n\t\tfor (const swap of exchange.swaps) {\n\t\t\tstate.tierUsed[swap.donor.item.tier] += swap.to.estimatedTokens - swap.from.estimatedTokens;\n\t\t\tstate.selection.set(swap.donor.item.id, toSelected(swap.donor.item.id, swap.to));\n\t\t\twriteSelected(swap.donor, swap.to, state);\n\t\t}\n\t\tstate.tierUsed[target.item.tier] += admit.estimatedTokens;\n\t\tstate.selection.set(target.item.id, toSelected(target.item.id, admit));\n\t\twriteSelected(target, admit, state);\n\t\t// The global pass already recorded this item as omitted; an admitted item\n\t\t// must leave that list, or the plan reports it as both included and\n\t\t// omitted and bills its full text in omittedTokens.\n\t\tconst omittedIndex = state.omitted.findIndex((entry) => entry.id === target.item.id);\n\t\tif (omittedIndex >= 0) state.omitted.splice(omittedIndex, 1);\n\t\tusedTokens = usedTokens - exchange.freed + admit.estimatedTokens;\n\t}\n\treturn usedTokens;\n}\n\nfunction writeSelected(\n\tplanned: PlannedItemV2,\n\tselected: ContextRepresentationCandidateV2,\n\tstate: PromotionState,\n): void {\n\tif (!state.cache) return;\n\twriteRepresentationCacheV2({\n\t\tplanned,\n\t\tselected,\n\t\tcache: state.cache,\n\t\tmaterializedEnabled: planned.item.representations === undefined,\n\t});\n}\n\n/**\n * Re-offer each selected optional item its costlier representations against\n * the leftover budget (audit F03). The floor pass admits an item at whatever\n * fits the tier's remaining floor and the global pass then skips it by id,\n * so a pointer chosen under a small floor stayed a pointer while most of the\n * budget went unused. Runs once, in selection-rank order, after the global\n * pass; a swap happens only when the selection policy itself prefers the\n * costlier representation under the real remainder and it fits both the\n * tier ceiling and the global budget. It never demotes, never touches hard\n * items, and never spends beyond either cap. Returns the updated token usage.\n */\nfunction promoteSelectedRepresentations(optionalPlanned: readonly PlannedItemV2[], state: PromotionState): number {\n\tlet usedTokens = state.usedTokens;\n\tfor (const planned of optionalPlanned) {\n\t\tconst current = state.selection.get(planned.item.id);\n\t\tif (current === undefined || current.kind === \"omit\") continue;\n\t\tconst remaining = state.available - usedTokens;\n\t\tif (remaining <= 0) break;\n\t\tconst ceiling = state.allocation.get(planned.item.tier)?.ceiling ?? state.available;\n\t\tconst tierUsedWithout = state.tierUsed[planned.item.tier] - current.estimatedTokens;\n\t\tconst upgrades = candidatesFor(planned, state).filter(\n\t\t\t(candidate) =>\n\t\t\t\tcandidate.kind !== \"omit\" &&\n\t\t\t\tcandidate.estimatedTokens > current.estimatedTokens &&\n\t\t\t\tcandidate.estimatedTokens <= remaining + current.estimatedTokens &&\n\t\t\t\ttierUsedWithout + candidate.estimatedTokens <= ceiling,\n\t\t);\n\t\tif (upgrades.length === 0) continue;\n\t\t// Let the policy rank the current choice against every affordable upgrade\n\t\t// under the real leftover; only a strictly costlier winner is a promotion.\n\t\tconst chosen = chooseHeadroomRepresentation(\n\t\t\t{ ...planned.item, representations: [...upgrades, toCandidate(current)] },\n\t\t\t{\n\t\t\t\ttierUsedTokens: tierUsedWithout,\n\t\t\t\ttierCeilingTokens: ceiling,\n\t\t\t\tremainingGlobalTokens: remaining + current.estimatedTokens,\n\t\t\t},\n\t\t\tstate.qualityPolicy,\n\t\t);\n\t\tif (chosen.kind === \"omit\" || chosen.estimatedTokens <= current.estimatedTokens) continue;\n\t\twriteSelected(planned, chosen, state);\n\t\tstate.tierUsed[planned.item.tier] = tierUsedWithout + chosen.estimatedTokens;\n\t\tusedTokens += chosen.estimatedTokens - current.estimatedTokens;\n\t\tstate.selection.set(planned.item.id, toSelected(planned.item.id, chosen));\n\t}\n\treturn usedTokens;\n}\n\nfunction toCandidate(selected: SelectedRepresentationV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: selected.kind,\n\t\ttext: selected.text,\n\t\testimatedTokens: selected.estimatedTokens,\n\t\tfidelity: selected.fidelity,\n\t\tsourceRef: selected.sourceRef,\n\t\tsummaryHash: selected.summaryHash,\n\t\tcompressorId: selected.compressorId,\n\t\tcache: selected.cache,\n\t};\n}\n"]}
@@ -1,5 +1,17 @@
1
1
  import { type ContextBudgetItemV2, deriveRepresentationCandidates } from "./context-budget-headroom.ts";
2
2
  import { type TokenCounterAdapter } from "./context-budget-token-counter.ts";
3
3
  import { type PlannedItemV2 } from "./context-budget-v2-scoring.ts";
4
+ /**
5
+ * Identity of the adapter that actually prices this run's text.
6
+ *
7
+ * Every representation price now depends on the planner's counter (audit F01),
8
+ * but the materialized representation key is bucketed rather than exact, so the
9
+ * static `heuristic-v1` default let two counters share one key space: a run
10
+ * could admit text at a price its own counter never produced, and the entry's
11
+ * `tokenizer_mismatch` check compared that constant against itself. Probe a
12
+ * non-empty string, because `countText("")` short-circuits to the fallback
13
+ * estimator in the registry and would report the wrong adapter.
14
+ */
15
+ export declare function resolveEffectiveTokenizerIdV2(tokenCounter: TokenCounterAdapter | undefined, modelId: string): string;
4
16
  export declare function createPlannedItems(items: readonly ContextBudgetItemV2[], tokenCounter: TokenCounterAdapter | undefined, modelId: string, qualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1]): PlannedItemV2[];
5
17
  //# sourceMappingURL=context-budget-v2-planned-items.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-v2-planned-items.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-planned-items.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,mBAAmB,EAExB,8BAA8B,EAI9B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAA8B,KAAK,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAEzG,OAAO,EAAE,KAAK,aAAa,EAA4B,MAAM,gCAAgC,CAAC;AAwB9F,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,YAAY,EAAE,mBAAmB,GAAG,SAAS,EAC7C,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,8BAA8B,CAAC,CAAC,CAAC,CAAC,GAClE,aAAa,EAAE,CAsBjB","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\tderiveRepresentationCandidates,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\tisRepresentationEligible,\n} from \"./context-budget-headroom.ts\";\nimport { createFallbackTokenCounter, type TokenCounterAdapter } from \"./context-budget-token-counter.ts\";\nimport { contentHashOf } from \"./context-budget-v2-plan-hash.ts\";\nimport { type PlannedItemV2, scoreContextBudgetItemV2 } from \"./context-budget-v2-scoring.ts\";\n\n// Cheapest representation the chooser could actually pick: only candidates\n// passing the selection policy gate count, so a pointer the chooser would\n// reject cannot make an item look cheaper to admit than it is.\nfunction minAdmissibleTokens(\n\tcandidates: readonly ContextRepresentationCandidateV2[],\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 | undefined,\n\tfullTokens: number,\n): number {\n\tlet min = Number.POSITIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (\n\t\t\tcandidate.kind !== \"omit\" &&\n\t\t\tisRepresentationEligible(candidate, item, policy) &&\n\t\t\tcandidate.estimatedTokens < min\n\t\t) {\n\t\t\tmin = candidate.estimatedTokens;\n\t\t}\n\t}\n\treturn Math.max(1, Number.isFinite(min) ? min : fullTokens);\n}\n\nexport function createPlannedItems(\n\titems: readonly ContextBudgetItemV2[],\n\ttokenCounter: TokenCounterAdapter | undefined,\n\tmodelId: string,\n\tqualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1],\n): PlannedItemV2[] {\n\tconst counter = tokenCounter ?? createFallbackTokenCounter();\n\treturn items.map((item) => {\n\t\tconst overrideTokenEstimate = item.tokenEstimate ?? counter.countText(item.text, modelId).tokens;\n\t\tconst itemWithTokens =\n\t\t\titem.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;\n\t\tconst fullTokens = fullTextTokens(itemWithTokens);\n\t\tconst isHard = item.priority === \"hard\" || item.required === true;\n\t\tconst baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);\n\t\tconst candidates =\n\t\t\titemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy);\n\t\treturn {\n\t\t\titem: itemWithTokens,\n\t\t\tfullTokens,\n\t\t\tadmissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),\n\t\t\tcontentHash: contentHashOf(item.text),\n\t\t\tbaseScore,\n\t\t\tredundancyPenalty: 0,\n\t\t\teffectiveScore: baseScore,\n\t\t\tisHard,\n\t\t};\n\t});\n}\n"]}
1
+ {"version":3,"file":"context-budget-v2-planned-items.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-planned-items.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,mBAAmB,EAExB,8BAA8B,EAI9B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAA8B,KAAK,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAEzG,OAAO,EAAE,KAAK,aAAa,EAA4B,MAAM,gCAAgC,CAAC;AAwB9F;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,mBAAmB,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpH;AAED,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,YAAY,EAAE,mBAAmB,GAAG,SAAS,EAC7C,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,8BAA8B,CAAC,CAAC,CAAC,CAAC,GAClE,aAAa,EAAE,CA2BjB","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\tderiveRepresentationCandidates,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\tisRepresentationEligible,\n} from \"./context-budget-headroom.ts\";\nimport { createFallbackTokenCounter, type TokenCounterAdapter } from \"./context-budget-token-counter.ts\";\nimport { contentHashOf } from \"./context-budget-v2-plan-hash.ts\";\nimport { type PlannedItemV2, scoreContextBudgetItemV2 } from \"./context-budget-v2-scoring.ts\";\n\n// Cheapest representation the chooser could actually pick: only candidates\n// passing the selection policy gate count, so a pointer the chooser would\n// reject cannot make an item look cheaper to admit than it is.\nfunction minAdmissibleTokens(\n\tcandidates: readonly ContextRepresentationCandidateV2[],\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 | undefined,\n\tfullTokens: number,\n): number {\n\tlet min = Number.POSITIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (\n\t\t\tcandidate.kind !== \"omit\" &&\n\t\t\tisRepresentationEligible(candidate, item, policy) &&\n\t\t\tcandidate.estimatedTokens < min\n\t\t) {\n\t\t\tmin = candidate.estimatedTokens;\n\t\t}\n\t}\n\treturn Math.max(1, Number.isFinite(min) ? min : fullTokens);\n}\n\n/**\n * Identity of the adapter that actually prices this run's text.\n *\n * Every representation price now depends on the planner's counter (audit F01),\n * but the materialized representation key is bucketed rather than exact, so the\n * static `heuristic-v1` default let two counters share one key space: a run\n * could admit text at a price its own counter never produced, and the entry's\n * `tokenizer_mismatch` check compared that constant against itself. Probe a\n * non-empty string, because `countText(\"\")` short-circuits to the fallback\n * estimator in the registry and would report the wrong adapter.\n */\nexport function resolveEffectiveTokenizerIdV2(tokenCounter: TokenCounterAdapter | undefined, modelId: string): string {\n\treturn (tokenCounter ?? createFallbackTokenCounter()).countText(\" \", modelId).adapterId;\n}\n\nexport function createPlannedItems(\n\titems: readonly ContextBudgetItemV2[],\n\ttokenCounter: TokenCounterAdapter | undefined,\n\tmodelId: string,\n\tqualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1],\n): PlannedItemV2[] {\n\tconst counter = tokenCounter ?? createFallbackTokenCounter();\n\t// One counter prices the full text and every derived representation, so\n\t// the selector compares like with like (audit F01: a ratio-priced summary\n\t// was admitted into a budget its materialized text could not fit).\n\tconst countTokens = (text: string): number => counter.countText(text, modelId).tokens;\n\treturn items.map((item) => {\n\t\tconst overrideTokenEstimate = item.tokenEstimate ?? countTokens(item.text);\n\t\tconst itemWithTokens =\n\t\t\titem.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;\n\t\tconst fullTokens = fullTextTokens(itemWithTokens);\n\t\tconst isHard = item.priority === \"hard\" || item.required === true;\n\t\tconst baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);\n\t\tconst candidates =\n\t\t\titemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy, countTokens);\n\t\treturn {\n\t\t\titem: itemWithTokens,\n\t\t\tfullTokens,\n\t\t\tadmissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),\n\t\t\tcandidates,\n\t\t\tcontentHash: contentHashOf(item.text),\n\t\t\tbaseScore,\n\t\t\tredundancyPenalty: 0,\n\t\t\teffectiveScore: baseScore,\n\t\t\tisHard,\n\t\t};\n\t});\n}\n"]}
@@ -16,19 +16,38 @@ function minAdmissibleTokens(candidates, item, policy, fullTokens) {
16
16
  }
17
17
  return Math.max(1, Number.isFinite(min) ? min : fullTokens);
18
18
  }
19
+ /**
20
+ * Identity of the adapter that actually prices this run's text.
21
+ *
22
+ * Every representation price now depends on the planner's counter (audit F01),
23
+ * but the materialized representation key is bucketed rather than exact, so the
24
+ * static `heuristic-v1` default let two counters share one key space: a run
25
+ * could admit text at a price its own counter never produced, and the entry's
26
+ * `tokenizer_mismatch` check compared that constant against itself. Probe a
27
+ * non-empty string, because `countText("")` short-circuits to the fallback
28
+ * estimator in the registry and would report the wrong adapter.
29
+ */
30
+ export function resolveEffectiveTokenizerIdV2(tokenCounter, modelId) {
31
+ return (tokenCounter ?? createFallbackTokenCounter()).countText(" ", modelId).adapterId;
32
+ }
19
33
  export function createPlannedItems(items, tokenCounter, modelId, qualityPolicy) {
20
34
  const counter = tokenCounter ?? createFallbackTokenCounter();
35
+ // One counter prices the full text and every derived representation, so
36
+ // the selector compares like with like (audit F01: a ratio-priced summary
37
+ // was admitted into a budget its materialized text could not fit).
38
+ const countTokens = (text) => counter.countText(text, modelId).tokens;
21
39
  return items.map((item) => {
22
- const overrideTokenEstimate = item.tokenEstimate ?? counter.countText(item.text, modelId).tokens;
40
+ const overrideTokenEstimate = item.tokenEstimate ?? countTokens(item.text);
23
41
  const itemWithTokens = item.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;
24
42
  const fullTokens = fullTextTokens(itemWithTokens);
25
43
  const isHard = item.priority === "hard" || item.required === true;
26
44
  const baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);
27
- const candidates = itemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy);
45
+ const candidates = itemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy, countTokens);
28
46
  return {
29
47
  item: itemWithTokens,
30
48
  fullTokens,
31
49
  admissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),
50
+ candidates,
32
51
  contentHash: contentHashOf(item.text),
33
52
  baseScore,
34
53
  redundancyPenalty: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-v2-planned-items.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-planned-items.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,8BAA8B,EAC9B,cAAc,EAEd,wBAAwB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,0BAA0B,EAA4B,MAAM,mCAAmC,CAAC;AACzG,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAsB,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE9F,2EAA2E;AAC3E,0EAA0E;AAC1E,+DAA+D;AAC/D,SAAS,mBAAmB,CAC3B,UAAuD,EACvD,IAAyB,EACzB,MAA2C,EAC3C,UAAkB,EACT;IACT,IAAI,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACnC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IACC,SAAS,CAAC,IAAI,KAAK,MAAM;YACzB,wBAAwB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC;YACjD,SAAS,CAAC,eAAe,GAAG,GAAG,EAC9B,CAAC;YACF,GAAG,GAAG,SAAS,CAAC,eAAe,CAAC;QACjC,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AAAA,CAC5D;AAED,MAAM,UAAU,kBAAkB,CACjC,KAAqC,EACrC,YAA6C,EAC7C,OAAe,EACf,aAAoE,EAClD;IAClB,MAAM,OAAO,GAAG,YAAY,IAAI,0BAA0B,EAAE,CAAC;IAC7D,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1B,MAAM,qBAAqB,GAAG,IAAI,CAAC,aAAa,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC;QACjG,MAAM,cAAc,GACnB,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7F,MAAM,UAAU,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;QAClE,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,wBAAwB,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAC3G,MAAM,UAAU,GACf,cAAc,CAAC,eAAe,IAAI,8BAA8B,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;QACjG,OAAO;YACN,IAAI,EAAE,cAAc;YACpB,UAAU;YACV,gBAAgB,EAAE,mBAAmB,CAAC,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,CAAC;YAC5F,WAAW,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YACrC,SAAS;YACT,iBAAiB,EAAE,CAAC;YACpB,cAAc,EAAE,SAAS;YACzB,MAAM;SACN,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACH","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\tderiveRepresentationCandidates,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\tisRepresentationEligible,\n} from \"./context-budget-headroom.ts\";\nimport { createFallbackTokenCounter, type TokenCounterAdapter } from \"./context-budget-token-counter.ts\";\nimport { contentHashOf } from \"./context-budget-v2-plan-hash.ts\";\nimport { type PlannedItemV2, scoreContextBudgetItemV2 } from \"./context-budget-v2-scoring.ts\";\n\n// Cheapest representation the chooser could actually pick: only candidates\n// passing the selection policy gate count, so a pointer the chooser would\n// reject cannot make an item look cheaper to admit than it is.\nfunction minAdmissibleTokens(\n\tcandidates: readonly ContextRepresentationCandidateV2[],\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 | undefined,\n\tfullTokens: number,\n): number {\n\tlet min = Number.POSITIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (\n\t\t\tcandidate.kind !== \"omit\" &&\n\t\t\tisRepresentationEligible(candidate, item, policy) &&\n\t\t\tcandidate.estimatedTokens < min\n\t\t) {\n\t\t\tmin = candidate.estimatedTokens;\n\t\t}\n\t}\n\treturn Math.max(1, Number.isFinite(min) ? min : fullTokens);\n}\n\nexport function createPlannedItems(\n\titems: readonly ContextBudgetItemV2[],\n\ttokenCounter: TokenCounterAdapter | undefined,\n\tmodelId: string,\n\tqualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1],\n): PlannedItemV2[] {\n\tconst counter = tokenCounter ?? createFallbackTokenCounter();\n\treturn items.map((item) => {\n\t\tconst overrideTokenEstimate = item.tokenEstimate ?? counter.countText(item.text, modelId).tokens;\n\t\tconst itemWithTokens =\n\t\t\titem.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;\n\t\tconst fullTokens = fullTextTokens(itemWithTokens);\n\t\tconst isHard = item.priority === \"hard\" || item.required === true;\n\t\tconst baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);\n\t\tconst candidates =\n\t\t\titemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy);\n\t\treturn {\n\t\t\titem: itemWithTokens,\n\t\t\tfullTokens,\n\t\t\tadmissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),\n\t\t\tcontentHash: contentHashOf(item.text),\n\t\t\tbaseScore,\n\t\t\tredundancyPenalty: 0,\n\t\t\teffectiveScore: baseScore,\n\t\t\tisHard,\n\t\t};\n\t});\n}\n"]}
1
+ {"version":3,"file":"context-budget-v2-planned-items.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-planned-items.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\tderiveRepresentationCandidates,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\tisRepresentationEligible,\n} from \"./context-budget-headroom.ts\";\nimport { createFallbackTokenCounter, type TokenCounterAdapter } from \"./context-budget-token-counter.ts\";\nimport { contentHashOf } from \"./context-budget-v2-plan-hash.ts\";\nimport { type PlannedItemV2, scoreContextBudgetItemV2 } from \"./context-budget-v2-scoring.ts\";\n\n// Cheapest representation the chooser could actually pick: only candidates\n// passing the selection policy gate count, so a pointer the chooser would\n// reject cannot make an item look cheaper to admit than it is.\nfunction minAdmissibleTokens(\n\tcandidates: readonly ContextRepresentationCandidateV2[],\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 | undefined,\n\tfullTokens: number,\n): number {\n\tlet min = Number.POSITIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (\n\t\t\tcandidate.kind !== \"omit\" &&\n\t\t\tisRepresentationEligible(candidate, item, policy) &&\n\t\t\tcandidate.estimatedTokens < min\n\t\t) {\n\t\t\tmin = candidate.estimatedTokens;\n\t\t}\n\t}\n\treturn Math.max(1, Number.isFinite(min) ? min : fullTokens);\n}\n\n/**\n * Identity of the adapter that actually prices this run's text.\n *\n * Every representation price now depends on the planner's counter (audit F01),\n * but the materialized representation key is bucketed rather than exact, so the\n * static `heuristic-v1` default let two counters share one key space: a run\n * could admit text at a price its own counter never produced, and the entry's\n * `tokenizer_mismatch` check compared that constant against itself. Probe a\n * non-empty string, because `countText(\"\")` short-circuits to the fallback\n * estimator in the registry and would report the wrong adapter.\n */\nexport function resolveEffectiveTokenizerIdV2(tokenCounter: TokenCounterAdapter | undefined, modelId: string): string {\n\treturn (tokenCounter ?? createFallbackTokenCounter()).countText(\" \", modelId).adapterId;\n}\n\nexport function createPlannedItems(\n\titems: readonly ContextBudgetItemV2[],\n\ttokenCounter: TokenCounterAdapter | undefined,\n\tmodelId: string,\n\tqualityPolicy?: Parameters<typeof deriveRepresentationCandidates>[1],\n): PlannedItemV2[] {\n\tconst counter = tokenCounter ?? createFallbackTokenCounter();\n\t// One counter prices the full text and every derived representation, so\n\t// the selector compares like with like (audit F01: a ratio-priced summary\n\t// was admitted into a budget its materialized text could not fit).\n\tconst countTokens = (text: string): number => counter.countText(text, modelId).tokens;\n\treturn items.map((item) => {\n\t\tconst overrideTokenEstimate = item.tokenEstimate ?? countTokens(item.text);\n\t\tconst itemWithTokens =\n\t\t\titem.tokenEstimate === undefined ? { ...item, tokenEstimate: overrideTokenEstimate } : item;\n\t\tconst fullTokens = fullTextTokens(itemWithTokens);\n\t\tconst isHard = item.priority === \"hard\" || item.required === true;\n\t\tconst baseScore = isHard ? Number.POSITIVE_INFINITY : scoreContextBudgetItemV2(itemWithTokens, fullTokens);\n\t\tconst candidates =\n\t\t\titemWithTokens.representations ?? deriveRepresentationCandidates(itemWithTokens, qualityPolicy, countTokens);\n\t\treturn {\n\t\t\titem: itemWithTokens,\n\t\t\tfullTokens,\n\t\t\tadmissibleTokens: minAdmissibleTokens(candidates, itemWithTokens, qualityPolicy, fullTokens),\n\t\t\tcandidates,\n\t\t\tcontentHash: contentHashOf(item.text),\n\t\t\tbaseScore,\n\t\t\tredundancyPenalty: 0,\n\t\t\teffectiveScore: baseScore,\n\t\t\tisHard,\n\t\t};\n\t});\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-v2-planner.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-planner.ts"],"names":[],"mappings":"AA8BA,OAAO,EAIN,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAI9B,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,0BAA0B,GAAG,yBAAyB,CAmOtG","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n} from \"./context-budget-headroom.ts\";\nimport {\n\tbuildContextBudgetPlanCacheKeyV2,\n\tcreateContextBudgetCacheKeyBaseV2,\n\tcreateContextBudgetSelectionCacheV2,\n\tfinalizeContextBudgetCacheTelemetryV2,\n\treadValidPlanCacheV2,\n\twritePlanCacheV2,\n} from \"./context-budget-v2-cache.ts\";\nimport { validateBudgetItems } from \"./context-budget-v2-input-validation.ts\";\nimport { buildObservability } from \"./context-budget-v2-observability.ts\";\nimport { computePlanHash } from \"./context-budget-v2-plan-hash.ts\";\nimport { createPlannedItems } from \"./context-budget-v2-planned-items.ts\";\nimport { compareOptionalForSelection } from \"./context-budget-v2-scoring.ts\";\nimport {\n\tapplyPlannerRedundancyPenalties,\n\tbuildTierAllocations,\n\tcreateTierUsage,\n\temitCoverageDiagnostics,\n\temitOmissionDiagnostics,\n\tselectHardItem,\n\tselectOptionalItem,\n} from \"./context-budget-v2-selection.ts\";\nimport { allocateTiers, computeTierDemand } from \"./context-budget-v2-tiers.ts\";\nimport {\n\tALL_TIERS_V2,\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\tDEFAULT_TIER_POLICY_V2,\n\ttype PromptContextBudgetInputV2,\n\ttype PromptContextBudgetPlanV2,\n\ttype QualityDiagnosticV2,\n\ttype SelectedRepresentationV2,\n\ttype TierBudgetPolicyV2,\n} from \"./context-budget-v2-types.ts\";\n\nexport function planPromptContextBudgetV2(input: PromptContextBudgetInputV2): PromptContextBudgetPlanV2 {\n\tconst modelId = input.modelId ?? \"unknown\";\n\tconst qualityPolicy = input.qualityPolicy ?? DEFAULT_HEADROOM_QUALITY_POLICY;\n\tconst policyVersion = input.policyVersion ?? CONTEXT_BUDGET_POLICY_VERSION_V2;\n\tconst diagnostics: QualityDiagnosticV2[] = [];\n\n\tconst maxTokens = normalizeBudgetTokens(\"maxTokens\", input.maxTokens, diagnostics);\n\tconst responseReserve = normalizeBudgetTokens(\n\t\t\"responseReserveTokens\",\n\t\tinput.responseReserveTokens ?? 0,\n\t\tdiagnostics,\n\t);\n\tconst safetyMargin = normalizeBudgetTokens(\n\t\t\"safetyMarginTokens\",\n\t\tinput.safetyMarginTokens ?? Math.ceil(maxTokens * 0.05),\n\t\tdiagnostics,\n\t);\n\tconst available = Math.max(0, maxTokens - responseReserve - safetyMargin);\n\n\tconst tierPolicy: Readonly<Record<ContextBudgetTierV2, TierBudgetPolicyV2>> = {\n\t\t...DEFAULT_TIER_POLICY_V2,\n\t\t...(input.tierPolicy ?? {}),\n\t};\n\tconst basePlanned = createPlannedItems(\n\t\tvalidateBudgetItems(input.items, diagnostics),\n\t\tinput.tokenCounter,\n\t\tmodelId,\n\t\tqualityPolicy,\n\t);\n\t// Sanitized items can share a key with valid input, but their diagnostics cannot.\n\tconst canUsePlanCache = diagnostics.length === 0;\n\tapplyPlannerRedundancyPenalties(basePlanned);\n\tconst rawTokens = basePlanned.reduce((sum, planned) => sum + planned.fullTokens, 0);\n\n\tconst demand = computeTierDemand(basePlanned);\n\tconst allocation = allocateTiers(available, tierPolicy, demand);\n\tconst cacheKeyBase = createContextBudgetCacheKeyBaseV2({\n\t\tbudgetBucket: input.cacheBudgetBucket ?? String(available),\n\t\tcacheInvalidationSnapshot: input.cacheInvalidationSnapshot ?? input.cacheProvider?.getInvalidationSnapshot?.(),\n\t\tmodelId,\n\t\tnamespace: input.cacheNamespace,\n\t\tpolicyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion,\n\t\tquery: input.query,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\ttokenizerId: input.tokenizerId,\n\t});\n\tconst planCacheKey = buildContextBudgetPlanCacheKeyV2({\n\t\tavailableTokens: available,\n\t\tkeyBase: cacheKeyBase,\n\t\tmaxTokens,\n\t\tplanned: basePlanned,\n\t\tpromptHash: input.promptHash,\n\t\tqualityPolicy,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\ttierPolicy,\n\t});\n\tconst cache = createContextBudgetSelectionCacheV2({\n\t\tkeyBase: cacheKeyBase,\n\t\tnowEpochMs: input.cacheNowEpochMs,\n\t\tplanCacheKey,\n\t\tprovider: input.cacheProvider,\n\t\tttlMs: input.cacheTtlMs,\n\t});\n\tconst cachedPlan = canUsePlanCache\n\t\t? readValidPlanCacheV2({\n\t\t\t\tavailableTokens: available,\n\t\t\t\tcache,\n\t\t\t\tkey: planCacheKey,\n\t\t\t\tplanned: basePlanned,\n\t\t\t})\n\t\t: undefined;\n\tif (cachedPlan) {\n\t\treturn cachedPlan;\n\t}\n\tconst selection = new Map<string, SelectedRepresentationV2>();\n\tconst tierUsed = createTierUsage();\n\tconst omitted: ContextBudgetItemV2[] = [];\n\tconst retrievalFallbacks: ContextSourceRefV2[] = [];\n\tlet usedTokens = 0;\n\n\tfor (const planned of basePlanned) {\n\t\tif (!planned.isHard) continue;\n\t\tusedTokens += selectHardItem(planned, selection, tierUsed);\n\t}\n\tif (usedTokens > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"hard_pin_over_capacity\",\n\t\t\tdetail: `hard-pinned context uses ${usedTokens} tokens against ${available} available tokens`,\n\t\t});\n\t}\n\n\tconst optionalPlanned = basePlanned.filter((planned) => !planned.isHard).sort(compareOptionalForSelection);\n\n\t// Floor-reservation pass: each tier spends its guaranteed claim (the\n\t// `allocated` field — hard demand, else demand capped at the floor) on its\n\t// own optional items before anything competes globally. A tier's floor is\n\t// therefore a real reservation, not a lower bound on its ceiling. Items\n\t// that cannot fit inside the remaining floor stay unselected for the\n\t// global pass; when the summed floors exceed the budget the reservation is\n\t// best-effort in tier order and the plan reports it explicitly.\n\t// One cache-resolution per item, shared by both passes so an item that is\n\t// deferred out of the floor and reconsidered globally does not double\n\t// count provider reads or redo materialization work.\n\tconst resolvedCandidates = new Map<string, readonly ContextRepresentationCandidateV2[]>();\n\tlet totalFloor = 0;\n\tfor (const tier of ALL_TIERS_V2) {\n\t\ttotalFloor += allocation.get(tier)?.floor ?? 0;\n\t}\n\tif (totalFloor > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"tier_floor_over_budget\",\n\t\t\tdetail: `tier floor guarantees total ${totalFloor} tokens against ${available} available; floors degrade to tier-order best effort`,\n\t\t});\n\t}\n\tfor (const tier of ALL_TIERS_V2) {\n\t\tconst floor = allocation.get(tier)?.floor ?? 0;\n\t\tif (floor <= 0 || tierUsed[tier] >= floor) continue;\n\t\tfor (const planned of optionalPlanned) {\n\t\t\tif (planned.item.tier !== tier || selection.has(planned.item.id)) continue;\n\t\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\t\tallocation,\n\t\t\t\tavailable,\n\t\t\t\tcache,\n\t\t\t\tdiagnostics,\n\t\t\t\tomitted,\n\t\t\t\tqualityPolicy,\n\t\t\t\tretrievalFallbacks,\n\t\t\t\tselection,\n\t\t\t\ttierUsed,\n\t\t\t\tusedTokens,\n\t\t\t\tfloorReservation: { tier, floorTokens: floor },\n\t\t\t\tresolvedCandidates,\n\t\t\t});\n\t\t\tif (tierUsed[tier] >= floor || usedTokens >= available) break;\n\t\t}\n\t}\n\n\tfor (const planned of optionalPlanned) {\n\t\tif (selection.has(planned.item.id)) continue;\n\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\tallocation,\n\t\t\tavailable,\n\t\t\tcache,\n\t\t\tdiagnostics,\n\t\t\tomitted,\n\t\t\tqualityPolicy,\n\t\t\tresolvedCandidates,\n\t\t\tretrievalFallbacks,\n\t\t\tselection,\n\t\t\ttierUsed,\n\t\t\tusedTokens,\n\t\t});\n\t}\n\n\tconst omittedHighPriority = omitted.some((item) => item.priority === \"high\" || item.required === true);\n\temitOmissionDiagnostics(omitted, diagnostics, retrievalFallbacks);\n\temitCoverageDiagnostics(input.query, selection, diagnostics);\n\n\tconst tierAllocations = buildTierAllocations(allocation, demand, tierUsed);\n\tconst omittedTokens = omitted.reduce(\n\t\t(sum, item) => sum + (basePlanned.find((planned) => planned.item.id === item.id)?.fullTokens ?? 0),\n\t\t0,\n\t);\n\tconst omittedItemIds = omitted.map((item) => item.id);\n\tconst planHash = computePlanHash({\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanned: basePlanned,\n\t\tselection,\n\t\tallocations: tierAllocations,\n\t\tomittedItemIds,\n\t});\n\tconst observability = buildObservability({\n\t\tavailable,\n\t\tdiagnostics,\n\t\tomittedItemIds,\n\t\tomittedTokens,\n\t\tplanHash,\n\t\trawTokens,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\tusedTokens,\n\t\tcacheTelemetry: finalizeContextBudgetCacheTelemetryV2(cache.telemetry, {\n\t\t\tomittedTokens,\n\t\t\trawTokens,\n\t\t\tusedTokens,\n\t\t}),\n\t});\n\n\tconst plan: PromptContextBudgetPlanV2 = {\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanHash,\n\t\tmaxTokens,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\tavailableTokens: available,\n\t\tusedTokens,\n\t\tomittedTokens,\n\t\trawTokens,\n\t\ttokenSavingsRatio: rawTokens > 0 ? Math.max(0, 1 - usedTokens / rawTokens) : 0,\n\t\temergency: usedTokens > available || diagnostics.some((diagnostic) => diagnostic.reason === \"invalid_budget\"),\n\t\tomittedHighPriority,\n\t\ttierAllocations,\n\t\tselectedRepresentations: [...selection.values()].sort((a, b) => a.itemId.localeCompare(b.itemId)),\n\t\tincludedItemIds: basePlanned\n\t\t\t.map((planned) => planned.item.id)\n\t\t\t.filter((id) => selection.has(id))\n\t\t\t.sort(),\n\t\tomittedItemIds,\n\t\tdiagnostics,\n\t\tretrievalFallbacks,\n\t\tobservability,\n\t};\n\tif (canUsePlanCache) {\n\t\twritePlanCacheV2({\n\t\t\tcache,\n\t\t\tkey: planCacheKey,\n\t\t\tplan,\n\t\t\tplanned: basePlanned,\n\t\t});\n\t}\n\treturn plan;\n}\n\nfunction normalizeBudgetTokens(field: string, value: number, diagnostics: QualityDiagnosticV2[]): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tdiagnostics.push({\n\t\t\treason: \"invalid_budget\",\n\t\t\tdetail: `${field} must be a non-negative finite number, got ${value}`,\n\t\t});\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
1
+ {"version":3,"file":"context-budget-v2-planner.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-planner.ts"],"names":[],"mappings":"AA+BA,OAAO,EAIN,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAI9B,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,0BAA0B,GAAG,yBAAyB,CAkOtG","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n} from \"./context-budget-headroom.ts\";\nimport {\n\tbuildContextBudgetPlanCacheKeyV2,\n\tcreateContextBudgetCacheKeyBaseV2,\n\tcreateContextBudgetSelectionCacheV2,\n\tfinalizeContextBudgetCacheTelemetryV2,\n\treadValidPlanCacheV2,\n\twritePlanCacheV2,\n} from \"./context-budget-v2-cache.ts\";\nimport { runGlobalSelectionPass } from \"./context-budget-v2-global-pass.ts\";\nimport { validateBudgetItems } from \"./context-budget-v2-input-validation.ts\";\nimport { buildObservability } from \"./context-budget-v2-observability.ts\";\nimport { computePlanHash } from \"./context-budget-v2-plan-hash.ts\";\nimport { createPlannedItems, resolveEffectiveTokenizerIdV2 } from \"./context-budget-v2-planned-items.ts\";\nimport { compareOptionalForSelection } from \"./context-budget-v2-scoring.ts\";\nimport {\n\tapplyPlannerRedundancyPenalties,\n\tbuildTierAllocations,\n\tcreateTierUsage,\n\temitCoverageDiagnostics,\n\temitOmissionDiagnostics,\n\tselectHardItem,\n\tselectOptionalItem,\n} from \"./context-budget-v2-selection.ts\";\nimport { allocateTiers, computeTierDemand } from \"./context-budget-v2-tiers.ts\";\nimport {\n\tALL_TIERS_V2,\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\tDEFAULT_TIER_POLICY_V2,\n\ttype PromptContextBudgetInputV2,\n\ttype PromptContextBudgetPlanV2,\n\ttype QualityDiagnosticV2,\n\ttype SelectedRepresentationV2,\n\ttype TierBudgetPolicyV2,\n} from \"./context-budget-v2-types.ts\";\n\nexport function planPromptContextBudgetV2(input: PromptContextBudgetInputV2): PromptContextBudgetPlanV2 {\n\tconst modelId = input.modelId ?? \"unknown\";\n\tconst qualityPolicy = input.qualityPolicy ?? DEFAULT_HEADROOM_QUALITY_POLICY;\n\tconst policyVersion = input.policyVersion ?? CONTEXT_BUDGET_POLICY_VERSION_V2;\n\tconst diagnostics: QualityDiagnosticV2[] = [];\n\n\tconst maxTokens = normalizeBudgetTokens(\"maxTokens\", input.maxTokens, diagnostics);\n\tconst responseReserve = normalizeBudgetTokens(\n\t\t\"responseReserveTokens\",\n\t\tinput.responseReserveTokens ?? 0,\n\t\tdiagnostics,\n\t);\n\tconst safetyMargin = normalizeBudgetTokens(\n\t\t\"safetyMarginTokens\",\n\t\tinput.safetyMarginTokens ?? Math.ceil(maxTokens * 0.05),\n\t\tdiagnostics,\n\t);\n\tconst available = Math.max(0, maxTokens - responseReserve - safetyMargin);\n\n\tconst tierPolicy: Readonly<Record<ContextBudgetTierV2, TierBudgetPolicyV2>> = {\n\t\t...DEFAULT_TIER_POLICY_V2,\n\t\t...(input.tierPolicy ?? {}),\n\t};\n\tconst basePlanned = createPlannedItems(\n\t\tvalidateBudgetItems(input.items, diagnostics),\n\t\tinput.tokenCounter,\n\t\tmodelId,\n\t\tqualityPolicy,\n\t);\n\t// Sanitized items can share a key with valid input, but their diagnostics cannot.\n\tconst canUsePlanCache = diagnostics.length === 0;\n\tapplyPlannerRedundancyPenalties(basePlanned);\n\tconst rawTokens = basePlanned.reduce((sum, planned) => sum + planned.fullTokens, 0);\n\n\tconst demand = computeTierDemand(basePlanned);\n\tconst allocation = allocateTiers(available, tierPolicy, demand);\n\tconst cacheKeyBase = createContextBudgetCacheKeyBaseV2({\n\t\tbudgetBucket: input.cacheBudgetBucket ?? String(available),\n\t\tcacheInvalidationSnapshot: input.cacheInvalidationSnapshot ?? input.cacheProvider?.getInvalidationSnapshot?.(),\n\t\tmodelId,\n\t\tnamespace: input.cacheNamespace,\n\t\tpolicyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion,\n\t\tquery: input.query,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\ttokenizerId: input.tokenizerId ?? resolveEffectiveTokenizerIdV2(input.tokenCounter, modelId),\n\t});\n\tconst planCacheKey = buildContextBudgetPlanCacheKeyV2({\n\t\tavailableTokens: available,\n\t\tkeyBase: cacheKeyBase,\n\t\tmaxTokens,\n\t\tplanned: basePlanned,\n\t\tpromptHash: input.promptHash,\n\t\tqualityPolicy,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\ttierPolicy,\n\t});\n\tconst cache = createContextBudgetSelectionCacheV2({\n\t\tkeyBase: cacheKeyBase,\n\t\tnowEpochMs: input.cacheNowEpochMs,\n\t\tplanCacheKey,\n\t\tprovider: input.cacheProvider,\n\t\tttlMs: input.cacheTtlMs,\n\t});\n\tconst cachedPlan = canUsePlanCache\n\t\t? readValidPlanCacheV2({\n\t\t\t\tavailableTokens: available,\n\t\t\t\tcache,\n\t\t\t\tkey: planCacheKey,\n\t\t\t\tplanned: basePlanned,\n\t\t\t})\n\t\t: undefined;\n\tif (cachedPlan) {\n\t\treturn cachedPlan;\n\t}\n\tconst selection = new Map<string, SelectedRepresentationV2>();\n\tconst tierUsed = createTierUsage();\n\tconst omitted: ContextBudgetItemV2[] = [];\n\tconst retrievalFallbacks: ContextSourceRefV2[] = [];\n\tlet usedTokens = 0;\n\n\tfor (const planned of basePlanned) {\n\t\tif (!planned.isHard) continue;\n\t\tusedTokens += selectHardItem(planned, selection, tierUsed);\n\t}\n\tif (usedTokens > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"hard_pin_over_capacity\",\n\t\t\tdetail: `hard-pinned context uses ${usedTokens} tokens against ${available} available tokens`,\n\t\t});\n\t}\n\n\tconst optionalPlanned = basePlanned.filter((planned) => !planned.isHard).sort(compareOptionalForSelection);\n\n\t// Floor-reservation pass: each tier spends its guaranteed claim (the\n\t// `allocated` field — hard demand, else demand capped at the floor) on its\n\t// own optional items before anything competes globally. A tier's floor is\n\t// therefore a real reservation, not a lower bound on its ceiling. Items\n\t// that cannot fit inside the remaining floor stay unselected for the\n\t// global pass; when the summed floors exceed the budget the reservation is\n\t// best-effort in tier order and the plan reports it explicitly.\n\t// One cache-resolution per item, shared by both passes so an item that is\n\t// deferred out of the floor and reconsidered globally does not double\n\t// count provider reads or redo materialization work.\n\tconst resolvedCandidates = new Map<string, readonly ContextRepresentationCandidateV2[]>();\n\tlet totalFloor = 0;\n\tfor (const tier of ALL_TIERS_V2) {\n\t\ttotalFloor += allocation.get(tier)?.floor ?? 0;\n\t}\n\tif (totalFloor > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"tier_floor_over_budget\",\n\t\t\tdetail: `tier floor guarantees total ${totalFloor} tokens against ${available} available; floors degrade to tier-order best effort`,\n\t\t});\n\t}\n\tfor (const tier of ALL_TIERS_V2) {\n\t\tconst floor = allocation.get(tier)?.floor ?? 0;\n\t\tif (floor <= 0 || tierUsed[tier] >= floor) continue;\n\t\tfor (const planned of optionalPlanned) {\n\t\t\tif (planned.item.tier !== tier || selection.has(planned.item.id)) continue;\n\t\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\t\tallocation,\n\t\t\t\tavailable,\n\t\t\t\tcache,\n\t\t\t\tdiagnostics,\n\t\t\t\tomitted,\n\t\t\t\tqualityPolicy,\n\t\t\t\tretrievalFallbacks,\n\t\t\t\tselection,\n\t\t\t\ttierUsed,\n\t\t\t\tusedTokens,\n\t\t\t\tfloorReservation: { tier, floorTokens: floor },\n\t\t\t\tresolvedCandidates,\n\t\t\t});\n\t\t\tif (tierUsed[tier] >= floor || usedTokens >= available) break;\n\t\t}\n\t}\n\n\t// Global pass under tier ceilings, then promotion of floor-admitted items\n\t// with whatever the global pass left unused (audit F03).\n\tusedTokens = runGlobalSelectionPass(optionalPlanned, {\n\t\tallocation,\n\t\tavailable,\n\t\tcache,\n\t\tdiagnostics,\n\t\tomitted,\n\t\tqualityPolicy,\n\t\tresolvedCandidates,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\ttierUsed,\n\t\tusedTokens,\n\t});\n\n\tconst omittedHighPriority = omitted.some((item) => item.priority === \"high\" || item.required === true);\n\temitOmissionDiagnostics(omitted, diagnostics, retrievalFallbacks);\n\temitCoverageDiagnostics(input.query, selection, diagnostics);\n\n\tconst tierAllocations = buildTierAllocations(allocation, demand, tierUsed);\n\tconst omittedTokens = omitted.reduce(\n\t\t(sum, item) => sum + (basePlanned.find((planned) => planned.item.id === item.id)?.fullTokens ?? 0),\n\t\t0,\n\t);\n\tconst omittedItemIds = omitted.map((item) => item.id);\n\tconst planHash = computePlanHash({\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanned: basePlanned,\n\t\tselection,\n\t\tallocations: tierAllocations,\n\t\tomittedItemIds,\n\t});\n\tconst observability = buildObservability({\n\t\tavailable,\n\t\tdiagnostics,\n\t\tomittedItemIds,\n\t\tomittedTokens,\n\t\tplanHash,\n\t\trawTokens,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\tusedTokens,\n\t\tcacheTelemetry: finalizeContextBudgetCacheTelemetryV2(cache.telemetry, {\n\t\t\tomittedTokens,\n\t\t\trawTokens,\n\t\t\tusedTokens,\n\t\t}),\n\t});\n\n\tconst plan: PromptContextBudgetPlanV2 = {\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanHash,\n\t\tmaxTokens,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\tavailableTokens: available,\n\t\tusedTokens,\n\t\tomittedTokens,\n\t\trawTokens,\n\t\ttokenSavingsRatio: rawTokens > 0 ? Math.max(0, 1 - usedTokens / rawTokens) : 0,\n\t\temergency: usedTokens > available || diagnostics.some((diagnostic) => diagnostic.reason === \"invalid_budget\"),\n\t\tomittedHighPriority,\n\t\ttierAllocations,\n\t\tselectedRepresentations: [...selection.values()].sort((a, b) => a.itemId.localeCompare(b.itemId)),\n\t\tincludedItemIds: basePlanned\n\t\t\t.map((planned) => planned.item.id)\n\t\t\t.filter((id) => selection.has(id))\n\t\t\t.sort(),\n\t\tomittedItemIds,\n\t\tdiagnostics,\n\t\tretrievalFallbacks,\n\t\tobservability,\n\t};\n\tif (canUsePlanCache) {\n\t\twritePlanCacheV2({\n\t\t\tcache,\n\t\t\tkey: planCacheKey,\n\t\t\tplan,\n\t\t\tplanned: basePlanned,\n\t\t});\n\t}\n\treturn plan;\n}\n\nfunction normalizeBudgetTokens(field: string, value: number, diagnostics: QualityDiagnosticV2[]): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tdiagnostics.push({\n\t\t\treason: \"invalid_budget\",\n\t\t\tdetail: `${field} must be a non-negative finite number, got ${value}`,\n\t\t});\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
@@ -1,9 +1,10 @@
1
1
  import { DEFAULT_HEADROOM_QUALITY_POLICY, } from "./context-budget-headroom.js";
2
2
  import { buildContextBudgetPlanCacheKeyV2, createContextBudgetCacheKeyBaseV2, createContextBudgetSelectionCacheV2, finalizeContextBudgetCacheTelemetryV2, readValidPlanCacheV2, writePlanCacheV2, } from "./context-budget-v2-cache.js";
3
+ import { runGlobalSelectionPass } from "./context-budget-v2-global-pass.js";
3
4
  import { validateBudgetItems } from "./context-budget-v2-input-validation.js";
4
5
  import { buildObservability } from "./context-budget-v2-observability.js";
5
6
  import { computePlanHash } from "./context-budget-v2-plan-hash.js";
6
- import { createPlannedItems } from "./context-budget-v2-planned-items.js";
7
+ import { createPlannedItems, resolveEffectiveTokenizerIdV2 } from "./context-budget-v2-planned-items.js";
7
8
  import { compareOptionalForSelection } from "./context-budget-v2-scoring.js";
8
9
  import { applyPlannerRedundancyPenalties, buildTierAllocations, createTierUsage, emitCoverageDiagnostics, emitOmissionDiagnostics, selectHardItem, selectOptionalItem, } from "./context-budget-v2-selection.js";
9
10
  import { allocateTiers, computeTierDemand } from "./context-budget-v2-tiers.js";
@@ -39,7 +40,7 @@ export function planPromptContextBudgetV2(input) {
39
40
  queryIntentHash: input.queryIntentHash,
40
41
  redactionPolicyHash: input.redactionPolicyHash,
41
42
  safetyProfileHash: input.safetyProfileHash,
42
- tokenizerId: input.tokenizerId,
43
+ tokenizerId: input.tokenizerId ?? resolveEffectiveTokenizerIdV2(input.tokenCounter, modelId),
43
44
  });
44
45
  const planCacheKey = buildContextBudgetPlanCacheKeyV2({
45
46
  availableTokens: available,
@@ -133,23 +134,21 @@ export function planPromptContextBudgetV2(input) {
133
134
  break;
134
135
  }
135
136
  }
136
- for (const planned of optionalPlanned) {
137
- if (selection.has(planned.item.id))
138
- continue;
139
- usedTokens = selectOptionalItem(planned, {
140
- allocation,
141
- available,
142
- cache,
143
- diagnostics,
144
- omitted,
145
- qualityPolicy,
146
- resolvedCandidates,
147
- retrievalFallbacks,
148
- selection,
149
- tierUsed,
150
- usedTokens,
151
- });
152
- }
137
+ // Global pass under tier ceilings, then promotion of floor-admitted items
138
+ // with whatever the global pass left unused (audit F03).
139
+ usedTokens = runGlobalSelectionPass(optionalPlanned, {
140
+ allocation,
141
+ available,
142
+ cache,
143
+ diagnostics,
144
+ omitted,
145
+ qualityPolicy,
146
+ resolvedCandidates,
147
+ retrievalFallbacks,
148
+ selection,
149
+ tierUsed,
150
+ usedTokens,
151
+ });
153
152
  const omittedHighPriority = omitted.some((item) => item.priority === "high" || item.required === true);
154
153
  emitOmissionDiagnostics(omitted, diagnostics, retrievalFallbacks);
155
154
  emitCoverageDiagnostics(input.query, selection, diagnostics);
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-v2-planner.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-planner.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n} from \"./context-budget-headroom.ts\";\nimport {\n\tbuildContextBudgetPlanCacheKeyV2,\n\tcreateContextBudgetCacheKeyBaseV2,\n\tcreateContextBudgetSelectionCacheV2,\n\tfinalizeContextBudgetCacheTelemetryV2,\n\treadValidPlanCacheV2,\n\twritePlanCacheV2,\n} from \"./context-budget-v2-cache.ts\";\nimport { validateBudgetItems } from \"./context-budget-v2-input-validation.ts\";\nimport { buildObservability } from \"./context-budget-v2-observability.ts\";\nimport { computePlanHash } from \"./context-budget-v2-plan-hash.ts\";\nimport { createPlannedItems } from \"./context-budget-v2-planned-items.ts\";\nimport { compareOptionalForSelection } from \"./context-budget-v2-scoring.ts\";\nimport {\n\tapplyPlannerRedundancyPenalties,\n\tbuildTierAllocations,\n\tcreateTierUsage,\n\temitCoverageDiagnostics,\n\temitOmissionDiagnostics,\n\tselectHardItem,\n\tselectOptionalItem,\n} from \"./context-budget-v2-selection.ts\";\nimport { allocateTiers, computeTierDemand } from \"./context-budget-v2-tiers.ts\";\nimport {\n\tALL_TIERS_V2,\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\tDEFAULT_TIER_POLICY_V2,\n\ttype PromptContextBudgetInputV2,\n\ttype PromptContextBudgetPlanV2,\n\ttype QualityDiagnosticV2,\n\ttype SelectedRepresentationV2,\n\ttype TierBudgetPolicyV2,\n} from \"./context-budget-v2-types.ts\";\n\nexport function planPromptContextBudgetV2(input: PromptContextBudgetInputV2): PromptContextBudgetPlanV2 {\n\tconst modelId = input.modelId ?? \"unknown\";\n\tconst qualityPolicy = input.qualityPolicy ?? DEFAULT_HEADROOM_QUALITY_POLICY;\n\tconst policyVersion = input.policyVersion ?? CONTEXT_BUDGET_POLICY_VERSION_V2;\n\tconst diagnostics: QualityDiagnosticV2[] = [];\n\n\tconst maxTokens = normalizeBudgetTokens(\"maxTokens\", input.maxTokens, diagnostics);\n\tconst responseReserve = normalizeBudgetTokens(\n\t\t\"responseReserveTokens\",\n\t\tinput.responseReserveTokens ?? 0,\n\t\tdiagnostics,\n\t);\n\tconst safetyMargin = normalizeBudgetTokens(\n\t\t\"safetyMarginTokens\",\n\t\tinput.safetyMarginTokens ?? Math.ceil(maxTokens * 0.05),\n\t\tdiagnostics,\n\t);\n\tconst available = Math.max(0, maxTokens - responseReserve - safetyMargin);\n\n\tconst tierPolicy: Readonly<Record<ContextBudgetTierV2, TierBudgetPolicyV2>> = {\n\t\t...DEFAULT_TIER_POLICY_V2,\n\t\t...(input.tierPolicy ?? {}),\n\t};\n\tconst basePlanned = createPlannedItems(\n\t\tvalidateBudgetItems(input.items, diagnostics),\n\t\tinput.tokenCounter,\n\t\tmodelId,\n\t\tqualityPolicy,\n\t);\n\t// Sanitized items can share a key with valid input, but their diagnostics cannot.\n\tconst canUsePlanCache = diagnostics.length === 0;\n\tapplyPlannerRedundancyPenalties(basePlanned);\n\tconst rawTokens = basePlanned.reduce((sum, planned) => sum + planned.fullTokens, 0);\n\n\tconst demand = computeTierDemand(basePlanned);\n\tconst allocation = allocateTiers(available, tierPolicy, demand);\n\tconst cacheKeyBase = createContextBudgetCacheKeyBaseV2({\n\t\tbudgetBucket: input.cacheBudgetBucket ?? String(available),\n\t\tcacheInvalidationSnapshot: input.cacheInvalidationSnapshot ?? input.cacheProvider?.getInvalidationSnapshot?.(),\n\t\tmodelId,\n\t\tnamespace: input.cacheNamespace,\n\t\tpolicyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion,\n\t\tquery: input.query,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\ttokenizerId: input.tokenizerId,\n\t});\n\tconst planCacheKey = buildContextBudgetPlanCacheKeyV2({\n\t\tavailableTokens: available,\n\t\tkeyBase: cacheKeyBase,\n\t\tmaxTokens,\n\t\tplanned: basePlanned,\n\t\tpromptHash: input.promptHash,\n\t\tqualityPolicy,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\ttierPolicy,\n\t});\n\tconst cache = createContextBudgetSelectionCacheV2({\n\t\tkeyBase: cacheKeyBase,\n\t\tnowEpochMs: input.cacheNowEpochMs,\n\t\tplanCacheKey,\n\t\tprovider: input.cacheProvider,\n\t\tttlMs: input.cacheTtlMs,\n\t});\n\tconst cachedPlan = canUsePlanCache\n\t\t? readValidPlanCacheV2({\n\t\t\t\tavailableTokens: available,\n\t\t\t\tcache,\n\t\t\t\tkey: planCacheKey,\n\t\t\t\tplanned: basePlanned,\n\t\t\t})\n\t\t: undefined;\n\tif (cachedPlan) {\n\t\treturn cachedPlan;\n\t}\n\tconst selection = new Map<string, SelectedRepresentationV2>();\n\tconst tierUsed = createTierUsage();\n\tconst omitted: ContextBudgetItemV2[] = [];\n\tconst retrievalFallbacks: ContextSourceRefV2[] = [];\n\tlet usedTokens = 0;\n\n\tfor (const planned of basePlanned) {\n\t\tif (!planned.isHard) continue;\n\t\tusedTokens += selectHardItem(planned, selection, tierUsed);\n\t}\n\tif (usedTokens > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"hard_pin_over_capacity\",\n\t\t\tdetail: `hard-pinned context uses ${usedTokens} tokens against ${available} available tokens`,\n\t\t});\n\t}\n\n\tconst optionalPlanned = basePlanned.filter((planned) => !planned.isHard).sort(compareOptionalForSelection);\n\n\t// Floor-reservation pass: each tier spends its guaranteed claim (the\n\t// `allocated` field — hard demand, else demand capped at the floor) on its\n\t// own optional items before anything competes globally. A tier's floor is\n\t// therefore a real reservation, not a lower bound on its ceiling. Items\n\t// that cannot fit inside the remaining floor stay unselected for the\n\t// global pass; when the summed floors exceed the budget the reservation is\n\t// best-effort in tier order and the plan reports it explicitly.\n\t// One cache-resolution per item, shared by both passes so an item that is\n\t// deferred out of the floor and reconsidered globally does not double\n\t// count provider reads or redo materialization work.\n\tconst resolvedCandidates = new Map<string, readonly ContextRepresentationCandidateV2[]>();\n\tlet totalFloor = 0;\n\tfor (const tier of ALL_TIERS_V2) {\n\t\ttotalFloor += allocation.get(tier)?.floor ?? 0;\n\t}\n\tif (totalFloor > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"tier_floor_over_budget\",\n\t\t\tdetail: `tier floor guarantees total ${totalFloor} tokens against ${available} available; floors degrade to tier-order best effort`,\n\t\t});\n\t}\n\tfor (const tier of ALL_TIERS_V2) {\n\t\tconst floor = allocation.get(tier)?.floor ?? 0;\n\t\tif (floor <= 0 || tierUsed[tier] >= floor) continue;\n\t\tfor (const planned of optionalPlanned) {\n\t\t\tif (planned.item.tier !== tier || selection.has(planned.item.id)) continue;\n\t\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\t\tallocation,\n\t\t\t\tavailable,\n\t\t\t\tcache,\n\t\t\t\tdiagnostics,\n\t\t\t\tomitted,\n\t\t\t\tqualityPolicy,\n\t\t\t\tretrievalFallbacks,\n\t\t\t\tselection,\n\t\t\t\ttierUsed,\n\t\t\t\tusedTokens,\n\t\t\t\tfloorReservation: { tier, floorTokens: floor },\n\t\t\t\tresolvedCandidates,\n\t\t\t});\n\t\t\tif (tierUsed[tier] >= floor || usedTokens >= available) break;\n\t\t}\n\t}\n\n\tfor (const planned of optionalPlanned) {\n\t\tif (selection.has(planned.item.id)) continue;\n\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\tallocation,\n\t\t\tavailable,\n\t\t\tcache,\n\t\t\tdiagnostics,\n\t\t\tomitted,\n\t\t\tqualityPolicy,\n\t\t\tresolvedCandidates,\n\t\t\tretrievalFallbacks,\n\t\t\tselection,\n\t\t\ttierUsed,\n\t\t\tusedTokens,\n\t\t});\n\t}\n\n\tconst omittedHighPriority = omitted.some((item) => item.priority === \"high\" || item.required === true);\n\temitOmissionDiagnostics(omitted, diagnostics, retrievalFallbacks);\n\temitCoverageDiagnostics(input.query, selection, diagnostics);\n\n\tconst tierAllocations = buildTierAllocations(allocation, demand, tierUsed);\n\tconst omittedTokens = omitted.reduce(\n\t\t(sum, item) => sum + (basePlanned.find((planned) => planned.item.id === item.id)?.fullTokens ?? 0),\n\t\t0,\n\t);\n\tconst omittedItemIds = omitted.map((item) => item.id);\n\tconst planHash = computePlanHash({\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanned: basePlanned,\n\t\tselection,\n\t\tallocations: tierAllocations,\n\t\tomittedItemIds,\n\t});\n\tconst observability = buildObservability({\n\t\tavailable,\n\t\tdiagnostics,\n\t\tomittedItemIds,\n\t\tomittedTokens,\n\t\tplanHash,\n\t\trawTokens,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\tusedTokens,\n\t\tcacheTelemetry: finalizeContextBudgetCacheTelemetryV2(cache.telemetry, {\n\t\t\tomittedTokens,\n\t\t\trawTokens,\n\t\t\tusedTokens,\n\t\t}),\n\t});\n\n\tconst plan: PromptContextBudgetPlanV2 = {\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanHash,\n\t\tmaxTokens,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\tavailableTokens: available,\n\t\tusedTokens,\n\t\tomittedTokens,\n\t\trawTokens,\n\t\ttokenSavingsRatio: rawTokens > 0 ? Math.max(0, 1 - usedTokens / rawTokens) : 0,\n\t\temergency: usedTokens > available || diagnostics.some((diagnostic) => diagnostic.reason === \"invalid_budget\"),\n\t\tomittedHighPriority,\n\t\ttierAllocations,\n\t\tselectedRepresentations: [...selection.values()].sort((a, b) => a.itemId.localeCompare(b.itemId)),\n\t\tincludedItemIds: basePlanned\n\t\t\t.map((planned) => planned.item.id)\n\t\t\t.filter((id) => selection.has(id))\n\t\t\t.sort(),\n\t\tomittedItemIds,\n\t\tdiagnostics,\n\t\tretrievalFallbacks,\n\t\tobservability,\n\t};\n\tif (canUsePlanCache) {\n\t\twritePlanCacheV2({\n\t\t\tcache,\n\t\t\tkey: planCacheKey,\n\t\t\tplan,\n\t\t\tplanned: basePlanned,\n\t\t});\n\t}\n\treturn plan;\n}\n\nfunction normalizeBudgetTokens(field: string, value: number, diagnostics: QualityDiagnosticV2[]): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tdiagnostics.push({\n\t\t\treason: \"invalid_budget\",\n\t\t\tdetail: `${field} must be a non-negative finite number, got ${value}`,\n\t\t});\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
1
+ {"version":3,"file":"context-budget-v2-planner.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-planner.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n} from \"./context-budget-headroom.ts\";\nimport {\n\tbuildContextBudgetPlanCacheKeyV2,\n\tcreateContextBudgetCacheKeyBaseV2,\n\tcreateContextBudgetSelectionCacheV2,\n\tfinalizeContextBudgetCacheTelemetryV2,\n\treadValidPlanCacheV2,\n\twritePlanCacheV2,\n} from \"./context-budget-v2-cache.ts\";\nimport { runGlobalSelectionPass } from \"./context-budget-v2-global-pass.ts\";\nimport { validateBudgetItems } from \"./context-budget-v2-input-validation.ts\";\nimport { buildObservability } from \"./context-budget-v2-observability.ts\";\nimport { computePlanHash } from \"./context-budget-v2-plan-hash.ts\";\nimport { createPlannedItems, resolveEffectiveTokenizerIdV2 } from \"./context-budget-v2-planned-items.ts\";\nimport { compareOptionalForSelection } from \"./context-budget-v2-scoring.ts\";\nimport {\n\tapplyPlannerRedundancyPenalties,\n\tbuildTierAllocations,\n\tcreateTierUsage,\n\temitCoverageDiagnostics,\n\temitOmissionDiagnostics,\n\tselectHardItem,\n\tselectOptionalItem,\n} from \"./context-budget-v2-selection.ts\";\nimport { allocateTiers, computeTierDemand } from \"./context-budget-v2-tiers.ts\";\nimport {\n\tALL_TIERS_V2,\n\tCONTEXT_BUDGET_POLICY_VERSION_V2,\n\tDEFAULT_TIER_POLICY_V2,\n\ttype PromptContextBudgetInputV2,\n\ttype PromptContextBudgetPlanV2,\n\ttype QualityDiagnosticV2,\n\ttype SelectedRepresentationV2,\n\ttype TierBudgetPolicyV2,\n} from \"./context-budget-v2-types.ts\";\n\nexport function planPromptContextBudgetV2(input: PromptContextBudgetInputV2): PromptContextBudgetPlanV2 {\n\tconst modelId = input.modelId ?? \"unknown\";\n\tconst qualityPolicy = input.qualityPolicy ?? DEFAULT_HEADROOM_QUALITY_POLICY;\n\tconst policyVersion = input.policyVersion ?? CONTEXT_BUDGET_POLICY_VERSION_V2;\n\tconst diagnostics: QualityDiagnosticV2[] = [];\n\n\tconst maxTokens = normalizeBudgetTokens(\"maxTokens\", input.maxTokens, diagnostics);\n\tconst responseReserve = normalizeBudgetTokens(\n\t\t\"responseReserveTokens\",\n\t\tinput.responseReserveTokens ?? 0,\n\t\tdiagnostics,\n\t);\n\tconst safetyMargin = normalizeBudgetTokens(\n\t\t\"safetyMarginTokens\",\n\t\tinput.safetyMarginTokens ?? Math.ceil(maxTokens * 0.05),\n\t\tdiagnostics,\n\t);\n\tconst available = Math.max(0, maxTokens - responseReserve - safetyMargin);\n\n\tconst tierPolicy: Readonly<Record<ContextBudgetTierV2, TierBudgetPolicyV2>> = {\n\t\t...DEFAULT_TIER_POLICY_V2,\n\t\t...(input.tierPolicy ?? {}),\n\t};\n\tconst basePlanned = createPlannedItems(\n\t\tvalidateBudgetItems(input.items, diagnostics),\n\t\tinput.tokenCounter,\n\t\tmodelId,\n\t\tqualityPolicy,\n\t);\n\t// Sanitized items can share a key with valid input, but their diagnostics cannot.\n\tconst canUsePlanCache = diagnostics.length === 0;\n\tapplyPlannerRedundancyPenalties(basePlanned);\n\tconst rawTokens = basePlanned.reduce((sum, planned) => sum + planned.fullTokens, 0);\n\n\tconst demand = computeTierDemand(basePlanned);\n\tconst allocation = allocateTiers(available, tierPolicy, demand);\n\tconst cacheKeyBase = createContextBudgetCacheKeyBaseV2({\n\t\tbudgetBucket: input.cacheBudgetBucket ?? String(available),\n\t\tcacheInvalidationSnapshot: input.cacheInvalidationSnapshot ?? input.cacheProvider?.getInvalidationSnapshot?.(),\n\t\tmodelId,\n\t\tnamespace: input.cacheNamespace,\n\t\tpolicyVersion,\n\t\tselectionPolicyVersion: input.selectionPolicyVersion,\n\t\tquery: input.query,\n\t\tqueryIntentHash: input.queryIntentHash,\n\t\tredactionPolicyHash: input.redactionPolicyHash,\n\t\tsafetyProfileHash: input.safetyProfileHash,\n\t\ttokenizerId: input.tokenizerId ?? resolveEffectiveTokenizerIdV2(input.tokenCounter, modelId),\n\t});\n\tconst planCacheKey = buildContextBudgetPlanCacheKeyV2({\n\t\tavailableTokens: available,\n\t\tkeyBase: cacheKeyBase,\n\t\tmaxTokens,\n\t\tplanned: basePlanned,\n\t\tpromptHash: input.promptHash,\n\t\tqualityPolicy,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\ttierPolicy,\n\t});\n\tconst cache = createContextBudgetSelectionCacheV2({\n\t\tkeyBase: cacheKeyBase,\n\t\tnowEpochMs: input.cacheNowEpochMs,\n\t\tplanCacheKey,\n\t\tprovider: input.cacheProvider,\n\t\tttlMs: input.cacheTtlMs,\n\t});\n\tconst cachedPlan = canUsePlanCache\n\t\t? readValidPlanCacheV2({\n\t\t\t\tavailableTokens: available,\n\t\t\t\tcache,\n\t\t\t\tkey: planCacheKey,\n\t\t\t\tplanned: basePlanned,\n\t\t\t})\n\t\t: undefined;\n\tif (cachedPlan) {\n\t\treturn cachedPlan;\n\t}\n\tconst selection = new Map<string, SelectedRepresentationV2>();\n\tconst tierUsed = createTierUsage();\n\tconst omitted: ContextBudgetItemV2[] = [];\n\tconst retrievalFallbacks: ContextSourceRefV2[] = [];\n\tlet usedTokens = 0;\n\n\tfor (const planned of basePlanned) {\n\t\tif (!planned.isHard) continue;\n\t\tusedTokens += selectHardItem(planned, selection, tierUsed);\n\t}\n\tif (usedTokens > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"hard_pin_over_capacity\",\n\t\t\tdetail: `hard-pinned context uses ${usedTokens} tokens against ${available} available tokens`,\n\t\t});\n\t}\n\n\tconst optionalPlanned = basePlanned.filter((planned) => !planned.isHard).sort(compareOptionalForSelection);\n\n\t// Floor-reservation pass: each tier spends its guaranteed claim (the\n\t// `allocated` field — hard demand, else demand capped at the floor) on its\n\t// own optional items before anything competes globally. A tier's floor is\n\t// therefore a real reservation, not a lower bound on its ceiling. Items\n\t// that cannot fit inside the remaining floor stay unselected for the\n\t// global pass; when the summed floors exceed the budget the reservation is\n\t// best-effort in tier order and the plan reports it explicitly.\n\t// One cache-resolution per item, shared by both passes so an item that is\n\t// deferred out of the floor and reconsidered globally does not double\n\t// count provider reads or redo materialization work.\n\tconst resolvedCandidates = new Map<string, readonly ContextRepresentationCandidateV2[]>();\n\tlet totalFloor = 0;\n\tfor (const tier of ALL_TIERS_V2) {\n\t\ttotalFloor += allocation.get(tier)?.floor ?? 0;\n\t}\n\tif (totalFloor > available) {\n\t\tdiagnostics.push({\n\t\t\treason: \"tier_floor_over_budget\",\n\t\t\tdetail: `tier floor guarantees total ${totalFloor} tokens against ${available} available; floors degrade to tier-order best effort`,\n\t\t});\n\t}\n\tfor (const tier of ALL_TIERS_V2) {\n\t\tconst floor = allocation.get(tier)?.floor ?? 0;\n\t\tif (floor <= 0 || tierUsed[tier] >= floor) continue;\n\t\tfor (const planned of optionalPlanned) {\n\t\t\tif (planned.item.tier !== tier || selection.has(planned.item.id)) continue;\n\t\t\tusedTokens = selectOptionalItem(planned, {\n\t\t\t\tallocation,\n\t\t\t\tavailable,\n\t\t\t\tcache,\n\t\t\t\tdiagnostics,\n\t\t\t\tomitted,\n\t\t\t\tqualityPolicy,\n\t\t\t\tretrievalFallbacks,\n\t\t\t\tselection,\n\t\t\t\ttierUsed,\n\t\t\t\tusedTokens,\n\t\t\t\tfloorReservation: { tier, floorTokens: floor },\n\t\t\t\tresolvedCandidates,\n\t\t\t});\n\t\t\tif (tierUsed[tier] >= floor || usedTokens >= available) break;\n\t\t}\n\t}\n\n\t// Global pass under tier ceilings, then promotion of floor-admitted items\n\t// with whatever the global pass left unused (audit F03).\n\tusedTokens = runGlobalSelectionPass(optionalPlanned, {\n\t\tallocation,\n\t\tavailable,\n\t\tcache,\n\t\tdiagnostics,\n\t\tomitted,\n\t\tqualityPolicy,\n\t\tresolvedCandidates,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\ttierUsed,\n\t\tusedTokens,\n\t});\n\n\tconst omittedHighPriority = omitted.some((item) => item.priority === \"high\" || item.required === true);\n\temitOmissionDiagnostics(omitted, diagnostics, retrievalFallbacks);\n\temitCoverageDiagnostics(input.query, selection, diagnostics);\n\n\tconst tierAllocations = buildTierAllocations(allocation, demand, tierUsed);\n\tconst omittedTokens = omitted.reduce(\n\t\t(sum, item) => sum + (basePlanned.find((planned) => planned.item.id === item.id)?.fullTokens ?? 0),\n\t\t0,\n\t);\n\tconst omittedItemIds = omitted.map((item) => item.id);\n\tconst planHash = computePlanHash({\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanned: basePlanned,\n\t\tselection,\n\t\tallocations: tierAllocations,\n\t\tomittedItemIds,\n\t});\n\tconst observability = buildObservability({\n\t\tavailable,\n\t\tdiagnostics,\n\t\tomittedItemIds,\n\t\tomittedTokens,\n\t\tplanHash,\n\t\trawTokens,\n\t\tretrievalFallbacks,\n\t\tselection,\n\t\tusedTokens,\n\t\tcacheTelemetry: finalizeContextBudgetCacheTelemetryV2(cache.telemetry, {\n\t\t\tomittedTokens,\n\t\t\trawTokens,\n\t\t\tusedTokens,\n\t\t}),\n\t});\n\n\tconst plan: PromptContextBudgetPlanV2 = {\n\t\tpolicyVersion,\n\t\tpromptHash: input.promptHash,\n\t\tplanHash,\n\t\tmaxTokens,\n\t\tresponseReserveTokens: responseReserve,\n\t\tsafetyMarginTokens: safetyMargin,\n\t\tavailableTokens: available,\n\t\tusedTokens,\n\t\tomittedTokens,\n\t\trawTokens,\n\t\ttokenSavingsRatio: rawTokens > 0 ? Math.max(0, 1 - usedTokens / rawTokens) : 0,\n\t\temergency: usedTokens > available || diagnostics.some((diagnostic) => diagnostic.reason === \"invalid_budget\"),\n\t\tomittedHighPriority,\n\t\ttierAllocations,\n\t\tselectedRepresentations: [...selection.values()].sort((a, b) => a.itemId.localeCompare(b.itemId)),\n\t\tincludedItemIds: basePlanned\n\t\t\t.map((planned) => planned.item.id)\n\t\t\t.filter((id) => selection.has(id))\n\t\t\t.sort(),\n\t\tomittedItemIds,\n\t\tdiagnostics,\n\t\tretrievalFallbacks,\n\t\tobservability,\n\t};\n\tif (canUsePlanCache) {\n\t\twritePlanCacheV2({\n\t\t\tcache,\n\t\t\tkey: planCacheKey,\n\t\t\tplan,\n\t\t\tplanned: basePlanned,\n\t\t});\n\t}\n\treturn plan;\n}\n\nfunction normalizeBudgetTokens(field: string, value: number, diagnostics: QualityDiagnosticV2[]): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tdiagnostics.push({\n\t\t\treason: \"invalid_budget\",\n\t\t\tdetail: `${field} must be a non-negative finite number, got ${value}`,\n\t\t});\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { ContextBudgetItemV2 } from "./context-budget-headroom.ts";
1
+ import type { ContextBudgetItemV2, ContextRepresentationCandidateV2 } from "./context-budget-headroom.ts";
2
2
  export interface PlannedItemV2 {
3
3
  readonly item: ContextBudgetItemV2;
4
4
  readonly fullTokens: number;
@@ -8,6 +8,12 @@ export interface PlannedItemV2 {
8
8
  * cheaper pointer/summary/headroom representation.
9
9
  */
10
10
  readonly admissibleTokens: number;
11
+ /**
12
+ * Representations priced with the planner's token counter. Selection must
13
+ * choose from these rather than re-deriving with a different estimator, or
14
+ * the cost an item was ranked on and the cost it pays can diverge.
15
+ */
16
+ readonly candidates?: readonly ContextRepresentationCandidateV2[];
11
17
  readonly contentHash: string;
12
18
  readonly baseScore: number;
13
19
  redundancyPenalty: number;
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-v2-scoring.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-scoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAgD,MAAM,8BAA8B,CAAC;AAEtH,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAsBD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAUnG;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA8B7F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAiBtF","sourcesContent":["import type { ContextBudgetItemV2, ContextBudgetPriorityV2, ContextBudgetTierV2 } from \"./context-budget-headroom.ts\";\n\nexport interface PlannedItemV2 {\n\treadonly item: ContextBudgetItemV2;\n\treadonly fullTokens: number;\n\t/**\n\t * Cheapest non-omit representation cost, i.e. the true marginal cost of\n\t * admitting this item at all. Equals `fullTokens` when the item has no\n\t * cheaper pointer/summary/headroom representation.\n\t */\n\treadonly admissibleTokens: number;\n\treadonly contentHash: string;\n\treadonly baseScore: number;\n\tredundancyPenalty: number;\n\teffectiveScore: number;\n\treadonly isHard: boolean;\n}\n\nconst PRIORITY_WEIGHT_V2: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\n/** E-folding time in turns: at this age the factor is exp(-1), not one half. */\nconst RECENCY_TIME_CONSTANT_BY_TIER: Readonly<Record<ContextBudgetTierV2, number>> = {\n\tsystem: 999,\n\t\"active-goal\": 12,\n\t\"current-files\": 8,\n\ttools: 4,\n\tskills: 6,\n\tmcp: 6,\n\thistory: 3,\n\tevidence: 8,\n\tscratch: 2,\n};\n\nexport function scoreContextBudgetItemV2(item: ContextBudgetItemV2, estimatedTokens: number): number {\n\tif (item.priority === \"hard\") {\n\t\treturn Number.POSITIVE_INFINITY;\n\t}\n\tconst priority = PRIORITY_WEIGHT_V2[item.priority];\n\tconst relevance = clamp01(item.relevance) * 25;\n\tconst recency = deriveRecency(item) * 15;\n\tconst evidence = clamp01(item.evidenceValue) * 25;\n\tconst cost = Math.sqrt(Math.max(0, estimatedTokens)) * 0.55;\n\treturn priority + relevance + recency + evidence - cost;\n}\n\nexport function applyRedundancyPenalties(items: readonly PlannedItemV2[]): Map<string, number> {\n\tconst penalties = new Map<string, number>();\n\tconst groups = new Map<string, PlannedItemV2[]>();\n\tfor (const planned of items) {\n\t\tif (!planned.item.redundancyKey) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst group = groups.get(planned.item.redundancyKey);\n\t\tif (group) {\n\t\t\tgroup.push(planned);\n\t\t} else {\n\t\t\tgroups.set(planned.item.redundancyKey, [planned]);\n\t\t}\n\t}\n\tfor (const group of groups.values()) {\n\t\tif (group.length <= 1) {\n\t\t\tcontinue;\n\t\t}\n\t\tgroup.sort((a, b) => {\n\t\t\tif (b.baseScore !== a.baseScore) {\n\t\t\t\treturn b.baseScore - a.baseScore;\n\t\t\t}\n\t\t\treturn a.item.id.localeCompare(b.item.id);\n\t\t});\n\t\tfor (let index = 1; index < group.length; index++) {\n\t\t\tconst duplicate = group[index];\n\t\t\tpenalties.set(duplicate.item.id, redundancyPenalty(duplicate.fullTokens) * index);\n\t\t}\n\t}\n\treturn penalties;\n}\n\n/**\n * Order optional items for the budgeted 0/1 knapsack the planner greedily fills.\n *\n * Value density (effective score per token) is the primary key. Ordering by\n * priority class first would let a single expensive `high` item evict an\n * arbitrary number of far denser `medium` items, which makes the greedy\n * unbounded-bad against the optimal packing. Priority still shapes the outcome\n * through `PRIORITY_WEIGHT_V2` in the score numerator, and non-negotiable\n * context uses `hard`/`required`, which the planner pins before this ordering\n * is consulted. Remaining keys break ties into a deterministic total order.\n */\nexport function compareOptionalForSelection(a: PlannedItemV2, b: PlannedItemV2): number {\n\tconst aDensity = density(a);\n\tconst bDensity = density(b);\n\tif (bDensity !== aDensity) {\n\t\treturn bDensity - aDensity;\n\t}\n\tconst aScore = Number.isNaN(a.effectiveScore) ? Number.NEGATIVE_INFINITY : a.effectiveScore;\n\tconst bScore = Number.isNaN(b.effectiveScore) ? Number.NEGATIVE_INFINITY : b.effectiveScore;\n\tif (bScore !== aScore) return bScore - aScore;\n\tconst priorityDelta = priorityRank(b.item.priority) - priorityRank(a.item.priority);\n\tif (priorityDelta !== 0) {\n\t\treturn priorityDelta;\n\t}\n\tconst aTokens = Number.isNaN(a.fullTokens) ? Number.POSITIVE_INFINITY : a.fullTokens;\n\tconst bTokens = Number.isNaN(b.fullTokens) ? Number.POSITIVE_INFINITY : b.fullTokens;\n\tif (aTokens !== bTokens) return aTokens - bTokens;\n\treturn a.item.id.localeCompare(b.item.id);\n}\n\nfunction priorityRank(priority: ContextBudgetPriorityV2): number {\n\tswitch (priority) {\n\t\tcase \"hard\":\n\t\t\treturn 4;\n\t\tcase \"high\":\n\t\t\treturn 3;\n\t\tcase \"medium\":\n\t\t\treturn 2;\n\t\tcase \"low\":\n\t\t\treturn 1;\n\t}\n}\n\nfunction clamp01(value: number | undefined): number {\n\tif (value === undefined || !Number.isFinite(value)) {\n\t\treturn 0;\n\t}\n\treturn Math.max(0, Math.min(1, value));\n}\n\nfunction deriveRecency(item: ContextBudgetItemV2): number {\n\tif (item.recency !== undefined) {\n\t\treturn clamp01(item.recency);\n\t}\n\tconst age = item.ageTurns ?? 0;\n\tconst timeConstant = RECENCY_TIME_CONSTANT_BY_TIER[item.tier] ?? 6;\n\treturn clamp01(Math.exp(-age / timeConstant));\n}\n\nfunction redundancyPenalty(tokens: number): number {\n\treturn Math.max(10, Math.sqrt(Math.max(0, tokens)) * 0.8);\n}\n\n/**\n * Value per token of the CHEAPEST admissible representation. Dividing by\n * `fullTokens` would systematically penalize retrievable or summarizable items,\n * which are exactly the items the planner admits cheaply when the budget is\n * tight — the regime where this ordering decides anything at all.\n */\nfunction density(planned: PlannedItemV2): number {\n\tif (!Number.isFinite(planned.effectiveScore) || planned.effectiveScore <= 0) {\n\t\treturn planned.effectiveScore > 0 ? planned.effectiveScore : 0;\n\t}\n\tconst value = planned.effectiveScore / Math.max(planned.admissibleTokens, 1);\n\treturn Number.isNaN(value) ? 0 : value;\n}\n"]}
1
+ {"version":3,"file":"context-budget-v2-scoring.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-scoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,mBAAmB,EAGnB,gCAAgC,EAChC,MAAM,8BAA8B,CAAC;AAEtC,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,gCAAgC,EAAE,CAAC;IAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAsBD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAUnG;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA8B7F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAiBtF","sourcesContent":["import type {\n\tContextBudgetItemV2,\n\tContextBudgetPriorityV2,\n\tContextBudgetTierV2,\n\tContextRepresentationCandidateV2,\n} from \"./context-budget-headroom.ts\";\n\nexport interface PlannedItemV2 {\n\treadonly item: ContextBudgetItemV2;\n\treadonly fullTokens: number;\n\t/**\n\t * Cheapest non-omit representation cost, i.e. the true marginal cost of\n\t * admitting this item at all. Equals `fullTokens` when the item has no\n\t * cheaper pointer/summary/headroom representation.\n\t */\n\treadonly admissibleTokens: number;\n\t/**\n\t * Representations priced with the planner's token counter. Selection must\n\t * choose from these rather than re-deriving with a different estimator, or\n\t * the cost an item was ranked on and the cost it pays can diverge.\n\t */\n\treadonly candidates?: readonly ContextRepresentationCandidateV2[];\n\treadonly contentHash: string;\n\treadonly baseScore: number;\n\tredundancyPenalty: number;\n\teffectiveScore: number;\n\treadonly isHard: boolean;\n}\n\nconst PRIORITY_WEIGHT_V2: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\n/** E-folding time in turns: at this age the factor is exp(-1), not one half. */\nconst RECENCY_TIME_CONSTANT_BY_TIER: Readonly<Record<ContextBudgetTierV2, number>> = {\n\tsystem: 999,\n\t\"active-goal\": 12,\n\t\"current-files\": 8,\n\ttools: 4,\n\tskills: 6,\n\tmcp: 6,\n\thistory: 3,\n\tevidence: 8,\n\tscratch: 2,\n};\n\nexport function scoreContextBudgetItemV2(item: ContextBudgetItemV2, estimatedTokens: number): number {\n\tif (item.priority === \"hard\") {\n\t\treturn Number.POSITIVE_INFINITY;\n\t}\n\tconst priority = PRIORITY_WEIGHT_V2[item.priority];\n\tconst relevance = clamp01(item.relevance) * 25;\n\tconst recency = deriveRecency(item) * 15;\n\tconst evidence = clamp01(item.evidenceValue) * 25;\n\tconst cost = Math.sqrt(Math.max(0, estimatedTokens)) * 0.55;\n\treturn priority + relevance + recency + evidence - cost;\n}\n\nexport function applyRedundancyPenalties(items: readonly PlannedItemV2[]): Map<string, number> {\n\tconst penalties = new Map<string, number>();\n\tconst groups = new Map<string, PlannedItemV2[]>();\n\tfor (const planned of items) {\n\t\tif (!planned.item.redundancyKey) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst group = groups.get(planned.item.redundancyKey);\n\t\tif (group) {\n\t\t\tgroup.push(planned);\n\t\t} else {\n\t\t\tgroups.set(planned.item.redundancyKey, [planned]);\n\t\t}\n\t}\n\tfor (const group of groups.values()) {\n\t\tif (group.length <= 1) {\n\t\t\tcontinue;\n\t\t}\n\t\tgroup.sort((a, b) => {\n\t\t\tif (b.baseScore !== a.baseScore) {\n\t\t\t\treturn b.baseScore - a.baseScore;\n\t\t\t}\n\t\t\treturn a.item.id.localeCompare(b.item.id);\n\t\t});\n\t\tfor (let index = 1; index < group.length; index++) {\n\t\t\tconst duplicate = group[index];\n\t\t\tpenalties.set(duplicate.item.id, redundancyPenalty(duplicate.fullTokens) * index);\n\t\t}\n\t}\n\treturn penalties;\n}\n\n/**\n * Order optional items for the budgeted 0/1 knapsack the planner greedily fills.\n *\n * Value density (effective score per token) is the primary key. Ordering by\n * priority class first would let a single expensive `high` item evict an\n * arbitrary number of far denser `medium` items, which makes the greedy\n * unbounded-bad against the optimal packing. Priority still shapes the outcome\n * through `PRIORITY_WEIGHT_V2` in the score numerator, and non-negotiable\n * context uses `hard`/`required`, which the planner pins before this ordering\n * is consulted. Remaining keys break ties into a deterministic total order.\n */\nexport function compareOptionalForSelection(a: PlannedItemV2, b: PlannedItemV2): number {\n\tconst aDensity = density(a);\n\tconst bDensity = density(b);\n\tif (bDensity !== aDensity) {\n\t\treturn bDensity - aDensity;\n\t}\n\tconst aScore = Number.isNaN(a.effectiveScore) ? Number.NEGATIVE_INFINITY : a.effectiveScore;\n\tconst bScore = Number.isNaN(b.effectiveScore) ? Number.NEGATIVE_INFINITY : b.effectiveScore;\n\tif (bScore !== aScore) return bScore - aScore;\n\tconst priorityDelta = priorityRank(b.item.priority) - priorityRank(a.item.priority);\n\tif (priorityDelta !== 0) {\n\t\treturn priorityDelta;\n\t}\n\tconst aTokens = Number.isNaN(a.fullTokens) ? Number.POSITIVE_INFINITY : a.fullTokens;\n\tconst bTokens = Number.isNaN(b.fullTokens) ? Number.POSITIVE_INFINITY : b.fullTokens;\n\tif (aTokens !== bTokens) return aTokens - bTokens;\n\treturn a.item.id.localeCompare(b.item.id);\n}\n\nfunction priorityRank(priority: ContextBudgetPriorityV2): number {\n\tswitch (priority) {\n\t\tcase \"hard\":\n\t\t\treturn 4;\n\t\tcase \"high\":\n\t\t\treturn 3;\n\t\tcase \"medium\":\n\t\t\treturn 2;\n\t\tcase \"low\":\n\t\t\treturn 1;\n\t}\n}\n\nfunction clamp01(value: number | undefined): number {\n\tif (value === undefined || !Number.isFinite(value)) {\n\t\treturn 0;\n\t}\n\treturn Math.max(0, Math.min(1, value));\n}\n\nfunction deriveRecency(item: ContextBudgetItemV2): number {\n\tif (item.recency !== undefined) {\n\t\treturn clamp01(item.recency);\n\t}\n\tconst age = item.ageTurns ?? 0;\n\tconst timeConstant = RECENCY_TIME_CONSTANT_BY_TIER[item.tier] ?? 6;\n\treturn clamp01(Math.exp(-age / timeConstant));\n}\n\nfunction redundancyPenalty(tokens: number): number {\n\treturn Math.max(10, Math.sqrt(Math.max(0, tokens)) * 0.8);\n}\n\n/**\n * Value per token of the CHEAPEST admissible representation. Dividing by\n * `fullTokens` would systematically penalize retrievable or summarizable items,\n * which are exactly the items the planner admits cheaply when the budget is\n * tight — the regime where this ordering decides anything at all.\n */\nfunction density(planned: PlannedItemV2): number {\n\tif (!Number.isFinite(planned.effectiveScore) || planned.effectiveScore <= 0) {\n\t\treturn planned.effectiveScore > 0 ? planned.effectiveScore : 0;\n\t}\n\tconst value = planned.effectiveScore / Math.max(planned.admissibleTokens, 1);\n\treturn Number.isNaN(value) ? 0 : value;\n}\n"]}