agentix-cli 0.26.0 → 0.27.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 (268) hide show
  1. package/dist/{App-BPNHIGUX.js → App-U52IPUK2.js} +2 -2
  2. package/dist/{ChatApp-6XSSZB4U.js → ChatApp-XP4OMHPL.js} +2 -2
  3. package/dist/agent-V2CA4TXD.js +2 -0
  4. package/dist/article-fields-L4D2BOGY.js +2 -0
  5. package/dist/article-quality-44ONVGLN.js +3 -0
  6. package/dist/article-quality-44ONVGLN.js.map +1 -0
  7. package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
  8. package/dist/backfill-MTD2B62I.js +6 -0
  9. package/dist/backfill-MTD2B62I.js.map +1 -0
  10. package/dist/{board-dashboard-M3372FLO.js → board-dashboard-TTMAYRLG.js} +1407 -447
  11. package/dist/board-dashboard-TTMAYRLG.js.map +1 -0
  12. package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
  13. package/dist/calibration-ILS5OI44.js +2 -0
  14. package/dist/chunk-2T5W3ETS.js +5 -0
  15. package/dist/{chunk-K2CAPYES.js.map → chunk-2T5W3ETS.js.map} +1 -1
  16. package/dist/chunk-3SCPRYJB.js +5 -0
  17. package/dist/chunk-3SCPRYJB.js.map +1 -0
  18. package/dist/chunk-4BR7D2OU.js +36 -0
  19. package/dist/chunk-4BR7D2OU.js.map +1 -0
  20. package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
  21. package/dist/chunk-52ZPELD2.js.map +1 -0
  22. package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
  23. package/dist/chunk-77OV7346.js.map +1 -0
  24. package/dist/chunk-AJ5RMU7X.js +2 -0
  25. package/dist/chunk-AJ5RMU7X.js.map +1 -0
  26. package/dist/chunk-AJONZWLT.js +2 -0
  27. package/dist/chunk-AJONZWLT.js.map +1 -0
  28. package/dist/chunk-AX2PWSRT.js +114 -0
  29. package/dist/chunk-AX2PWSRT.js.map +1 -0
  30. package/dist/chunk-BH3GTCZ6.js +2 -0
  31. package/dist/chunk-BH3GTCZ6.js.map +1 -0
  32. package/dist/chunk-BPIRO4YD.js +126 -0
  33. package/dist/chunk-BPIRO4YD.js.map +1 -0
  34. package/dist/chunk-CJQQIRPD.js +13 -0
  35. package/dist/chunk-CJQQIRPD.js.map +1 -0
  36. package/dist/chunk-DCDQZR7I.js +24 -0
  37. package/dist/chunk-DCDQZR7I.js.map +1 -0
  38. package/dist/chunk-EVWEG3VD.js +3 -0
  39. package/dist/chunk-EVWEG3VD.js.map +1 -0
  40. package/dist/chunk-F4HZTZHW.js +2 -0
  41. package/dist/chunk-F4HZTZHW.js.map +1 -0
  42. package/dist/chunk-F6CNF7V3.js +25 -0
  43. package/dist/chunk-F6CNF7V3.js.map +1 -0
  44. package/dist/chunk-FIEJDZVC.js +121 -0
  45. package/dist/chunk-FIEJDZVC.js.map +1 -0
  46. package/dist/chunk-FIOB3MEX.js +2 -0
  47. package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
  48. package/dist/chunk-FKBGJYWQ.js.map +1 -0
  49. package/dist/chunk-GDWJUGB5.js +9 -0
  50. package/dist/chunk-GDWJUGB5.js.map +1 -0
  51. package/dist/{chunk-BVGJSPW4.js → chunk-IB3F7VBE.js} +7 -7
  52. package/dist/{chunk-BVGJSPW4.js.map → chunk-IB3F7VBE.js.map} +1 -1
  53. package/dist/chunk-IBUSGCLQ.js +109 -0
  54. package/dist/chunk-IBUSGCLQ.js.map +1 -0
  55. package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
  56. package/dist/chunk-IWMW5432.js.map +1 -0
  57. package/dist/chunk-K33GIBSE.js +2 -0
  58. package/dist/chunk-K33GIBSE.js.map +1 -0
  59. package/dist/chunk-L2IZPR6P.js +45 -0
  60. package/dist/chunk-L2IZPR6P.js.map +1 -0
  61. package/dist/chunk-L2WSPY6S.js +2 -0
  62. package/dist/chunk-L2WSPY6S.js.map +1 -0
  63. package/dist/chunk-LCIREYTD.js +54 -0
  64. package/dist/chunk-LCIREYTD.js.map +1 -0
  65. package/dist/chunk-LNRB5MCU.js +530 -0
  66. package/dist/chunk-LNRB5MCU.js.map +1 -0
  67. package/dist/chunk-OPHIXXGI.js +157 -0
  68. package/dist/chunk-OPHIXXGI.js.map +1 -0
  69. package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
  70. package/dist/chunk-PNWNVOEZ.js.map +1 -0
  71. package/dist/{chunk-V3U4OF7L.js → chunk-UFAGYX73.js} +45 -47
  72. package/dist/chunk-UFAGYX73.js.map +1 -0
  73. package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
  74. package/dist/chunk-VDMYBPYB.js.map +1 -0
  75. package/dist/chunk-VTE7L6LO.js +5 -0
  76. package/dist/{chunk-O6YPEOQF.js.map → chunk-VTE7L6LO.js.map} +1 -1
  77. package/dist/chunk-W5C2GDNC.js +32 -0
  78. package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
  79. package/dist/chunk-WPMX3VGI.js +3 -0
  80. package/dist/chunk-WPMX3VGI.js.map +1 -0
  81. package/dist/chunk-WZ72HX5C.js +1182 -0
  82. package/dist/chunk-WZ72HX5C.js.map +1 -0
  83. package/dist/chunk-ZTEU5JVQ.js +2 -0
  84. package/dist/chunk-ZTEU5JVQ.js.map +1 -0
  85. package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
  86. package/dist/chunk-ZVP6GB4G.js.map +1 -0
  87. package/dist/cli.js +1 -1
  88. package/dist/cli.js.map +1 -1
  89. package/dist/client-PX25MD47.js +2 -0
  90. package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
  91. package/dist/config-HL5F36V4.js +2 -0
  92. package/dist/config-mutator-HBX53OMJ.js +2 -0
  93. package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
  94. package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
  95. package/dist/context-planner-AQHJMVY2.js +17 -0
  96. package/dist/context-planner-AQHJMVY2.js.map +1 -0
  97. package/dist/debug-3MEUO2P2.js +2 -0
  98. package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
  99. package/dist/discover-D4VVRNAI.js.map +1 -0
  100. package/dist/doctor-PBAH6XL3.js +2 -0
  101. package/dist/editor-chat-OB7IQOB5.js +137 -0
  102. package/dist/editor-chat-OB7IQOB5.js.map +1 -0
  103. package/dist/entry-triage-LK34TSQD.js +2 -0
  104. package/dist/entry-triage-LK34TSQD.js.map +1 -0
  105. package/dist/facts-YVEJOM7O.js +22 -0
  106. package/dist/facts-YVEJOM7O.js.map +1 -0
  107. package/dist/heal-FVLBHYP7.js +2 -0
  108. package/dist/index.d.ts +1016 -65
  109. package/dist/index.js +13 -13
  110. package/dist/index.js.map +1 -1
  111. package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
  112. package/dist/loader-4ZGTK7LD.js +2 -0
  113. package/dist/loader-BOKE5M5S.js +2 -0
  114. package/dist/memory-extract-F5ZHWY5E.js +2 -0
  115. package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
  116. package/dist/ntfy-X7EPB3TG.js +4 -0
  117. package/dist/ntfy-X7EPB3TG.js.map +1 -0
  118. package/dist/processes-WU23U2OJ.js +2 -0
  119. package/dist/program-YGXVCV2Y.js +2 -0
  120. package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
  121. package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
  122. package/dist/providers-KO2DODIO.js +2 -0
  123. package/dist/query-ZKTFJMNE.js +2 -0
  124. package/dist/questions-4B4KSPXG.js +3 -0
  125. package/dist/questions-4B4KSPXG.js.map +1 -0
  126. package/dist/recipes-7S4MC5OJ.js +2 -0
  127. package/dist/registry-PSZDPFZK.js +2 -0
  128. package/dist/review-candidates-AEUXRQQC.js +4 -0
  129. package/dist/review-candidates-AEUXRQQC.js.map +1 -0
  130. package/dist/review-store-B4YE3EFK.js +4 -0
  131. package/dist/review-store-B4YE3EFK.js.map +1 -0
  132. package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
  133. package/dist/routing-4PV66BK6.js +2 -0
  134. package/dist/routing-4PV66BK6.js.map +1 -0
  135. package/dist/runner-YRBVNYTY.js +2 -0
  136. package/dist/scheduler-GZZAN6ZG.js +2 -0
  137. package/dist/seat-VZ7AVO27.js +2 -0
  138. package/dist/select-F5I5PO2O.js +2 -0
  139. package/dist/select-F5I5PO2O.js.map +1 -0
  140. package/dist/store-MCOA6N4F.js +2 -0
  141. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  142. package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
  143. package/dist/timers-PCB4ZIBD.js +2 -0
  144. package/dist/triage-backtest-JNO7RKX7.js +2 -0
  145. package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
  146. package/dist/types-H5ENIUIT.js +2 -0
  147. package/dist/types-OIFU3KSQ.js +2 -0
  148. package/dist/web/workflow-editor.global.js +1542 -0
  149. package/dist/web/workflow-editor.global.js.map +1 -0
  150. package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
  151. package/dist/wiki-N5SMP3DT.js +2 -0
  152. package/dist/wiki-N5SMP3DT.js.map +1 -0
  153. package/dist/workflows/templates/branching.yaml +1 -1
  154. package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
  155. package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
  156. package/package.json +1 -1
  157. package/dist/agent-5QMITZT6.js +0 -2
  158. package/dist/architect-SEPWMWS4.js +0 -2
  159. package/dist/board-dashboard-M3372FLO.js.map +0 -1
  160. package/dist/chunk-2WVX3LEC.js.map +0 -1
  161. package/dist/chunk-3WQFCIB6.js +0 -24
  162. package/dist/chunk-3WQFCIB6.js.map +0 -1
  163. package/dist/chunk-4YCH6IZV.js +0 -2
  164. package/dist/chunk-53NRX2NA.js +0 -2
  165. package/dist/chunk-53NRX2NA.js.map +0 -1
  166. package/dist/chunk-5U35WAZF.js.map +0 -1
  167. package/dist/chunk-BFXONUUN.js +0 -108
  168. package/dist/chunk-BFXONUUN.js.map +0 -1
  169. package/dist/chunk-BFZUG66X.js +0 -45
  170. package/dist/chunk-BFZUG66X.js.map +0 -1
  171. package/dist/chunk-BTHHSY7D.js +0 -13
  172. package/dist/chunk-BTHHSY7D.js.map +0 -1
  173. package/dist/chunk-D46VZ7K7.js +0 -116
  174. package/dist/chunk-D46VZ7K7.js.map +0 -1
  175. package/dist/chunk-DJEZ2HVU.js.map +0 -1
  176. package/dist/chunk-E5CXCI33.js +0 -155
  177. package/dist/chunk-E5CXCI33.js.map +0 -1
  178. package/dist/chunk-FDWOECJY.js +0 -54
  179. package/dist/chunk-FDWOECJY.js.map +0 -1
  180. package/dist/chunk-H5D7ATXS.js.map +0 -1
  181. package/dist/chunk-HR4JSRZS.js +0 -3
  182. package/dist/chunk-HR4JSRZS.js.map +0 -1
  183. package/dist/chunk-ISEBLHWX.js +0 -118
  184. package/dist/chunk-ISEBLHWX.js.map +0 -1
  185. package/dist/chunk-JP5IEXR7.js +0 -32
  186. package/dist/chunk-JTJIPNXY.js.map +0 -1
  187. package/dist/chunk-K2CAPYES.js +0 -5
  188. package/dist/chunk-LDHPMJ7M.js.map +0 -1
  189. package/dist/chunk-METYYZKQ.js +0 -1115
  190. package/dist/chunk-METYYZKQ.js.map +0 -1
  191. package/dist/chunk-MTAPOCGA.js +0 -47
  192. package/dist/chunk-MTAPOCGA.js.map +0 -1
  193. package/dist/chunk-O6YPEOQF.js +0 -5
  194. package/dist/chunk-P4VWIVKN.js.map +0 -1
  195. package/dist/chunk-SR3X25TN.js +0 -3
  196. package/dist/chunk-SR3X25TN.js.map +0 -1
  197. package/dist/chunk-UHE5FCPB.js +0 -431
  198. package/dist/chunk-UHE5FCPB.js.map +0 -1
  199. package/dist/chunk-V3U4OF7L.js.map +0 -1
  200. package/dist/chunk-VJUTQAPJ.js +0 -2
  201. package/dist/chunk-VJUTQAPJ.js.map +0 -1
  202. package/dist/chunk-VT5XYFDD.js +0 -126
  203. package/dist/chunk-VT5XYFDD.js.map +0 -1
  204. package/dist/client-HGKAC6VP.js +0 -2
  205. package/dist/config-A4RWEJHV.js +0 -2
  206. package/dist/config-mutator-MNSFUN7D.js +0 -2
  207. package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
  208. package/dist/context-planner-45GEWKGL.js +0 -17
  209. package/dist/context-planner-45GEWKGL.js.map +0 -1
  210. package/dist/debug-N3B5NVJU.js +0 -2
  211. package/dist/discover-XJXQXXIO.js.map +0 -1
  212. package/dist/doctor-3Q36ZFE6.js +0 -2
  213. package/dist/heal-HZ3EQD52.js +0 -2
  214. package/dist/loader-5CA3O5GM.js +0 -2
  215. package/dist/loader-6IHUCPVO.js +0 -2
  216. package/dist/memory-extract-AENFVVO5.js +0 -2
  217. package/dist/processes-UIC4XXA6.js +0 -2
  218. package/dist/program-SAUNRCCD.js +0 -2
  219. package/dist/providers-L27BJ5A6.js +0 -2
  220. package/dist/query-HZYYRCLU.js +0 -2
  221. package/dist/recipes-EPLE2SNT.js +0 -2
  222. package/dist/registry-HK3WQ7BS.js +0 -2
  223. package/dist/runner-BFYDWPUO.js +0 -2
  224. package/dist/scheduler-6T2SU5ST.js +0 -2
  225. package/dist/store-I3HEWVLQ.js +0 -2
  226. package/dist/timers-5LPZN55J.js +0 -2
  227. package/dist/types-VECTMYTO.js +0 -2
  228. package/dist/types-Y6ZCZ52Z.js +0 -2
  229. package/dist/wiki-M6YNRFC4.js +0 -2
  230. /package/dist/{App-BPNHIGUX.js.map → App-U52IPUK2.js.map} +0 -0
  231. /package/dist/{ChatApp-6XSSZB4U.js.map → ChatApp-XP4OMHPL.js.map} +0 -0
  232. /package/dist/{agent-5QMITZT6.js.map → agent-V2CA4TXD.js.map} +0 -0
  233. /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
  234. /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
  235. /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
  236. /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
  237. /package/dist/{client-HGKAC6VP.js.map → chunk-FIOB3MEX.js.map} +0 -0
  238. /package/dist/{config-A4RWEJHV.js.map → client-PX25MD47.js.map} +0 -0
  239. /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
  240. /package/dist/{config-mutator-MNSFUN7D.js.map → config-HL5F36V4.js.map} +0 -0
  241. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-HBX53OMJ.js.map} +0 -0
  242. /package/dist/{doctor-3Q36ZFE6.js.map → debug-3MEUO2P2.js.map} +0 -0
  243. /package/dist/{heal-HZ3EQD52.js.map → doctor-PBAH6XL3.js.map} +0 -0
  244. /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
  245. /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
  246. /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
  247. /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
  248. /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
  249. /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
  250. /package/dist/{program-SAUNRCCD.js.map → processes-WU23U2OJ.js.map} +0 -0
  251. /package/dist/{providers-L27BJ5A6.js.map → program-YGXVCV2Y.js.map} +0 -0
  252. /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
  253. /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
  254. /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
  255. /package/dist/{recipes-EPLE2SNT.js.map → query-ZKTFJMNE.js.map} +0 -0
  256. /package/dist/{registry-HK3WQ7BS.js.map → recipes-7S4MC5OJ.js.map} +0 -0
  257. /package/dist/{runner-BFYDWPUO.js.map → registry-PSZDPFZK.js.map} +0 -0
  258. /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
  259. /package/dist/{scheduler-6T2SU5ST.js.map → runner-YRBVNYTY.js.map} +0 -0
  260. /package/dist/{store-I3HEWVLQ.js.map → scheduler-GZZAN6ZG.js.map} +0 -0
  261. /package/dist/{surface-usage-PRL6Z4U3.js.map → seat-VZ7AVO27.js.map} +0 -0
  262. /package/dist/{timers-5LPZN55J.js.map → store-MCOA6N4F.js.map} +0 -0
  263. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
  264. /package/dist/{types-VECTMYTO.js.map → surface-usage-NS74AXSE.js.map} +0 -0
  265. /package/dist/{types-Y6ZCZ52Z.js.map → timers-PCB4ZIBD.js.map} +0 -0
  266. /package/dist/{wiki-M6YNRFC4.js.map → types-H5ENIUIT.js.map} +0 -0
  267. /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-OIFU3KSQ.js.map} +0 -0
  268. /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/decisions/backend.ts","../src/decisions/store.ts","../src/decisions/types.ts","../src/decisions/normalize.ts","../src/decisions/schema.ts","../src/decisions/prompt.ts","../src/decisions/recalibrate.ts","../src/decisions/consistency.ts","../src/decisions/backends/mock.ts","../src/agent/providers/capabilities.ts","../src/decisions/backends/local-llm.ts","../src/decisions/backends/simple-jev.ts","../src/decisions/index.ts","../src/decisions/seat.ts"],"sourcesContent":["import type { AnswersFor, Questions, StateValue } from \"./types\"\n\n// The backend contract, and the registry that resolves one by name.\n//\n// Why this is its own registry and not `AgentProvider.decide?()`:\n//\n// 1. `ProviderName` is what an AGENT is configured with. Adding a\n// decision-only model to that union would make it selectable as an\n// agent's chat provider in agentx.json, the setup wizard and\n// `agentx model` — where it fails, because a System One model has no\n// prose, no stream and no tools. That is a type that lies.\n//\n// 2. The unit of selection differs. Chat providers are per-agent;\n// decision backends are per-SEAT. Running one seat on the local\n// adapter and another on a hosted model simultaneously is exactly how\n// a backend swap gets graded, and a per-agent union cannot say it.\n//\n// 3. The local backend COMPOSES the chat registry (it calls\n// createProvider internally) rather than extending it. One adapter\n// over N providers is less code than N providers each reimplementing\n// normalization, retry and confidence.\n\n/** How the structured answer was obtained. Recorded on every call, and\n * never pooled across values when computing calibration: a verbalized\n * probability and a token posterior are different measurements. */\nexport type StructureMode =\n | \"tool\"\n | \"json_schema\"\n | \"text\"\n | \"logprobs\"\n | \"vote\"\n | \"native\"\n | \"mock\"\n\nexport type AnswerMode = \"probabilities\" | \"discrete\"\n\nexport interface DecisionMeta {\n backend: string\n structureMode: StructureMode\n answerMode: AnswerMode\n /** At least one answer had to be repaired during normalization. */\n repaired: boolean\n /** Correction rounds spent on malformed structure. */\n retries: number\n /** State exceeded the backend's budget and was clipped. Shadow analysis\n * needs this to know which rows to exclude. */\n stateTruncated: boolean\n latencyMs: number\n}\n\nexport interface DecisionRequest<Q extends Questions> {\n state: StateValue\n questions: Q\n /** Backend-scoped model id. Omitted means the backend's default. */\n model?: string\n abortSignal?: AbortSignal\n timeoutMs?: number\n}\n\nexport interface DecisionResponse<Q extends Questions> {\n model: string\n answers: AnswersFor<Q>\n usage: { inputTokens: number; outputTokens: number }\n meta: DecisionMeta\n}\n\n/** Where the numbers come from. Factual, not a judgement of quality.\n *\n * verbalized the model wrote the probability into its answer. Well-formed,\n * quantized at round values, and not the model's own posterior.\n * logits read off the next-token distribution at the answer position.\n * This IS the model's posterior over the label set.\n * native a model whose training objective targeted the distribution. */\nexport type ProbabilitySource = \"verbalized\" | \"logits\" | \"native\" | \"synthetic\"\n\nexport interface DecisionBackendCapabilities {\n probabilitySource: ProbabilitySource\n /** Calibrated to CORRECTNESS on your traffic — i.e. an answer given 0.8\n * is right about 80% of the time here.\n *\n * This is not the same as reading real logits. A token posterior is the\n * model's own belief, which can be confidently wrong; simple-jev makes\n * exactly this point about its own output. Correctness calibration comes\n * from a recalibrator fitted on your labeled rows, not from a backend,\n * so every backend reports false until one is demonstrably trained for\n * it. Prefer `probabilitySource` when choosing a backend. */\n calibratedProbabilities: boolean\n maxChoiceOptions: number\n maxStateChars: number\n /** False forces the caller to loop per question. Every backend worth\n * shipping is true; the flag exists so that a backend that isn't cannot\n * quietly multiply request counts. */\n parallelQuestions: boolean\n images: false\n}\n\nexport interface DecisionBackend {\n readonly name: string\n readonly capabilities: DecisionBackendCapabilities\n decide<Q extends Questions>(request: DecisionRequest<Q>): Promise<DecisionResponse<Q>>\n}\n\ntype BackendFactory = () => DecisionBackend\n\nconst factories = new Map<string, BackendFactory>()\nconst instances = new Map<string, DecisionBackend>()\n\n/** Register a backend under a name. Factories are lazy so that registering\n * a backend never reads config, opens a socket or requires a key that the\n * operator may not have. */\nexport function registerDecisionBackend(name: string, factory: BackendFactory): void {\n factories.set(name, factory)\n instances.delete(name)\n}\n\nexport function getDecisionBackend(name: string): DecisionBackend {\n const cached = instances.get(name)\n if (cached) return cached\n const factory = factories.get(name)\n if (!factory) {\n const known = listDecisionBackends()\n throw new Error(\n `unknown decision backend \"${name}\"` +\n (known.length > 0 ? ` — registered: ${known.join(\", \")}` : \" — none registered\"),\n )\n }\n const backend = factory()\n instances.set(name, backend)\n return backend\n}\n\nexport function listDecisionBackends(): string[] {\n return [...factories.keys()].sort()\n}\n\nexport function _resetDecisionBackendsForTesting(): void {\n factories.clear()\n instances.clear()\n}\n","import Database from \"better-sqlite3\"\nimport { createHash } from \"crypto\"\nimport { mkdirSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\nimport { newEventId } from \"@/intent/ulid\"\nimport type { DecisionMeta } from \"./backend\"\nimport type { AnyAnswer, AnyQuestion, Questions, StateValue } from \"./types\"\n\n// The shadow store: every decision the seat makes, and what actually\n// happened afterwards.\n//\n// Deliberately NOT the intent ledger, for four reasons that are mechanical\n// rather than stylistic:\n//\n// 1. `IntentDivergence.source` is a closed 9-member union of channel\n// names. \"classifier\" and \"monitor-prefilter\" have no representable\n// value, and that closedness is the ledger's stated design goal.\n// 2. `IntentDecision.outcome` is dispatched|halted|deduped|queued.\n// A score or a probability has no projection onto it.\n// 3. The ledger is canonical state. Speculative model output is derived\n// data, with a different retention and durability story.\n// 4. The ledger's promotion gate is \"zero divergences for >= 7 days\".\n// A shadow seat is EXPECTED to disagree with the incumbent — that\n// disagreement is the measurement — so writing it into\n// intent_divergences would destroy the signal that gate depends on.\n//\n// Same storage discipline as the ledger, though: own file, WAL,\n// synchronous=NORMAL, versioned migrations.\n\nexport interface OpenStoreOptions {\n /** Resolved relative to cwd. Default: .agentx/decisions/decisions.sqlite */\n path?: string\n readonly?: boolean\n}\n\nexport type SeatMode = \"off\" | \"shadow\" | \"active\"\nexport type LabelKind = \"human\" | \"outcome\" | \"replay\"\n\n/** What the policy decided to do with the answer. Recorded in shadow too,\n * where it is the counterfactual rather than the action taken. */\nexport type DecisionAction = \"review\" | \"skip\"\n\nexport interface RecordCallInput {\n seat: string\n mode: SeatMode\n backend: string\n model: string\n meta: Pick<DecisionMeta, \"structureMode\" | \"answerMode\" | \"retries\" | \"stateTruncated\" | \"latencyMs\">\n state: StateValue\n questions: Questions\n answers: Record<string, AnyAnswer>\n usage?: { inputTokens: number; outputTokens: number }\n /** What the code would have done without the seat, per question. */\n incumbent?: Record<string, { value?: string | number; score?: number; source?: string }>\n links?: Array<{ kind: string; id: string }>\n /** Low-cardinality covariates for recalibration: agent, channel, and\n * anything else that plausibly shifts the miscalibration curve. Keep\n * these categorical and few — this is a calibration model fitted on\n * hundreds of rows, not thousands. */\n features?: Record<string, string | number | boolean | null>\n /** A failed call is still a row. The failure rate is a measured quantity,\n * not a hunch, and a seat that only records its successes flatters\n * itself exactly where it matters. */\n error?: string\n /** Keep the serialized state for replay. Callers pass false once a seat\n * has collected enough, and always false when redaction is on. */\n keepState?: boolean\n ts?: number\n}\n\n/** One decision, one question, joined to its incumbent and its truth.\n * The unit every calibration metric consumes. */\nexport interface GradedRow {\n callId: string\n seat: string\n ts: number\n backend: string\n model: string\n structureMode: string\n question: string\n type: AnyQuestion[\"type\"]\n /** The seat's answer, as a comparable scalar. */\n predicted: string\n confidence: number\n probabilities: Record<string, number>\n incumbent?: string\n truth?: string\n features: Record<string, string | number | boolean | null>\n mode: SeatMode\n /** What the policy decided. Undefined for calls whose caller never\n * recorded one. */\n action?: DecisionAction\n /** True when the policy said skip and exploration overrode it, so the\n * expensive path ran anyway. These rows are the unbiased sample of the\n * skip region — the only place a skip decision can ever be graded. */\n explored: boolean\n}\n\nexport interface GradedRowFilter {\n seat?: string\n question?: string\n backend?: string\n model?: string\n structureMode?: string\n /** ms since epoch; rows at or after this time. */\n since?: number\n /** Only rows that have a ground-truth label. */\n labeledOnly?: boolean\n /** Only rows where exploration forced the expensive path. Calibration on\n * these alone is the unbiased estimate of how the policy performs on the\n * decisions it wants to skip. */\n exploredOnly?: boolean\n action?: DecisionAction\n limit?: number\n}\n\nexport class DecisionStore {\n readonly db: Database.Database\n readonly path: string\n\n constructor(opts: OpenStoreOptions = {}) {\n this.path = resolve(process.cwd(), opts.path ?? \".agentx/decisions/decisions.sqlite\")\n if (!opts.readonly) mkdirSync(dirname(this.path), { recursive: true })\n this.db = new Database(this.path, { readonly: opts.readonly ?? false })\n if (!opts.readonly) {\n this.db.pragma(\"journal_mode = WAL\")\n this.db.pragma(\"synchronous = NORMAL\")\n this.db.pragma(\"foreign_keys = ON\")\n runMigrations(this.db)\n }\n }\n\n close(): void {\n this.db.close()\n }\n\n schemaVersion(): number {\n const row = this.db.prepare(\"SELECT MAX(v) AS v FROM schema_version\").get() as {\n v: number | null\n }\n return row.v ?? 0\n }\n\n /** Write one call and everything hanging off it, atomically. Returns the\n * call id, which is what a later label or link refers to. */\n recordCall(input: RecordCallInput): string {\n const ts = input.ts ?? Date.now()\n const callId = newEventId(ts)\n const stateJson = JSON.stringify(input.state ?? null)\n const stateHash = createHash(\"sha256\").update(stateJson).digest(\"hex\")\n\n const tx = this.db.transaction(() => {\n this.db\n .prepare(\n `INSERT INTO decision_calls\n (id, ts, seat, mode, backend, model, structure_mode, answer_mode,\n state_hash, state_json, questions_json, latency_ms,\n input_tokens, output_tokens, retries, truncated, error, features_json)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n callId,\n ts,\n input.seat,\n input.mode,\n input.backend,\n input.model,\n input.meta.structureMode,\n input.meta.answerMode,\n stateHash,\n input.keepState === false ? null : stateJson,\n JSON.stringify(input.questions),\n input.meta.latencyMs,\n input.usage?.inputTokens ?? 0,\n input.usage?.outputTokens ?? 0,\n input.meta.retries,\n input.meta.stateTruncated ? 1 : 0,\n input.error ?? null,\n input.features ? JSON.stringify(input.features) : null,\n )\n\n const insertAnswer = this.db.prepare(\n `INSERT INTO decision_answers\n (call_id, question, type, answer_json, top_label, top_prob,\n expected_score, p_max, neg_entropy)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n for (const [question, answer] of Object.entries(input.answers)) {\n const view = answerView(answer)\n insertAnswer.run(\n callId,\n question,\n answer.type,\n JSON.stringify(answer),\n view.topLabel,\n view.topProb,\n view.expectedScore,\n view.pMax,\n view.negEntropy,\n )\n }\n\n if (input.incumbent) {\n const insertIncumbent = this.db.prepare(\n `INSERT OR REPLACE INTO decision_incumbent (call_id, question, value, score, source)\n VALUES (?, ?, ?, ?, ?)`,\n )\n for (const [question, row] of Object.entries(input.incumbent)) {\n insertIncumbent.run(\n callId,\n question,\n row.value === undefined ? null : String(row.value),\n row.score ?? null,\n row.source ?? null,\n )\n }\n }\n\n if (input.links) {\n const insertLink = this.db.prepare(\n `INSERT OR IGNORE INTO decision_links (call_id, ref_kind, ref_id) VALUES (?, ?, ?)`,\n )\n for (const link of input.links) insertLink.run(callId, link.kind, link.id)\n }\n })\n\n tx()\n return callId\n }\n\n /** Attach ground truth. Append-only: a correction is a new row, and reads\n * take the newest, so a mislabel is fixable without losing the audit. */\n label(\n callId: string,\n question: string,\n value: string | number,\n opts: { kind?: LabelKind; labeledBy?: string; note?: string; ts?: number } = {},\n ): string {\n const ts = opts.ts ?? Date.now()\n const id = newEventId(ts)\n this.db\n .prepare(\n `INSERT INTO decision_labels (id, call_id, question, value, kind, labeled_at, labeled_by, note)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n id,\n callId,\n question,\n String(value),\n opts.kind ?? \"human\",\n ts,\n opts.labeledBy ?? null,\n opts.note ?? null,\n )\n return id\n }\n\n /** Record what the policy did with an answer. Separate from recordCall\n * because the seat records the answer and the CALLER owns the policy —\n * askSeat cannot know whether its caller acted on what it returned. */\n recordOutcome(callId: string, action: DecisionAction, explored = false): void {\n this.db\n .prepare(\"UPDATE decision_calls SET action = ?, explored = ? WHERE id = ?\")\n .run(action, explored ? 1 : 0, callId)\n }\n\n findCallsByLink(kind: string, id: string): string[] {\n const rows = this.db\n .prepare(`SELECT call_id FROM decision_links WHERE ref_kind = ? AND ref_id = ?`)\n .all(kind, id) as Array<{ call_id: string }>\n return rows.map((r) => r.call_id)\n }\n\n gradedRows(filter: GradedRowFilter = {}): GradedRow[] {\n const where: string[] = [\"c.error IS NULL\"]\n const params: unknown[] = []\n if (filter.seat) (where.push(\"c.seat = ?\"), params.push(filter.seat))\n if (filter.question) (where.push(\"a.question = ?\"), params.push(filter.question))\n if (filter.backend) (where.push(\"c.backend = ?\"), params.push(filter.backend))\n if (filter.model) (where.push(\"c.model = ?\"), params.push(filter.model))\n if (filter.structureMode) (where.push(\"c.structure_mode = ?\"), params.push(filter.structureMode))\n if (filter.since !== undefined) (where.push(\"c.ts >= ?\"), params.push(filter.since))\n if (filter.action) (where.push(\"c.action = ?\"), params.push(filter.action))\n if (filter.exploredOnly) where.push(\"c.explored = 1\")\n\n // The newest label per (call, question) wins; ULIDs sort by time, so\n // MAX(id) is the newest without a second timestamp comparison.\n const sql = `\n SELECT c.id AS call_id, c.seat, c.ts, c.backend, c.model, c.structure_mode,\n c.mode, c.action, c.explored, c.features_json,\n a.question, a.type, a.answer_json, a.top_label, a.expected_score, a.neg_entropy,\n i.value AS incumbent,\n (SELECT l.value FROM decision_labels l\n WHERE l.call_id = c.id AND l.question = a.question\n ORDER BY l.id DESC LIMIT 1) AS truth\n FROM decision_calls c\n JOIN decision_answers a ON a.call_id = c.id\n LEFT JOIN decision_incumbent i ON i.call_id = c.id AND i.question = a.question\n WHERE ${where.join(\" AND \")}\n ORDER BY c.ts DESC\n ${filter.limit ? \"LIMIT ?\" : \"\"}`\n if (filter.limit) params.push(filter.limit)\n\n const rows = this.db.prepare(sql).all(...params) as Array<Record<string, any>>\n return rows\n .map((r) => {\n const answer = JSON.parse(r.answer_json) as AnyAnswer\n const view = answerView(answer)\n return {\n callId: r.call_id,\n seat: r.seat,\n ts: r.ts,\n backend: r.backend,\n model: r.model,\n structureMode: r.structure_mode,\n question: r.question,\n type: answer.type,\n predicted: view.predicted,\n confidence: view.confidence,\n probabilities: view.probabilities,\n incumbent: r.incumbent ?? undefined,\n truth: r.truth ?? undefined,\n features: parseFeatures(r.features_json),\n mode: r.mode as SeatMode,\n action: (r.action ?? undefined) as GradedRow[\"action\"],\n explored: r.explored === 1,\n } as GradedRow\n })\n .filter((row) => !filter.labeledOnly || row.truth !== undefined)\n }\n\n /** Drop stored state once a seat has enough for replay, keeping the hash.\n * Returns how many rows were cleared. */\n pruneState(seat: string, keepRows: number): number {\n const res = this.db\n .prepare(\n `UPDATE decision_calls SET state_json = NULL\n WHERE seat = ? AND state_json IS NOT NULL\n AND id NOT IN (\n SELECT id FROM decision_calls WHERE seat = ? ORDER BY ts DESC LIMIT ?\n )`,\n )\n .run(seat, seat, keepRows)\n return res.changes\n }\n}\n\n/** Project any answer onto the scalars the store and the metrics need.\n * A noul becomes a two-outcome distribution so it grades through exactly\n * the same machinery as a choice. */\nexport function answerView(answer: AnyAnswer): {\n predicted: string\n confidence: number\n probabilities: Record<string, number>\n topLabel: string\n topProb: number\n expectedScore: number | null\n pMax: number\n negEntropy: number\n} {\n if (answer.type === \"noul\") {\n const p = answer.noul\n const probabilities = { yes: p, no: 1 - p }\n const predicted = p >= 0.5 ? \"yes\" : \"no\"\n // Distance from a coin flip, scaled to [0,1] — the only uncertainty a\n // binary carries, and comparable with the entropy statistic elsewhere.\n const confidence = Math.abs(p - 0.5) * 2\n return {\n predicted,\n confidence,\n probabilities,\n topLabel: predicted,\n topProb: Math.max(p, 1 - p),\n expectedScore: null,\n pMax: Math.max(p, 1 - p),\n negEntropy: confidence,\n }\n }\n\n if (answer.type === \"choice\") {\n return {\n predicted: answer.choice,\n confidence: answer.confidence,\n probabilities: { ...answer.probabilities },\n topLabel: answer.choice,\n topProb: answer.probabilities[answer.choice] ?? 0,\n expectedScore: null,\n pMax: answer.pMax,\n negEntropy: answer.negEntropy,\n }\n }\n\n const rounded = String(Math.round(answer.score))\n return {\n predicted: rounded,\n confidence: answer.confidence,\n probabilities: { ...answer.probabilities },\n topLabel: rounded,\n topProb: answer.probabilities[rounded] ?? 0,\n expectedScore: answer.score,\n pMax: answer.pMax,\n negEntropy: answer.negEntropy,\n }\n}\n\nfunction parseFeatures(raw: unknown): Record<string, string | number | boolean | null> {\n if (typeof raw !== \"string\" || raw.length === 0) return {}\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? parsed : {}\n } catch {\n return {}\n }\n}\n\nfunction runMigrations(db: Database.Database): void {\n db.exec(`CREATE TABLE IF NOT EXISTS schema_version (v INTEGER PRIMARY KEY);`)\n const current =\n (db.prepare(\"SELECT MAX(v) AS v FROM schema_version\").get() as { v: number | null }).v ?? 0\n if (current < 1) migrationV1(db)\n if (current < 2) migrationV2(db)\n if (current < 3) migrationV3(db)\n}\n\n/** Caller-supplied covariates for recalibration.\n *\n * A single global temperature assumes one miscalibration curve for the\n * whole seat. That is usually false: a chatty support agent and a cron\n * runner are different distributions, and a model can be well calibrated\n * on one while badly overconfident on the other. Storing a few\n * low-cardinality covariates per call is what lets that be fitted instead\n * of assumed away. */\nfunction migrationV3(db: Database.Database): void {\n db.exec(`\n ALTER TABLE decision_calls ADD COLUMN features_json TEXT;\n INSERT INTO schema_version (v) VALUES (3);\n `)\n}\n\n/** What the policy decided, and whether exploration overrode it.\n *\n * Recorded in every mode, including shadow, so the counterfactual is\n * measurable before anything is ever actually skipped: \"the policy would\n * have skipped 40% of these, and here is how it did on them.\"\n *\n * Without this the labeled sample under active mode is the set of reviews\n * the policy chose to run, which is exactly the biased subsample its own\n * metrics would then be computed on. */\nfunction migrationV2(db: Database.Database): void {\n db.exec(`\n ALTER TABLE decision_calls ADD COLUMN action TEXT;\n ALTER TABLE decision_calls ADD COLUMN explored INTEGER NOT NULL DEFAULT 0;\n CREATE INDEX IF NOT EXISTS idx_decision_calls_action ON decision_calls (seat, action, explored);\n INSERT INTO schema_version (v) VALUES (2);\n `)\n}\n\nfunction migrationV1(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS decision_calls (\n id TEXT NOT NULL PRIMARY KEY,\n ts INTEGER NOT NULL,\n seat TEXT NOT NULL,\n mode TEXT NOT NULL,\n backend TEXT NOT NULL,\n model TEXT NOT NULL,\n structure_mode TEXT NOT NULL,\n answer_mode TEXT NOT NULL,\n state_hash TEXT NOT NULL,\n state_json TEXT,\n questions_json TEXT NOT NULL,\n latency_ms INTEGER,\n input_tokens INTEGER NOT NULL DEFAULT 0,\n output_tokens INTEGER NOT NULL DEFAULT 0,\n retries INTEGER NOT NULL DEFAULT 0,\n truncated INTEGER NOT NULL DEFAULT 0,\n error TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_decision_calls_seat ON decision_calls (seat, ts);\n CREATE INDEX IF NOT EXISTS idx_decision_calls_backend ON decision_calls (backend, model, ts);\n CREATE INDEX IF NOT EXISTS idx_decision_calls_state ON decision_calls (state_hash);\n\n CREATE TABLE IF NOT EXISTS decision_answers (\n call_id TEXT NOT NULL REFERENCES decision_calls(id),\n question TEXT NOT NULL,\n type TEXT NOT NULL,\n answer_json TEXT NOT NULL,\n top_label TEXT,\n top_prob REAL,\n expected_score REAL,\n p_max REAL,\n neg_entropy REAL,\n PRIMARY KEY (call_id, question)\n );\n\n CREATE TABLE IF NOT EXISTS decision_incumbent (\n call_id TEXT NOT NULL REFERENCES decision_calls(id),\n question TEXT NOT NULL,\n value TEXT,\n score REAL,\n source TEXT,\n PRIMARY KEY (call_id, question)\n );\n\n CREATE TABLE IF NOT EXISTS decision_labels (\n id TEXT NOT NULL PRIMARY KEY,\n call_id TEXT NOT NULL REFERENCES decision_calls(id),\n question TEXT NOT NULL,\n value TEXT NOT NULL,\n kind TEXT NOT NULL,\n labeled_at INTEGER NOT NULL,\n labeled_by TEXT,\n note TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_decision_labels_call ON decision_labels (call_id, question);\n\n CREATE TABLE IF NOT EXISTS decision_links (\n call_id TEXT NOT NULL REFERENCES decision_calls(id),\n ref_kind TEXT NOT NULL,\n ref_id TEXT NOT NULL,\n PRIMARY KEY (call_id, ref_kind, ref_id)\n );\n CREATE INDEX IF NOT EXISTS idx_decision_links_ref ON decision_links (ref_kind, ref_id);\n\n INSERT INTO schema_version (v) VALUES (1);\n `)\n}\n","// Typed-decision seat — the wire contract.\n//\n// This mirrors TypeSafe AI's \"System One\" shape (POST /v1/systemone) on\n// purpose: one `state` plus a map of named questions, with every answer\n// returned in a single call carrying a full probability distribution.\n// Mirroring it means a future Jev backend is a new file in ./backends,\n// not a refactor of every call site.\n//\n// Three question types, and only three. A call site that wants prose does\n// not belong here.\n//\n// Two deliberate divergences from \"whatever is easiest to ask a model for\":\n//\n// - `NoulAnswer` has NO `confidence` field, because Jev's noul answer has\n// none. A consumer reaching for it is a compile error today rather than\n// a surprise on the day we swap backends.\n//\n// - The model is never asked for `choice`, `score`, `confidence` or\n// `legend`. It reports `probabilities` and nothing else; the rest is\n// derived in ./normalize.ts. Self-reported confidence is the exact\n// thing this module exists to replace — see src/graph/classifier.ts,\n// where `graph.autoApproveConfidence` defaults to 1.0 (i.e. disabled)\n// precisely because the model's own number isn't trusted.\n\n/** A JSON-compatible value. State is JSON-only: no images, matching Jev. */\nexport type StateValue =\n | string\n | number\n | boolean\n | null\n | StateValue[]\n | { [key: string]: StateValue }\n\n// ---------------------------------------------------------------------------\n// Questions\n// ---------------------------------------------------------------------------\n\n/** A yes/no question. `criteria` optionally describes either outcome. */\nexport interface NoulQuestion {\n readonly type: \"noul\"\n readonly instructions?: string\n readonly criteria?: {\n readonly true?: string\n readonly false?: string\n }\n}\n\n/** Select one of a named set. `criteria` maps label -> description\n * (`null` leaves a label undescribed). */\nexport interface ChoiceQuestion<L extends string = string> {\n readonly type: \"choice\"\n readonly instructions?: string\n readonly criteria: Readonly<Record<L, string | null>>\n}\n\n/** Place the state on an ordered rubric. Index i describes level i, so the\n * array order IS the scale. At least two levels. */\nexport interface ScoreQuestion {\n readonly type: \"score\"\n readonly instructions?: string\n readonly criteria: readonly string[]\n}\n\nexport type AnyQuestion = NoulQuestion | ChoiceQuestion<string> | ScoreQuestion\n\n/** Questions keyed by the names their answers come back under. */\nexport type Questions = Record<string, AnyQuestion>\n\n// ---------------------------------------------------------------------------\n// Answers\n// ---------------------------------------------------------------------------\n\n/** Probability of \"yes\". No confidence — the probability IS the answer, and\n * distance from 0.5 is the only uncertainty signal a binary has. */\nexport interface NoulAnswer {\n readonly type: \"noul\"\n readonly noul: number\n}\n\nexport interface ChoiceAnswer<L extends string = string> {\n readonly type: \"choice\"\n /** argmax of `probabilities`, derived — never model-reported. */\n readonly choice: L\n /** Derived from the shape of `probabilities`. See confidenceFor(). */\n readonly confidence: number\n readonly probabilities: Readonly<Record<L, number>>\n /** Both statistics are kept on every answer because Jev's own confidence\n * formula is unpublished; storing both lets the swap-day comparison run\n * against already-collected shadow rows without recollecting them. */\n readonly pMax: number\n readonly negEntropy: number\n}\n\nexport interface ScoreAnswer {\n readonly type: \"score\"\n /** Expected value over the rubric, so it may fall between levels. */\n readonly score: number\n readonly confidence: number\n /** Rubric descriptions keyed by level index, echoed back for display. */\n readonly legend: Readonly<Record<string, string>>\n readonly probabilities: Readonly<Record<string, number>>\n readonly pMax: number\n readonly negEntropy: number\n}\n\nexport type AnyAnswer = NoulAnswer | ChoiceAnswer<string> | ScoreAnswer\n\n/** The answer shape implied by a question shape.\n *\n * Inference reads `criteria` directly rather than going through\n * `ChoiceQuestion<infer L>`, so a question built by hand (not via the\n * ./questions.ts builders) still narrows correctly. */\nexport type AnswerFor<Q extends AnyQuestion> =\n Q extends { type: \"noul\" } ? NoulAnswer\n : Q extends { type: \"score\" } ? ScoreAnswer\n : Q extends { type: \"choice\"; criteria: infer C } ? ChoiceAnswer<Extract<keyof C, string>>\n : never\n\nexport type AnswersFor<Q extends Questions> = {\n readonly [K in keyof Q]: AnswerFor<Q[K]>\n}\n\n// ---------------------------------------------------------------------------\n// Raw model output\n// ---------------------------------------------------------------------------\n//\n// What a backend is allowed to receive from a model, before normalization.\n// Narrower than the public answer on purpose — see the header.\n\nexport interface RawNoulAnswer {\n readonly noul: number\n}\n\nexport interface RawDistributionAnswer {\n readonly probabilities: Record<string, unknown>\n}\n\nexport type RawAnswer = RawNoulAnswer | RawDistributionAnswer\n\nexport type RawAnswers = Record<string, RawAnswer>\n","import { labelsOf } from \"./questions\"\nimport type { AnyAnswer, AnyQuestion, RawAnswer } from \"./types\"\n\n// Turning what a model said into a distribution we can do arithmetic on.\n//\n// Everything a caller reads off an answer except the raw numbers is computed\n// here: the argmax, the expected score, and both confidence statistics. The\n// model supplies `probabilities` (or `noul`) and nothing else.\n//\n// Nothing in this file throws. A model that returns garbage yields a uniform\n// distribution with `repaired: true`, because a seat that throws on the\n// critical path is worse than a seat that says \"I don't know\" — and a\n// repaired row is visible in the shadow store, so the failure is measured\n// rather than hidden.\n\nexport interface NormalizeOptions {\n /** Rescale so the values sum to 1. Default true. Mirrors the Python\n * system-one-adapter's `normalize_probabilities`. */\n normalize?: boolean\n /** Mass given to a label the model omitted entirely. Default 1e-6. */\n epsilon?: number\n}\n\nexport interface NormalizedDistribution {\n probs: Record<string, number>\n /** True when the model's output had to be corrected: a missing or extra\n * label, a negative or non-finite value, or an all-zero distribution.\n * Recorded per call so \"how often does the backend misbehave\" is itself\n * a measured quantity rather than a hunch. */\n repaired: boolean\n}\n\nexport function normalizeDistribution(\n raw: unknown,\n labels: readonly string[],\n opts: NormalizeOptions = {},\n): NormalizedDistribution {\n const normalize = opts.normalize ?? true\n const epsilon = opts.epsilon ?? 1e-6\n const source: Record<string, unknown> =\n raw !== null && typeof raw === \"object\" && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : {}\n\n let repaired = raw === null || typeof raw !== \"object\" || Array.isArray(raw)\n for (const key of Object.keys(source)) {\n if (!labels.includes(key)) repaired = true\n }\n\n const probs: Record<string, number> = {}\n let sum = 0\n for (const label of labels) {\n if (!(label in source)) {\n probs[label] = epsilon\n sum += epsilon\n repaired = true\n continue\n }\n const value = Number(source[label])\n if (!Number.isFinite(value) || value < 0) {\n probs[label] = 0\n repaired = true\n continue\n }\n probs[label] = value\n sum += value\n }\n\n if (labels.length === 0) return { probs, repaired }\n\n if (sum <= 0) {\n // The model gave us nothing usable. Uniform is the honest answer: it\n // carries zero confidence, so a confidence-gated call site escalates.\n const uniform = 1 / labels.length\n for (const label of labels) probs[label] = uniform\n return { probs, repaired: true }\n }\n\n if (normalize) {\n for (const label of labels) probs[label] = probs[label] / sum\n if (Math.abs(sum - 1) > 1e-6) repaired = true\n }\n\n return { probs, repaired }\n}\n\n/** Largest probability. Simple, but not comparable across different option\n * counts — uniform over 2 is 0.5, uniform over 10 is 0.1. */\nexport function pMax(probs: Record<string, number>): number {\n const values = Object.values(probs)\n if (values.length === 0) return 0\n return Math.max(...values)\n}\n\n/** 1 - H(p) / ln(k). Zero on a uniform distribution, one on a point mass,\n * and comparable across option counts because it divides out ln(k) — which\n * is why this, not pMax, is what `confidence` reports. */\nexport function normalizedNegEntropy(probs: Record<string, number>): number {\n const values = Object.values(probs).filter((v) => Number.isFinite(v) && v > 0)\n const k = Object.keys(probs).length\n if (k <= 1) return 1\n const total = values.reduce((a, b) => a + b, 0)\n if (total <= 0) return 0\n let h = 0\n for (const v of values) {\n const p = v / total\n h -= p * Math.log(p)\n }\n return clamp01(1 - h / Math.log(k))\n}\n\n/** Expected value over a distribution whose keys are level indices. */\nexport function expectedScore(probs: Record<string, number>): number {\n let total = 0\n let weighted = 0\n for (const [key, value] of Object.entries(probs)) {\n const level = Number(key)\n if (!Number.isFinite(level) || !Number.isFinite(value) || value <= 0) continue\n total += value\n weighted += level * value\n }\n return total > 0 ? weighted / total : 0\n}\n\nexport function confidenceFor(probs: Record<string, number>): number {\n return normalizedNegEntropy(probs)\n}\n\nfunction clamp01(n: number): number {\n if (!Number.isFinite(n)) return 0\n return Math.max(0, Math.min(1, n))\n}\n\n/** Build the public answer from a question and what the model reported.\n * The caller-visible `choice`, `score`, `confidence` and `legend` are all\n * derived right here — the model never reports any of them. */\nexport function finalizeAnswer(\n question: AnyQuestion,\n raw: RawAnswer,\n opts: NormalizeOptions = {},\n): { answer: AnyAnswer; repaired: boolean } {\n if (question.type === \"noul\") {\n const value = Number((raw as { noul?: unknown }).noul)\n const ok = Number.isFinite(value) && value >= 0 && value <= 1\n return {\n answer: { type: \"noul\", noul: ok ? value : 0.5 },\n repaired: !ok,\n }\n }\n\n const labels = labelsOf(question)\n const { probs, repaired } = normalizeDistribution(\n (raw as { probabilities?: unknown }).probabilities,\n labels,\n opts,\n )\n const max = pMax(probs)\n const negEntropy = normalizedNegEntropy(probs)\n\n if (question.type === \"choice\") {\n let best = labels[0]\n for (const label of labels) if (probs[label] > probs[best]) best = label\n return {\n answer: {\n type: \"choice\",\n choice: best,\n confidence: confidenceFor(probs),\n probabilities: probs,\n pMax: max,\n negEntropy,\n },\n repaired,\n }\n }\n\n const legend: Record<string, string> = {}\n question.criteria.forEach((description, i) => {\n legend[String(i)] = description\n })\n return {\n answer: {\n type: \"score\",\n score: expectedScore(probs),\n confidence: confidenceFor(probs),\n legend,\n probabilities: probs,\n pMax: max,\n negEntropy,\n },\n repaired,\n }\n}\n","import { z } from \"zod\"\nimport type { AnyQuestion, Questions } from \"./types\"\n\n// Runtime validation of raw model output, DERIVED from the questions.\n//\n// Derived, not hand-written, because a hand-written schema is free to drift\n// from the Jev wire format and drift is the one failure this whole module\n// exists to prevent. There is exactly one definition of what an answer looks\n// like, and both the static type (AnswerFor, in ./types.ts) and this runtime\n// check come from it.\n//\n// These validate SHAPE, not content. `probabilities` values are `unknown`\n// on purpose: a model that writes \"0.7\" instead of 0.7 is coerced by\n// normalizeDistribution and flagged `repaired`, which is cheaper and more\n// informative than burning a correction round on a cosmetic slip. What we\n// do insist on is that the container is there and is an object — that is\n// the failure that means \"the model ignored the schema and wrote prose\",\n// and that one is worth a retry.\n\nconst noulSchema = z.object({\n noul: z.number(),\n})\n\nconst distributionSchema = z.object({\n probabilities: z.record(z.string(), z.unknown()),\n})\n\nexport function rawAnswerSchema(question: AnyQuestion): z.ZodType<unknown> {\n return question.type === \"noul\" ? noulSchema : distributionSchema\n}\n\n/** Every question must be answered. A backend that drops one has not done\n * the job, and silently returning a partial map would leave the caller\n * reading `undefined.choice`. */\nexport function rawAnswersSchema(questions: Questions): z.ZodType<unknown> {\n const shape: Record<string, z.ZodType<unknown>> = {}\n for (const [name, question] of Object.entries(questions)) {\n shape[name] = rawAnswerSchema(question)\n }\n return z.object(shape)\n}\n\n/** Flatten a ZodError into the one-line-per-problem form we feed back to a\n * model on a correction round. Mirrors the correction-round shape already\n * used by src/procedures/mine/distill.ts. */\nexport function describeIssues(error: z.ZodError): string {\n return error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join(\".\") : \"(root)\"\n return `- ${path}: ${issue.message}`\n })\n .join(\"\\n\")\n}\n","import { labelsOf } from \"./questions\"\nimport type { AnyQuestion, Questions, StateValue } from \"./types\"\n\n// Turning a question set into something a chat model can answer.\n//\n// The schema below asks for `probabilities` and nothing else. No `choice`,\n// no `score`, no `confidence`, no `legend` — those are derived in\n// ./normalize.ts. Two reasons, and the second is the important one:\n//\n// 1. If a model reports both an argmax and a distribution they can\n// disagree, and there is no principled way to pick a winner.\n// 2. A confidence a model writes out is a number it chose, not a\n// property of its own uncertainty. Asking for one reproduces exactly\n// the signal that `graph.autoApproveConfidence` already defaults to\n// 1.0 to avoid trusting.\n\nexport const TOOL_NAME = \"answer_questions\"\n\nexport const SYSTEM_PROMPT = [\n \"You answer questions about a piece of state by reporting a probability distribution for each one.\",\n \"\",\n \"Rules:\",\n \"- Answer every question. Never add a question that was not asked.\",\n \"- For a yes/no question, report `noul`: the probability the answer is yes, between 0 and 1.\",\n \"- For every other question, report `probabilities`: one number per listed option, summing to 1.\",\n \"- Use only the exact option names given. Never invent one.\",\n \"- Spread the distribution when the state genuinely does not settle the question. A flat\",\n \" distribution is a useful answer; a confident wrong one is not.\",\n \"- Output the structure only. No prose, no explanation, no code fences.\",\n].join(\"\\n\")\n\n/** JSON Schema for the forced tool's `input` — one object per question,\n * keyed by question name, with `additionalProperties: false` throughout so\n * a provider with strict structured output rejects drift server-side. */\nexport function toolInputSchema(questions: Questions): Record<string, unknown> {\n const properties: Record<string, unknown> = {}\n for (const [name, question] of Object.entries(questions)) {\n properties[name] = answerObjectSchema(question)\n }\n return {\n type: \"object\",\n additionalProperties: false,\n required: [\"answers\"],\n properties: {\n answers: {\n type: \"object\",\n additionalProperties: false,\n required: Object.keys(questions),\n properties,\n },\n },\n }\n}\n\nfunction answerObjectSchema(question: AnyQuestion): Record<string, unknown> {\n if (question.type === \"noul\") {\n return {\n type: \"object\",\n additionalProperties: false,\n required: [\"noul\"],\n properties: {\n noul: {\n type: \"number\",\n minimum: 0,\n maximum: 1,\n description: \"Probability the answer is yes.\",\n },\n },\n }\n }\n\n const labels = labelsOf(question)\n const properties: Record<string, unknown> = {}\n for (const label of labels) {\n properties[label] = { type: \"number\", minimum: 0, maximum: 1 }\n }\n return {\n type: \"object\",\n additionalProperties: false,\n required: [\"probabilities\"],\n properties: {\n probabilities: {\n type: \"object\",\n additionalProperties: false,\n required: labels,\n properties,\n description: \"One probability per option. Must sum to 1.\",\n },\n },\n }\n}\n\nexport interface RenderedState {\n text: string\n truncated: boolean\n}\n\n/** Serialize state and clip it to the backend's budget. Truncation is\n * reported rather than hidden, because a calibration report has to be able\n * to exclude rows where the model never saw the whole input. */\nexport function renderState(state: StateValue, maxChars: number): RenderedState {\n const text = typeof state === \"string\" ? state : JSON.stringify(state, null, 2) ?? \"null\"\n if (text.length <= maxChars) return { text, truncated: false }\n return {\n text: `${text.slice(0, maxChars)}\\n…[truncated: ${text.length - maxChars} more characters]`,\n truncated: true,\n }\n}\n\n/** The human-readable question block. Providers without strict structured\n * output only get this, so it has to carry the full contract on its own. */\nexport function renderQuestions(questions: Questions): string {\n const blocks: string[] = []\n for (const [name, question] of Object.entries(questions)) {\n const lines: string[] = [`### ${name}`]\n if (question.instructions) lines.push(question.instructions)\n\n if (question.type === \"noul\") {\n lines.push(\"Type: yes/no. Report `noul` — the probability the answer is yes.\")\n if (question.criteria?.true) lines.push(`- yes means: ${question.criteria.true}`)\n if (question.criteria?.false) lines.push(`- no means: ${question.criteria.false}`)\n } else if (question.type === \"choice\") {\n lines.push(\"Type: choice. Report `probabilities` over exactly these options:\")\n for (const [label, description] of Object.entries(question.criteria)) {\n lines.push(description ? `- ${label}: ${description}` : `- ${label}`)\n }\n } else {\n lines.push(\"Type: score. Report `probabilities` over exactly these levels:\")\n question.criteria.forEach((description, i) => {\n lines.push(`- \"${i}\": ${description}`)\n })\n }\n blocks.push(lines.join(\"\\n\"))\n }\n return blocks.join(\"\\n\\n\")\n}\n\n/** The user turn. Used verbatim in tool mode and in text mode; text mode\n * appends the shape instructions, since it has no schema to lean on. */\nexport function renderUserPrompt(\n state: StateValue,\n questions: Questions,\n maxChars: number,\n): { text: string; truncated: boolean } {\n const rendered = renderState(state, maxChars)\n const text = [\n \"## State\",\n rendered.text,\n \"\",\n \"## Questions\",\n renderQuestions(questions),\n ].join(\"\\n\")\n return { text, truncated: rendered.truncated }\n}\n\n/** Appended in text mode only, where nothing enforces the shape. */\nexport function renderShapeInstructions(questions: Questions): string {\n const example: Record<string, unknown> = {}\n for (const [name, question] of Object.entries(questions)) {\n if (question.type === \"noul\") {\n example[name] = { noul: 0.5 }\n continue\n }\n const probabilities: Record<string, number> = {}\n const labels = labelsOf(question)\n for (const label of labels) probabilities[label] = Number((1 / labels.length).toFixed(4))\n example[name] = { probabilities }\n }\n return [\n \"\",\n \"## Output\",\n \"Reply with this JSON object and nothing else:\",\n JSON.stringify({ answers: example }, null, 2),\n ].join(\"\\n\")\n}\n","import type { GradedRow } from \"./store\"\n\n// Recalibration with covariates.\n//\n// `fitTemperature` assumes one miscalibration curve for a whole seat. That\n// is usually false. A model can be well calibrated answering about a cron\n// runner and badly overconfident answering about a chat agent, and a single\n// scalar averages the two into something wrong for both.\n//\n// This fits p(correct | confidence, covariates) with logistic regression on\n// the logit of the reported confidence plus one-hot covariates. With no\n// covariates it reduces to Platt scaling, which is already a strict\n// generalisation of temperature scaling on a binary outcome — so the\n// covariate model can only help in-sample.\n//\n// Which is exactly why nothing here reports an in-sample number. More\n// parameters always fit the training data better; the only question worth\n// asking is whether they predict BETTER OUT OF SAMPLE. `compareCalibrators`\n// runs k-fold cross-validation over raw, temperature and covariate models\n// and reports held-out log loss for each. If the covariate model does not\n// win there, it does not get used.\n\n/** Target: was the seat's prediction right? Calibrating `confidence` this\n * way covers Choice, Score and Noul uniformly, because answerView already\n * projects all three to (predicted, confidence). */\nfunction outcome(row: GradedRow): number {\n return row.predicted === row.truth ? 1 : 0\n}\n\nconst EPS = 1e-6\n\nexport function logit(p: number): number {\n const clamped = Math.min(1 - EPS, Math.max(EPS, p))\n return Math.log(clamped / (1 - clamped))\n}\n\nexport function sigmoid(z: number): number {\n if (z >= 0) return 1 / (1 + Math.exp(-z))\n const e = Math.exp(z)\n return e / (1 + e)\n}\n\nexport interface DesignSpec {\n /** Covariate names, read from row.features first and then from the row's\n * own fields (question, model, backend, structureMode, predicted). */\n covariates: string[]\n /** Levels appearing fewer than this many times are folded into the\n * reference level. Rare levels are noise with a free parameter attached. */\n minLevelCount?: number\n}\n\nexport interface Design {\n X: number[][]\n y: number[]\n names: string[]\n /** level -> column, per covariate. The dropped reference level is absent. */\n levels: Record<string, string[]>\n}\n\nfunction covariateValue(row: GradedRow, name: string): string {\n const fromFeatures = row.features?.[name]\n if (fromFeatures !== undefined && fromFeatures !== null) return String(fromFeatures)\n const own = (row as unknown as Record<string, unknown>)[name]\n return own === undefined || own === null ? \"(none)\" : String(own)\n}\n\nexport function buildDesign(rows: GradedRow[], spec: DesignSpec): Design {\n const minCount = spec.minLevelCount ?? 5\n const levels: Record<string, string[]> = {}\n\n for (const name of spec.covariates) {\n const counts = new Map<string, number>()\n for (const row of rows) {\n const value = covariateValue(row, name)\n counts.set(value, (counts.get(value) ?? 0) + 1)\n }\n const kept = [...counts.entries()]\n .filter(([, n]) => n >= minCount)\n .map(([value]) => value)\n .sort()\n // Drop one level as the reference, or the design is collinear with\n // the intercept and the fit is not identified.\n levels[name] = kept.slice(1)\n }\n\n const names = [\"intercept\", \"logitConfidence\"]\n for (const name of spec.covariates) for (const level of levels[name]) names.push(`${name}=${level}`)\n\n const X: number[][] = []\n const y: number[] = []\n for (const row of rows) {\n const features = [1, logit(row.confidence)]\n for (const name of spec.covariates) {\n const value = covariateValue(row, name)\n for (const level of levels[name]) features.push(value === level ? 1 : 0)\n }\n X.push(features)\n y.push(outcome(row))\n }\n\n return { X, y, names, levels }\n}\n\n/**\n * Logistic regression by iteratively reweighted least squares.\n *\n * Newton's method rather than gradient descent: it converges in a handful\n * of iterations with no learning rate to guess at, which matters because\n * this runs unattended on a few hundred rows. The ridge term is not\n * optional — with one-hot covariates and a small sample, a level that\n * happens to be perfectly separable sends its weight to infinity.\n */\nexport function fitLogistic(\n X: number[][],\n y: number[],\n opts: { l2?: number; iterations?: number } = {},\n): number[] {\n const l2 = opts.l2 ?? 1\n const iterations = opts.iterations ?? 25\n const n = X.length\n const d = n > 0 ? X[0].length : 0\n let w = new Array(d).fill(0)\n if (n === 0 || d === 0) return w\n\n for (let iter = 0; iter < iterations; iter++) {\n // Hessian (X'WX + lambda I) and gradient X'(y - p) - lambda w\n const H: number[][] = Array.from({ length: d }, () => new Array(d).fill(0))\n const g = new Array(d).fill(0)\n\n for (let i = 0; i < n; i++) {\n let z = 0\n for (let j = 0; j < d; j++) z += w[j] * X[i][j]\n const p = sigmoid(z)\n const weight = Math.max(p * (1 - p), 1e-8)\n const residual = y[i] - p\n for (let j = 0; j < d; j++) {\n g[j] += residual * X[i][j]\n for (let k = j; k < d; k++) H[j][k] += weight * X[i][j] * X[i][k]\n }\n }\n for (let j = 0; j < d; j++) {\n // Never penalise the intercept: shrinking it biases the base rate.\n if (j > 0) {\n H[j][j] += l2\n g[j] -= l2 * w[j]\n }\n for (let k = 0; k < j; k++) H[j][k] = H[k][j]\n }\n\n const step = solve(H, g)\n if (!step) break\n let delta = 0\n for (let j = 0; j < d; j++) {\n w[j] += step[j]\n delta += Math.abs(step[j])\n }\n if (delta < 1e-8) break\n }\n return w\n}\n\n/** Gaussian elimination with partial pivoting. Returns null on a singular\n * system, which the caller treats as \"stop iterating\" rather than a crash. */\nfunction solve(A: number[][], b: number[]): number[] | null {\n const n = b.length\n const M = A.map((row, i) => [...row, b[i]])\n\n for (let col = 0; col < n; col++) {\n let pivot = col\n for (let r = col + 1; r < n; r++) if (Math.abs(M[r][col]) > Math.abs(M[pivot][col])) pivot = r\n if (Math.abs(M[pivot][col]) < 1e-12) return null\n ;[M[col], M[pivot]] = [M[pivot], M[col]]\n\n for (let r = 0; r < n; r++) {\n if (r === col) continue\n const factor = M[r][col] / M[col][col]\n if (factor === 0) continue\n for (let c = col; c <= n; c++) M[r][c] -= factor * M[col][c]\n }\n }\n // Full Gauss-Jordan above, so the matrix is diagonal and back-substitution\n // is a single division per row.\n const x = new Array(n)\n for (let i = 0; i < n; i++) x[i] = M[i][n] / M[i][i]\n return x\n}\n\nexport interface RecalibrationModel {\n weights: number[]\n names: string[]\n levels: Record<string, string[]>\n covariates: string[]\n n: number\n}\n\nexport function fitRecalibrator(rows: GradedRow[], spec: DesignSpec): RecalibrationModel {\n const labeled = rows.filter((r) => r.truth !== undefined)\n const design = buildDesign(labeled, spec)\n return {\n weights: fitLogistic(design.X, design.y),\n names: design.names,\n levels: design.levels,\n covariates: spec.covariates,\n n: labeled.length,\n }\n}\n\n/** Corrected probability that this row's prediction is right. */\nexport function applyRecalibrator(model: RecalibrationModel, row: GradedRow): number {\n let z = model.weights[0] + model.weights[1] * logit(row.confidence)\n let col = 2\n for (const name of model.covariates) {\n const value = covariateValue(row, name)\n for (const level of model.levels[name] ?? []) {\n if (value === level) z += model.weights[col]\n col++\n }\n }\n return sigmoid(z)\n}\n\n// ---------------------------------------------------------------------------\n// Honest comparison\n// ---------------------------------------------------------------------------\n\nexport interface CalibratorScore {\n name: string\n /** Held-out log loss, averaged over folds. Lower is better. */\n logLoss: number\n /** Held-out Brier score. */\n brier: number\n}\n\nexport interface CalibratorComparison {\n insufficient: boolean\n n: number\n minN: number\n folds: number\n scores: CalibratorScore[]\n /** The winner on held-out log loss. */\n best?: string\n /** Fitted on everything, for use once `best` says it is worth using. */\n model?: RecalibrationModel\n}\n\n/**\n * k-fold cross-validated comparison of raw confidence, a single global\n * temperature, and the covariate model.\n *\n * The whole point of this function is that it can say \"the covariates are\n * not worth it\". More parameters always look better in-sample, so an\n * in-sample improvement is not evidence of anything.\n */\nexport function compareCalibrators(\n rows: GradedRow[],\n spec: DesignSpec,\n opts: { folds?: number; minN?: number; rng?: () => number } = {},\n): CalibratorComparison {\n const folds = opts.folds ?? 5\n const minN = opts.minN ?? 100\n const labeled = rows.filter((r) => r.truth !== undefined)\n const base = { insufficient: labeled.length < minN, n: labeled.length, minN, folds, scores: [] }\n if (base.insufficient) return base\n\n const rng = opts.rng ?? Math.random\n const shuffled = [...labeled]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n\n const predictors: Record<string, number[]> = { raw: [], temperature: [], covariate: [] }\n const actuals: number[] = []\n\n for (let fold = 0; fold < folds; fold++) {\n const test = shuffled.filter((_, i) => i % folds === fold)\n const train = shuffled.filter((_, i) => i % folds !== fold)\n if (train.length === 0 || test.length === 0) continue\n\n const flat = fitRecalibrator(train, { ...spec, covariates: [] })\n const full = fitRecalibrator(train, spec)\n\n for (const row of test) {\n actuals.push(outcome(row))\n predictors.raw.push(row.confidence)\n predictors.temperature.push(applyRecalibrator(flat, row))\n predictors.covariate.push(applyRecalibrator(full, row))\n }\n }\n\n const scores = Object.entries(predictors).map(([name, predicted]) => ({\n name,\n logLoss: binaryLogLoss(predicted, actuals),\n brier: binaryBrier(predicted, actuals),\n }))\n const best = [...scores].sort((a, b) => a.logLoss - b.logLoss)[0]?.name\n\n return {\n ...base,\n scores,\n best,\n model: best === \"covariate\" ? fitRecalibrator(labeled, spec) : fitRecalibrator(labeled, { ...spec, covariates: [] }),\n }\n}\n\nfunction binaryLogLoss(predicted: number[], actual: number[]): number {\n if (predicted.length === 0) return 0\n let sum = 0\n for (let i = 0; i < predicted.length; i++) {\n const p = Math.min(1 - EPS, Math.max(EPS, predicted[i]))\n sum += actual[i] === 1 ? -Math.log(p) : -Math.log(1 - p)\n }\n return sum / predicted.length\n}\n\nfunction binaryBrier(predicted: number[], actual: number[]): number {\n if (predicted.length === 0) return 0\n let sum = 0\n for (let i = 0; i < predicted.length; i++) sum += (predicted[i] - actual[i]) ** 2\n return sum / predicted.length\n}\n","import { randomBytes } from \"crypto\"\nimport { getDecisionBackend } from \"./backend\"\nimport { answerView } from \"./store\"\nimport type { Questions, StateValue } from \"./types\"\n\n// Does the question hold still?\n//\n// Everything else in this module measures the BACKEND — is its confidence\n// calibrated, does it beat the incumbent. Nothing measured the QUESTIONS,\n// and a badly-phrased question does not announce itself: it produces\n// plausible numbers that happen to move every time you ask.\n//\n// TypeSafe's own consistency cookbook is the method. Ask the same rubric\n// over the same state N times, with a throwaway `uid` in the state so each\n// repeat is an independent draw rather than a cached one, and look at the\n// spread. Their published figure for jev across a 14-question rubric is a\n// mean per-question standard deviation of 0.0102; sampled LLM answers move\n// far more, and on the judgment calls disagree with themselves at\n// temperature 0.\n//\n// What this is for, concretely: a compound question — \"did it leave\n// anything unresolved, surprising, risky, or needing a decision\" — is four\n// questions sharing a name, and it scatters. An atomic one holds. This\n// turns that from an opinion about phrasing into a number, BEFORE any\n// ground truth exists to calibrate against. It is the cheapest quality\n// signal available: fifteen samples of a two-question seat costs about a\n// twentieth of a cent.\n//\n// It does not tell you the answer is RIGHT. A question can be perfectly\n// stable and perfectly wrong. Stability is a precondition for calibration\n// being meaningful, not a substitute for measuring it.\n\n/** The vendor's review band: act below LOW or above HIGH, escalate between. */\nexport const UNCERTAINTY_LOW = 0.3\nexport const UNCERTAINTY_HIGH = 0.7\n\nexport type BandDecision = \"no\" | \"uncertain\" | \"yes\"\n\nexport function band(\n probability: number,\n low = UNCERTAINTY_LOW,\n high = UNCERTAINTY_HIGH,\n): BandDecision {\n if (probability <= low) return \"no\"\n if (probability > high) return \"yes\"\n return \"uncertain\"\n}\n\nexport interface QuestionConsistency {\n question: string\n type: string\n n: number\n mean: number\n /** Population standard deviation of the per-sample probability. For a\n * Noul that probability is P(true); for Choice and Score it is the\n * winning label's share. */\n stdev: number\n min: number\n max: number\n /** Distinct predicted labels across the samples. More than one means the\n * seat's own answer flips between identical calls. */\n distinctAnswers: string[]\n /** Distinct band decisions. Two or more means the samples straddle a\n * threshold, so which action fires is decided by luck. */\n bands: BandDecision[]\n /** True when the samples disagree about what to DO, not merely by how\n * much. This is the finding that matters. */\n crossesThreshold: boolean\n samples: number[]\n}\n\nexport interface ConsistencyReport {\n backend: string\n model: string\n samples: number\n /** Mean of the per-question standard deviations — TypeSafe publishes\n * 0.0102 for jev over a 14-question rubric, as a reference point. */\n meanStdev: number\n unstable: string[]\n questions: QuestionConsistency[]\n usage: { inputTokens: number; outputTokens: number }\n totalMs: number\n}\n\n/**\n * Resample one state N times and report the spread per question.\n *\n * The `uid` is not decoration. Without a field that changes per call the\n * backend may serve a cached answer, and a cache returns a standard\n * deviation of zero for any question, however badly phrased.\n */\nexport async function measureConsistency(\n backendName: string,\n state: StateValue,\n questions: Questions,\n opts: { samples?: number; model?: string; low?: number; high?: number } = {},\n): Promise<ConsistencyReport> {\n const samples = Math.max(2, opts.samples ?? 15)\n const backend = getDecisionBackend(backendName)\n const started = Date.now()\n\n const runs = await Promise.all(\n Array.from({ length: samples }, (_, i) =>\n backend.decide({\n state: withUid(state, `${i}:${randomBytes(4).toString(\"hex\")}`),\n questions,\n model: opts.model,\n }),\n ),\n )\n\n const names = Object.keys(questions)\n const usage = runs.reduce(\n (acc, r) => ({\n inputTokens: acc.inputTokens + r.usage.inputTokens,\n outputTokens: acc.outputTokens + r.usage.outputTokens,\n }),\n { inputTokens: 0, outputTokens: 0 },\n )\n\n const perQuestion = names.map((name) => {\n const views = runs.map((r) => answerView((r.answers as Record<string, any>)[name]))\n // The right scalar differs by type, and getting it wrong is silent.\n //\n // For a Noul the quantity is P(true) — that is what the answer IS, and\n // what the 0.30/0.70 band is defined on. Using the winning label's\n // probability instead reports 1-p whenever the answer is \"no\", so a\n // P(true) of 0.26 displays as 0.74 and every noul lands above 0.5,\n // which makes the band useless. For Choice and Score there is no\n // single \"true\" outcome, so the winning label's probability is the\n // comparable scalar.\n const probs = views.map((v, i) => {\n const answer = (runs[i].answers as Record<string, any>)[name]\n return answer.type === \"noul\" ? answer.noul : v.topProb\n })\n const answers = [...new Set(views.map((v) => v.predicted))]\n const bands = [...new Set(probs.map((p) => band(p, opts.low, opts.high)))]\n const m = mean(probs)\n return {\n question: name,\n type: (runs[0].answers as Record<string, any>)[name].type,\n n: samples,\n mean: m,\n stdev: stdev(probs, m),\n min: Math.min(...probs),\n max: Math.max(...probs),\n distinctAnswers: answers,\n bands,\n crossesThreshold: answers.length > 1 || bands.length > 1,\n samples: probs,\n }\n })\n\n return {\n backend: backendName,\n model: runs[0].model,\n samples,\n meanStdev: mean(perQuestion.map((q) => q.stdev)),\n unstable: perQuestion.filter((q) => q.crossesThreshold).map((q) => q.question),\n questions: perQuestion,\n usage,\n totalMs: Date.now() - started,\n }\n}\n\n/** Add a throwaway field without disturbing a string state's meaning. */\nexport function withUid(state: StateValue, uid: string): StateValue {\n if (state !== null && typeof state === \"object\" && !Array.isArray(state)) {\n return { ...(state as Record<string, StateValue>), uid }\n }\n return { uid, state }\n}\n\nfunction mean(values: number[]): number {\n if (values.length === 0) return 0\n return values.reduce((a, b) => a + b, 0) / values.length\n}\n\nfunction stdev(values: number[], m: number): number {\n if (values.length < 2) return 0\n return Math.sqrt(values.reduce((sum, v) => sum + (v - m) ** 2, 0) / values.length)\n}\n","import type {\n AnswerMode,\n DecisionBackend,\n DecisionBackendCapabilities,\n DecisionRequest,\n DecisionResponse,\n} from \"../backend\"\nimport { finalizeAnswer } from \"../normalize\"\nimport { labelsOf, validateQuestions } from \"../questions\"\nimport type { AnswersFor, AnyAnswer, Questions, RawAnswer } from \"../types\"\n\n// A backend with no model behind it.\n//\n// Two jobs: let the seat, the store and the calibration maths be tested\n// without a network or an API key, and give `agentx decisions` something to\n// smoke against. Answers are deterministic in (state, question, label), so\n// the same input always produces the same distribution and a test can assert\n// on exact numbers.\n\nexport interface MockBackendOptions {\n /** Raw answers keyed by question name. Anything not scripted is derived\n * deterministically from the state. */\n answers?: Record<string, RawAnswer>\n /** Rejects with this instead of answering. For exercising the seat's\n * fail-open path. */\n fail?: Error\n latencyMs?: number\n}\n\nconst capabilities: DecisionBackendCapabilities = {\n probabilitySource: \"synthetic\",\n calibratedProbabilities: false,\n maxChoiceOptions: 255,\n maxStateChars: 24_000,\n parallelQuestions: true,\n images: false,\n}\n\nexport function createMockDecisionBackend(opts: MockBackendOptions = {}): DecisionBackend {\n return {\n name: \"mock\",\n capabilities,\n async decide<Q extends Questions>(\n request: DecisionRequest<Q>,\n ): Promise<DecisionResponse<Q>> {\n if (opts.fail) throw opts.fail\n validateQuestions(request.questions)\n\n const stateText = JSON.stringify(request.state ?? null)\n const answers: Record<string, AnyAnswer> = {}\n let repaired = false\n\n for (const [name, question] of Object.entries(request.questions)) {\n const raw = opts.answers?.[name] ?? syntheticRaw(stateText, name, question)\n const result = finalizeAnswer(question, raw)\n answers[name] = result.answer\n if (result.repaired) repaired = true\n }\n\n const answerMode: AnswerMode = \"probabilities\"\n return {\n model: request.model ?? \"mock\",\n answers: answers as AnswersFor<Q>,\n usage: { inputTokens: stateText.length, outputTokens: 0 },\n meta: {\n backend: \"mock\",\n structureMode: \"mock\",\n answerMode,\n repaired,\n retries: 0,\n stateTruncated: false,\n latencyMs: opts.latencyMs ?? 0,\n },\n }\n },\n }\n}\n\nfunction syntheticRaw(\n stateText: string,\n name: string,\n question: Questions[string],\n): RawAnswer {\n if (question.type === \"noul\") {\n return { noul: unitHash(`${stateText}|${name}|noul`) }\n }\n const labels = labelsOf(question)\n const weights = labels.map((label) => unitHash(`${stateText}|${name}|${label}`) + 1e-3)\n const total = weights.reduce((a, b) => a + b, 0)\n const probabilities: Record<string, number> = {}\n labels.forEach((label, i) => {\n probabilities[label] = weights[i] / total\n })\n return { probabilities }\n}\n\n/** FNV-1a, mapped to [0, 1). Stable across runs and platforms, which is the\n * only property that matters here. */\nfunction unitHash(input: string): number {\n let h = 0x811c9dc5\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i)\n h = Math.imul(h, 0x01000193) >>> 0\n }\n return h / 0x100000000\n}\n","// --- Provider capability matrix ---\n// Used to warn users when an agent's config requires features\n// the selected provider doesn't support.\n\nexport interface ProviderCapabilities {\n streaming: boolean\n tools: boolean\n vision: boolean\n thinking: boolean\n maxContext: number\n /** Can be made to return a value matching a schema, rather than prose the\n * caller has to parse. False does NOT mean \"no structured output\" — it\n * means no *guarantee*, so callers fall back to JSON-in-text plus a\n * correction round. The claude-code CLI on an OAuth credential is the\n * case that matters: ClaudeCodeProvider.generateRaw() throws there. */\n structuredOutput: boolean\n /** Exposes per-token logprobs. The only source of a probability that is\n * the model's own posterior rather than a number it wrote out. */\n logprobs: boolean\n}\n\nexport const PROVIDER_CAPABILITIES: Record<string, ProviderCapabilities> = {\n // structuredOutput is false here on purpose. ClaudeCodeProvider can force\n // a tool on an api-key credential but throws on OAuth (\"generateRaw() not\n // available for OAuth/CLI mode\"), and this matrix is static — it cannot\n // see which credential an operator has. False is the safe default: a\n // caller that believes a guarantee it doesn't have crashes on the\n // critical path, whereas a caller that falls back to JSON-in-text only\n // spends a correction round.\n \"claude-code\": {\n streaming: true,\n tools: true,\n vision: true,\n thinking: true,\n maxContext: 1_000_000,\n structuredOutput: false,\n logprobs: false,\n },\n claude: {\n streaming: true,\n tools: true,\n vision: true,\n thinking: true,\n maxContext: 1_000_000,\n structuredOutput: true,\n logprobs: false,\n },\n openai: {\n streaming: true,\n tools: true,\n vision: true,\n thinking: false,\n maxContext: 128_000,\n structuredOutput: true,\n logprobs: true,\n },\n deepseek: {\n streaming: true,\n tools: true,\n vision: false,\n thinking: true,\n maxContext: 128_000,\n structuredOutput: true,\n logprobs: true,\n },\n ollama: {\n streaming: true,\n tools: false,\n vision: false,\n thinking: false,\n maxContext: 32_000,\n structuredOutput: false,\n logprobs: false,\n },\n demo: {\n streaming: true,\n tools: false,\n vision: false,\n thinking: false,\n maxContext: 8_000,\n structuredOutput: false,\n logprobs: false,\n },\n // An OpenAI-compatible endpoint behind providers.<name>.baseUrl. What it\n // supports depends entirely on what is running there, so claim nothing.\n custom: {\n streaming: true,\n tools: false,\n vision: false,\n thinking: false,\n maxContext: 32_000,\n structuredOutput: false,\n logprobs: false,\n },\n}\n\n/**\n * Check provider capabilities and return warnings for missing features.\n */\nexport function checkCapabilities(\n providerName: string,\n requiredFeatures?: string[],\n): string[] {\n const caps = PROVIDER_CAPABILITIES[providerName]\n if (!caps) {\n return [`Unknown provider \"${providerName}\" — capabilities unknown`]\n }\n\n if (!requiredFeatures?.length) return []\n\n const warnings: string[] = []\n const missing: string[] = []\n\n for (const feature of requiredFeatures) {\n if (feature in caps && !(caps as any)[feature]) {\n missing.push(feature)\n }\n }\n\n if (missing.length) {\n warnings.push(\n `Provider \"${providerName}\" lacks: ${missing.join(\", \")}. Some features will be degraded.`,\n )\n }\n\n return warnings\n}\n","import { createProvider, type ProviderName } from \"@/agent/providers\"\nimport { PROVIDER_CAPABILITIES } from \"@/agent/providers/capabilities\"\nimport type { AgentProvider } from \"@/agent/providers/types\"\nimport { extractJson } from \"@/utils/extract-json\"\nimport type {\n AnswerMode,\n DecisionBackend,\n DecisionBackendCapabilities,\n DecisionRequest,\n DecisionResponse,\n StructureMode,\n} from \"../backend\"\nimport { finalizeAnswer } from \"../normalize\"\nimport {\n SYSTEM_PROMPT,\n TOOL_NAME,\n renderShapeInstructions,\n renderUserPrompt,\n toolInputSchema,\n} from \"../prompt\"\nimport { validateQuestions } from \"../questions\"\nimport { describeIssues, rawAnswersSchema } from \"../schema\"\nimport type { AnswersFor, AnyAnswer, Questions, RawAnswer, RawAnswers } from \"../types\"\nimport { z } from \"zod\"\n\n// The System One contract, served by an ordinary chat model.\n//\n// READ THIS BEFORE TRUSTING A NUMBER THIS BACKEND RETURNS.\n//\n// In \"tool\" mode the model is forced to emit a value matching a schema, so\n// the answer is guaranteed well-formed. In \"text\" mode we ask nicely and\n// repair. Neither produces a CALIBRATED probability. A number the model\n// wrote into its answer is a number it chose; it is not a measurement of\n// its own uncertainty, and models tuned on human preference are\n// systematically overconfident about it — which is the entire premise of\n// the decision-model vendors.\n//\n// What this backend genuinely buys:\n// - one call for N questions against one shared state\n// - a guaranteed shape, so no call site hand-parses prose\n// - an ordinal signal that is usually better than nothing\n// - rows in the shadow store, which is where calibration actually\n// comes from: a temperature fitted on a few hundred of your own\n// labeled decisions beats any number the model hands you\n//\n// A backend reporting `calibratedProbabilities: false` is not a defect to\n// fix later. It is the honest value, and the harness exists to measure how\n// much it costs.\n\nconst DEFAULT_PROVIDER: ProviderName = \"claude-code\"\nconst DEFAULT_MODEL = \"claude-haiku-4-5-20251001\"\nconst DEFAULT_MAX_STATE_CHARS = 24_000\nconst DEFAULT_MAX_TOKENS = 2_000\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nexport interface LocalBackendOptions {\n provider?: ProviderName\n model?: string\n /** \"auto\" resolves to \"tool\" when the provider guarantees structured\n * output and \"text\" otherwise. Never resolves to \"logprobs\": that mode\n * costs one call per question, so it has to be asked for explicitly. */\n structureMode?: \"auto\" | \"tool\" | \"text\"\n answerMode?: AnswerMode\n normalizeProbabilities?: boolean\n nRetryMalformedStructure?: number\n temperature?: number\n maxStateChars?: number\n maxTokens?: number\n timeoutMs?: number\n /** Injected in tests. Production resolves through createProvider. */\n providerFactory?: () => AgentProvider\n}\n\nexport function createLocalDecisionBackend(opts: LocalBackendOptions = {}): DecisionBackend {\n if (opts.answerMode && opts.answerMode !== \"probabilities\") {\n throw new Error(\n `local decision backend supports answerMode \"probabilities\" only (got \"${opts.answerMode}\")`,\n )\n }\n\n const maxStateChars = opts.maxStateChars ?? DEFAULT_MAX_STATE_CHARS\n const capabilities: DecisionBackendCapabilities = {\n probabilitySource: \"verbalized\",\n calibratedProbabilities: false,\n maxChoiceOptions: 255,\n maxStateChars,\n parallelQuestions: true,\n images: false,\n }\n\n let provider: AgentProvider | null = null\n const resolveProvider = (): AgentProvider => {\n if (!provider) {\n provider = opts.providerFactory\n ? opts.providerFactory()\n : createProvider(opts.provider ?? DEFAULT_PROVIDER)\n }\n return provider\n }\n\n return {\n name: \"local\",\n capabilities,\n async decide<Q extends Questions>(\n request: DecisionRequest<Q>,\n ): Promise<DecisionResponse<Q>> {\n validateQuestions(request.questions)\n\n const started = Date.now()\n const model = request.model ?? opts.model ?? DEFAULT_MODEL\n const agent = resolveProvider()\n const structureMode = resolveStructureMode(opts.structureMode ?? \"auto\", agent)\n const prompt = renderUserPrompt(request.state, request.questions, maxStateChars)\n const schema = rawAnswersSchema(request.questions)\n const maxRetries = opts.nRetryMalformedStructure ?? 1\n\n const { signal, dispose } = deadline(\n request.abortSignal,\n request.timeoutMs ?? opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n )\n\n let usage = { inputTokens: 0, outputTokens: 0 }\n let correction: string | null = null\n let retries = 0\n let raw: RawAnswers | null = null\n let lastError = \"no attempt was made\"\n\n try {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) retries++\n\n const call =\n structureMode === \"tool\"\n ? callWithForcedTool(agent, request.questions, prompt.text, correction, {\n model,\n maxTokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,\n temperature: opts.temperature ?? 0,\n abortSignal: signal,\n })\n : callWithText(agent, request.questions, prompt.text, correction, {\n model,\n maxTokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,\n temperature: opts.temperature ?? 0,\n abortSignal: signal,\n })\n\n const result = await call\n usage = {\n inputTokens: usage.inputTokens + result.usage.inputTokens,\n outputTokens: usage.outputTokens + result.usage.outputTokens,\n }\n\n const parsed = schema.safeParse(result.answers)\n if (parsed.success) {\n raw = parsed.data as RawAnswers\n break\n }\n lastError = describeIssues(parsed.error as z.ZodError)\n correction = [\n \"Your previous reply did not match the required structure:\",\n lastError,\n \"\",\n \"Reply again with the corrected structure only.\",\n ].join(\"\\n\")\n }\n\n if (!raw) {\n throw new Error(\n `local decision backend could not get a valid answer after ${retries + 1} attempt(s): ${lastError}`,\n )\n }\n\n const answers: Record<string, AnyAnswer> = {}\n let repaired = false\n for (const [name, question] of Object.entries(request.questions)) {\n const result = finalizeAnswer(question, raw[name] as RawAnswer, {\n normalize: opts.normalizeProbabilities ?? true,\n })\n answers[name] = result.answer\n if (result.repaired) repaired = true\n }\n\n return {\n model,\n answers: answers as AnswersFor<Q>,\n usage,\n meta: {\n backend: \"local\",\n structureMode,\n answerMode: \"probabilities\",\n repaired,\n retries,\n stateTruncated: prompt.truncated,\n latencyMs: Date.now() - started,\n },\n }\n } finally {\n dispose()\n }\n },\n }\n}\n\ninterface AttemptResult {\n answers: unknown\n usage: { inputTokens: number; outputTokens: number }\n}\n\ninterface CallOptions {\n model: string\n maxTokens: number\n temperature: number\n abortSignal: AbortSignal\n}\n\nasync function callWithForcedTool(\n agent: AgentProvider,\n questions: Questions,\n userPrompt: string,\n correction: string | null,\n options: CallOptions,\n): Promise<AttemptResult> {\n if (!agent.generateRaw) {\n throw new Error(`provider \"${agent.name}\" has no generateRaw(); use structureMode \"text\"`)\n }\n\n const messages = [\n { role: \"user\" as const, content: userPrompt },\n ...(correction ? [{ role: \"user\" as const, content: correction }] : []),\n ]\n\n const result = await agent.generateRaw(\n messages,\n SYSTEM_PROMPT,\n [\n {\n name: TOOL_NAME,\n description: \"Report a probability distribution for every question.\",\n input_schema: toolInputSchema(questions),\n },\n ],\n { ...options, toolChoice: { type: \"tool\", name: TOOL_NAME } },\n )\n\n const block = result.content.find((b) => b.type === \"tool_use\" && b.name === TOOL_NAME)\n const input = block && block.type === \"tool_use\" ? block.input : null\n\n return {\n answers: unwrapAnswers(input),\n usage: {\n inputTokens: result.usage.input_tokens,\n outputTokens: result.usage.output_tokens,\n },\n }\n}\n\nasync function callWithText(\n agent: AgentProvider,\n questions: Questions,\n userPrompt: string,\n correction: string | null,\n options: CallOptions,\n): Promise<AttemptResult> {\n const content = [userPrompt, renderShapeInstructions(questions)].join(\"\\n\")\n const messages = [\n { role: \"system\" as const, content: SYSTEM_PROMPT },\n { role: \"user\" as const, content },\n ...(correction ? [{ role: \"user\" as const, content: correction }] : []),\n ]\n\n const result = await agent.generate(messages, options)\n return {\n answers: unwrapAnswers(extractJson(result.content ?? \"\")),\n usage: { inputTokens: 0, outputTokens: result.tokensUsed ?? 0 },\n }\n}\n\n/** Accept both `{ answers: {...} }` and a bare answers map. Models drop the\n * wrapper often enough that rejecting it would spend correction rounds on\n * a difference that carries no information. */\nfunction unwrapAnswers(value: unknown): unknown {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n const wrapper = value as { answers?: unknown }\n if (wrapper.answers && typeof wrapper.answers === \"object\") return wrapper.answers\n }\n return value\n}\n\nfunction resolveStructureMode(\n requested: \"auto\" | \"tool\" | \"text\",\n agent: AgentProvider,\n): StructureMode {\n if (requested !== \"auto\") return requested\n if (!agent.generateRaw) return \"text\"\n // Resolved name, not the configured one: createProvider(\"claude-code\")\n // falls through to whatever the stored auth config selected.\n return PROVIDER_CAPABILITIES[agent.name]?.structuredOutput ? \"tool\" : \"text\"\n}\n\n/** One signal that fires on either the caller's cancel or our own timeout.\n * Hand-rolled rather than AbortSignal.any so this does not depend on the\n * lib target's typings. */\nfunction deadline(\n caller: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; dispose: () => void } {\n const controller = new AbortController()\n const timer = setTimeout(\n () => controller.abort(new Error(`decision timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n const onCallerAbort = () => controller.abort(caller?.reason)\n\n if (caller) {\n if (caller.aborted) onCallerAbort()\n else caller.addEventListener(\"abort\", onCallerAbort, { once: true })\n }\n\n return {\n signal: controller.signal,\n dispose: () => {\n clearTimeout(timer)\n caller?.removeEventListener(\"abort\", onCallerAbort)\n },\n }\n}\n","import type {\n DecisionBackend,\n DecisionBackendCapabilities,\n DecisionRequest,\n DecisionResponse,\n ProbabilitySource,\n} from \"../backend\"\nimport { finalizeAnswer } from \"../normalize\"\nimport { validateQuestions } from \"../questions\"\nimport { rawAnswersSchema, describeIssues } from \"../schema\"\nimport type { AnswersFor, AnyAnswer, Questions, RawAnswer } from \"../types\"\nimport { renderState } from \"../prompt\"\nimport type { z } from \"zod\"\nimport { readFileSync } from \"fs\"\nimport { homedir } from \"os\"\nimport { join } from \"path\"\n\n// A backend for a simple-jev server (featherless-ai/simple-jev).\n//\n// Why this exists alongside the local one: simple-jev prefills the prompt\n// and reads the model's next-token logits over the allowed answer labels,\n// instead of asking a chat model to write a probability into its reply. The\n// difference is visible in the output — a verbalized distribution comes back\n// as 0.25/0.35/0.40, a logit one as 7.48e-05/9.61e-05/0.99983. One is a\n// number a model chose; the other is its posterior over the label set.\n//\n// It is NOT a Jev replacement, and its own documentation says so: no\n// reproduction of TypeSafe's architecture or training, and its output is\n// explicitly \"not calibrated probabilities of correctness\". That last point\n// is why this reports `calibratedProbabilities: false` like everything else\n// — correctness calibration comes from a recalibrator fitted on your rows.\n//\n// Two deliberate choices about its response:\n//\n// 1. Its `confidence` is discarded and recomputed. simple-jev reports the\n// largest probability, which is not comparable across option counts:\n// uniform over 2 scores 0.5 and uniform over 10 scores 0.1, though both\n// are maximally uncertain. Our normalized-entropy definition is, and\n// rows from different backends have to be comparable in one store or\n// calibration pools apples and oranges.\n//\n// 2. No correction rounds. simple-jev builds the JSON server-side from\n// scores, so a schema mismatch is contract drift, not a model slip, and\n// re-asking cannot fix it. Failing loudly is the honest response.\n//\n// Licence note: as of 2026-09-18 the upstream repository ships no LICENSE\n// file, which under default copyright means all rights reserved. That is a\n// blocker for depending on a self-hosted deployment in production, though\n// not for talking to a server someone else is running. Resolve it before\n// this backend carries real traffic.\n\nexport interface SimpleJevOptions {\n /** Server root, e.g. http://127.0.0.1:8000/v1 */\n baseUrl?: string\n /** Route under baseUrl. simple-jev serves /classifier (and /systemone as\n * an alias); other System One-shaped endpoints differ. */\n path?: string\n /** Reported in capabilities and used to keep incomparable rows out of one\n * calibration pool. Does not change behaviour. */\n probabilitySource?: ProbabilitySource\n /** Name this backend reports. Rows are grouped by it in the store. */\n name?: string\n model?: string\n apiKeyEnv?: string\n /** Read the key from this file when the env var is unset. The daemon\n * gets its key from the launchd plist / systemd unit, but the CLI and\n * the wiki server are separate processes with a bare environment —\n * without this a seat silently fails open on every CLI call. Mirrors\n * ~/.agentx/mesh-token.txt. */\n apiKeyFile?: string\n timeoutMs?: number\n maxStateChars?: number\n /** Their Choice accepts 2-50 candidates, well under Jev's 255. */\n maxChoiceOptions?: number\n fetchImpl?: typeof fetch\n}\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:8000/v1\"\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst DEFAULT_MAX_STATE_CHARS = 6_000\nconst DEFAULT_MAX_CHOICE_OPTIONS = 50\n\nexport function createSimpleJevBackend(opts: SimpleJevOptions = {}): DecisionBackend {\n const baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\")\n const path = opts.path ?? \"/classifier\"\n const name = opts.name ?? \"simple-jev\"\n const maxStateChars = opts.maxStateChars ?? DEFAULT_MAX_STATE_CHARS\n const maxChoiceOptions = opts.maxChoiceOptions ?? DEFAULT_MAX_CHOICE_OPTIONS\n\n const capabilities: DecisionBackendCapabilities = {\n probabilitySource: opts.probabilitySource ?? \"logits\",\n calibratedProbabilities: false,\n maxChoiceOptions,\n maxStateChars,\n parallelQuestions: true,\n images: false,\n }\n\n return {\n name,\n capabilities,\n async decide<Q extends Questions>(\n request: DecisionRequest<Q>,\n ): Promise<DecisionResponse<Q>> {\n validateQuestions(request.questions)\n assertChoiceCardinality(request.questions, maxChoiceOptions)\n\n const started = Date.now()\n const model = request.model ?? opts.model\n if (!model) throw new Error(\"simple-jev backend needs a model id\")\n\n const rendered = renderState(request.state, maxStateChars)\n const doFetch = opts.fetchImpl ?? fetch\n\n const { signal, dispose } = deadline(\n request.abortSignal,\n request.timeoutMs ?? opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n )\n\n try {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" }\n const apiKey = resolveKey(opts.apiKeyEnv, opts.apiKeyFile)\n if (apiKey) headers.Authorization = `Bearer ${apiKey}`\n\n const res = await doFetch(`${baseUrl}${path}`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n model,\n state: rendered.text,\n questions: withInstructions(request.questions),\n }),\n signal,\n })\n\n if (!res.ok) {\n const detail = await res.text().catch(() => \"\")\n throw new Error(`${name} ${res.status}: ${detail.slice(0, 300)}`)\n }\n\n const body = (await res.json()) as {\n model?: string\n answers?: unknown\n usage?: { input_tokens?: number; output_tokens?: number }\n }\n\n const parsed = rawAnswersSchema(request.questions).safeParse(body.answers)\n if (!parsed.success) {\n throw new Error(\n `${name} returned a response that does not match the requested questions ` +\n `(contract drift, not a model slip — retrying will not help):\\n${describeIssues(parsed.error as z.ZodError)}`,\n )\n }\n\n const raw = parsed.data as Record<string, RawAnswer>\n const answers: Record<string, AnyAnswer> = {}\n let repaired = false\n for (const [name, question] of Object.entries(request.questions)) {\n const result = finalizeAnswer(question, raw[name])\n answers[name] = result.answer\n if (result.repaired) repaired = true\n }\n\n return {\n model: body.model ?? model,\n answers: answers as AnswersFor<Q>,\n usage: {\n inputTokens: body.usage?.input_tokens ?? 0,\n outputTokens: body.usage?.output_tokens ?? 0,\n },\n meta: {\n backend: name,\n // Mirrors probabilitySource, because structure_mode is what keeps\n // incomparable rows out of one calibration pool. Hardcoding\n // \"logprobs\" here quietly pooled Jev — a model claiming a trained\n // objective — with logit-reading over an untrained open model.\n // Different mechanisms, different miscalibration curves.\n structureMode: capabilities.probabilitySource === \"native\" ? \"native\" : \"logprobs\",\n answerMode: \"probabilities\",\n repaired,\n retries: 0,\n stateTruncated: rendered.truncated,\n latencyMs: Date.now() - started,\n },\n }\n } finally {\n dispose()\n }\n },\n }\n}\n\n/** simple-jev requires `instructions` on every question; TypeSafe's own SDK\n * marks it optional. Adapting here rather than tightening our builders is\n * deliberate: the point of a backend registry is that one question set runs\n * on every backend and can be graded against identical states. A contract\n * that only works on one server would defeat the comparison this exists for.\n *\n * The substitute is neutral and deterministic — it restates what the\n * criteria already encode, and the caller's original questions are what get\n * stored in the shadow store, so nothing about the recorded request is\n * silently rewritten. */\nconst DEFAULT_INSTRUCTIONS: Record<string, string> = {\n choice: \"Which option best fits?\",\n score: \"Which level best fits?\",\n noul: \"Is this true of the state?\",\n}\n\nfunction withInstructions(questions: Questions): Questions {\n const out: Record<string, unknown> = {}\n for (const [name, question] of Object.entries(questions)) {\n out[name] = question.instructions\n ? question\n : { ...question, instructions: DEFAULT_INSTRUCTIONS[question.type] }\n }\n return out as Questions\n}\n\n/** Env var first, then the file. Cached per path: a seat on a hot path\n * must not stat the filesystem on every call. */\nconst keyFileCache = new Map<string, string | null>()\n\nfunction resolveKey(envVar?: string, file?: string): string | undefined {\n const fromEnv = envVar ? process.env[envVar] : undefined\n if (fromEnv) return fromEnv\n if (!file) return undefined\n\n const path = file.startsWith(\"~/\") ? join(homedir(), file.slice(2)) : file\n if (!keyFileCache.has(path)) {\n try {\n const raw = readFileSync(path, \"utf-8\").trim()\n keyFileCache.set(path, raw.length > 0 ? raw : null)\n } catch {\n keyFileCache.set(path, null)\n }\n }\n return keyFileCache.get(path) ?? undefined\n}\n\nfunction assertChoiceCardinality(questions: Questions, max: number): void {\n for (const [questionName, question] of Object.entries(questions)) {\n if (question.type === \"choice\") {\n const n = Object.keys(question.criteria).length\n if (n > max) {\n throw new Error(\n `question \"${questionName}\": accepts at most ${max} choice options, got ${n}`,\n )\n }\n } else if (question.type === \"score\" && question.criteria.length > max) {\n throw new Error(\n `question \"${questionName}\": accepts at most ${max} score levels, got ${question.criteria.length}`,\n )\n }\n }\n}\n\nfunction deadline(\n caller: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; dispose: () => void } {\n const controller = new AbortController()\n const timer = setTimeout(\n () => controller.abort(new Error(`decision timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n const onCallerAbort = () => controller.abort(caller?.reason)\n if (caller) {\n if (caller.aborted) onCallerAbort()\n else caller.addEventListener(\"abort\", onCallerAbort, { once: true })\n }\n return {\n signal: controller.signal,\n dispose: () => {\n clearTimeout(timer)\n caller?.removeEventListener(\"abort\", onCallerAbort)\n },\n }\n}\n","// Typed-decision seat.\n//\n// A decision seat does not make a decision better. It makes the decision's\n// uncertainty legible. Attached to a call site with no ground truth, it buys\n// a more expensive number and a dashboard that cannot be wrong — so the\n// calibration harness is not optional polish, it is the only thing that\n// separates this from the self-reported `confidence` fields the codebase\n// already has several of and trusts none of.\n//\n// See docs/architecture (and the plan that introduced this module) for the\n// full contract. Start at ./types.ts.\n\nexport * from \"./types\"\nexport * from \"./questions\"\nexport * from \"./normalize\"\nexport * from \"./schema\"\nexport * from \"./backend\"\nexport * from \"./prompt\"\nexport * from \"./store\"\nexport * from \"./calibration\"\nexport * from \"./recalibrate\"\nexport * from \"./consistency\"\nexport * from \"./seat\"\nexport { createMockDecisionBackend } from \"./backends/mock\"\nexport type { MockBackendOptions } from \"./backends/mock\"\nexport { createLocalDecisionBackend } from \"./backends/local-llm\"\nexport type { LocalBackendOptions } from \"./backends/local-llm\"\nexport { createSimpleJevBackend } from \"./backends/simple-jev\"\nexport type { SimpleJevOptions } from \"./backends/simple-jev\"\n\nimport { registerDecisionBackend } from \"./backend\"\nimport { createMockDecisionBackend } from \"./backends/mock\"\nimport { createLocalDecisionBackend, type LocalBackendOptions } from \"./backends/local-llm\"\nimport { createSimpleJevBackend, type SimpleJevOptions } from \"./backends/simple-jev\"\n\n/** Register the backends that ship in-tree. Explicit rather than\n * import-time so tests can start from an empty registry. */\nexport function registerBuiltinDecisionBackends(\n local: LocalBackendOptions = {},\n simpleJev: SimpleJevOptions = {},\n jev: SimpleJevOptions = {},\n typesafe: SimpleJevOptions = {},\n): void {\n registerDecisionBackend(\"mock\", () => createMockDecisionBackend())\n registerDecisionBackend(\"local\", () => createLocalDecisionBackend(local))\n registerDecisionBackend(\"simple-jev\", () => createSimpleJevBackend(simpleJev))\n // Jev via OpenRouter's alpha decisions endpoint. Verified against a live\n // key on 2026-09-19: POST /api/alpha/decisions with model \"jev-latest\"\n // resolves to typesafe/jev-1.13-20260917 with provider \"TypeSafe\", so\n // this is the real model rather than a proxy to something else. All\n // three primitives round-tripped in 0.49s from Tunisia at a cost of\n // $1.8e-05 for 429 input tokens.\n //\n // The model id is \"jev-latest\", NOT \"typesafe/jev-latest\" — the\n // namespaced form 400s with \"does not exist\", and the endpoint accepts\n // no generative models at all. It does not appear in OpenRouter's public\n // model list either, so the id cannot be discovered from /v1/models.\n registerDecisionBackend(\"jev\", () =>\n createSimpleJevBackend({\n name: \"jev\",\n baseUrl: \"https://openrouter.ai/api/alpha\",\n path: \"/decisions\",\n model: \"jev-latest\",\n apiKeyEnv: \"OPENROUTER_API_KEY\",\n apiKeyFile: \"~/.agentx/openrouter-key.txt\",\n // TypeSafe's claim is a model trained for calibrated decisions. The\n // claim is what this records; whether it holds on our traffic is what\n // `agentx decisions recalibrate` is for, and calibratedProbabilities\n // stays false until it is measured.\n probabilitySource: \"native\",\n maxChoiceOptions: 255,\n // Jev's request budget is ~32k TOKENS. 24k characters is roughly 6k\n // tokens — a quarter of what it accepts, and it was my conservative\n // carry-over from the simple-jev demo's 2k context. It silently\n // truncated re-ranking shortlists at the tail.\n maxStateChars: 90_000,\n ...jev,\n }),\n )\n // Jev direct from TypeSafe, bypassing OpenRouter. Same System One\n // contract on /v1/systemone; the difference is billing and that it needs\n // a TypeSafe key rather than an OpenRouter one. Prefer this once you have\n // direct access: one less hop, and the vendor's own rate limits.\n registerDecisionBackend(\"typesafe\", () =>\n createSimpleJevBackend({\n name: \"typesafe\",\n baseUrl: \"https://api.typesafe.ai/v1\",\n path: \"/systemone\",\n model: \"jev-latest\",\n apiKeyEnv: \"TYPESAFE_API_KEY\",\n apiKeyFile: \"~/.agentx/typesafe-key.txt\",\n probabilitySource: \"native\",\n maxChoiceOptions: 255,\n maxStateChars: 90_000,\n ...typesafe,\n }),\n )\n}\n","import { getDecisionBackend } from \"./backend\"\nimport { DecisionStore, type DecisionAction, type SeatMode } from \"./store\"\nimport type { AnswersFor, Questions, StateValue } from \"./types\"\n\n// askSeat — the only entry point a call site should use.\n//\n// Three properties it must have, in priority order:\n//\n// 1. It never throws. A seat is an addition to a call site that already\n// works; a decision backend having a bad day must not take down the\n// dispatch path it was bolted onto. Every failure returns null and the\n// caller keeps its existing behaviour.\n//\n// 2. Every failure is still a row. A seat that records only its successes\n// flatters itself exactly where it matters — a backend that times out\n// on the hard half of the traffic would otherwise show beautiful\n// calibration on the easy half.\n//\n// 3. Default off. A seat does nothing until an operator names it in\n// config or the environment, and `shadow` never changes behaviour —\n// it only records, so the incumbent stays authoritative through the\n// whole soak.\n\nexport interface SeatSettings {\n mode: SeatMode\n backend?: string\n model?: string\n timeoutMs?: number\n /** Post-hoc temperature from a fit on this seat's own labeled rows.\n * 1 means \"not calibrated yet\", which is where every seat starts. */\n temperature?: number\n /** Stop persisting state once this many rows exist for the seat. */\n keepStateRows?: number\n redactState?: boolean\n /** Fraction of would-be-skips to run anyway, keeping an unbiased sample\n * of the skip region. See DEFAULT_EXPLORE_RATE — this is a correctness\n * requirement, not a tuning knob. */\n explore?: number\n}\n\nexport interface DecisionsRuntime {\n enabled: boolean\n defaultBackend: string\n seats: Record<string, SeatSettings>\n store: DecisionStore | null\n}\n\nconst DEFAULT_RUNTIME: DecisionsRuntime = {\n enabled: false,\n defaultBackend: \"local\",\n seats: {},\n store: null,\n}\n\nlet runtime: DecisionsRuntime = { ...DEFAULT_RUNTIME }\nlet configured = false\n\n/** Called by the daemon after config load, and by the lazy path below for\n * every other process. */\nexport function configureDecisions(next: Partial<DecisionsRuntime>): void {\n runtime = { ...DEFAULT_RUNTIME, ...runtime, ...next }\n configured = true\n}\n\nexport function resetDecisionsRuntime(): void {\n runtime = { ...DEFAULT_RUNTIME, seats: {} }\n configured = false\n}\n\n/**\n * Configure from agentx.json on first use, for processes that are not the\n * daemon.\n *\n * Seats are not a daemon feature, but only the daemon was calling\n * configureDecisions — so `agentx wiki search` and the wiki server ran\n * every seat as \"off\" and silently fell back, with no error and no\n * recorded row. The failure was invisible precisely because the seat is\n * designed to fail quietly.\n *\n * Best-effort and cached, including on failure: a CLI run outside a\n * project has no config, and that must cost one failed read rather than\n * one per call.\n */\nfunction ensureConfigured(): void {\n if (configured) return\n configured = true\n try {\n // Required lazily: importing the daemon config from module scope would\n // pull the config graph into every process that touches a seat.\n const { loadDaemonConfig } = require(\"@/daemon/config\") as {\n loadDaemonConfig: () => { decisions?: any }\n }\n const cfg = loadDaemonConfig()?.decisions\n if (!cfg?.enabled) return\n\n const { registerBuiltinDecisionBackends } = require(\"./index\") as {\n registerBuiltinDecisionBackends: (...a: any[]) => void\n }\n const b = cfg.backends ?? {}\n registerBuiltinDecisionBackends(b.local, b.simpleJev, b.jev, b.typesafe)\n\n let store: DecisionStore | null = null\n try {\n store = new DecisionStore({ path: cfg.dbPath })\n } catch {\n /* answer without recording rather than not answer */\n }\n runtime = {\n enabled: true,\n defaultBackend: cfg.defaultBackend,\n store,\n seats: Object.fromEntries(\n Object.entries(cfg.seats ?? {}).map(([name, seat]: [string, any]) => [\n name,\n { ...seat, redactState: cfg.redactState, keepStateRows: cfg.keepStateRows },\n ]),\n ),\n }\n } catch {\n /* no config, no seats — exactly today's behaviour */\n }\n}\n\nexport function decisionsRuntime(): DecisionsRuntime {\n return runtime\n}\n\nconst VALID_MODES: readonly SeatMode[] = [\"off\", \"shadow\", \"active\"]\n\nexport function parseSeatMode(value: unknown): SeatMode | null {\n if (typeof value !== \"string\") return null\n const normalized = value.trim().toLowerCase()\n return (VALID_MODES as readonly string[]).includes(normalized)\n ? (normalized as SeatMode)\n : null\n}\n\n/** `monitor-prefilter` -> AGENTX_DECISION_SEAT_MONITOR_PREFILTER */\nexport function seatEnvVar(seat: string): string {\n return `AGENTX_DECISION_SEAT_${seat.toUpperCase().replace(/[^A-Z0-9]+/g, \"_\")}`\n}\n\n/**\n * Resolve a seat's mode. Order: per-seat env var, then config, then \"off\".\n *\n * An invalid value at any level falls through to the next rather than\n * failing loudly — same trade as src/intent/mode.ts. The cost is silence on\n * a typo; the cost of the alternative is a seat accidentally promoted to\n * `active` because someone wrote \"Active \" with a trailing space.\n *\n * Read every call, never cached, so an operator can flip a seat off without\n * restarting the daemon.\n */\nexport function getSeatMode(seat: string, env: NodeJS.ProcessEnv = process.env): SeatMode {\n // Configure FIRST, even when the env var will decide the answer.\n //\n // This used to return on the env override before touching the config,\n // which meant the one documented way to switch a seat on for a single\n // process — AGENTX_DECISION_SEAT_<SEAT>=active — also skipped the lazy\n // setup that REGISTERS THE BACKENDS. The seat then reported `active` and\n // every call failed with `unknown decision backend \"local\"`, so turning\n // a seat on by hand quietly guaranteed it could never answer.\n //\n // It went unnoticed because the commands built around seats\n // (`agentx decide`, `agentx decisions`) call registerBuiltinDecisionBackends\n // themselves, so the path that was broken was the one an operator or a\n // new seat would take.\n ensureConfigured()\n const fromEnv = parseSeatMode(env[seatEnvVar(seat)])\n if (fromEnv) return fromEnv\n if (!runtime.enabled) return \"off\"\n return parseSeatMode(runtime.seats[seat]?.mode) ?? \"off\"\n}\n\nexport interface AskSeatOptions<Q extends Questions> {\n /** What the code would do without the seat. Recorded alongside, which is\n * what makes agreement and the eventual promotion decision measurable. */\n incumbent?: Partial<Record<keyof Q & string, string | number>>\n links?: Array<{ kind: string; id: string }>\n /** Covariates for recalibration — agent, channel, anything categorical\n * that plausibly shifts how well calibrated this seat is. */\n features?: Record<string, string | number | boolean | null>\n signal?: AbortSignal\n timeoutMs?: number\n backend?: string\n model?: string\n}\n\nexport interface SeatResult<Q extends Questions> {\n answers: AnswersFor<Q>\n callId: string | null\n mode: SeatMode\n}\n\n/** Record what the caller did with an answer.\n *\n * Separate from askSeat because the seat owns the answer and the CALLER\n * owns the policy: askSeat cannot know whether its caller acted. Silent\n * no-op when there is no store or no call id, like every other recording\n * path here — observability never breaks the thing it observes. */\nexport function recordSeatOutcome(\n callId: string | null,\n action: DecisionAction,\n explored = false,\n): void {\n if (!callId || !runtime.store) return\n try {\n runtime.store.recordOutcome(callId, action, explored)\n } catch {\n /* observability never breaks the caller */\n }\n}\n\nlet lastWarnAt = 0\n\nexport async function askSeat<Q extends Questions>(\n seat: string,\n state: StateValue,\n questions: Q,\n opts: AskSeatOptions<Q> = {},\n): Promise<SeatResult<Q> | null> {\n const mode = getSeatMode(seat)\n if (mode === \"off\") return null\n\n const settings = runtime.seats[seat] ?? { mode }\n const backendName = opts.backend ?? settings.backend ?? runtime.defaultBackend\n const started = Date.now()\n\n try {\n const backend = getDecisionBackend(backendName)\n const response = await backend.decide({\n state,\n questions,\n model: opts.model ?? settings.model,\n abortSignal: opts.signal,\n timeoutMs: opts.timeoutMs ?? settings.timeoutMs,\n })\n\n const callId = record(seat, mode, state, questions, response, opts, settings)\n return { answers: response.answers, callId, mode }\n } catch (err: any) {\n recordFailure(seat, mode, state, questions, backendName, opts, settings, started, err)\n warnThrottled(seat, backendName, err)\n return null\n }\n}\n\nfunction record<Q extends Questions>(\n seat: string,\n mode: SeatMode,\n state: StateValue,\n questions: Q,\n response: Awaited<ReturnType<ReturnType<typeof getDecisionBackend>[\"decide\"]>>,\n opts: AskSeatOptions<Q>,\n settings: SeatSettings,\n): string | null {\n if (!runtime.store) return null\n try {\n return runtime.store.recordCall({\n seat,\n mode,\n backend: response.meta.backend,\n model: response.model,\n meta: response.meta,\n state,\n questions,\n answers: response.answers as Record<string, any>,\n usage: response.usage,\n incumbent: toIncumbent(opts.incumbent),\n links: opts.links,\n features: opts.features,\n keepState: !settings.redactState,\n })\n } catch {\n // Recording is observability. It never breaks the call it observes.\n return null\n }\n}\n\nfunction recordFailure<Q extends Questions>(\n seat: string,\n mode: SeatMode,\n state: StateValue,\n questions: Q,\n backend: string,\n opts: AskSeatOptions<Q>,\n settings: SeatSettings,\n started: number,\n err: any,\n): void {\n if (!runtime.store) return\n try {\n runtime.store.recordCall({\n seat,\n mode,\n backend,\n model: opts.model ?? settings.model ?? \"unknown\",\n meta: {\n structureMode: \"text\",\n answerMode: \"probabilities\",\n retries: 0,\n stateTruncated: false,\n latencyMs: Date.now() - started,\n },\n state,\n questions,\n answers: {},\n incumbent: toIncumbent(opts.incumbent),\n links: opts.links,\n features: opts.features,\n error: String(err?.message ?? err).slice(0, 500),\n keepState: !settings.redactState,\n })\n } catch {\n /* observability never breaks the caller */\n }\n}\n\nfunction toIncumbent(\n incumbent: Record<string, string | number | undefined> | undefined,\n): Record<string, { value: string | number }> | undefined {\n if (!incumbent) return undefined\n const out: Record<string, { value: string | number }> = {}\n for (const [question, value] of Object.entries(incumbent)) {\n if (value !== undefined) out[question] = { value }\n }\n return out\n}\n\n/** At most one line a minute. A backend that is down fails on every call,\n * and a log line per call would bury the thing that actually broke. */\nfunction warnThrottled(seat: string, backend: string, err: any): void {\n const now = Date.now()\n if (now - lastWarnAt < 60_000) return\n lastWarnAt = now\n console.warn(\n `[decisions] seat \"${seat}\" on backend \"${backend}\" failed: ${err?.message ?? err}`,\n )\n}\n"],"mappings":"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","names":["registerDecisionBackend","name","factory","factories","instances","getDecisionBackend","cached","known","listDecisionBackends","backend","_resetDecisionBackendsForTesting","init_backend","__esmMin","Database","createHash","mkdirSync","dirname","resolve","answerView","answer","p","probabilities","predicted","confidence","rounded","parseFeatures","raw","parsed","runMigrations","db","current","migrationV1","migrationV2","migrationV3","DecisionStore","init_store","__esmMin","init_ulid","opts","input","ts","callId","newEventId","stateJson","stateHash","insertAnswer","question","view","insertIncumbent","row","insertLink","link","value","id","action","explored","kind","r","filter","where","params","sql","seat","keepRows","init_types","__esmMin","normalizeDistribution","raw","labels","opts","normalize","epsilon","source","repaired","key","probs","sum","label","value","uniform","pMax","values","normalizedNegEntropy","v","k","total","a","b","h","p","clamp01","expectedScore","weighted","level","confidenceFor","n","finalizeAnswer","question","ok","labelsOf","max","negEntropy","best","legend","description","i","init_normalize","__esmMin","init_questions","z","rawAnswerSchema","question","noulSchema","distributionSchema","rawAnswersSchema","questions","shape","name","describeIssues","error","issue","init_schema","__esmMin","toolInputSchema","questions","properties","name","question","answerObjectSchema","labels","labelsOf","label","renderState","state","maxChars","text","renderQuestions","blocks","lines","description","renderUserPrompt","rendered","renderShapeInstructions","example","probabilities","TOOL_NAME","SYSTEM_PROMPT","init_prompt","__esmMin","init_questions","outcome","row","logit","p","clamped","EPS","sigmoid","z","e","covariateValue","name","fromFeatures","own","buildDesign","rows","spec","minCount","levels","counts","value","kept","n","names","level","X","y","features","fitLogistic","opts","l2","iterations","d","w","iter","H","g","i","j","weight","residual","k","step","solve","delta","A","b","M","col","pivot","r","factor","x","fitRecalibrator","labeled","design","applyRecalibrator","model","compareCalibrators","folds","minN","base","rng","shuffled","predictors","actuals","fold","test","_","train","flat","full","scores","predicted","binaryLogLoss","binaryBrier","best","a","actual","sum","init_recalibrate","__esmMin","randomBytes","band","probability","low","UNCERTAINTY_LOW","high","UNCERTAINTY_HIGH","measureConsistency","backendName","state","questions","opts","samples","backend","getDecisionBackend","started","runs","_","i","withUid","names","usage","acc","r","perQuestion","name","views","answerView","probs","v","answer","answers","bands","p","m","mean","stdev","q","uid","values","a","b","sum","init_consistency","__esmMin","init_backend","init_store","createMockDecisionBackend","opts","capabilities","request","validateQuestions","stateText","answers","repaired","name","question","raw","syntheticRaw","result","finalizeAnswer","answerMode","unitHash","labels","labelsOf","weights","label","total","b","probabilities","i","input","h","init_mock","__esmMin","init_normalize","init_questions","checkCapabilities","providerName","requiredFeatures","caps","PROVIDER_CAPABILITIES","warnings","missing","feature","init_capabilities","__esmMin","createLocalDecisionBackend","opts","maxStateChars","DEFAULT_MAX_STATE_CHARS","capabilities","provider","resolveProvider","createProvider","DEFAULT_PROVIDER","request","validateQuestions","started","model","DEFAULT_MODEL","agent","structureMode","resolveStructureMode","prompt","renderUserPrompt","schema","rawAnswersSchema","maxRetries","signal","dispose","deadline","DEFAULT_TIMEOUT_MS","usage","correction","retries","raw","lastError","attempt","result","callWithForcedTool","DEFAULT_MAX_TOKENS","callWithText","parsed","describeIssues","answers","repaired","name","question","finalizeAnswer","questions","userPrompt","options","messages","SYSTEM_PROMPT","TOOL_NAME","toolInputSchema","block","b","input","unwrapAnswers","content","renderShapeInstructions","extractJson","value","wrapper","requested","PROVIDER_CAPABILITIES","caller","timeoutMs","controller","timer","onCallerAbort","init_local_llm","__esmMin","init_providers","init_capabilities","init_extract_json","init_normalize","init_prompt","init_questions","init_schema","readFileSync","homedir","join","createSimpleJevBackend","opts","baseUrl","DEFAULT_BASE_URL","path","name","maxStateChars","DEFAULT_MAX_STATE_CHARS","maxChoiceOptions","DEFAULT_MAX_CHOICE_OPTIONS","capabilities","request","validateQuestions","assertChoiceCardinality","started","model","rendered","renderState","doFetch","signal","dispose","deadline","DEFAULT_TIMEOUT_MS","headers","apiKey","resolveKey","res","withInstructions","detail","body","parsed","rawAnswersSchema","describeIssues","raw","answers","repaired","question","result","finalizeAnswer","questions","out","DEFAULT_INSTRUCTIONS","envVar","file","fromEnv","keyFileCache","max","questionName","n","caller","timeoutMs","controller","timer","onCallerAbort","init_simple_jev","__esmMin","init_normalize","init_questions","init_schema","init_prompt","decisions_exports","__export","DecisionStore","SYSTEM_PROMPT","TOOL_NAME","UNCERTAINTY_HIGH","UNCERTAINTY_LOW","_resetDecisionBackendsForTesting","agreement","answerView","applyRecalibrator","applyTemperature","askSeat","band","bootstrapInterval","brier","buildDesign","calibrationReport","choice","compareCalibrators","confidenceFor","configureDecisions","coverageCurve","createLocalDecisionBackend","createMockDecisionBackend","createSimpleJevBackend","decisionsRuntime","describeIssues","ece","expectedScore","finalizeAnswer","fitLogistic","fitRecalibrator","fitTemperature","getDecisionBackend","getSeatMode","labelsOf","listDecisionBackends","logLoss","logit","mce","measureConsistency","normalizeDistribution","normalizedNegEntropy","noul","pMax","parseSeatMode","rawAnswerSchema","rawAnswersSchema","recordSeatOutcome","registerBuiltinDecisionBackends","registerDecisionBackend","reliabilityBins","renderQuestions","renderShapeInstructions","renderState","renderUserPrompt","resetDecisionsRuntime","score","seatEnvVar","sigmoid","toolInputSchema","validateQuestions","withUid","local","simpleJev","jev","typesafe","init_decisions","__esmMin","init_types","init_questions","init_normalize","init_schema","init_backend","init_prompt","init_store","init_calibration","init_recalibrate","init_consistency","init_seat","init_mock","init_local_llm","init_simple_jev","configureDecisions","next","runtime","DEFAULT_RUNTIME","configured","resetDecisionsRuntime","ensureConfigured","loadDaemonConfig","cfg","registerBuiltinDecisionBackends","b","store","DecisionStore","name","seat","decisionsRuntime","parseSeatMode","value","normalized","VALID_MODES","seatEnvVar","getSeatMode","env","fromEnv","recordSeatOutcome","callId","action","explored","askSeat","state","questions","opts","mode","settings","backendName","started","response","getDecisionBackend","record","err","recordFailure","warnThrottled","toIncumbent","backend","incumbent","out","question","now","lastWarnAt","init_seat","__esmMin","init_backend","init_store"]}
