open-multi-agent-kit 0.99.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +3 -3
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +1 -0
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/bun/register-bundled-coding-agent.d.ts +10 -0
  7. package/dist/bun/register-bundled-coding-agent.d.ts.map +1 -0
  8. package/dist/bun/register-bundled-coding-agent.js +12 -0
  9. package/dist/bun/register-bundled-coding-agent.js.map +1 -0
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.d.ts.map +1 -1
  12. package/dist/cli/args.js +1 -1
  13. package/dist/cli/args.js.map +1 -1
  14. package/dist/cli/help.d.ts.map +1 -1
  15. package/dist/cli/help.js +2 -1
  16. package/dist/cli/help.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +14 -2
  19. package/dist/cli.js.map +1 -1
  20. package/dist/commands/neo-cli.d.ts +9 -0
  21. package/dist/commands/neo-cli.d.ts.map +1 -0
  22. package/dist/commands/neo-cli.js +61 -0
  23. package/dist/commands/neo-cli.js.map +1 -0
  24. package/dist/coordination/awareness.d.ts +32 -0
  25. package/dist/coordination/awareness.d.ts.map +1 -0
  26. package/dist/coordination/awareness.js +55 -0
  27. package/dist/coordination/awareness.js.map +1 -0
  28. package/dist/coordination/broker.d.ts +79 -0
  29. package/dist/coordination/broker.d.ts.map +1 -0
  30. package/dist/coordination/broker.js +195 -0
  31. package/dist/coordination/broker.js.map +1 -0
  32. package/dist/coordination/index.d.ts +16 -0
  33. package/dist/coordination/index.d.ts.map +1 -0
  34. package/dist/coordination/index.js +16 -0
  35. package/dist/coordination/index.js.map +1 -0
  36. package/dist/coordination/integration.d.ts +59 -0
  37. package/dist/coordination/integration.d.ts.map +1 -0
  38. package/dist/coordination/integration.js +126 -0
  39. package/dist/coordination/integration.js.map +1 -0
  40. package/dist/coordination/operation.d.ts +106 -0
  41. package/dist/coordination/operation.d.ts.map +1 -0
  42. package/dist/coordination/operation.js +179 -0
  43. package/dist/coordination/operation.js.map +1 -0
  44. package/dist/coordination/resource.d.ts +33 -0
  45. package/dist/coordination/resource.d.ts.map +1 -0
  46. package/dist/coordination/resource.js +89 -0
  47. package/dist/coordination/resource.js.map +1 -0
  48. package/dist/coordination/session.d.ts +74 -0
  49. package/dist/coordination/session.d.ts.map +1 -0
  50. package/dist/coordination/session.js +143 -0
  51. package/dist/coordination/session.js.map +1 -0
  52. package/dist/coordination/types.d.ts +68 -0
  53. package/dist/coordination/types.d.ts.map +1 -0
  54. package/dist/coordination/types.js +26 -0
  55. package/dist/coordination/types.js.map +1 -0
  56. package/dist/core/agent-session.d.ts +29 -2
  57. package/dist/core/agent-session.d.ts.map +1 -1
  58. package/dist/core/agent-session.js +135 -37
  59. package/dist/core/agent-session.js.map +1 -1
  60. package/dist/core/bundled-skills.d.ts +4 -0
  61. package/dist/core/bundled-skills.d.ts.map +1 -0
  62. package/dist/core/bundled-skills.js +32 -0
  63. package/dist/core/bundled-skills.js.map +1 -0
  64. package/dist/core/cli-diagnostics.d.ts +6 -0
  65. package/dist/core/cli-diagnostics.d.ts.map +1 -0
  66. package/dist/core/cli-diagnostics.js +20 -0
  67. package/dist/core/cli-diagnostics.js.map +1 -0
  68. package/dist/core/compaction/control-state.d.ts +41 -0
  69. package/dist/core/compaction/control-state.d.ts.map +1 -0
  70. package/dist/core/compaction/control-state.js +85 -0
  71. package/dist/core/compaction/control-state.js.map +1 -0
  72. package/dist/core/compaction/fallback.d.ts +60 -0
  73. package/dist/core/compaction/fallback.d.ts.map +1 -0
  74. package/dist/core/compaction/fallback.js +117 -0
  75. package/dist/core/compaction/fallback.js.map +1 -0
  76. package/dist/core/compaction/index.d.ts +2 -0
  77. package/dist/core/compaction/index.d.ts.map +1 -1
  78. package/dist/core/compaction/index.js +2 -0
  79. package/dist/core/compaction/index.js.map +1 -1
  80. package/dist/core/context-budget-headroom-candidates.d.ts +16 -1
  81. package/dist/core/context-budget-headroom-candidates.d.ts.map +1 -1
  82. package/dist/core/context-budget-headroom-candidates.js +31 -13
  83. package/dist/core/context-budget-headroom-candidates.js.map +1 -1
  84. package/dist/core/context-budget-headroom-types.d.ts +7 -1
  85. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  86. package/dist/core/context-budget-headroom-types.js +0 -1
  87. package/dist/core/context-budget-headroom-types.js.map +1 -1
  88. package/dist/core/context-budget-headroom.d.ts +22 -0
  89. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  90. package/dist/core/context-budget-headroom.js +46 -2
  91. package/dist/core/context-budget-headroom.js.map +1 -1
  92. package/dist/core/context-budget-v2-global-pass.d.ts +21 -0
  93. package/dist/core/context-budget-v2-global-pass.d.ts.map +1 -0
  94. package/dist/core/context-budget-v2-global-pass.js +203 -0
  95. package/dist/core/context-budget-v2-global-pass.js.map +1 -0
  96. package/dist/core/context-budget-v2-input-validation.d.ts +4 -0
  97. package/dist/core/context-budget-v2-input-validation.d.ts.map +1 -0
  98. package/dist/core/context-budget-v2-input-validation.js +79 -0
  99. package/dist/core/context-budget-v2-input-validation.js.map +1 -0
  100. package/dist/core/context-budget-v2-observability.d.ts +15 -0
  101. package/dist/core/context-budget-v2-observability.d.ts.map +1 -0
  102. package/dist/core/context-budget-v2-observability.js +35 -0
  103. package/dist/core/context-budget-v2-observability.js.map +1 -0
  104. package/dist/core/context-budget-v2-planned-items.d.ts +17 -0
  105. package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -0
  106. package/dist/core/context-budget-v2-planned-items.js +59 -0
  107. package/dist/core/context-budget-v2-planned-items.js.map +1 -0
  108. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  109. package/dist/core/context-budget-v2-planner.js +69 -51
  110. package/dist/core/context-budget-v2-planner.js.map +1 -1
  111. package/dist/core/context-budget-v2-scoring.d.ts +7 -1
  112. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  113. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  114. package/dist/core/context-budget-v2-selection.d.ts +22 -3
  115. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  116. package/dist/core/context-budget-v2-selection.js +41 -59
  117. package/dist/core/context-budget-v2-selection.js.map +1 -1
  118. package/dist/core/context-budget-v2-tiers.d.ts.map +1 -1
  119. package/dist/core/context-budget-v2-tiers.js +9 -42
  120. package/dist/core/context-budget-v2-tiers.js.map +1 -1
  121. package/dist/core/context-budget-v2-types.d.ts +14 -2
  122. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  123. package/dist/core/context-budget-v2-types.js +6 -1
  124. package/dist/core/context-budget-v2-types.js.map +1 -1
  125. package/dist/core/extensions/bundled-virtual-modules.d.ts +15 -0
  126. package/dist/core/extensions/bundled-virtual-modules.d.ts.map +1 -0
  127. package/dist/core/extensions/bundled-virtual-modules.js +63 -0
  128. package/dist/core/extensions/bundled-virtual-modules.js.map +1 -0
  129. package/dist/core/extensions/loader.d.ts.map +1 -1
  130. package/dist/core/extensions/loader.js +2 -42
  131. package/dist/core/extensions/loader.js.map +1 -1
  132. package/dist/core/extensions/runner.d.ts +1 -0
  133. package/dist/core/extensions/runner.d.ts.map +1 -1
  134. package/dist/core/extensions/runner.js +6 -0
  135. package/dist/core/extensions/runner.js.map +1 -1
  136. package/dist/core/extensions/types.d.ts +10 -0
  137. package/dist/core/extensions/types.d.ts.map +1 -1
  138. package/dist/core/extensions/types.js.map +1 -1
  139. package/dist/core/index.d.ts +2 -1
  140. package/dist/core/index.d.ts.map +1 -1
  141. package/dist/core/index.js +2 -1
  142. package/dist/core/index.js.map +1 -1
  143. package/dist/core/loadout-runtime.d.ts +2 -8
  144. package/dist/core/loadout-runtime.d.ts.map +1 -1
  145. package/dist/core/loadout-runtime.js.map +1 -1
  146. package/dist/core/mcp/client.d.ts +27 -6
  147. package/dist/core/mcp/client.d.ts.map +1 -1
  148. package/dist/core/mcp/client.js +80 -20
  149. package/dist/core/mcp/client.js.map +1 -1
  150. package/dist/core/mcp/manager.d.ts +10 -1
  151. package/dist/core/mcp/manager.d.ts.map +1 -1
  152. package/dist/core/mcp/manager.js +68 -9
  153. package/dist/core/mcp/manager.js.map +1 -1
  154. package/dist/core/mcp/protocol.d.ts +9 -3
  155. package/dist/core/mcp/protocol.d.ts.map +1 -1
  156. package/dist/core/mcp/protocol.js +61 -15
  157. package/dist/core/mcp/protocol.js.map +1 -1
  158. package/dist/core/mcp/stdio-transport.d.ts +12 -1
  159. package/dist/core/mcp/stdio-transport.d.ts.map +1 -1
  160. package/dist/core/mcp/stdio-transport.js +30 -2
  161. package/dist/core/mcp/stdio-transport.js.map +1 -1
  162. package/dist/core/mcp-descriptor-injection.d.ts +26 -0
  163. package/dist/core/mcp-descriptor-injection.d.ts.map +1 -0
  164. package/dist/core/mcp-descriptor-injection.js +25 -0
  165. package/dist/core/mcp-descriptor-injection.js.map +1 -0
  166. package/dist/core/mcp-public-presets.d.ts +1 -3
  167. package/dist/core/mcp-public-presets.d.ts.map +1 -1
  168. package/dist/core/mcp-public-presets.js +3 -15
  169. package/dist/core/mcp-public-presets.js.map +1 -1
  170. package/dist/core/model-registry.d.ts.map +1 -1
  171. package/dist/core/model-registry.js +24 -3
  172. package/dist/core/model-registry.js.map +1 -1
  173. package/dist/core/neo/catalog.d.ts +20 -0
  174. package/dist/core/neo/catalog.d.ts.map +1 -0
  175. package/dist/core/neo/catalog.js +50 -0
  176. package/dist/core/neo/catalog.js.map +1 -0
  177. package/dist/core/neo/setup.d.ts +3 -0
  178. package/dist/core/neo/setup.d.ts.map +1 -0
  179. package/dist/core/neo/setup.js +47 -0
  180. package/dist/core/neo/setup.js.map +1 -0
  181. package/dist/core/provider-default-models.d.ts +2 -0
  182. package/dist/core/provider-default-models.d.ts.map +1 -1
  183. package/dist/core/provider-default-models.js +2 -0
  184. package/dist/core/provider-default-models.js.map +1 -1
  185. package/dist/core/provider-display-names.d.ts.map +1 -1
  186. package/dist/core/provider-display-names.js +2 -0
  187. package/dist/core/provider-display-names.js.map +1 -1
  188. package/dist/core/provider-error-classification.d.ts +57 -0
  189. package/dist/core/provider-error-classification.d.ts.map +1 -0
  190. package/dist/core/provider-error-classification.js +103 -0
  191. package/dist/core/provider-error-classification.js.map +1 -0
  192. package/dist/core/provider-resilience.d.ts +10 -0
  193. package/dist/core/provider-resilience.d.ts.map +1 -1
  194. package/dist/core/provider-resilience.js +36 -3
  195. package/dist/core/provider-resilience.js.map +1 -1
  196. package/dist/core/provider-usage-commandcode.d.ts +9 -0
  197. package/dist/core/provider-usage-commandcode.d.ts.map +1 -0
  198. package/dist/core/provider-usage-commandcode.js +198 -0
  199. package/dist/core/provider-usage-commandcode.js.map +1 -0
  200. package/dist/core/provider-usage-devin.d.ts +5 -3
  201. package/dist/core/provider-usage-devin.d.ts.map +1 -1
  202. package/dist/core/provider-usage-devin.js +15 -5
  203. package/dist/core/provider-usage-devin.js.map +1 -1
  204. package/dist/core/provider-usage-types.d.ts +1 -1
  205. package/dist/core/provider-usage-types.d.ts.map +1 -1
  206. package/dist/core/provider-usage-types.js.map +1 -1
  207. package/dist/core/provider-usage.d.ts.map +1 -1
  208. package/dist/core/provider-usage.js +6 -0
  209. package/dist/core/provider-usage.js.map +1 -1
  210. package/dist/core/reasoning-router-resolver.d.ts +7 -0
  211. package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
  212. package/dist/core/reasoning-router-resolver.js +21 -3
  213. package/dist/core/reasoning-router-resolver.js.map +1 -1
  214. package/dist/core/reasoning-router-v4.d.ts +8 -5
  215. package/dist/core/reasoning-router-v4.d.ts.map +1 -1
  216. package/dist/core/reasoning-router-v4.js +8 -5
  217. package/dist/core/reasoning-router-v4.js.map +1 -1
  218. package/dist/core/resource-admission.d.ts +36 -0
  219. package/dist/core/resource-admission.d.ts.map +1 -1
  220. package/dist/core/resource-admission.js +59 -0
  221. package/dist/core/resource-admission.js.map +1 -1
  222. package/dist/core/resource-loader.d.ts.map +1 -1
  223. package/dist/core/resource-loader.js +2 -2
  224. package/dist/core/resource-loader.js.map +1 -1
  225. package/dist/core/run-usage-ledger.d.ts +47 -0
  226. package/dist/core/run-usage-ledger.d.ts.map +1 -0
  227. package/dist/core/run-usage-ledger.js +162 -0
  228. package/dist/core/run-usage-ledger.js.map +1 -0
  229. package/dist/core/run-usage-operation.d.ts +8 -0
  230. package/dist/core/run-usage-operation.d.ts.map +1 -0
  231. package/dist/core/run-usage-operation.js +12 -0
  232. package/dist/core/run-usage-operation.js.map +1 -0
  233. package/dist/core/sdk.d.ts.map +1 -1
  234. package/dist/core/sdk.js +5 -6
  235. package/dist/core/sdk.js.map +1 -1
  236. package/dist/core/session-compaction-service.d.ts +15 -0
  237. package/dist/core/session-compaction-service.d.ts.map +1 -1
  238. package/dist/core/session-compaction-service.js +17 -4
  239. package/dist/core/session-compaction-service.js.map +1 -1
  240. package/dist/core/session-failure-cause.d.ts.map +1 -1
  241. package/dist/core/session-failure-cause.js +14 -5
  242. package/dist/core/session-failure-cause.js.map +1 -1
  243. package/dist/core/session-prompt-lifecycle.d.ts +6 -0
  244. package/dist/core/session-prompt-lifecycle.d.ts.map +1 -1
  245. package/dist/core/session-prompt-lifecycle.js +47 -2
  246. package/dist/core/session-prompt-lifecycle.js.map +1 -1
  247. package/dist/core/session-termination.d.ts.map +1 -1
  248. package/dist/core/session-termination.js +4 -2
  249. package/dist/core/session-termination.js.map +1 -1
  250. package/dist/core/subagent-lane-authority.d.ts +28 -0
  251. package/dist/core/subagent-lane-authority.d.ts.map +1 -0
  252. package/dist/core/subagent-lane-authority.js +119 -0
  253. package/dist/core/subagent-lane-authority.js.map +1 -0
  254. package/dist/core/subagent-lane-contract.d.ts +114 -0
  255. package/dist/core/subagent-lane-contract.d.ts.map +1 -0
  256. package/dist/core/subagent-lane-contract.js +18 -0
  257. package/dist/core/subagent-lane-contract.js.map +1 -0
  258. package/dist/core/subagent-lane-launcher.d.ts +3 -6
  259. package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
  260. package/dist/core/subagent-lane-launcher.js +53 -12
  261. package/dist/core/subagent-lane-launcher.js.map +1 -1
  262. package/dist/core/subagent-orchestration.d.ts +4 -17
  263. package/dist/core/subagent-orchestration.d.ts.map +1 -1
  264. package/dist/core/subagent-orchestration.js.map +1 -1
  265. package/dist/core/todo-runtime-state.d.ts +12 -0
  266. package/dist/core/todo-runtime-state.d.ts.map +1 -1
  267. package/dist/core/todo-runtime-state.js +23 -0
  268. package/dist/core/todo-runtime-state.js.map +1 -1
  269. package/dist/core/verified-run/broker.d.ts.map +1 -1
  270. package/dist/core/verified-run/broker.js +4 -1
  271. package/dist/core/verified-run/broker.js.map +1 -1
  272. package/dist/core/workload-permit-pool.d.ts +1 -1
  273. package/dist/core/workload-permit-pool.d.ts.map +1 -1
  274. package/dist/core/workload-permit-pool.js +3 -0
  275. package/dist/core/workload-permit-pool.js.map +1 -1
  276. package/dist/guardrails/strict-evidence-approval-adapter.d.ts +34 -0
  277. package/dist/guardrails/strict-evidence-approval-adapter.d.ts.map +1 -0
  278. package/dist/guardrails/strict-evidence-approval-adapter.js +70 -0
  279. package/dist/guardrails/strict-evidence-approval-adapter.js.map +1 -0
  280. package/dist/index.d.ts +5 -0
  281. package/dist/index.d.ts.map +1 -1
  282. package/dist/index.js +3 -0
  283. package/dist/index.js.map +1 -1
  284. package/dist/main.d.ts.map +1 -1
  285. package/dist/main.js +11 -18
  286. package/dist/main.js.map +1 -1
  287. package/dist/metacognition/calibration-selective.d.ts +78 -0
  288. package/dist/metacognition/calibration-selective.d.ts.map +1 -0
  289. package/dist/metacognition/calibration-selective.js +161 -0
  290. package/dist/metacognition/calibration-selective.js.map +1 -0
  291. package/dist/metacognition/calibration.d.ts +62 -0
  292. package/dist/metacognition/calibration.d.ts.map +1 -0
  293. package/dist/metacognition/calibration.js +122 -0
  294. package/dist/metacognition/calibration.js.map +1 -0
  295. package/dist/metacognition/checkpoint.d.ts +60 -0
  296. package/dist/metacognition/checkpoint.d.ts.map +1 -0
  297. package/dist/metacognition/checkpoint.js +57 -0
  298. package/dist/metacognition/checkpoint.js.map +1 -0
  299. package/dist/metacognition/context7.d.ts +48 -0
  300. package/dist/metacognition/context7.d.ts.map +1 -0
  301. package/dist/metacognition/context7.js +154 -0
  302. package/dist/metacognition/context7.js.map +1 -0
  303. package/dist/metacognition/decision.d.ts +44 -0
  304. package/dist/metacognition/decision.d.ts.map +1 -0
  305. package/dist/metacognition/decision.js +111 -0
  306. package/dist/metacognition/decision.js.map +1 -0
  307. package/dist/metacognition/evaluation.d.ts +73 -0
  308. package/dist/metacognition/evaluation.d.ts.map +1 -0
  309. package/dist/metacognition/evaluation.js +97 -0
  310. package/dist/metacognition/evaluation.js.map +1 -0
  311. package/dist/metacognition/index.d.ts +31 -0
  312. package/dist/metacognition/index.d.ts.map +1 -0
  313. package/dist/metacognition/index.js +31 -0
  314. package/dist/metacognition/index.js.map +1 -0
  315. package/dist/metacognition/knowledge-action.d.ts +43 -0
  316. package/dist/metacognition/knowledge-action.d.ts.map +1 -0
  317. package/dist/metacognition/knowledge-action.js +60 -0
  318. package/dist/metacognition/knowledge-action.js.map +1 -0
  319. package/dist/metacognition/knowledge.d.ts +84 -0
  320. package/dist/metacognition/knowledge.d.ts.map +1 -0
  321. package/dist/metacognition/knowledge.js +165 -0
  322. package/dist/metacognition/knowledge.js.map +1 -0
  323. package/dist/metacognition/obligations.d.ts +82 -0
  324. package/dist/metacognition/obligations.d.ts.map +1 -0
  325. package/dist/metacognition/obligations.js +139 -0
  326. package/dist/metacognition/obligations.js.map +1 -0
  327. package/dist/metacognition/observation-validity.d.ts +83 -0
  328. package/dist/metacognition/observation-validity.d.ts.map +1 -0
  329. package/dist/metacognition/observation-validity.js +118 -0
  330. package/dist/metacognition/observation-validity.js.map +1 -0
  331. package/dist/metacognition/observe.d.ts +45 -0
  332. package/dist/metacognition/observe.d.ts.map +1 -0
  333. package/dist/metacognition/observe.js +59 -0
  334. package/dist/metacognition/observe.js.map +1 -0
  335. package/dist/metacognition/policy.d.ts +53 -0
  336. package/dist/metacognition/policy.d.ts.map +1 -0
  337. package/dist/metacognition/policy.js +155 -0
  338. package/dist/metacognition/policy.js.map +1 -0
  339. package/dist/metacognition/predictions.d.ts +78 -0
  340. package/dist/metacognition/predictions.d.ts.map +1 -0
  341. package/dist/metacognition/predictions.js +181 -0
  342. package/dist/metacognition/predictions.js.map +1 -0
  343. package/dist/metacognition/retrieval.d.ts +53 -0
  344. package/dist/metacognition/retrieval.d.ts.map +1 -0
  345. package/dist/metacognition/retrieval.js +170 -0
  346. package/dist/metacognition/retrieval.js.map +1 -0
  347. package/dist/metacognition/risk.d.ts +48 -0
  348. package/dist/metacognition/risk.d.ts.map +1 -0
  349. package/dist/metacognition/risk.js +165 -0
  350. package/dist/metacognition/risk.js.map +1 -0
  351. package/dist/metacognition/route-economics.d.ts +95 -0
  352. package/dist/metacognition/route-economics.d.ts.map +1 -0
  353. package/dist/metacognition/route-economics.js +110 -0
  354. package/dist/metacognition/route-economics.js.map +1 -0
  355. package/dist/metacognition/runtime-bridge.d.ts +54 -0
  356. package/dist/metacognition/runtime-bridge.d.ts.map +1 -0
  357. package/dist/metacognition/runtime-bridge.js +140 -0
  358. package/dist/metacognition/runtime-bridge.js.map +1 -0
  359. package/dist/metacognition/skills.d.ts +42 -0
  360. package/dist/metacognition/skills.d.ts.map +1 -0
  361. package/dist/metacognition/skills.js +220 -0
  362. package/dist/metacognition/skills.js.map +1 -0
  363. package/dist/metacognition/state.d.ts +110 -0
  364. package/dist/metacognition/state.d.ts.map +1 -0
  365. package/dist/metacognition/state.js +52 -0
  366. package/dist/metacognition/state.js.map +1 -0
  367. package/dist/metacognition/validation.d.ts +19 -0
  368. package/dist/metacognition/validation.d.ts.map +1 -0
  369. package/dist/metacognition/validation.js +58 -0
  370. package/dist/metacognition/validation.js.map +1 -0
  371. package/dist/metacognition/verification.d.ts +93 -0
  372. package/dist/metacognition/verification.d.ts.map +1 -0
  373. package/dist/metacognition/verification.js +132 -0
  374. package/dist/metacognition/verification.js.map +1 -0
  375. package/dist/metacognition/verifier.d.ts +49 -0
  376. package/dist/metacognition/verifier.d.ts.map +1 -0
  377. package/dist/metacognition/verifier.js +88 -0
  378. package/dist/metacognition/verifier.js.map +1 -0
  379. package/dist/modes/acp/acp-agent.d.ts +24 -0
  380. package/dist/modes/acp/acp-agent.d.ts.map +1 -0
  381. package/dist/modes/acp/acp-agent.js +133 -0
  382. package/dist/modes/acp/acp-agent.js.map +1 -0
  383. package/dist/modes/acp/acp-mode.d.ts +4 -0
  384. package/dist/modes/acp/acp-mode.d.ts.map +1 -0
  385. package/dist/modes/acp/acp-mode.js +34 -0
  386. package/dist/modes/acp/acp-mode.js.map +1 -0
  387. package/dist/modes/acp/acp-session.d.ts +4 -0
  388. package/dist/modes/acp/acp-session.d.ts.map +1 -0
  389. package/dist/modes/acp/acp-session.js +76 -0
  390. package/dist/modes/acp/acp-session.js.map +1 -0
  391. package/dist/modes/acp/acp-transport.d.ts +5 -0
  392. package/dist/modes/acp/acp-transport.d.ts.map +1 -0
  393. package/dist/modes/acp/acp-transport.js +90 -0
  394. package/dist/modes/acp/acp-transport.js.map +1 -0
  395. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  396. package/dist/modes/interactive/interactive-mode.js +1 -0
  397. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  398. package/dist/observation/identity.d.ts +14 -0
  399. package/dist/observation/identity.d.ts.map +1 -0
  400. package/dist/observation/identity.js +29 -0
  401. package/dist/observation/identity.js.map +1 -0
  402. package/dist/observation/index.d.ts +13 -0
  403. package/dist/observation/index.d.ts.map +1 -0
  404. package/dist/observation/index.js +13 -0
  405. package/dist/observation/index.js.map +1 -0
  406. package/dist/observation/observe-mode.d.ts +46 -0
  407. package/dist/observation/observe-mode.d.ts.map +1 -0
  408. package/dist/observation/observe-mode.js +83 -0
  409. package/dist/observation/observe-mode.js.map +1 -0
  410. package/dist/observation/store.d.ts +53 -0
  411. package/dist/observation/store.d.ts.map +1 -0
  412. package/dist/observation/store.js +126 -0
  413. package/dist/observation/store.js.map +1 -0
  414. package/dist/observation/types.d.ts +72 -0
  415. package/dist/observation/types.d.ts.map +1 -0
  416. package/dist/observation/types.js +9 -0
  417. package/dist/observation/types.js.map +1 -0
  418. package/dist/observation/view.d.ts +39 -0
  419. package/dist/observation/view.d.ts.map +1 -0
  420. package/dist/observation/view.js +173 -0
  421. package/dist/observation/view.js.map +1 -0
  422. package/docs/audit-revalidation-2026-09-17.md +77 -0
  423. package/docs/devin-harness.md +33 -4
  424. package/docs/ecraf-normalization.md +83 -0
  425. package/docs/metacognition.md +51 -0
  426. package/docs/model-catalog-refresh.md +110 -1
  427. package/docs/models.md +1 -1
  428. package/docs/neo.md +134 -0
  429. package/docs/providers.md +125 -6
  430. package/docs/run-usage-ledger.md +56 -0
  431. package/docs/runtime-algorithms.md +151 -3
  432. package/docs/skills.md +1 -1
  433. package/docs/usage.md +2 -1
  434. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  435. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  436. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  437. package/examples/extensions/gondolin/package-lock.json +2 -2
  438. package/examples/extensions/gondolin/package.json +1 -1
  439. package/examples/extensions/sandbox/package-lock.json +2 -2
  440. package/examples/extensions/sandbox/package.json +1 -1
  441. package/examples/extensions/subagent/adaptive-agent-runtime.ts +14 -1
  442. package/examples/extensions/subagent/graph-result.ts +48 -0
  443. package/examples/extensions/subagent/index.ts +246 -111
  444. package/examples/extensions/subagent/managed-process-tree.ts +42 -0
  445. package/examples/extensions/subagent/managed-process.test.ts +24 -0
  446. package/examples/extensions/subagent/managed-process.ts +93 -112
  447. package/examples/extensions/subagent/subagent-runtime-types.ts +17 -1
  448. package/examples/extensions/subagent/subagent-stream.ts +161 -0
  449. package/examples/extensions/terminal-browser/README.md +54 -0
  450. package/examples/extensions/terminal-browser/bridge-protocol.ts +29 -0
  451. package/examples/extensions/terminal-browser/bridge.ts +219 -0
  452. package/examples/extensions/terminal-browser/browser-surface.ts +288 -0
  453. package/examples/extensions/terminal-browser/index.ts +215 -0
  454. package/examples/extensions/terminal-browser/placeholders.ts +49 -0
  455. package/examples/extensions/with-deps/package-lock.json +2 -2
  456. package/examples/extensions/with-deps/package.json +1 -1
  457. package/npm-shrinkwrap.json +18 -18
  458. package/package.json +8 -7
  459. package/resources/neo/skills/omk-browser/SKILL.md +32 -0
  460. package/resources/neo/skills/omk-code-review/SKILL.md +24 -0
  461. package/resources/neo/skills/omk-computeruse/SKILL.md +34 -0
  462. package/resources/neo/skills/omk-mcp-setup/SKILL.md +48 -0
  463. package/resources/neo/skills/omk-research/SKILL.md +24 -0
  464. package/resources/neo/skills/omk-site/SKILL.md +28 -0
