chainlesschain 0.166.100 → 0.166.102

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 (187) hide show
  1. package/package.json +3 -3
  2. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  3. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-Cd4iUOPt.js → AIOps-CyHIMEd8.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton--ucr-zko.js → ActionButton-vuNxDpHm.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DbejYMWk.js → Analytics-D0quEM0q.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CLgKxeGw.js → AppLayout-xRSVpbCs.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-CTT5BIL5.js → Artifacts-ktfVAZBC.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DP-WBWe9.js → Audit-UJW6maH4.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-B6VqheJk.js → BackgroundAgents-BYrfdCoT.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-VWJK5zim.js → Backup-IBgsJ7BK.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-YGFO9RHd.js → BaseInput-BmP8_zxy.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-CVfydnCd.js → Chat-urkw8_BF.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BICpB-qU.js → ChatBubbleRenderer-Cw44zZbh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-Bd5vKRMk.js → Checkbox-CYboo-__.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-DHNgPHvg.js → Codegen-DaqR_Ay8.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-CA2BYQ8T.js → Col-04BSIj_z.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-DnKJ-qpI.js → Community-DxFk7Trv.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-C9BZ8t4r.js → Compact-mjgWlMCB.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-ByyQ2FOl.js → Compliance-CC5ee7QV.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-rXTwXE_c.js → Cowork-BK4BhHZd.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-BuW-HODy.js → Cron-CP6jAxq0.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-OudNtbYW.js → Crosschain-DJPaViZS.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-BQ6BxYNN.js → DID-BTLxkuOj.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Cz5HG8tz.js → Dashboard-Djkiqh3d.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-BzclHOVv.js → Dropdown-t_eUhqom.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-CbJETFdy.js → EmailListRenderer-7w6USx_J.js} +1 -1
  28. package/src/assets/web-panel/assets/{EvolutionSettings-Dx_pJG3d.js → EvolutionSettings-mU5IHEk5.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-6JCS1MNg.js → FamilyGuardDashboard-BCh-VBBC.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-BrPkMYaf.js → Federation-Be_VpjI2.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-Bhc9pobs.js → FormItemContext-D_lzQ7PI.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-BxOh07uQ.js → GenericCardRenderer-LYu3KapV.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-D_idoujr.js → Git-BBeF-PP4.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-ZGfIRE-x.js → Governance-CoF8Kzg7.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-DpAmcwbw.js → Inference-C4DLMnXS.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-D1S1UTgP.js → KnowledgeGraph-B2mlNewk.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-2sc8V5XV.js → Logs-DMZ_44Ya.js} +2 -2
  38. package/src/assets/web-panel/assets/MarkdownRenderer-Ck7Iv_9e.js +1 -0
  39. package/src/assets/web-panel/assets/{Marketplace-0xlFUKbe.js → Marketplace-BqDzTew_.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-D25eJLGm.js → McpTools-BbwSbDN9.js} +3 -3
  41. package/src/assets/web-panel/assets/{Memory-BLRGd9hM.js → Memory-DAaTWgrm.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-zpFINGzn.js → MobileBridge-BjbTGxoR.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-Cbyd-8kx.js → MobileProjects-DMODOBr-.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-DgXgUlHc.js → Mtc-DT9N2rCs.js} +2 -2
  45. package/src/assets/web-panel/assets/{MtcAudit-DzcL-dKH.js → MtcAudit-DagMfUVv.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-C3OrZ6VN.js → Multisig-C1BKP6nM.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-Bqp_vWA7.js → NLProgramming-B5LFh72i.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-Dyn45-Ht.js → Notes-Q77V3ltW.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-CLjafGZr.js → NotificationSettings-HxdpNIBs.js} +1 -1
  50. package/src/assets/web-panel/assets/OrderTableRenderer-Dc0DpvBH.js +1 -0
  51. package/src/assets/web-panel/assets/{Organization-5gr9hyLm.js → Organization-QL3NZ7uW.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-BRdU02hT.js → Overflow-B_gqOKho.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-DIi7fzh6.js → P2P-BBJubU8Z.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-BCbS1KpA.js → PdhVaultBrowser-CFnGd-2E.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-BjeP9VtA.js → Permissions-BKkznIUM.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-y8PXOQUc.js → PersonalDataHub-DYK7ZAcd.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-B0fL5uxY.js → Pipeline-CoeKpjoN.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-C8jCDn8Z.js → Privacy-Diw4G-92.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-B_L6Dc6V.js → ProjectInit-CHBOFkma.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-Bb1CYbZh.js → ProjectSettings-BJIsgdOf.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-CUSV10Cn.js → Projects-GJjItXIk.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-mBSTDJ0E.js → Providers-o8QCimor.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-CHxgalvt.js → QrScannerModal-z2W-zvhW.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-DI9KIMEV.js → QuickAsk-_yubhsna.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-B5_zW1JH.js → Recommend-CucgJwih.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-BkH4bmM5.js → RemoteSession-EzfIIRg1.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-DJrAqwAq.js → Reputation-_shttE67.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-xZ3MAM34.js → Row-DQA_S8-9.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-CCsrLLiu.js → RssFeed-BOqWawXT.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-CTCRKvaP.js → Search-Dx1v2xOq.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-D7joyfp2.js → Security-Bt4HHsT4.js} +4 -4
  72. package/src/assets/web-panel/assets/{Services-oqHPmTG4.js → Services-sruHiQ-r.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-BR_32bTG.js → Skeleton-CiBxvmOJ.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-BoRz9mln.js → Skills-De9qKODU.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-UAGUHZ9b.js → Sla-B_6yn8Wp.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-DeP_rMgv.js → SpeechSettings-C2CafExS.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-CSzzMmeA.js → SyncSettings-Dhh3Qmb0.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-CcqOLsLB.js → Tasks-Mvo5mTWm.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-cDbkGSDG.js → Templates-D7WVTQQy.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-DmOy58cq.js → Tenant-7ur9Yfxd.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-CsRZH5vr.js → Terminal-BgzrOA6k.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-BgJrk6HG.js → TimelineRenderer-Dco20pT1.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-CCQglKDE.js → Tokens-hN6qBPag.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-t-Gt3oBY.js → Trigger-C4f_twAM.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-DQgu2kI0.js → Trust-Wf1SMypp.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-Cod2WUGj.js → UkeySign-DUXSyg3N.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-SGN5zaVK.js → VideoEditing-BvqsJaL0.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-CnLEL-LD.js → Wallet-CgAbcjKl.js} +3 -3
  89. package/src/assets/web-panel/assets/{WebAuthn-C2keUpD6.js → WebAuthn-DaLT0W3s.js} +2 -2
  90. package/src/assets/web-panel/assets/{WorkflowEditor-B1GvrwWJ.js → WorkflowEditor-CQqAXoaS.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-1E9_P_qY.js → chat-BhlohFTB.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-DuU6Os4q.js → colors-D7HqXIL8.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-C5NfOAcM.js → compact-item-Bqk4FCLY.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-D4Fxz9qQ.js → createContext-CEmWrWf0.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-YPX_TU7w.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-C6C49iFk.js → hasIn-CDY0pFi2.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DP2DCKD-.js → index-1HBam9t1.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B-bb-R99.js → index-6f6n0KFR.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BmvU4Tp_.js → index-B8L3_-sG.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BsH8mFp4.js → index-BHGOdvOQ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BVE2wCeb.js → index-BNrvBWDS.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CJS6GTpv.js → index-BPmRkxeX.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-C7mh6xlu.js → index-BXRooi-O.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BZKRZYTX.js → index-BiLu-ImR.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-jFg3CPlQ.js → index-BuKGuH_y.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BULggEX6.js → index-BxXFRLTZ.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DPzunJER.js → index-BxYO6nde.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-AO6iDCSL.js → index-C02yDu4f.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BU5mPkNX.js → index-C3opQg47.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DSypQU1h.js → index-C9mvnSii.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DZgT7pH-.js → index-CVrwozwH.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CpiOzt7U.js → index-CYcjOnDX.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BlHkc48f.js → index-Cf1ha8l0.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CXb8gEWh.js → index-Chxt0t-A.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-HQXFl8fh.js → index-CkjgSHVz.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DpaGe-dr.js → index-CkqBoCiu.js} +3 -3
  117. package/src/assets/web-panel/assets/{index-C2nO02uW.js → index-CqK0J4Sj.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Bu6qMjf4.js → index-CrsM-oiD.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Bk-paNL3.js → index-Cur88hoN.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CXn-e3ki.js → index-D0nfNmbq.js} +1 -1
  121. package/src/assets/web-panel/assets/index-D53szVZQ.js +1 -0
  122. package/src/assets/web-panel/assets/{index-CDVwN9Sv.js → index-DEzL7s7P.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-B_ON9La1.js → index-DGKRAO61.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CQJoh5rw.js → index-DPBoWNwq.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-cnDfwWU5.js → index-DRcDWMwP.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BBVqlelF.js → index-DmKdCWq1.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BYg1dtne.js → index-DpI1LqJX.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-B5lzRoHp.js → index-DuMt3ewl.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-gh11zJ9h.js → index-Dwob5XoD.js} +1 -1
  130. package/src/assets/web-panel/assets/index-Ub2nJbV2.js +1 -0
  131. package/src/assets/web-panel/assets/{index-D1TXgNPA.js → index-XErJQ1yF.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DFK6PTGJ.js → index-aBHlgMrq.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-Dh-vezHe.js → index-e9oCy7_4.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-bW6vfpLH.js → index-lBAbMOzg.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-lNmDaf3J.js → index-o_lc3eww.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-3DSfnIpD.js → initDefaultProps-CIeEUxIN.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-DxGLmgA0.js → motion-IPIj1ZtP.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-DrlLG6m5.js → move-BfSeXZ-f.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-BITbrJSb.js → mtc-parser-DX03-Xp4.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-D7k5vIbk.js → omit-z9EannfX.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-BJSvYFWo.js → pickAttrs-DXKmRkqZ.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-Bf4to1jA.js → placementArrow-k2HV7cIU.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-BXzeyzTa.js → responsiveObserve-bX7CsyWy.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-DLzNUNdj.js → slide-NskjQLbJ.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-wBBnrRm_.js → statusUtils-B3IoG-Er.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-BnmJzXNr.js → styleChecker-DHv_UilX.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-DrN8MrJb.js → useFlexGapSupport-DEqGB5Ng.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-dnkYGkjY.js → useFs-Bl9CbrqZ.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-k_xkv7-k.js → usePersonalDataHub-O-Ukv93M.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-B-v8PXpK.js → vnode-hGCHTaZt.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-Coue8wAK.js → zoom-BWhIVeKQ.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/command-help-index.json +1 -1
  154. package/src/commands/agent.js +64 -40
  155. package/src/commands/eval.js +11 -8
  156. package/src/data/changelog.json +26 -0
  157. package/src/harness/provider-backed-compaction.js +13 -5
  158. package/src/lib/config-schema.js +18 -5
  159. package/src/lib/context-memory-kernel/live-compaction.js +142 -61
  160. package/src/lib/context-memory-kernel/memory-query-index.js +5 -2
  161. package/src/lib/context-memory-kernel/runtime.js +4 -0
  162. package/src/lib/context-memory-kernel/segmented-memory-port.js +28 -3
  163. package/src/lib/context-source-ledger.js +52 -9
  164. package/src/lib/formal-budget-authority.js +533 -0
  165. package/src/lib/formal-budget-contract.js +609 -0
  166. package/src/lib/formal-budget-transport.js +345 -0
  167. package/src/lib/goal-condition-engine.js +25 -2
  168. package/src/lib/llm-config-defaults.js +10 -4
  169. package/src/lib/llm-pricing.js +16 -4
  170. package/src/lib/model-capabilities.js +26 -0
  171. package/src/lib/model-catalog-review.js +18 -1
  172. package/src/lib/model-context-catalog.js +41 -1
  173. package/src/lib/openai-responses.js +8 -0
  174. package/src/lib/skill-retrieval-router.js +1 -0
  175. package/src/lib/usage-pricing-context.js +33 -2
  176. package/src/repl/agent-repl.js +247 -153
  177. package/src/runtime/agent-core.js +371 -97
  178. package/src/runtime/coding-agent-contract-shared.cjs +14 -1
  179. package/src/runtime/headless-runner.js +1 -0
  180. package/src/runtime/headless-stream.js +137 -4
  181. package/src/runtime/mcp-tool-search.js +0 -0
  182. package/src/runtime/provider-stream-state.js +14 -1
  183. package/src/assets/web-panel/assets/MarkdownRenderer-575jjYE6.js +0 -1
  184. package/src/assets/web-panel/assets/OrderTableRenderer-Bx8wMauC.js +0 -1
  185. package/src/assets/web-panel/assets/devWarning-zDWbYMr7.js +0 -1
  186. package/src/assets/web-panel/assets/index-BPBsZZnT.js +0 -1
  187. package/src/assets/web-panel/assets/index-CtGQDg7m.js +0 -1
@@ -0,0 +1,345 @@
1
+ import {
2
+ budgetError,
3
+ canonical,
4
+ requireBudget,
5
+ } from "./formal-budget-contract.js";
6
+
7
+ export const FORMAL_BUDGET_RUNTIME_READINESS = Object.freeze({
8
+ paidReady: false,
9
+ reason:
10
+ "Agent, Chat, compaction, advisor, probes, retries and child-process egress are not yet all routed through this authority",
11
+ });
12
+ export function requireFormalBudgetRuntime() {
13
+ throw budgetError(
14
+ "CC_BUDGET_RUNTIME_NOT_COVERED",
15
+ FORMAL_BUDGET_RUNTIME_READINESS.reason,
16
+ );
17
+ }
18
+
19
+ // JSON.parse alone accepts duplicate keys (including escaped aliases) and
20
+ // overflows numeric literals to Infinity. Neither is safe accounting evidence.
21
+ export function parseBudgetJson(text) {
22
+ requireBudget(typeof text === "string", "JSON text required");
23
+ let at = 0;
24
+ const whitespace = () => {
25
+ while (at < text.length && /[\x20\t\r\n]/u.test(text[at])) at++;
26
+ };
27
+ function string() {
28
+ requireBudget(text[at] === '"', "JSON string expected");
29
+ const start = at++;
30
+ while (at < text.length) {
31
+ const char = text[at++];
32
+ if (char === '"') return JSON.parse(text.slice(start, at));
33
+ if (char === "\\") at++;
34
+ }
35
+ requireBudget(false, "Unterminated JSON string");
36
+ }
37
+ function value(depth) {
38
+ requireBudget(depth <= 64, "JSON nesting exceeds accounting parser bound");
39
+ whitespace();
40
+ if (text[at] === '"') {
41
+ string();
42
+ return;
43
+ }
44
+ if (text[at] === "{") {
45
+ at++;
46
+ whitespace();
47
+ const keys = new Set();
48
+ if (text[at] === "}") {
49
+ at++;
50
+ return;
51
+ }
52
+ for (;;) {
53
+ whitespace();
54
+ const key = string();
55
+ requireBudget(
56
+ !keys.has(key),
57
+ "Duplicate JSON member in provider evidence",
58
+ );
59
+ keys.add(key);
60
+ whitespace();
61
+ requireBudget(text[at++] === ":", "JSON member separator expected");
62
+ value(depth + 1);
63
+ whitespace();
64
+ const separator = text[at++];
65
+ if (separator === "}") return;
66
+ requireBudget(separator === ",", "JSON object separator expected");
67
+ }
68
+ }
69
+ if (text[at] === "[") {
70
+ at++;
71
+ whitespace();
72
+ if (text[at] === "]") {
73
+ at++;
74
+ return;
75
+ }
76
+ for (;;) {
77
+ value(depth + 1);
78
+ whitespace();
79
+ const separator = text[at++];
80
+ if (separator === "]") return;
81
+ requireBudget(separator === ",", "JSON array separator expected");
82
+ }
83
+ }
84
+ for (const literal of ["true", "false", "null"])
85
+ if (text.startsWith(literal, at)) {
86
+ at += literal.length;
87
+ return;
88
+ }
89
+ const number = text
90
+ .slice(at)
91
+ .match(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/u)?.[0];
92
+ requireBudget(
93
+ number && Number.isFinite(Number(number)),
94
+ "Invalid or non-finite JSON number",
95
+ );
96
+ at += number.length;
97
+ }
98
+ value(0);
99
+ whitespace();
100
+ requireBudget(at === text.length, "Trailing JSON input");
101
+ return JSON.parse(text);
102
+ }
103
+
104
+ /** Allowlist only; the caller must still supply the existing OS sandbox. */
105
+ export function formalBudgetTaskEnvironment(environment = process.env) {
106
+ return Object.fromEntries(
107
+ [
108
+ "PATH",
109
+ "Path",
110
+ "SystemRoot",
111
+ "SYSTEMROOT",
112
+ "WINDIR",
113
+ "ComSpec",
114
+ "PATHEXT",
115
+ "TEMP",
116
+ "TMP",
117
+ "TMPDIR",
118
+ "LANG",
119
+ "LC_ALL",
120
+ "LC_CTYPE",
121
+ ]
122
+ .filter((key) => typeof environment[key] === "string")
123
+ .map((key) => [key, environment[key]]),
124
+ );
125
+ }
126
+
127
+ async function boundedText(response, maximumBytes) {
128
+ requireBudget(response.body?.getReader, "Response body unavailable");
129
+ const reader = response.body.getReader(),
130
+ chunks = [];
131
+ let size = 0;
132
+ try {
133
+ for (;;) {
134
+ const { done, value } = await reader.read();
135
+ if (done) break;
136
+ size += value.byteLength;
137
+ requireBudget(
138
+ size <= maximumBytes,
139
+ "Provider response exceeded bounded parser",
140
+ );
141
+ chunks.push(Buffer.from(value));
142
+ }
143
+ } catch (error) {
144
+ try {
145
+ await reader.cancel();
146
+ } catch {
147
+ /* keep original unknown outcome */
148
+ }
149
+ throw error;
150
+ } finally {
151
+ reader.releaseLock();
152
+ }
153
+ return new TextDecoder("utf-8", { fatal: true }).decode(
154
+ Buffer.concat(chunks),
155
+ );
156
+ }
157
+ function streamReceipt(text) {
158
+ let terminal = false,
159
+ receipt = null,
160
+ usage = null,
161
+ finishReason = null;
162
+ for (const frame of text.replaceAll("\r\n", "\n").split("\n\n")) {
163
+ if (!frame.trim()) continue;
164
+ const data = frame
165
+ .split("\n")
166
+ .filter((line) => line.startsWith("data:"))
167
+ .map((line) => line.slice(5).trimStart())
168
+ .join("\n");
169
+ if (!data) continue;
170
+ requireBudget(!terminal, "Unexpected stream data after terminal marker");
171
+ if (data === "[DONE]") {
172
+ requireBudget(
173
+ finishReason !== null,
174
+ "Stream ended without a completed choice",
175
+ );
176
+ terminal = true;
177
+ continue;
178
+ }
179
+ const row = parseBudgetJson(data);
180
+ requireBudget(row && !Object.hasOwn(row, "error"), "Provider stream error");
181
+ const identity = {
182
+ id: row.id,
183
+ model: row.model,
184
+ service_tier: row.service_tier,
185
+ };
186
+ if (!receipt) receipt = identity;
187
+ else {
188
+ // Some providers only send service_tier on the final usage frame.
189
+ requireBudget(
190
+ row.id === receipt.id && row.model === receipt.model,
191
+ "Stream identity changed",
192
+ );
193
+ if (row.service_tier !== undefined) {
194
+ requireBudget(
195
+ receipt.service_tier === undefined ||
196
+ row.service_tier === receipt.service_tier,
197
+ "Stream tier changed",
198
+ );
199
+ receipt.service_tier = row.service_tier;
200
+ }
201
+ }
202
+ requireBudget(
203
+ Array.isArray(row.choices) &&
204
+ row.choices.length <= 1 &&
205
+ row.choices.every((choice) => choice.index === 0),
206
+ "Multiple/unknown streamed completions",
207
+ );
208
+ for (const choice of row.choices) {
209
+ requireBudget(
210
+ finishReason === null,
211
+ "Choice data received after completed stream choice",
212
+ );
213
+ if (choice.finish_reason != null) {
214
+ requireBudget(
215
+ ["stop", "length", "tool_calls"].includes(choice.finish_reason),
216
+ "Unsupported stream finish reason",
217
+ );
218
+ finishReason = choice.finish_reason;
219
+ }
220
+ }
221
+ if (row.usage != null) {
222
+ requireBudget(
223
+ finishReason !== null,
224
+ "Usage received before complete stream choice",
225
+ );
226
+ requireBudget(
227
+ usage === null || canonical(usage) === canonical(row.usage),
228
+ "Conflicting streamed usage",
229
+ );
230
+ usage = row.usage;
231
+ }
232
+ }
233
+ requireBudget(
234
+ terminal && finishReason !== null && receipt && usage,
235
+ "Incomplete stream or missing usage",
236
+ );
237
+ return { ...receipt, usage };
238
+ }
239
+
240
+ /**
241
+ * Trusted-host JSON/SSE transport: credential stays in this closure, and every
242
+ * physical request consumes a durable dispatch exactly once. No auto retries.
243
+ * This is not the unrestricted Agent/Chat transport and is not auto-enabled.
244
+ */
245
+ export function createFormalBudgetTransport({
246
+ authority,
247
+ credentialProvider,
248
+ fetchImpl = globalThis.fetch,
249
+ maximumResponseBytes = 16 * 1024 * 1024,
250
+ timeoutMs = 60_000,
251
+ }) {
252
+ requireBudget(
253
+ authority &&
254
+ typeof credentialProvider === "function" &&
255
+ typeof fetchImpl === "function",
256
+ "Authority and trusted credential provider required",
257
+ );
258
+ requireBudget(
259
+ Number.isSafeInteger(maximumResponseBytes) &&
260
+ maximumResponseBytes > 0 &&
261
+ maximumResponseBytes <= 16 * 1024 * 1024,
262
+ "Invalid response byte bound",
263
+ );
264
+ requireBudget(
265
+ Number.isSafeInteger(timeoutMs) && timeoutMs > 0 && timeoutMs <= 300_000,
266
+ "Invalid timeout",
267
+ );
268
+ return Object.freeze({
269
+ async dispatch({ runId, callId, body, signal }) {
270
+ // Snapshot before await so caller mutation cannot alter the reserved body.
271
+ const request = JSON.parse(canonical(body));
272
+ authority.reserve({ runId, callId, body: request });
273
+ let settled = false;
274
+ try {
275
+ const reserved = authority.reservedContract(callId);
276
+ const credential = await credentialProvider({
277
+ contractId: reserved.id,
278
+ provider: reserved.provider,
279
+ endpoint: reserved.endpoint,
280
+ });
281
+ requireBudget(
282
+ typeof credential === "string" &&
283
+ credential.length > 0 &&
284
+ !/[\r\n]/u.test(credential),
285
+ "Credential unavailable",
286
+ );
287
+ const deadline = AbortSignal.timeout(timeoutMs);
288
+ const options = {
289
+ method: "POST",
290
+ redirect: "error",
291
+ headers: {
292
+ "Content-Type": "application/json",
293
+ Authorization: `Bearer ${credential}`,
294
+ },
295
+ body: JSON.stringify(request),
296
+ signal: signal ? AbortSignal.any([signal, deadline]) : deadline,
297
+ };
298
+ // Final expiry/request check is AFTER every awaited preparation. There
299
+ // must be no await between this durable admission and physical fetch.
300
+ const contract = authority.markDispatched(callId, request);
301
+ const response = await fetchImpl(contract.endpoint, options);
302
+ requireBudget(response.ok, "Provider HTTP response not successful");
303
+ const text = await boundedText(response, maximumResponseBytes);
304
+ let receipt;
305
+ if (request.stream === true) {
306
+ requireBudget(
307
+ response.headers.get("content-type")?.includes("text/event-stream"),
308
+ "Unexpected stream content type",
309
+ );
310
+ receipt = streamReceipt(text);
311
+ } else {
312
+ receipt = parseBudgetJson(text);
313
+ requireBudget(
314
+ receipt && !Object.hasOwn(receipt, "error"),
315
+ "Provider error envelope cannot settle",
316
+ );
317
+ requireBudget(
318
+ Array.isArray(receipt.choices) &&
319
+ receipt.choices.length === 1 &&
320
+ receipt.choices[0].index === 0 &&
321
+ ["stop", "length", "tool_calls"].includes(
322
+ receipt.choices[0].finish_reason,
323
+ ),
324
+ "Incomplete or multiple completion response",
325
+ );
326
+ }
327
+ const cost = authority.settle(callId, receipt);
328
+ settled = true;
329
+ return { body: text, streaming: request.stream === true, cost };
330
+ } catch {
331
+ if (!settled) {
332
+ // Do not persist provider errors which may contain credentials/content.
333
+ authority.markUnknown(
334
+ callId,
335
+ "transport or settlement outcome unknown",
336
+ );
337
+ }
338
+ throw budgetError(
339
+ "CC_BUDGET_TRANSPORT_UNKNOWN",
340
+ "Provider outcome unknown; reservation retained and further dispatch blocked",
341
+ );
342
+ }
343
+ },
344
+ });
345
+ }
@@ -246,6 +246,16 @@ function whichLimitHit(state, budget, nowMs) {
246
246
  * the loop stops. Does NOT mutate `state` — returns the next one.
247
247
  */
248
248
  export function evaluateGoalStep(state, evaluation, budget, opts = {}) {
249
+ if (state.done === true) {
250
+ return {
251
+ state: { ...state },
252
+ decision:
253
+ state.outcome === "completed"
254
+ ? GOAL_DECISION.COMPLETE
255
+ : GOAL_DECISION.EXHAUSTED,
256
+ events: [],
257
+ };
258
+ }
249
259
  const nowMs = Number(opts.now ?? state.startedAtMs);
250
260
  const next = {
251
261
  ...state,
@@ -326,10 +336,23 @@ export class GoalConditionEngine {
326
336
 
327
337
  /** Accumulate a turn's usage before evaluating it. */
328
338
  recordTurnUsage({ tokens = 0, costUsd = 0 } = {}) {
339
+ if (
340
+ !Number.isSafeInteger(tokens) ||
341
+ tokens < 0 ||
342
+ typeof costUsd !== "number" ||
343
+ !Number.isFinite(costUsd) ||
344
+ costUsd < 0 ||
345
+ !Number.isSafeInteger(this.state.tokens + tokens) ||
346
+ !Number.isFinite(this.state.costUsd + costUsd)
347
+ ) {
348
+ throw new RangeError(
349
+ "Goal usage requires finite non-negative cost and safe token counts",
350
+ );
351
+ }
329
352
  this.state = {
330
353
  ...this.state,
331
- tokens: this.state.tokens + (Number(tokens) || 0),
332
- costUsd: this.state.costUsd + (Number(costUsd) || 0),
354
+ tokens: this.state.tokens + tokens,
355
+ costUsd: this.state.costUsd + costUsd,
333
356
  };
334
357
  return this.state;
335
358
  }
@@ -38,16 +38,18 @@ export async function reconcileConfigLlmProvider(cfgLlm = {}) {
38
38
  if (!cfgLlm || !cfgLlm.provider || !cfgLlm.baseUrl) {
39
39
  return { llm: cfgLlm, changed: false };
40
40
  }
41
- const { inferProviderFromBaseUrl, modelForeignToProvider } =
42
- await import("./runnable-provider.js");
41
+ const { inferProviderFromBaseUrl, modelForeignToProvider } = await import(
42
+ "./runnable-provider.js"
43
+ );
43
44
  const inferred = inferProviderFromBaseUrl(cfgLlm.baseUrl);
44
45
  if (!inferred || inferred === cfgLlm.provider) {
45
46
  return { llm: cfgLlm, changed: false };
46
47
  }
47
48
  const corrected = { ...cfgLlm, provider: inferred };
48
49
  if (modelForeignToProvider(inferred, cfgLlm.model)) {
49
- const { selectModelForTask, TaskType } =
50
- await import("./task-model-selector.js");
50
+ const { selectModelForTask, TaskType } = await import(
51
+ "./task-model-selector.js"
52
+ );
51
53
  const def = selectModelForTask(inferred, TaskType.CHAT);
52
54
  if (def) corrected.model = def;
53
55
  }
@@ -88,6 +90,8 @@ export function applyConfigLlmDefaults(options = {}, cfgLlm = {}, opts = {}) {
88
90
  options.provider === cfgLlm.provider
89
91
  ) {
90
92
  if (!options.baseUrl && cfgLlm.baseUrl) options.baseUrl = cfgLlm.baseUrl;
93
+ if (options.serviceTier == null && cfgLlm.serviceTier != null)
94
+ options.serviceTier = cfgLlm.serviceTier;
91
95
  backfillApiKey(options, cfgLlm);
92
96
  if (!options.model && !opts.explicitModel && cfgLlm.model) {
93
97
  options.model = cfgLlm.model;
@@ -96,6 +100,8 @@ export function applyConfigLlmDefaults(options = {}, cfgLlm = {}, opts = {}) {
96
100
  }
97
101
  if (options.provider || !cfgLlm.provider) return options;
98
102
  options.provider = cfgLlm.provider;
103
+ if (options.serviceTier == null && cfgLlm.serviceTier != null)
104
+ options.serviceTier = cfgLlm.serviceTier;
99
105
  if (opts.explicitModel) {
100
106
  options.model = opts.explicitModel;
101
107
  } else if (cfgLlm.model) {
@@ -67,6 +67,9 @@ export const PRICE_TABLE = Object.freeze({
67
67
  exact: true,
68
68
  terms: Object.freeze({
69
69
  ...GPT6_PRICING_TERMS,
70
+ ...(model.serviceMultipliers
71
+ ? { serviceMultipliers: model.serviceMultipliers }
72
+ : {}),
70
73
  ...(model.cacheReadMultiplier == null
71
74
  ? {}
72
75
  : { cacheReadMultiplier: model.cacheReadMultiplier }),
@@ -259,9 +262,16 @@ export function normalizePricingTerms(value) {
259
262
  Reflect.ownKeys(tiers).length === 0 ||
260
263
  Reflect.ownKeys(tiers).some(
261
264
  (key) =>
262
- !["standard", "default", "auto", "batch", "flex", "fast"].includes(
263
- key,
264
- ) || !finite(tiers[key]),
265
+ ![
266
+ "standard",
267
+ "default",
268
+ "auto",
269
+ "batch",
270
+ "flex",
271
+ "fast",
272
+ "priority",
273
+ "ultrafast",
274
+ ].includes(key) || !finite(tiers[key]),
265
275
  )
266
276
  )
267
277
  fail();
@@ -378,7 +388,9 @@ export function estimateCost({
378
388
  ? Object.hasOwn(rate.terms.serviceMultipliers, serviceTier)
379
389
  ? rate.terms.serviceMultipliers[serviceTier]
380
390
  : null
381
- : 1;
391
+ : ["standard", "default"].includes(serviceTier)
392
+ ? 1
393
+ : null;
382
394
  if (
383
395
  !rate ||
384
396
  tierMultiplier == null ||
@@ -263,6 +263,10 @@ export function resolveModelCapabilityProfile({
263
263
  const profile = {
264
264
  schema: PROFILE_SCHEMA,
265
265
  catalogVersion: MODEL_CAPABILITY_CATALOG_VERSION,
266
+ reviewedAt: documented ? documented.reviewedAt || "2026-10-05" : null,
267
+ supportedServiceTiers: documented?.serviceTiers
268
+ ? [...documented.serviceTiers]
269
+ : ["standard"],
266
270
  profileId: `model-capability-v1:${identityDigest.slice(7, 39)}`,
267
271
  provider: selectedProvider || "unknown",
268
272
  model: selectedModel,
@@ -288,3 +292,25 @@ export function resolveModelCapabilityProfile({
288
292
  profile.digest = canonicalDigest(profile, PROFILE_SCHEMA);
289
293
  return deepFreeze(profile);
290
294
  }
295
+
296
+ /** Resolve a requested live tier without certifying account availability. */
297
+ export function resolveModelServiceTier(profile, serviceTier) {
298
+ const requested = serviceTier ?? "standard";
299
+ if (
300
+ typeof requested !== "string" ||
301
+ !profile?.supportedServiceTiers?.includes(requested)
302
+ ) {
303
+ throw Object.assign(
304
+ new RangeError(
305
+ `Unsupported service tier ${String(requested)} for ${profile?.provider || "unknown"}/${profile?.model || "unknown"}`,
306
+ ),
307
+ { code: "CC_MODEL_SERVICE_TIER_UNSUPPORTED" },
308
+ );
309
+ }
310
+ return Object.freeze({
311
+ requestedServiceTier: requested,
312
+ requestServiceTier: requested === "standard" ? null : requested,
313
+ effectiveServiceTier: null,
314
+ accountVerified: false,
315
+ });
316
+ }
@@ -65,8 +65,12 @@ export function reviewModelCatalog(review, snapshots = {}) {
65
65
  "runtimeProtocol",
66
66
  "contextWindowTokens",
67
67
  "advertisedMaxOutputTokens",
68
+ "reviewedAt",
68
69
  ]) {
69
- if (Object.hasOwn(expected, field) && actual[field] !== expected[field])
70
+ if (
71
+ Object.hasOwn(expected, field) &&
72
+ !isDeepStrictEqual(actual[field], expected[field])
73
+ )
70
74
  mismatches.push({
71
75
  model: expected.model,
72
76
  field,
@@ -80,6 +84,19 @@ export function reviewModelCatalog(review, snapshots = {}) {
80
84
  field: "official-source",
81
85
  actual: null,
82
86
  });
87
+ if (
88
+ Object.hasOwn(expected, "supportedServiceTiers") &&
89
+ !isDeepStrictEqual(
90
+ actual.supportedServiceTiers,
91
+ expected.supportedServiceTiers,
92
+ )
93
+ )
94
+ mismatches.push({
95
+ model: expected.model,
96
+ field: "supportedServiceTiers",
97
+ expected: expected.supportedServiceTiers,
98
+ actual: actual.supportedServiceTiers,
99
+ });
83
100
  for (const field of ["in", "out"]) {
84
101
  if (rate?.[field] !== expected.pricing[field])
85
102
  mismatches.push({
@@ -45,7 +45,7 @@ export const CONTEXT_WINDOWS = Object.freeze({
45
45
  }),
46
46
  });
47
47
 
48
- export const MODEL_CAPABILITY_CATALOG_VERSION = "2026-10-05";
48
+ export const MODEL_CAPABILITY_CATALOG_VERSION = "2026-10-15";
49
49
 
50
50
  // Explicit ownership prevents a model name served by an unrelated provider
51
51
  // from silently inheriting another provider's catalog window.
@@ -86,6 +86,26 @@ export const LEGACY_MODEL_PROVIDERS = Object.freeze({
86
86
  // named models, not endpoint access, account entitlement, or a live CLI run.
87
87
  export const DOCUMENTED_OPENAI_MODELS = Object.freeze({
88
88
  "gpt-6.1-sol": Object.freeze({
89
+ reviewedAt: "2026-10-15",
90
+ serviceTiers: Object.freeze([
91
+ "standard",
92
+ "default",
93
+ "auto",
94
+ "fast",
95
+ "priority",
96
+ "flex",
97
+ "ultrafast",
98
+ ]),
99
+ serviceMultipliers: Object.freeze({
100
+ standard: 1,
101
+ default: 1,
102
+ auto: 1,
103
+ batch: 0.5,
104
+ flex: 0.5,
105
+ fast: 2,
106
+ priority: 2,
107
+ ultrafast: 6,
108
+ }),
89
109
  contextWindowTokens: 1050000,
90
110
  advertisedMaxOutputTokens: 128000,
91
111
  requiresResponsesForTools: true,
@@ -106,6 +126,26 @@ export const DOCUMENTED_OPENAI_MODELS = Object.freeze({
106
126
  ]),
107
127
  }),
108
128
  "gpt-6-astra": Object.freeze({
129
+ reviewedAt: "2026-10-15",
130
+ serviceTiers: Object.freeze([
131
+ "standard",
132
+ "default",
133
+ "auto",
134
+ "fast",
135
+ "priority",
136
+ "flex",
137
+ "ultrafast",
138
+ ]),
139
+ serviceMultipliers: Object.freeze({
140
+ standard: 1,
141
+ default: 1,
142
+ auto: 1,
143
+ batch: 0.5,
144
+ flex: 0.5,
145
+ fast: 2,
146
+ priority: 2,
147
+ ultrafast: 6,
148
+ }),
109
149
  contextWindowTokens: 1050000,
110
150
  advertisedMaxOutputTokens: 128000,
111
151
  requiresResponsesForTools: true,
@@ -1,3 +1,5 @@
1
+ import { resolveModelServiceTier } from "./model-capabilities.js";
2
+
1
3
  const RESPONSES_FAILURE_CODE = "CC_OPENAI_RESPONSES_FAILED";
2
4
  const MAX_REASONING_ITEMS = 16;
3
5
  const MAX_REASONING_SUMMARY_CHARS = 8_192;
@@ -177,7 +179,12 @@ export function createOpenAIResponsesBody({
177
179
  reasoning = null,
178
180
  stream = false,
179
181
  modelProfile = null,
182
+ serviceTier = null,
180
183
  }) {
184
+ const tier =
185
+ serviceTier == null
186
+ ? null
187
+ : resolveModelServiceTier(modelProfile, serviceTier).requestServiceTier;
181
188
  if (
182
189
  modelProfile?.advertisedMaxOutputTokens != null &&
183
190
  maxOutputTokens > modelProfile.advertisedMaxOutputTokens
@@ -195,6 +202,7 @@ export function createOpenAIResponsesBody({
195
202
  ...(maxOutputTokens ? { max_output_tokens: maxOutputTokens } : {}),
196
203
  ...(reasoning ? { reasoning } : {}),
197
204
  ...(stream ? { stream: true } : {}),
205
+ ...(tier ? { service_tier: tier } : {}),
198
206
  };
199
207
  }
200
208
 
@@ -248,6 +248,7 @@ export function routeSkillDescriptors({
248
248
  schema: SKILL_RETRIEVAL_RESULT_SCHEMA,
249
249
  query: query.trim(),
250
250
  selected: returned.length > 0 && !topConflict ? returned[0] : null,
251
+ matchedCount: candidates.length,
251
252
  candidates: Object.freeze(returned.map(Object.freeze)),
252
253
  conflicts: Object.freeze(conflicts.map(Object.freeze)),
253
254
  rejected: Object.freeze(rejected.map(Object.freeze)),
@@ -4,6 +4,38 @@ import {
4
4
  GPT6_PRICING_TERMS,
5
5
  } from "./model-context-catalog.js";
6
6
 
7
+ const KNOWN_SERVICE_TIERS = new Set([
8
+ ...Object.keys(GPT6_PRICING_TERMS.serviceMultipliers),
9
+ ...Object.values(DOCUMENTED_OPENAI_MODELS).flatMap((model) =>
10
+ Object.keys(model.serviceMultipliers || {}),
11
+ ),
12
+ ]);
13
+
14
+ /** Preserve response-envelope pricing labels, rejecting conflicting evidence. */
15
+ export function projectEnvelopePricingContext(usage = {}, envelope = {}) {
16
+ const input = { ...usage };
17
+ for (const key of ["service_tier", "regional_processing", "inference_geo"]) {
18
+ if (!Object.hasOwn(envelope, key)) continue;
19
+ if (Object.hasOwn(input, key) && input[key] !== envelope[key])
20
+ input.pricingContextInvalid = true;
21
+ input[key] = envelope[key];
22
+ }
23
+ return projectUsagePricingContext(input);
24
+ }
25
+
26
+ /** Requested paid tiers require actual response evidence for settlement. */
27
+ export function preserveRequestedServiceTierEvidence(usage, requested) {
28
+ if (
29
+ usage &&
30
+ requested != null &&
31
+ requested !== "standard" &&
32
+ (!usage.service_tier || usage.service_tier === "auto")
33
+ ) {
34
+ return { ...usage, pricingContextInvalid: true };
35
+ }
36
+ return usage;
37
+ }
38
+
7
39
  /** Keep TTL evidence distinct from the aggregate cache-write count. */
8
40
  export function projectCacheCreationUsage(usage = {}) {
9
41
  if (usage.cacheCreationInvalid === true)
@@ -62,8 +94,7 @@ export function projectUsagePricingContext(usage = {}) {
62
94
  const value = Object.hasOwn(usage, key) ? usage[key] : usage[alias];
63
95
  if (key === "service_tier") {
64
96
  projected[key] =
65
- typeof value === "string" &&
66
- Object.hasOwn(GPT6_PRICING_TERMS.serviceMultipliers, value)
97
+ typeof value === "string" && KNOWN_SERVICE_TIERS.has(value)
67
98
  ? value
68
99
  : "unknown";
69
100
  } else {