@@ -0,0 +1,2 @@
1
+ import{b as h}from"./chunk-FIOB3MEX.js";function p(i){let o=i.filter(l=>l.incumbent!==void 0),a=o.length;if(a===0)return{n:0,agree:0,rate:0,kappa:0};let t=0,n=new Map,r=new Map;for(let l of o)l.predicted===l.incumbent&&t++,n.set(l.predicted,(n.get(l.predicted)??0)+1),r.set(l.incumbent,(r.get(l.incumbent)??0)+1);let e=t/a,u=0;for(let[l,f]of n)u+=f/a*((r.get(l)??0)/a);let c=u<1?(e-u)/(1-u):1;return{n:a,agree:t,rate:e,kappa:c}}function s(i,o=10,a="width"){let t=i.filter(e=>e.truth!==void 0);if(t.length===0)return[];let n=[...t].sort((e,u)=>e.confidence-u.confidence);return(a==="mass"?v(n,o):y(n,o)).filter(e=>e.length>0).map(e=>{let u=e.map(c=>c.confidence);return{lo:Math.min(...u),hi:Math.max(...u),n:e.length,meanConfidence:m(u),accuracy:m(e.map(c=>c.predicted===c.truth?1:0))}})}function d(i,o=10,a="width"){let t=s(i,o,a),n=t.reduce((r,e)=>r+e.n,0);return n===0?0:t.reduce((r,e)=>r+e.n/n*Math.abs(e.accuracy-e.meanConfidence),0)}function g(i,o=10,a="width"){let t=s(i,o,a);return t.length===0?0:Math.max(...t.map(n=>Math.abs(n.accuracy-n.meanConfidence)))}function R(i){let o=i.filter(a=>a.truth!==void 0);return o.length===0?0:m(o.map(a=>{let t=0;for(let[n,r]of Object.entries(a.probabilities)){let e=n===a.truth?1:0;t+=(r-e)**2}return t}))}function b(i,o=1e-12){let a=i.filter(t=>t.truth!==void 0);return a.length===0?0:m(a.map(t=>{let n=t.probabilities[t.truth]??0;return-Math.log(Math.max(n,o))}))}function M(i,o=20){let a=i.filter(r=>r.truth!==void 0),t=a.length;if(t===0)return[];let n=[];for(let r=0;r<=o;r++){let e=r/o,u=a.filter(c=>c.confidence>=e);n.push({threshold:e,coverage:u.length/t,accuracy:u.length>0?m(u.map(c=>c.predicted===c.truth?1:0)):0,n:u.length})}return n}function x(i,o){let a=Math.max(o,1e-6),t={},n=0;for(let[r,e]of Object.entries(i)){let u=Math.pow(Math.max(e,0),1/a);t[r]=u,n+=u}if(n<=0){let r=Object.keys(i);for(let e of r)t[e]=1/r.length;return t}for(let r of Object.keys(t))t[r]=t[r]/n;return t}function w(i){let o=i.filter(e=>e.truth!==void 0),a=b(o);if(o.length===0)return{temperature:1,nll:0,nllBefore:0};let t=e=>m(o.map(u=>{let c=x(u.probabilities,e);return-Math.log(Math.max(c[u.truth]??0,1e-12))})),n=1,r=t(1);for(let e=.1;e<=10.0001;e+=.1){let u=t(e);u<r&&(r=u,n=e)}for(let e=Math.max(.01,n-.1);e<=n+.1;e+=.01){let u=t(e);u<r&&(r=u,n=e)}return{temperature:Number(n.toFixed(3)),nll:r,nllBefore:a}}function G(i,o,a=1e3,t=.05,n=Math.random){if(i.length===0)return{lo:0,hi:0};let r=[];for(let e=0;e<a;e++){let u=[];for(let c=0;c<i.length;c++)u.push(i[Math.floor(n()*i.length)]);r.push(o(u))}return r.sort((e,u)=>e-u),{lo:r[Math.floor(t/2*r.length)],hi:r[Math.min(r.length-1,Math.floor((1-t/2)*r.length))]}}function C(i,o={}){let a=o.minN??100,t=o.bins??10,n=i.filter(e=>e.truth!==void 0),r={insufficient:n.length<a,n:i.length,nLabeled:n.length,minN:a,agreement:p(i)};return r.insufficient?r:{...r,accuracy:m(n.map(e=>e.predicted===e.truth?1:0)),ece:d(n,t,"width"),eceEqualMass:d(n,t,"mass"),eceInterval:G(n,e=>d(e,t,"width"),o.bootstrap??1e3,.05,o.rng),mce:g(n,t,"width"),brier:R(n),logLoss:b(n),bins:s(n,t,"width"),binsEqualMass:s(n,t,"mass"),coverage:M(n),temperature:w(n)}}function y(i,o){let a=Array.from({length:o},()=>[]);for(let t of i){let n=Math.min(o-1,Math.max(0,Math.floor(t.confidence*o)));a[n].push(t)}return a}function v(i,o){let a=Array.from({length:o},()=>[]);return i.forEach((t,n)=>{a[Math.min(o-1,Math.floor(n*o/i.length))].push(t)}),a}function m(i){return i.length===0?0:i.reduce((o,a)=>o+a,0)/i.length}var B=h(()=>{});export{p as a,s as b,d as c,g as d,R as e,b as f,M as g,x as h,w as i,G as j,C as k,B as l};
2
+ //# sourceMappingURL=chunk-BH3GTCZ6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/decisions/calibration.ts"],"sourcesContent":["import type { GradedRow } from \"./store\"\n\n// Does the confidence mean anything?\n//\n// Every metric here is a pure function of GradedRow[]. Three choices are\n// deliberate and worth defending:\n//\n// 1. `minN` (default 100). Below it the report refuses to print an ECE.\n// An expected calibration error computed on thirty rows is noise, and\n// a noisy number on a dashboard gets acted on exactly as if it were a\n// real one.\n//\n// 2. `coverageCurve` is the headline, not ECE. The question a call site\n// actually asks is \"what threshold do I set for autoRunThreshold\" —\n// and that is answered by accuracy-at-threshold against the fraction\n// of traffic retained, not by a single aggregate score.\n//\n// 3. Equal-mass bins are reported alongside equal-width. Equal-width ECE\n// is dominated by whichever bin holds most of the mass, which for an\n// overconfident model is the top bin — precisely the regime we are\n// trying to detect.\n\nexport interface AgreementResult {\n n: number\n agree: number\n rate: number\n /** Cohen's kappa: agreement corrected for what chance alone would give.\n * Two systems that both answer \"no\" 95% of the time agree 90% of the\n * time while sharing no information, and kappa says so. */\n kappa: number\n}\n\nexport function agreement(rows: GradedRow[]): AgreementResult {\n const usable = rows.filter((r) => r.incumbent !== undefined)\n const n = usable.length\n if (n === 0) return { n: 0, agree: 0, rate: 0, kappa: 0 }\n\n let agree = 0\n const predicted = new Map<string, number>()\n const incumbent = new Map<string, number>()\n for (const row of usable) {\n if (row.predicted === row.incumbent) agree++\n predicted.set(row.predicted, (predicted.get(row.predicted) ?? 0) + 1)\n incumbent.set(row.incumbent!, (incumbent.get(row.incumbent!) ?? 0) + 1)\n }\n\n const observed = agree / n\n let expected = 0\n for (const [label, count] of predicted) {\n expected += (count / n) * ((incumbent.get(label) ?? 0) / n)\n }\n const kappa = expected < 1 ? (observed - expected) / (1 - expected) : 1\n\n return { n, agree, rate: observed, kappa }\n}\n\nexport interface ReliabilityBin {\n lo: number\n hi: number\n n: number\n meanConfidence: number\n accuracy: number\n}\n\nexport type BinScheme = \"width\" | \"mass\"\n\nexport function reliabilityBins(\n rows: GradedRow[],\n bins = 10,\n scheme: BinScheme = \"width\",\n): ReliabilityBin[] {\n const labeled = rows.filter((r) => r.truth !== undefined)\n if (labeled.length === 0) return []\n\n const sorted = [...labeled].sort((a, b) => a.confidence - b.confidence)\n const groups: GradedRow[][] =\n scheme === \"mass\" ? equalMassGroups(sorted, bins) : equalWidthGroups(sorted, bins)\n\n return groups\n .filter((g) => g.length > 0)\n .map((group) => {\n const confidences = group.map((r) => r.confidence)\n return {\n lo: Math.min(...confidences),\n hi: Math.max(...confidences),\n n: group.length,\n meanConfidence: mean(confidences),\n accuracy: mean(group.map((r) => (r.predicted === r.truth ? 1 : 0))),\n }\n })\n}\n\n/** Expected calibration error: the mass-weighted gap between how confident\n * the model was and how often it was right. Zero is perfect. */\nexport function ece(rows: GradedRow[], bins = 10, scheme: BinScheme = \"width\"): number {\n const grouped = reliabilityBins(rows, bins, scheme)\n const total = grouped.reduce((sum, b) => sum + b.n, 0)\n if (total === 0) return 0\n return grouped.reduce(\n (sum, b) => sum + (b.n / total) * Math.abs(b.accuracy - b.meanConfidence),\n 0,\n )\n}\n\n/** The worst single bin. ECE can look fine while one region is badly wrong. */\nexport function mce(rows: GradedRow[], bins = 10, scheme: BinScheme = \"width\"): number {\n const grouped = reliabilityBins(rows, bins, scheme)\n if (grouped.length === 0) return 0\n return Math.max(...grouped.map((b) => Math.abs(b.accuracy - b.meanConfidence)))\n}\n\n/** Multiclass Brier score. A proper scoring rule, so unlike ECE it cannot\n * be gamed by a model that reports a constant confidence. Lower is better. */\nexport function brier(rows: GradedRow[]): number {\n const labeled = rows.filter((r) => r.truth !== undefined)\n if (labeled.length === 0) return 0\n return mean(\n labeled.map((row) => {\n let sum = 0\n for (const [label, p] of Object.entries(row.probabilities)) {\n const actual = label === row.truth ? 1 : 0\n sum += (p - actual) ** 2\n }\n return sum\n }),\n )\n}\n\nexport function logLoss(rows: GradedRow[], epsilon = 1e-12): number {\n const labeled = rows.filter((r) => r.truth !== undefined)\n if (labeled.length === 0) return 0\n return mean(\n labeled.map((row) => {\n const p = row.probabilities[row.truth!] ?? 0\n return -Math.log(Math.max(p, epsilon))\n }),\n )\n}\n\nexport interface CoveragePoint {\n threshold: number\n /** Fraction of rows at or above the threshold. */\n coverage: number\n /** Accuracy among those rows. */\n accuracy: number\n n: number\n}\n\n/** The curve a call site actually reads: \"if I only act above confidence X,\n * how often am I right, and how much traffic do I still handle?\" */\nexport function coverageCurve(rows: GradedRow[], steps = 20): CoveragePoint[] {\n const labeled = rows.filter((r) => r.truth !== undefined)\n const total = labeled.length\n if (total === 0) return []\n\n const points: CoveragePoint[] = []\n for (let i = 0; i <= steps; i++) {\n const threshold = i / steps\n const kept = labeled.filter((r) => r.confidence >= threshold)\n points.push({\n threshold,\n coverage: kept.length / total,\n accuracy: kept.length > 0 ? mean(kept.map((r) => (r.predicted === r.truth ? 1 : 0))) : 0,\n n: kept.length,\n })\n }\n return points\n}\n\n/** Sharpen or soften a distribution: p^(1/T), renormalized. T > 1 softens\n * (the fix for overconfidence), T < 1 sharpens. */\nexport function applyTemperature(\n probs: Record<string, number>,\n temperature: number,\n): Record<string, number> {\n const t = Math.max(temperature, 1e-6)\n const scaled: Record<string, number> = {}\n let sum = 0\n for (const [label, p] of Object.entries(probs)) {\n const v = Math.pow(Math.max(p, 0), 1 / t)\n scaled[label] = v\n sum += v\n }\n if (sum <= 0) {\n const labels = Object.keys(probs)\n for (const label of labels) scaled[label] = 1 / labels.length\n return scaled\n }\n for (const label of Object.keys(scaled)) scaled[label] = scaled[label] / sum\n return scaled\n}\n\nexport interface TemperatureFit {\n temperature: number\n nll: number\n nllBefore: number\n}\n\n/** Fit the single parameter that turns a well-formed distribution into a\n * calibrated one ON THIS TRAFFIC. This, not the backend, is where\n * calibration comes from — which is why an uncalibrated backend plus a few\n * hundred labeled rows is a defensible position and an uncalibrated\n * backend alone is not.\n *\n * Coarse grid then local refinement: one parameter, a smooth convex-ish\n * objective, and a few hundred rows. Anything fancier is false precision. */\nexport function fitTemperature(rows: GradedRow[]): TemperatureFit {\n const labeled = rows.filter((r) => r.truth !== undefined)\n const nllBefore = logLoss(labeled)\n if (labeled.length === 0) return { temperature: 1, nll: 0, nllBefore: 0 }\n\n const nllAt = (t: number) =>\n mean(\n labeled.map((row) => {\n const scaled = applyTemperature(row.probabilities, t)\n return -Math.log(Math.max(scaled[row.truth!] ?? 0, 1e-12))\n }),\n )\n\n let best = 1\n let bestNll = nllAt(1)\n for (let t = 0.1; t <= 10.0001; t += 0.1) {\n const value = nllAt(t)\n if (value < bestNll) {\n bestNll = value\n best = t\n }\n }\n for (let t = Math.max(0.01, best - 0.1); t <= best + 0.1; t += 0.01) {\n const value = nllAt(t)\n if (value < bestNll) {\n bestNll = value\n best = t\n }\n }\n\n return { temperature: Number(best.toFixed(3)), nll: bestNll, nllBefore }\n}\n\nexport interface Interval {\n lo: number\n hi: number\n}\n\n/** Percentile bootstrap. Without an interval, a difference between two\n * backends' ECEs is not a result. */\nexport function bootstrapInterval(\n rows: GradedRow[],\n statistic: (sample: GradedRow[]) => number,\n resamples = 1000,\n alpha = 0.05,\n rng: () => number = Math.random,\n): Interval {\n if (rows.length === 0) return { lo: 0, hi: 0 }\n const values: number[] = []\n for (let i = 0; i < resamples; i++) {\n const sample: GradedRow[] = []\n for (let j = 0; j < rows.length; j++) {\n sample.push(rows[Math.floor(rng() * rows.length)])\n }\n values.push(statistic(sample))\n }\n values.sort((a, b) => a - b)\n return {\n lo: values[Math.floor((alpha / 2) * values.length)],\n hi: values[Math.min(values.length - 1, Math.floor((1 - alpha / 2) * values.length))],\n }\n}\n\nexport interface CalibrationReport {\n insufficient: boolean\n n: number\n nLabeled: number\n minN: number\n agreement: AgreementResult\n accuracy?: number\n ece?: number\n eceEqualMass?: number\n eceInterval?: Interval\n mce?: number\n brier?: number\n logLoss?: number\n bins?: ReliabilityBin[]\n binsEqualMass?: ReliabilityBin[]\n coverage?: CoveragePoint[]\n temperature?: TemperatureFit\n}\n\nexport function calibrationReport(\n rows: GradedRow[],\n opts: { minN?: number; bins?: number; bootstrap?: number; rng?: () => number } = {},\n): CalibrationReport {\n const minN = opts.minN ?? 100\n const bins = opts.bins ?? 10\n const labeled = rows.filter((r) => r.truth !== undefined)\n const base: CalibrationReport = {\n insufficient: labeled.length < minN,\n n: rows.length,\n nLabeled: labeled.length,\n minN,\n agreement: agreement(rows),\n }\n\n // Agreement needs no ground truth, so it is always reported. Everything\n // that does need ground truth is withheld until there is enough of it.\n if (base.insufficient) return base\n\n return {\n ...base,\n accuracy: mean(labeled.map((r) => (r.predicted === r.truth ? 1 : 0))),\n ece: ece(labeled, bins, \"width\"),\n eceEqualMass: ece(labeled, bins, \"mass\"),\n eceInterval: bootstrapInterval(\n labeled,\n (sample) => ece(sample, bins, \"width\"),\n opts.bootstrap ?? 1000,\n 0.05,\n opts.rng,\n ),\n mce: mce(labeled, bins, \"width\"),\n brier: brier(labeled),\n logLoss: logLoss(labeled),\n bins: reliabilityBins(labeled, bins, \"width\"),\n binsEqualMass: reliabilityBins(labeled, bins, \"mass\"),\n coverage: coverageCurve(labeled),\n temperature: fitTemperature(labeled),\n }\n}\n\nfunction equalWidthGroups(sorted: GradedRow[], bins: number): GradedRow[][] {\n const groups: GradedRow[][] = Array.from({ length: bins }, () => [])\n for (const row of sorted) {\n const index = Math.min(bins - 1, Math.max(0, Math.floor(row.confidence * bins)))\n groups[index].push(row)\n }\n return groups\n}\n\nfunction equalMassGroups(sorted: GradedRow[], bins: number): GradedRow[][] {\n const groups: GradedRow[][] = Array.from({ length: bins }, () => [])\n sorted.forEach((row, i) => {\n groups[Math.min(bins - 1, Math.floor((i * bins) / sorted.length))].push(row)\n })\n return groups\n}\n\nfunction mean(values: number[]): number {\n if (values.length === 0) return 0\n return values.reduce((a, b) => a + b, 0) / values.length\n}\n"],"mappings":"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","names":["agreement","rows","usable","r","n","agree","predicted","incumbent","row","observed","expected","label","count","kappa","reliabilityBins","bins","scheme","labeled","sorted","a","b","equalMassGroups","equalWidthGroups","g","group","confidences","mean","ece","grouped","total","sum","mce","brier","p","actual","logLoss","epsilon","coverageCurve","steps","points","i","threshold","kept","applyTemperature","probs","temperature","t","scaled","v","labels","fitTemperature","nllBefore","nllAt","best","bestNll","value","bootstrapInterval","statistic","resamples","alpha","rng","values","sample","j","calibrationReport","opts","minN","base","groups","index","init_calibration","__esmMin"]}