@@ -1,11 +1,28 @@
1
1
  import { DEFAULT_HEADROOM_QUALITY_POLICY, fnv1aHex, fullTextTokens, heuristicTokenCount, } from "./context-budget-headroom-types.js";
2
2
  const SUMMARY_HEAD_CHARS = 160;
3
3
  const HEADROOM_HEAD_CHARS = 120;
4
- export function deriveRepresentationCandidates(item, policy = DEFAULT_HEADROOM_QUALITY_POLICY) {
4
+ /**
5
+ * Derive the representations the selector may choose for one item.
6
+ *
7
+ * Every non-full candidate is priced by counting the string it actually
8
+ * materializes with `countTokens` — the same counter that priced the full
9
+ * text — so the cost the selector checks against the budget is the cost of
10
+ * the text that would be sent. A compression ratio is never a cost: a summary
11
+ * that returns its source unchanged saves nothing, and a representation whose
12
+ * materialized text is not cheaper than the full text is not offered at all.
13
+ * Pass the counter that produced `item.tokenEstimate`; mixing an exact
14
+ * tokenizer for the full text with the heuristic for its alternatives makes
15
+ * the candidates incomparable.
16
+ */
17
+ export function deriveRepresentationCandidates(item, policy = DEFAULT_HEADROOM_QUALITY_POLICY, countTokens = heuristicTokenCount) {
5
18
  const full = fullTextTokens(item);
6
19
  const sourceRef = item.sourceRef;
7
20
  const retrievable = sourceRef?.retrievable === true;
8
21
  const candidates = [];
22
+ const offerIfCheaper = (candidate) => {
23
+ if (candidate.text !== item.text && candidate.estimatedTokens < full)
24
+ candidates.push(candidate);
25
+ };
9
26
  candidates.push({
10
27
  kind: "full",
11
28
  text: item.text,
@@ -15,31 +32,32 @@ export function deriveRepresentationCandidates(item, policy = DEFAULT_HEADROOM_Q
15
32
  });
16
33
  if (retrievable && sourceRef) {
17
34
  const pointerText = formatPointer(sourceRef);
18
- candidates.push({
35
+ offerIfCheaper({
19
36
  kind: "pointer",
20
37
  text: pointerText,
21
- estimatedTokens: heuristicTokenCount(pointerText),
38
+ estimatedTokens: countTokens(pointerText),
22
39
  fidelity: "bounded",
23
40
  sourceRef,
24
41
  });
25
42
  }
26
- const summaryTokens = Math.ceil(full * 0.15) + 8;
27
- if (isSummaryEligible(item, full, policy) && summaryTokens < full) {
28
- candidates.push({
43
+ if (isSummaryEligible(item, full, policy)) {
44
+ const summaryText = summarizeText(item.text);
45
+ offerIfCheaper({
29
46
  kind: "summary",
30
- text: summarizeText(item.text),
31
- estimatedTokens: summaryTokens,
47
+ text: summaryText,
48
+ estimatedTokens: countTokens(summaryText),
32
49
  fidelity: "lossy",
33
50
  summaryHash: fnv1aHex(item.text),
34
51
  });
35
52
  }
36
- const headroomText = formatHeadroom(item.text, sourceRef);
37
- const headroomTokens = Math.max(Math.ceil(full * 0.35) + 16, heuristicTokenCount(headroomText));
38
- if (full > policy.headroomThresholdTokens && retrievable && headroomTokens < full) {
39
- candidates.push({
53
+ if (full > policy.headroomThresholdTokens && retrievable) {
54
+ const headroomText = formatHeadroom(item.text, sourceRef);
55
+ // The shadow compressor's reversible payload is larger than the visible
56
+ // head, so its price keeps a floor above the counted head text.
57
+ offerIfCheaper({
40
58
  kind: "headroom-compressed",
41
59
  text: headroomText,
42
- estimatedTokens: headroomTokens,
60
+ estimatedTokens: Math.max(Math.ceil(full * 0.35) + 16, countTokens(headroomText)),
43
61
  fidelity: "reversible",
44
62
  sourceRef,
45
63
  compressorId: "headroom-shadow",
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-headroom-candidates.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom-candidates.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfnv1aHex,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\theuristicTokenCount,\n} from \"./context-budget-headroom-types.ts\";\n\nconst SUMMARY_HEAD_CHARS = 160;\nconst HEADROOM_HEAD_CHARS = 120;\n\nexport function deriveRepresentationCandidates(\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): readonly ContextRepresentationCandidateV2[] {\n\tconst full = fullTextTokens(item);\n\tconst sourceRef = item.sourceRef;\n\tconst retrievable = sourceRef?.retrievable === true;\n\tconst candidates: ContextRepresentationCandidateV2[] = [];\n\n\tcandidates.push({\n\t\tkind: \"full\",\n\t\ttext: item.text,\n\t\testimatedTokens: full,\n\t\tfidelity: item.tokenEstimate !== undefined ? \"exact\" : \"bounded\",\n\t\tsourceRef,\n\t});\n\n\tif (retrievable && sourceRef) {\n\t\tconst pointerText = formatPointer(sourceRef);\n\t\tcandidates.push({\n\t\t\tkind: \"pointer\",\n\t\t\ttext: pointerText,\n\t\t\testimatedTokens: heuristicTokenCount(pointerText),\n\t\t\tfidelity: \"bounded\",\n\t\t\tsourceRef,\n\t\t});\n\t}\n\n\tconst summaryTokens = Math.ceil(full * 0.15) + 8;\n\tif (isSummaryEligible(item, full, policy) && summaryTokens < full) {\n\t\tcandidates.push({\n\t\t\tkind: \"summary\",\n\t\t\ttext: summarizeText(item.text),\n\t\t\testimatedTokens: summaryTokens,\n\t\t\tfidelity: \"lossy\",\n\t\t\tsummaryHash: fnv1aHex(item.text),\n\t\t});\n\t}\n\n\tconst headroomText = formatHeadroom(item.text, sourceRef);\n\tconst headroomTokens = Math.max(Math.ceil(full * 0.35) + 16, heuristicTokenCount(headroomText));\n\tif (full > policy.headroomThresholdTokens && retrievable && headroomTokens < full) {\n\t\tcandidates.push({\n\t\t\tkind: \"headroom-compressed\",\n\t\t\ttext: headroomText,\n\t\t\testimatedTokens: headroomTokens,\n\t\t\tfidelity: \"reversible\",\n\t\t\tsourceRef,\n\t\t\tcompressorId: \"headroom-shadow\",\n\t\t});\n\t}\n\n\tif (policy.allowOmit) {\n\t\tcandidates.push({\n\t\t\tkind: \"omit\",\n\t\t\ttext: \"\",\n\t\t\testimatedTokens: 0,\n\t\t\tfidelity: \"lossy\",\n\t\t\tsourceRef,\n\t\t});\n\t}\n\n\treturn candidates;\n}\n\nfunction isSummaryEligible(item: ContextBudgetItemV2, fullTokens: number, policy: HeadroomQualityPolicyV2): boolean {\n\treturn (\n\t\titem.tier === \"history\" ||\n\t\titem.tier === \"evidence\" ||\n\t\titem.tier === \"scratch\" ||\n\t\t(item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns ||\n\t\tfullTokens > policy.headroomThresholdTokens\n\t);\n}\n\nfunction formatPointer(ref: ContextSourceRefV2): string {\n\tconst parts: string[] = [`uri=\"${escapeMetadataValue(ref.uri)}\"`];\n\tif (ref.symbol) parts.push(`symbol=\"${escapeMetadataValue(ref.symbol)}\"`);\n\tif (ref.range) parts.push(`lines=\"${ref.range.startLine}-${ref.range.endLine}\"`);\n\tparts.push(`hash=\"${escapeMetadataValue(ref.contentHash)}\"`);\n\treturn `<pointer ${parts.join(\" \")} />`;\n}\n\nfunction summarizeText(text: string): string {\n\tif (text.length <= SUMMARY_HEAD_CHARS) {\n\t\treturn text;\n\t}\n\treturn `${text.slice(0, SUMMARY_HEAD_CHARS)} …[summary]`;\n}\n\nfunction formatHeadroom(text: string, ref?: ContextSourceRefV2): string {\n\tconst head = text.slice(0, HEADROOM_HEAD_CHARS);\n\tconst hash = fnv1aHex(text);\n\tconst where = ref ? ` uri=\"${escapeMetadataValue(ref.uri)}\"` : \"\";\n\treturn `${head} …[headroom-compressed${where} hash=\"${hash}\"]`;\n}\n\nfunction escapeMetadataValue(value: string): string {\n\treturn value\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n"]}
1
+ {"version":3,"file":"context-budget-headroom-candidates.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom-candidates.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ContextBudgetItemV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfnv1aHex,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\theuristicTokenCount,\n} from \"./context-budget-headroom-types.ts\";\n\nconst SUMMARY_HEAD_CHARS = 160;\nconst HEADROOM_HEAD_CHARS = 120;\n\n/** Prices one materialized representation string; must be the counter that produced the item's full-text cost. */\nexport type RepresentationTokenCounter = (text: string) => number;\n\n/**\n * Derive the representations the selector may choose for one item.\n *\n * Every non-full candidate is priced by counting the string it actually\n * materializes with `countTokens` — the same counter that priced the full\n * text — so the cost the selector checks against the budget is the cost of\n * the text that would be sent. A compression ratio is never a cost: a summary\n * that returns its source unchanged saves nothing, and a representation whose\n * materialized text is not cheaper than the full text is not offered at all.\n * Pass the counter that produced `item.tokenEstimate`; mixing an exact\n * tokenizer for the full text with the heuristic for its alternatives makes\n * the candidates incomparable.\n */\nexport function deriveRepresentationCandidates(\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n\tcountTokens: RepresentationTokenCounter = heuristicTokenCount,\n): readonly ContextRepresentationCandidateV2[] {\n\tconst full = fullTextTokens(item);\n\tconst sourceRef = item.sourceRef;\n\tconst retrievable = sourceRef?.retrievable === true;\n\tconst candidates: ContextRepresentationCandidateV2[] = [];\n\tconst offerIfCheaper = (candidate: ContextRepresentationCandidateV2): void => {\n\t\tif (candidate.text !== item.text && candidate.estimatedTokens < full) candidates.push(candidate);\n\t};\n\n\tcandidates.push({\n\t\tkind: \"full\",\n\t\ttext: item.text,\n\t\testimatedTokens: full,\n\t\tfidelity: item.tokenEstimate !== undefined ? \"exact\" : \"bounded\",\n\t\tsourceRef,\n\t});\n\n\tif (retrievable && sourceRef) {\n\t\tconst pointerText = formatPointer(sourceRef);\n\t\tofferIfCheaper({\n\t\t\tkind: \"pointer\",\n\t\t\ttext: pointerText,\n\t\t\testimatedTokens: countTokens(pointerText),\n\t\t\tfidelity: \"bounded\",\n\t\t\tsourceRef,\n\t\t});\n\t}\n\n\tif (isSummaryEligible(item, full, policy)) {\n\t\tconst summaryText = summarizeText(item.text);\n\t\tofferIfCheaper({\n\t\t\tkind: \"summary\",\n\t\t\ttext: summaryText,\n\t\t\testimatedTokens: countTokens(summaryText),\n\t\t\tfidelity: \"lossy\",\n\t\t\tsummaryHash: fnv1aHex(item.text),\n\t\t});\n\t}\n\n\tif (full > policy.headroomThresholdTokens && retrievable) {\n\t\tconst headroomText = formatHeadroom(item.text, sourceRef);\n\t\t// The shadow compressor's reversible payload is larger than the visible\n\t\t// head, so its price keeps a floor above the counted head text.\n\t\tofferIfCheaper({\n\t\t\tkind: \"headroom-compressed\",\n\t\t\ttext: headroomText,\n\t\t\testimatedTokens: Math.max(Math.ceil(full * 0.35) + 16, countTokens(headroomText)),\n\t\t\tfidelity: \"reversible\",\n\t\t\tsourceRef,\n\t\t\tcompressorId: \"headroom-shadow\",\n\t\t});\n\t}\n\n\tif (policy.allowOmit) {\n\t\tcandidates.push({\n\t\t\tkind: \"omit\",\n\t\t\ttext: \"\",\n\t\t\testimatedTokens: 0,\n\t\t\tfidelity: \"lossy\",\n\t\t\tsourceRef,\n\t\t});\n\t}\n\n\treturn candidates;\n}\n\nfunction isSummaryEligible(item: ContextBudgetItemV2, fullTokens: number, policy: HeadroomQualityPolicyV2): boolean {\n\treturn (\n\t\titem.tier === \"history\" ||\n\t\titem.tier === \"evidence\" ||\n\t\titem.tier === \"scratch\" ||\n\t\t(item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns ||\n\t\tfullTokens > policy.headroomThresholdTokens\n\t);\n}\n\nfunction formatPointer(ref: ContextSourceRefV2): string {\n\tconst parts: string[] = [`uri=\"${escapeMetadataValue(ref.uri)}\"`];\n\tif (ref.symbol) parts.push(`symbol=\"${escapeMetadataValue(ref.symbol)}\"`);\n\tif (ref.range) parts.push(`lines=\"${ref.range.startLine}-${ref.range.endLine}\"`);\n\tparts.push(`hash=\"${escapeMetadataValue(ref.contentHash)}\"`);\n\treturn `<pointer ${parts.join(\" \")} />`;\n}\n\nfunction summarizeText(text: string): string {\n\tif (text.length <= SUMMARY_HEAD_CHARS) {\n\t\treturn text;\n\t}\n\treturn `${text.slice(0, SUMMARY_HEAD_CHARS)} …[summary]`;\n}\n\nfunction formatHeadroom(text: string, ref?: ContextSourceRefV2): string {\n\tconst head = text.slice(0, HEADROOM_HEAD_CHARS);\n\tconst hash = fnv1aHex(text);\n\tconst where = ref ? ` uri=\"${escapeMetadataValue(ref.uri)}\"` : \"\";\n\treturn `${head} …[headroom-compressed${where} hash=\"${hash}\"]`;\n}\n\nfunction escapeMetadataValue(value: string): string {\n\treturn value\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n"]}
@@ -48,7 +48,13 @@ export interface ContextBudgetItemV2 {
48
48
  readonly representations?: readonly ContextRepresentationCandidateV2[];
49
49
  }
50
50
  export interface HeadroomQualityPolicyV2 {
51
- readonly preferFullForHighPriority: boolean;
51
+ /**
52
+ * @deprecated Never read by the candidate generator or the selector; the
53
+ * priority weight already prefers full text for high-priority items. Kept
54
+ * optional so existing configs still type-check. Flipping it changes no
55
+ * decision (see context-budget-quality-policy-semantics.test.ts).
56
+ */
57
+ readonly preferFullForHighPriority?: boolean;
52
58
  readonly preferPointerForRetrievable: boolean;
53
59
  readonly summaryMaxAgeTurns: number;
54
60
  readonly headroomThresholdTokens: number;
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-headroom-types.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAC5B,QAAQ,GACR,aAAa,GACb,eAAe,GACf,OAAO,GACP,QAAQ,GACR,KAAK,GACL,SAAS,GACT,UAAU,GACV,SAAS,CAAC;AAEb,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEzE,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAExG,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,qBAAqB,GAAG,MAAM,CAAC;AAE1G,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,+BAA+B,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;AAE3F,MAAM,WAAW,oCAAoC;IACpD,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC7D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,gCAAgC;IAChD,QAAQ,CAAC,IAAI,EAAE,2BAA2B,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,+BAA+B,CAAC;IACnD,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,oCAAoC,CAAC;CACtD;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,gCAAgC,EAAE,CAAC;CACvE;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,yBAAyB,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,2BAA2B,EAAE,OAAO,CAAC;IAC9C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,eAAO,MAAM,+BAA+B,EAAE,uBAM7C,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,eAAe,EAAE,SAAS,2BAA2B,EAAE,CAAC;CACjE;AAED,wBAAgB,wBAAwB,CACvC,MAAM,GAAE,uBAAyD,GAC/D,uBAAuB,CAQzB;AAED,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACvC;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAKhE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAKrE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO9C","sourcesContent":["export type ContextBudgetTierV2 =\n\t| \"system\"\n\t| \"active-goal\"\n\t| \"current-files\"\n\t| \"tools\"\n\t| \"skills\"\n\t| \"mcp\"\n\t| \"history\"\n\t| \"evidence\"\n\t| \"scratch\";\n\nexport type ContextBudgetPriorityV2 = \"hard\" | \"high\" | \"medium\" | \"low\";\n\nexport type ContextEvidenceKindV2 = \"test\" | \"command\" | \"file\" | \"decision\" | \"lane\" | \"user\" | \"none\";\n\nexport type ContextRepresentationKindV2 = \"full\" | \"pointer\" | \"summary\" | \"headroom-compressed\" | \"omit\";\n\nexport interface ContextSourceRefV2 {\n\treadonly uri: string;\n\treadonly symbol?: string;\n\treadonly range?: { readonly startLine: number; readonly endLine: number };\n\treadonly contentHash: string;\n\treadonly retrievable: boolean;\n}\n\nexport type ContextRepresentationFidelityV2 = \"exact\" | \"bounded\" | \"lossy\" | \"reversible\";\n\nexport interface ContextRepresentationCacheMetadataV2 {\n\treadonly hit: boolean;\n\treadonly key: string;\n\treadonly keyKind?: \"exact\" | \"materialized\";\n\treadonly layer?: \"turn\" | \"session\" | \"workspace\" | \"shared\";\n\treadonly rejectedReason?: string;\n}\n\nexport interface ContextRepresentationCandidateV2 {\n\treadonly kind: ContextRepresentationKindV2;\n\treadonly text: string;\n\treadonly estimatedTokens: number;\n\treadonly fidelity: ContextRepresentationFidelityV2;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly summaryHash?: string;\n\treadonly compressorId?: string;\n\treadonly cache?: ContextRepresentationCacheMetadataV2;\n}\n\nexport interface ContextBudgetItemV2 {\n\treadonly id: string;\n\treadonly tier: ContextBudgetTierV2;\n\treadonly priority: ContextBudgetPriorityV2;\n\treadonly text: string;\n\treadonly tokenEstimate?: number;\n\treadonly relevance?: number;\n\treadonly recency?: number;\n\treadonly evidenceValue?: number;\n\treadonly evidenceKind?: ContextEvidenceKindV2;\n\treadonly ageTurns?: number;\n\treadonly redundancyKey?: string;\n\treadonly required?: boolean;\n\treadonly pinReason?: string;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly representations?: readonly ContextRepresentationCandidateV2[];\n}\n\nexport interface HeadroomQualityPolicyV2 {\n\treadonly preferFullForHighPriority: boolean;\n\treadonly preferPointerForRetrievable: boolean;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly headroomThresholdTokens: number;\n\treadonly allowOmit: boolean;\n}\n\nexport const DEFAULT_HEADROOM_QUALITY_POLICY: HeadroomQualityPolicyV2 = {\n\tpreferFullForHighPriority: true,\n\tpreferPointerForRetrievable: true,\n\tsummaryMaxAgeTurns: 4,\n\theadroomThresholdTokens: 400,\n\tallowOmit: true,\n};\n\nexport interface HeadroomRuntimeStatusV2 {\n\treadonly policyId: string;\n\treadonly selector: string;\n\treadonly headroomThresholdTokens: number;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly representations: readonly ContextRepresentationKindV2[];\n}\n\nexport function getHeadroomRuntimeStatus(\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): HeadroomRuntimeStatusV2 {\n\treturn {\n\t\tpolicyId: \"context-budget-v2\",\n\t\tselector: \"headroom-selector\",\n\t\theadroomThresholdTokens: policy.headroomThresholdTokens,\n\t\tsummaryMaxAgeTurns: policy.summaryMaxAgeTurns,\n\t\trepresentations: [\"full\", \"pointer\", \"summary\", \"headroom-compressed\", \"omit\"],\n\t};\n}\n\nexport interface RepresentationBudgetContext {\n\treadonly tierUsedTokens: number;\n\treadonly tierCeilingTokens: number;\n\treadonly remainingGlobalTokens: number;\n}\n\nconst CHARS_PER_TOKEN_HEURISTIC = 4;\n\nexport function heuristicTokenCount(text: string): number {\n\tif (text.length === 0) {\n\t\treturn 0;\n\t}\n\treturn Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fullTextTokens(item: ContextBudgetItemV2): number {\n\tif (item.tokenEstimate !== undefined && Number.isFinite(item.tokenEstimate)) {\n\t\treturn Math.max(0, Math.floor(item.tokenEstimate));\n\t}\n\treturn heuristicTokenCount(item.text);\n}\n\nexport function estimatePointerTokens(ref: ContextSourceRefV2): number {\n\tlet total = ref.uri.length + (ref.contentHash?.length ?? 0) + 16;\n\tif (ref.symbol) total += ref.symbol.length;\n\tif (ref.range) total += 8;\n\treturn Math.max(8, Math.ceil(total / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fnv1aHex(input: string): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < input.length; i++) {\n\t\thash ^= input.charCodeAt(i);\n\t\thash = Math.imul(hash, 0x01000193) >>> 0;\n\t}\n\treturn hash.toString(16).padStart(8, \"0\");\n}\n"]}
1
+ {"version":3,"file":"context-budget-headroom-types.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom-types.ts"],"names":[],"mappings":"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","sourcesContent":["export type ContextBudgetTierV2 =\n\t| \"system\"\n\t| \"active-goal\"\n\t| \"current-files\"\n\t| \"tools\"\n\t| \"skills\"\n\t| \"mcp\"\n\t| \"history\"\n\t| \"evidence\"\n\t| \"scratch\";\n\nexport type ContextBudgetPriorityV2 = \"hard\" | \"high\" | \"medium\" | \"low\";\n\nexport type ContextEvidenceKindV2 = \"test\" | \"command\" | \"file\" | \"decision\" | \"lane\" | \"user\" | \"none\";\n\nexport type ContextRepresentationKindV2 = \"full\" | \"pointer\" | \"summary\" | \"headroom-compressed\" | \"omit\";\n\nexport interface ContextSourceRefV2 {\n\treadonly uri: string;\n\treadonly symbol?: string;\n\treadonly range?: { readonly startLine: number; readonly endLine: number };\n\treadonly contentHash: string;\n\treadonly retrievable: boolean;\n}\n\nexport type ContextRepresentationFidelityV2 = \"exact\" | \"bounded\" | \"lossy\" | \"reversible\";\n\nexport interface ContextRepresentationCacheMetadataV2 {\n\treadonly hit: boolean;\n\treadonly key: string;\n\treadonly keyKind?: \"exact\" | \"materialized\";\n\treadonly layer?: \"turn\" | \"session\" | \"workspace\" | \"shared\";\n\treadonly rejectedReason?: string;\n}\n\nexport interface ContextRepresentationCandidateV2 {\n\treadonly kind: ContextRepresentationKindV2;\n\treadonly text: string;\n\treadonly estimatedTokens: number;\n\treadonly fidelity: ContextRepresentationFidelityV2;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly summaryHash?: string;\n\treadonly compressorId?: string;\n\treadonly cache?: ContextRepresentationCacheMetadataV2;\n}\n\nexport interface ContextBudgetItemV2 {\n\treadonly id: string;\n\treadonly tier: ContextBudgetTierV2;\n\treadonly priority: ContextBudgetPriorityV2;\n\treadonly text: string;\n\treadonly tokenEstimate?: number;\n\treadonly relevance?: number;\n\treadonly recency?: number;\n\treadonly evidenceValue?: number;\n\treadonly evidenceKind?: ContextEvidenceKindV2;\n\treadonly ageTurns?: number;\n\treadonly redundancyKey?: string;\n\treadonly required?: boolean;\n\treadonly pinReason?: string;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly representations?: readonly ContextRepresentationCandidateV2[];\n}\n\nexport interface HeadroomQualityPolicyV2 {\n\t/**\n\t * @deprecated Never read by the candidate generator or the selector; the\n\t * priority weight already prefers full text for high-priority items. Kept\n\t * optional so existing configs still type-check. Flipping it changes no\n\t * decision (see context-budget-quality-policy-semantics.test.ts).\n\t */\n\treadonly preferFullForHighPriority?: boolean;\n\treadonly preferPointerForRetrievable: boolean;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly headroomThresholdTokens: number;\n\treadonly allowOmit: boolean;\n}\n\nexport const DEFAULT_HEADROOM_QUALITY_POLICY: HeadroomQualityPolicyV2 = {\n\tpreferPointerForRetrievable: true,\n\tsummaryMaxAgeTurns: 4,\n\theadroomThresholdTokens: 400,\n\tallowOmit: true,\n};\n\nexport interface HeadroomRuntimeStatusV2 {\n\treadonly policyId: string;\n\treadonly selector: string;\n\treadonly headroomThresholdTokens: number;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly representations: readonly ContextRepresentationKindV2[];\n}\n\nexport function getHeadroomRuntimeStatus(\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): HeadroomRuntimeStatusV2 {\n\treturn {\n\t\tpolicyId: \"context-budget-v2\",\n\t\tselector: \"headroom-selector\",\n\t\theadroomThresholdTokens: policy.headroomThresholdTokens,\n\t\tsummaryMaxAgeTurns: policy.summaryMaxAgeTurns,\n\t\trepresentations: [\"full\", \"pointer\", \"summary\", \"headroom-compressed\", \"omit\"],\n\t};\n}\n\nexport interface RepresentationBudgetContext {\n\treadonly tierUsedTokens: number;\n\treadonly tierCeilingTokens: number;\n\treadonly remainingGlobalTokens: number;\n}\n\nconst CHARS_PER_TOKEN_HEURISTIC = 4;\n\nexport function heuristicTokenCount(text: string): number {\n\tif (text.length === 0) {\n\t\treturn 0;\n\t}\n\treturn Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fullTextTokens(item: ContextBudgetItemV2): number {\n\tif (item.tokenEstimate !== undefined && Number.isFinite(item.tokenEstimate)) {\n\t\treturn Math.max(0, Math.floor(item.tokenEstimate));\n\t}\n\treturn heuristicTokenCount(item.text);\n}\n\nexport function estimatePointerTokens(ref: ContextSourceRefV2): number {\n\tlet total = ref.uri.length + (ref.contentHash?.length ?? 0) + 16;\n\tif (ref.symbol) total += ref.symbol.length;\n\tif (ref.range) total += 8;\n\treturn Math.max(8, Math.ceil(total / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fnv1aHex(input: string): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < input.length; i++) {\n\t\thash ^= input.charCodeAt(i);\n\t\thash = Math.imul(hash, 0x01000193) >>> 0;\n\t}\n\treturn hash.toString(16).padStart(8, \"0\");\n}\n"]}
@@ -1,5 +1,4 @@
1
1
  export const DEFAULT_HEADROOM_QUALITY_POLICY = {
2
- preferFullForHighPriority: true,
3
2
  preferPointerForRetrievable: true,
4
3
  summaryMaxAgeTurns: 4,
5
4
  headroomThresholdTokens: 400,
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-headroom-types.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom-types.ts"],"names":[],"mappings":"AAwEA,MAAM,CAAC,MAAM,+BAA+B,GAA4B;IACvE,yBAAyB,EAAE,IAAI;IAC/B,2BAA2B,EAAE,IAAI;IACjC,kBAAkB,EAAE,CAAC;IACrB,uBAAuB,EAAE,GAAG;IAC5B,SAAS,EAAE,IAAI;CACf,CAAC;AAUF,MAAM,UAAU,wBAAwB,CACvC,MAAM,GAA4B,+BAA+B,EACvC;IAC1B,OAAO;QACN,QAAQ,EAAE,mBAAmB;QAC7B,QAAQ,EAAE,mBAAmB;QAC7B,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;QACvD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,eAAe,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,CAAC;KAC9E,CAAC;AAAA,CACF;AAQD,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAEpC,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAU;IACzD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC;IACV,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,CAAC,CAAC;AAAA,CACvE;AAED,MAAM,UAAU,cAAc,CAAC,IAAyB,EAAU;IACjE,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACtC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAuB,EAAU;IACtE,IAAI,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;IACjE,IAAI,GAAG,CAAC,MAAM;QAAE,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;IAC3C,IAAI,GAAG,CAAC,KAAK;QAAE,KAAK,IAAI,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,yBAAyB,CAAC,CAAC,CAAC;AAAA,CACjE;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAU;IAC/C,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAAA,CAC1C","sourcesContent":["export type ContextBudgetTierV2 =\n\t| \"system\"\n\t| \"active-goal\"\n\t| \"current-files\"\n\t| \"tools\"\n\t| \"skills\"\n\t| \"mcp\"\n\t| \"history\"\n\t| \"evidence\"\n\t| \"scratch\";\n\nexport type ContextBudgetPriorityV2 = \"hard\" | \"high\" | \"medium\" | \"low\";\n\nexport type ContextEvidenceKindV2 = \"test\" | \"command\" | \"file\" | \"decision\" | \"lane\" | \"user\" | \"none\";\n\nexport type ContextRepresentationKindV2 = \"full\" | \"pointer\" | \"summary\" | \"headroom-compressed\" | \"omit\";\n\nexport interface ContextSourceRefV2 {\n\treadonly uri: string;\n\treadonly symbol?: string;\n\treadonly range?: { readonly startLine: number; readonly endLine: number };\n\treadonly contentHash: string;\n\treadonly retrievable: boolean;\n}\n\nexport type ContextRepresentationFidelityV2 = \"exact\" | \"bounded\" | \"lossy\" | \"reversible\";\n\nexport interface ContextRepresentationCacheMetadataV2 {\n\treadonly hit: boolean;\n\treadonly key: string;\n\treadonly keyKind?: \"exact\" | \"materialized\";\n\treadonly layer?: \"turn\" | \"session\" | \"workspace\" | \"shared\";\n\treadonly rejectedReason?: string;\n}\n\nexport interface ContextRepresentationCandidateV2 {\n\treadonly kind: ContextRepresentationKindV2;\n\treadonly text: string;\n\treadonly estimatedTokens: number;\n\treadonly fidelity: ContextRepresentationFidelityV2;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly summaryHash?: string;\n\treadonly compressorId?: string;\n\treadonly cache?: ContextRepresentationCacheMetadataV2;\n}\n\nexport interface ContextBudgetItemV2 {\n\treadonly id: string;\n\treadonly tier: ContextBudgetTierV2;\n\treadonly priority: ContextBudgetPriorityV2;\n\treadonly text: string;\n\treadonly tokenEstimate?: number;\n\treadonly relevance?: number;\n\treadonly recency?: number;\n\treadonly evidenceValue?: number;\n\treadonly evidenceKind?: ContextEvidenceKindV2;\n\treadonly ageTurns?: number;\n\treadonly redundancyKey?: string;\n\treadonly required?: boolean;\n\treadonly pinReason?: string;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly representations?: readonly ContextRepresentationCandidateV2[];\n}\n\nexport interface HeadroomQualityPolicyV2 {\n\treadonly preferFullForHighPriority: boolean;\n\treadonly preferPointerForRetrievable: boolean;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly headroomThresholdTokens: number;\n\treadonly allowOmit: boolean;\n}\n\nexport const DEFAULT_HEADROOM_QUALITY_POLICY: HeadroomQualityPolicyV2 = {\n\tpreferFullForHighPriority: true,\n\tpreferPointerForRetrievable: true,\n\tsummaryMaxAgeTurns: 4,\n\theadroomThresholdTokens: 400,\n\tallowOmit: true,\n};\n\nexport interface HeadroomRuntimeStatusV2 {\n\treadonly policyId: string;\n\treadonly selector: string;\n\treadonly headroomThresholdTokens: number;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly representations: readonly ContextRepresentationKindV2[];\n}\n\nexport function getHeadroomRuntimeStatus(\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): HeadroomRuntimeStatusV2 {\n\treturn {\n\t\tpolicyId: \"context-budget-v2\",\n\t\tselector: \"headroom-selector\",\n\t\theadroomThresholdTokens: policy.headroomThresholdTokens,\n\t\tsummaryMaxAgeTurns: policy.summaryMaxAgeTurns,\n\t\trepresentations: [\"full\", \"pointer\", \"summary\", \"headroom-compressed\", \"omit\"],\n\t};\n}\n\nexport interface RepresentationBudgetContext {\n\treadonly tierUsedTokens: number;\n\treadonly tierCeilingTokens: number;\n\treadonly remainingGlobalTokens: number;\n}\n\nconst CHARS_PER_TOKEN_HEURISTIC = 4;\n\nexport function heuristicTokenCount(text: string): number {\n\tif (text.length === 0) {\n\t\treturn 0;\n\t}\n\treturn Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fullTextTokens(item: ContextBudgetItemV2): number {\n\tif (item.tokenEstimate !== undefined && Number.isFinite(item.tokenEstimate)) {\n\t\treturn Math.max(0, Math.floor(item.tokenEstimate));\n\t}\n\treturn heuristicTokenCount(item.text);\n}\n\nexport function estimatePointerTokens(ref: ContextSourceRefV2): number {\n\tlet total = ref.uri.length + (ref.contentHash?.length ?? 0) + 16;\n\tif (ref.symbol) total += ref.symbol.length;\n\tif (ref.range) total += 8;\n\treturn Math.max(8, Math.ceil(total / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fnv1aHex(input: string): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < input.length; i++) {\n\t\thash ^= input.charCodeAt(i);\n\t\thash = Math.imul(hash, 0x01000193) >>> 0;\n\t}\n\treturn hash.toString(16).padStart(8, \"0\");\n}\n"]}
1
+ {"version":3,"file":"context-budget-headroom-types.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom-types.ts"],"names":[],"mappings":"AA8EA,MAAM,CAAC,MAAM,+BAA+B,GAA4B;IACvE,2BAA2B,EAAE,IAAI;IACjC,kBAAkB,EAAE,CAAC;IACrB,uBAAuB,EAAE,GAAG;IAC5B,SAAS,EAAE,IAAI;CACf,CAAC;AAUF,MAAM,UAAU,wBAAwB,CACvC,MAAM,GAA4B,+BAA+B,EACvC;IAC1B,OAAO;QACN,QAAQ,EAAE,mBAAmB;QAC7B,QAAQ,EAAE,mBAAmB;QAC7B,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;QACvD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,eAAe,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,CAAC;KAC9E,CAAC;AAAA,CACF;AAQD,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAEpC,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAU;IACzD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC;IACV,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,CAAC,CAAC;AAAA,CACvE;AAED,MAAM,UAAU,cAAc,CAAC,IAAyB,EAAU;IACjE,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACtC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAuB,EAAU;IACtE,IAAI,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;IACjE,IAAI,GAAG,CAAC,MAAM;QAAE,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;IAC3C,IAAI,GAAG,CAAC,KAAK;QAAE,KAAK,IAAI,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,yBAAyB,CAAC,CAAC,CAAC;AAAA,CACjE;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAU;IAC/C,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAAA,CAC1C","sourcesContent":["export type ContextBudgetTierV2 =\n\t| \"system\"\n\t| \"active-goal\"\n\t| \"current-files\"\n\t| \"tools\"\n\t| \"skills\"\n\t| \"mcp\"\n\t| \"history\"\n\t| \"evidence\"\n\t| \"scratch\";\n\nexport type ContextBudgetPriorityV2 = \"hard\" | \"high\" | \"medium\" | \"low\";\n\nexport type ContextEvidenceKindV2 = \"test\" | \"command\" | \"file\" | \"decision\" | \"lane\" | \"user\" | \"none\";\n\nexport type ContextRepresentationKindV2 = \"full\" | \"pointer\" | \"summary\" | \"headroom-compressed\" | \"omit\";\n\nexport interface ContextSourceRefV2 {\n\treadonly uri: string;\n\treadonly symbol?: string;\n\treadonly range?: { readonly startLine: number; readonly endLine: number };\n\treadonly contentHash: string;\n\treadonly retrievable: boolean;\n}\n\nexport type ContextRepresentationFidelityV2 = \"exact\" | \"bounded\" | \"lossy\" | \"reversible\";\n\nexport interface ContextRepresentationCacheMetadataV2 {\n\treadonly hit: boolean;\n\treadonly key: string;\n\treadonly keyKind?: \"exact\" | \"materialized\";\n\treadonly layer?: \"turn\" | \"session\" | \"workspace\" | \"shared\";\n\treadonly rejectedReason?: string;\n}\n\nexport interface ContextRepresentationCandidateV2 {\n\treadonly kind: ContextRepresentationKindV2;\n\treadonly text: string;\n\treadonly estimatedTokens: number;\n\treadonly fidelity: ContextRepresentationFidelityV2;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly summaryHash?: string;\n\treadonly compressorId?: string;\n\treadonly cache?: ContextRepresentationCacheMetadataV2;\n}\n\nexport interface ContextBudgetItemV2 {\n\treadonly id: string;\n\treadonly tier: ContextBudgetTierV2;\n\treadonly priority: ContextBudgetPriorityV2;\n\treadonly text: string;\n\treadonly tokenEstimate?: number;\n\treadonly relevance?: number;\n\treadonly recency?: number;\n\treadonly evidenceValue?: number;\n\treadonly evidenceKind?: ContextEvidenceKindV2;\n\treadonly ageTurns?: number;\n\treadonly redundancyKey?: string;\n\treadonly required?: boolean;\n\treadonly pinReason?: string;\n\treadonly sourceRef?: ContextSourceRefV2;\n\treadonly representations?: readonly ContextRepresentationCandidateV2[];\n}\n\nexport interface HeadroomQualityPolicyV2 {\n\t/**\n\t * @deprecated Never read by the candidate generator or the selector; the\n\t * priority weight already prefers full text for high-priority items. Kept\n\t * optional so existing configs still type-check. Flipping it changes no\n\t * decision (see context-budget-quality-policy-semantics.test.ts).\n\t */\n\treadonly preferFullForHighPriority?: boolean;\n\treadonly preferPointerForRetrievable: boolean;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly headroomThresholdTokens: number;\n\treadonly allowOmit: boolean;\n}\n\nexport const DEFAULT_HEADROOM_QUALITY_POLICY: HeadroomQualityPolicyV2 = {\n\tpreferPointerForRetrievable: true,\n\tsummaryMaxAgeTurns: 4,\n\theadroomThresholdTokens: 400,\n\tallowOmit: true,\n};\n\nexport interface HeadroomRuntimeStatusV2 {\n\treadonly policyId: string;\n\treadonly selector: string;\n\treadonly headroomThresholdTokens: number;\n\treadonly summaryMaxAgeTurns: number;\n\treadonly representations: readonly ContextRepresentationKindV2[];\n}\n\nexport function getHeadroomRuntimeStatus(\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): HeadroomRuntimeStatusV2 {\n\treturn {\n\t\tpolicyId: \"context-budget-v2\",\n\t\tselector: \"headroom-selector\",\n\t\theadroomThresholdTokens: policy.headroomThresholdTokens,\n\t\tsummaryMaxAgeTurns: policy.summaryMaxAgeTurns,\n\t\trepresentations: [\"full\", \"pointer\", \"summary\", \"headroom-compressed\", \"omit\"],\n\t};\n}\n\nexport interface RepresentationBudgetContext {\n\treadonly tierUsedTokens: number;\n\treadonly tierCeilingTokens: number;\n\treadonly remainingGlobalTokens: number;\n}\n\nconst CHARS_PER_TOKEN_HEURISTIC = 4;\n\nexport function heuristicTokenCount(text: string): number {\n\tif (text.length === 0) {\n\t\treturn 0;\n\t}\n\treturn Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fullTextTokens(item: ContextBudgetItemV2): number {\n\tif (item.tokenEstimate !== undefined && Number.isFinite(item.tokenEstimate)) {\n\t\treturn Math.max(0, Math.floor(item.tokenEstimate));\n\t}\n\treturn heuristicTokenCount(item.text);\n}\n\nexport function estimatePointerTokens(ref: ContextSourceRefV2): number {\n\tlet total = ref.uri.length + (ref.contentHash?.length ?? 0) + 16;\n\tif (ref.symbol) total += ref.symbol.length;\n\tif (ref.range) total += 8;\n\treturn Math.max(8, Math.ceil(total / CHARS_PER_TOKEN_HEURISTIC));\n}\n\nexport function fnv1aHex(input: string): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < input.length; i++) {\n\t\thash ^= input.charCodeAt(i);\n\t\thash = Math.imul(hash, 0x01000193) >>> 0;\n\t}\n\treturn hash.toString(16).padStart(8, \"0\");\n}\n"]}
@@ -1,5 +1,27 @@
1
1
  import { type ContextBudgetItemV2, type ContextRepresentationCandidateV2, type HeadroomQualityPolicyV2, type RepresentationBudgetContext } from "./context-budget-headroom-types.ts";
2
+ /**
3
+ * Budget-independent admissibility for one representation candidate — the
4
+ * same gate {@link chooseHeadroomRepresentation} applies via its per-kind
5
+ * rejections. Ranking and selection must share this set, otherwise a cheap
6
+ * representation the chooser could never pick distorts the density
7
+ * denominator (an item ranked on a 1-token pointer it can only use as a
8
+ * 80-token full text is admitted ahead of genuinely denser items).
9
+ *
10
+ * Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT
11
+ * here: it is a ranking-time property, not an admissibility property.
12
+ */
13
+ export declare function isRepresentationEligible(candidate: ContextRepresentationCandidateV2, item: ContextBudgetItemV2, policy?: HeadroomQualityPolicyV2): boolean;
2
14
  export declare function chooseHeadroomRepresentation(item: ContextBudgetItemV2, budget: RepresentationBudgetContext, policy?: HeadroomQualityPolicyV2): ContextRepresentationCandidateV2;
15
+ /**
16
+ * The selector's own preference score for one representation — the closest
17
+ * thing this policy has to a utility function, and the quantity the planner's
18
+ * exchange passes compare when they promote or step an item down. Exported so
19
+ * an oracle can score a plan with the same rule the planner used instead of
20
+ * restating the formula. `tight` is the congestion flag
21
+ * {@link chooseHeadroomRepresentation} derives from the budget context; pass
22
+ * `false` for a congestion-free reference score.
23
+ */
24
+ export declare function scoreRepresentationPreferenceV2(candidate: ContextRepresentationCandidateV2, item: ContextBudgetItemV2, policy?: HeadroomQualityPolicyV2, tight?: boolean): number;
3
25
  export { deriveRepresentationCandidates } from "./context-budget-headroom-candidates.ts";
4
26
  export { type ContextBudgetItemV2, type ContextBudgetPriorityV2, type ContextBudgetTierV2, type ContextEvidenceKindV2, type ContextRepresentationCacheMetadataV2, type ContextRepresentationCandidateV2, type ContextRepresentationFidelityV2, type ContextRepresentationKindV2, type ContextSourceRefV2, DEFAULT_HEADROOM_QUALITY_POLICY, estimatePointerTokens, fnv1aHex, fullTextTokens, getHeadroomRuntimeStatus, type HeadroomQualityPolicyV2, type HeadroomRuntimeStatusV2, heuristicTokenCount, type RepresentationBudgetContext, } from "./context-budget-headroom-types.ts";
5
27
  //# sourceMappingURL=context-budget-headroom.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-headroom.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,gCAAgC,EAGrC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,MAAM,oCAAoC,CAAC;AAqB5C,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,2BAA2B,EACnC,MAAM,GAAE,uBAAyD,GAC/D,gCAAgC,CAwClC;AAgGD,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EACN,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oCAAoC,EACzC,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,+BAA+B,EAC/B,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,wBAAwB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,mBAAmB,EACnB,KAAK,2BAA2B,GAChC,MAAM,oCAAoC,CAAC","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
1
+ {"version":3,"file":"context-budget-headroom.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,gCAAgC,EAGrC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,MAAM,oCAAoC,CAAC;AAqB5C;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACvC,SAAS,EAAE,gCAAgC,EAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,GAAE,uBAAyD,GAC/D,OAAO,CAkBT;AAED,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,2BAA2B,EACnC,MAAM,GAAE,uBAAyD,GAC/D,gCAAgC,CA2ClC;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC9C,SAAS,EAAE,gCAAgC,EAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,GAAE,uBAAyD,EACjE,KAAK,UAAQ,GACX,MAAM,CA0DR;AAiBD,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EACN,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oCAAoC,EACzC,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,+BAA+B,EAC/B,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,wBAAwB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,mBAAmB,EACnB,KAAK,2BAA2B,GAChC,MAAM,oCAAoC,CAAC","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\n/**\n * Budget-independent admissibility for one representation candidate — the\n * same gate {@link chooseHeadroomRepresentation} applies via its per-kind\n * rejections. Ranking and selection must share this set, otherwise a cheap\n * representation the chooser could never pick distorts the density\n * denominator (an item ranked on a 1-token pointer it can only use as a\n * 80-token full text is admitted ahead of genuinely denser items).\n *\n * Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT\n * here: it is a ranking-time property, not an admissibility property.\n */\nexport function isRepresentationEligible(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): boolean {\n\tif (candidate.kind === \"omit\") return policy.allowOmit;\n\t// A non-full representation that does not actually save tokens is never a\n\t// real choice; counting it as admissible only flatters the cost model.\n\tif (candidate.kind !== \"full\" && candidate.estimatedTokens >= fullTextTokens(item)) return false;\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tswitch (candidate.kind) {\n\t\tcase \"full\":\n\t\t\treturn true;\n\t\tcase \"pointer\":\n\t\t\treturn policy.preferPointerForRetrievable && retrievable;\n\t\tcase \"headroom-compressed\":\n\t\t\treturn retrievable;\n\t\tcase \"summary\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (!isRepresentationEligible(candidate, item, policy)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = scoreRepresentationPreferenceV2(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\n/**\n * The selector's own preference score for one representation — the closest\n * thing this policy has to a utility function, and the quantity the planner's\n * exchange passes compare when they promote or step an item down. Exported so\n * an oracle can score a plan with the same rule the planner used instead of\n * restating the formula. `tight` is the congestion flag\n * {@link chooseHeadroomRepresentation} derives from the budget context; pass\n * `false` for a congestion-free reference score.\n */\nexport function scoreRepresentationPreferenceV2(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n\ttight = false,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
@@ -16,6 +16,38 @@ const CACHE_HIT_BONUS = 8;
16
16
  const TIGHT_CACHE_HIT_BONUS = 20;
17
17
  const MATERIALIZED_CACHE_HIT_BONUS = 8;
18
18
  const TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;
19
+ /**
20
+ * Budget-independent admissibility for one representation candidate — the
21
+ * same gate {@link chooseHeadroomRepresentation} applies via its per-kind
22
+ * rejections. Ranking and selection must share this set, otherwise a cheap
23
+ * representation the chooser could never pick distorts the density
24
+ * denominator (an item ranked on a 1-token pointer it can only use as a
25
+ * 80-token full text is admitted ahead of genuinely denser items).
26
+ *
27
+ * Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT
28
+ * here: it is a ranking-time property, not an admissibility property.
29
+ */
30
+ export function isRepresentationEligible(candidate, item, policy = DEFAULT_HEADROOM_QUALITY_POLICY) {
31
+ if (candidate.kind === "omit")
32
+ return policy.allowOmit;
33
+ // A non-full representation that does not actually save tokens is never a
34
+ // real choice; counting it as admissible only flatters the cost model.
35
+ if (candidate.kind !== "full" && candidate.estimatedTokens >= fullTextTokens(item))
36
+ return false;
37
+ const retrievable = item.sourceRef?.retrievable === true;
38
+ switch (candidate.kind) {
39
+ case "full":
40
+ return true;
41
+ case "pointer":
42
+ return policy.preferPointerForRetrievable && retrievable;
43
+ case "headroom-compressed":
44
+ return retrievable;
45
+ case "summary":
46
+ return true;
47
+ default:
48
+ return false;
49
+ }
50
+ }
19
51
  export function chooseHeadroomRepresentation(item, budget, policy = DEFAULT_HEADROOM_QUALITY_POLICY) {
20
52
  const candidates = item.representations ?? deriveRepresentationCandidates(item, policy);
21
53
  const full = fullTextTokens(item);
@@ -35,13 +67,16 @@ export function chooseHeadroomRepresentation(item, budget, policy = DEFAULT_HEAD
35
67
  let best;
36
68
  let bestScore = Number.NEGATIVE_INFINITY;
37
69
  for (const candidate of candidates) {
70
+ if (!isRepresentationEligible(candidate, item, policy)) {
71
+ continue;
72
+ }
38
73
  if (isMoreExpensiveThanFull(candidate, full)) {
39
74
  continue;
40
75
  }
41
76
  if (candidate.kind !== "omit" && candidate.estimatedTokens > spendable) {
42
77
  continue;
43
78
  }
44
- const score = representationPreference(candidate, item, policy, tight);
79
+ const score = scoreRepresentationPreferenceV2(candidate, item, policy, tight);
45
80
  if (score > bestScore ||
46
81
  (score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)) {
47
82
  best = candidate;
@@ -62,7 +97,16 @@ function omitCandidate(item) {
62
97
  function isMoreExpensiveThanFull(candidate, fullTokens) {
63
98
  return candidate.kind !== "full" && candidate.kind !== "omit" && candidate.estimatedTokens >= fullTokens;
64
99
  }
65
- function representationPreference(candidate, item, policy, tight) {
100
+ /**
101
+ * The selector's own preference score for one representation — the closest
102
+ * thing this policy has to a utility function, and the quantity the planner's
103
+ * exchange passes compare when they promote or step an item down. Exported so
104
+ * an oracle can score a plan with the same rule the planner used instead of
105
+ * restating the formula. `tight` is the congestion flag
106
+ * {@link chooseHeadroomRepresentation} derives from the budget context; pass
107
+ * `false` for a congestion-free reference score.
108
+ */
109
+ export function scoreRepresentationPreferenceV2(candidate, item, policy = DEFAULT_HEADROOM_QUALITY_POLICY, tight = false) {
66
110
  const priority = PRIORITY_WEIGHT[item.priority];
67
111
  const cost = candidate.estimatedTokens;
68
112
  const fidelity = FIDELITY_BONUS[candidate.fidelity];
@@ -1 +1 @@
1
- {"version":3,"file":"context-budget-headroom.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"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","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
1
+ {"version":3,"file":"context-budget-headroom.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"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","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\n/**\n * Budget-independent admissibility for one representation candidate — the\n * same gate {@link chooseHeadroomRepresentation} applies via its per-kind\n * rejections. Ranking and selection must share this set, otherwise a cheap\n * representation the chooser could never pick distorts the density\n * denominator (an item ranked on a 1-token pointer it can only use as a\n * 80-token full text is admitted ahead of genuinely denser items).\n *\n * Budget feasibility (`estimatedTokens <= spendable`) is deliberately NOT\n * here: it is a ranking-time property, not an admissibility property.\n */\nexport function isRepresentationEligible(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): boolean {\n\tif (candidate.kind === \"omit\") return policy.allowOmit;\n\t// A non-full representation that does not actually save tokens is never a\n\t// real choice; counting it as admissible only flatters the cost model.\n\tif (candidate.kind !== \"full\" && candidate.estimatedTokens >= fullTextTokens(item)) return false;\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tswitch (candidate.kind) {\n\t\tcase \"full\":\n\t\t\treturn true;\n\t\tcase \"pointer\":\n\t\t\treturn policy.preferPointerForRetrievable && retrievable;\n\t\tcase \"headroom-compressed\":\n\t\t\treturn retrievable;\n\t\tcase \"summary\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (!isRepresentationEligible(candidate, item, policy)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = scoreRepresentationPreferenceV2(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\n/**\n * The selector's own preference score for one representation — the closest\n * thing this policy has to a utility function, and the quantity the planner's\n * exchange passes compare when they promote or step an item down. Exported so\n * an oracle can score a plan with the same rule the planner used instead of\n * restating the formula. `tight` is the congestion flag\n * {@link chooseHeadroomRepresentation} derives from the budget context; pass\n * `false` for a congestion-free reference score.\n */\nexport function scoreRepresentationPreferenceV2(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n\ttight = false,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
@@ -0,0 +1,21 @@
1
+ import type { PlannedItemV2 } from "./context-budget-v2-scoring.ts";
2
+ import { type OptionalSelectionState } from "./context-budget-v2-selection.ts";
3
+ /** Selection state after the floor-reservation passes; `usedTokens` is the running total so far. */
4
+ export type GlobalPassState = Omit<OptionalSelectionState, "floorReservation">;
5
+ export type PromotionState = Pick<GlobalPassState, "allocation" | "available" | "cache" | "qualityPolicy" | "resolvedCandidates" | "selection" | "tierUsed" | "usedTokens">;
6
+ /**
7
+ * Everything that happens once every tier has spent its floor:
8
+ *
9
+ * 1. the global pass admits still-unselected optional items in rank order
10
+ * under their tier ceilings;
11
+ * 2. the exchange pass lets a still-omitted item pay for its admission by
12
+ * stepping selected items down to their cheapest admissible form (F04);
13
+ * 3. the promotion pass spends whatever is left on costlier representations
14
+ * the policy prefers (F03).
15
+ *
16
+ * Breadth is settled before quality: an exchange that admits another item
17
+ * cannot be undone by a promotion, because promotion only spends the budget
18
+ * the exchange left. Returns the final token usage.
19
+ */
20
+ export declare function runGlobalSelectionPass(optionalPlanned: readonly PlannedItemV2[], state: GlobalPassState): number;
21
+ //# sourceMappingURL=context-budget-v2-global-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-budget-v2-global-pass.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-global-pass.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,KAAK,sBAAsB,EAAkC,MAAM,kCAAkC,CAAC;AAG/G,oGAAoG;AACpG,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,EAAE,kBAAkB,CAAC,CAAC;AAE/E,MAAM,MAAM,cAAc,GAAG,IAAI,CAChC,eAAe,EACb,YAAY,GACZ,WAAW,GACX,OAAO,GACP,eAAe,GACf,oBAAoB,GACpB,WAAW,GACX,UAAU,GACV,YAAY,CACd,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,SAAS,aAAa,EAAE,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CAQhH","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"]}