tianshu-harness 3.23.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 (255) hide show
  1. package/LICENSE +189 -0
  2. package/README.en.md +1066 -0
  3. package/README.ja.md +1214 -0
  4. package/README.ko.md +1216 -0
  5. package/README.md +309 -0
  6. package/completions/rivet.bash +118 -0
  7. package/completions/rivet.fish +110 -0
  8. package/completions/rivet.ps1 +230 -0
  9. package/completions/rivet.zsh +187 -0
  10. package/dist/account-OAEKORZE.js +47 -0
  11. package/dist/account-login-V6FOLHJR.js +65 -0
  12. package/dist/account-status-KHNPPAFL.js +23 -0
  13. package/dist/agent/worker-process/child.js +407 -0
  14. package/dist/browser-cli-FLNY5ZHI.js +19 -0
  15. package/dist/browser-readiness-LWVGSBD5.js +12 -0
  16. package/dist/build-backends-5KXKR2S4.js +13 -0
  17. package/dist/build-options-66Y4ZE7C.js +9 -0
  18. package/dist/bundled-skills/brainstorming/SKILL.md +139 -0
  19. package/dist/bundled-skills/visual-acceptance/SKILL.md +64 -0
  20. package/dist/chain-7MAMFXZ3.js +10 -0
  21. package/dist/checkpoint-MND4DUIX.js +33 -0
  22. package/dist/chunk-2E6CK2RC.js +392 -0
  23. package/dist/chunk-2EBWGAXD.js +51 -0
  24. package/dist/chunk-2HDP7RMR.js +119 -0
  25. package/dist/chunk-2HKD2UFM.js +1000 -0
  26. package/dist/chunk-2VYRLULP.js +51 -0
  27. package/dist/chunk-2XN7PM4N.js +125 -0
  28. package/dist/chunk-35H7GVNR.js +66 -0
  29. package/dist/chunk-3C3R7JXY.js +77 -0
  30. package/dist/chunk-3VGD2FXB.js +118 -0
  31. package/dist/chunk-424AP7FM.js +534 -0
  32. package/dist/chunk-4J5TZGOE.js +3707 -0
  33. package/dist/chunk-4QJ3Y5JM.js +898 -0
  34. package/dist/chunk-4U2ET7ZY.js +1326 -0
  35. package/dist/chunk-4XPFLICE.js +876 -0
  36. package/dist/chunk-5B2OCU5P.js +31707 -0
  37. package/dist/chunk-5GGOGLSN.js +73 -0
  38. package/dist/chunk-5JRZPLI6.js +251 -0
  39. package/dist/chunk-5TQ3JYBU.js +397 -0
  40. package/dist/chunk-66JPZSGX.js +55 -0
  41. package/dist/chunk-6HSVXHCZ.js +764 -0
  42. package/dist/chunk-6S5FPUS7.js +21 -0
  43. package/dist/chunk-722LQO64.js +131 -0
  44. package/dist/chunk-73NJ7IT7.js +354 -0
  45. package/dist/chunk-7FW3I7WC.js +95 -0
  46. package/dist/chunk-7KUCBNLB.js +173 -0
  47. package/dist/chunk-7RDOV3DF.js +201 -0
  48. package/dist/chunk-A5MNH4ER.js +79 -0
  49. package/dist/chunk-ADIVZ2D5.js +15 -0
  50. package/dist/chunk-AE3P3F6W.js +366 -0
  51. package/dist/chunk-AERM3VGP.js +280 -0
  52. package/dist/chunk-AEWHC4WM.js +246 -0
  53. package/dist/chunk-AOVQ7VIF.js +169 -0
  54. package/dist/chunk-AOYHP3WI.js +12411 -0
  55. package/dist/chunk-B3YEBABJ.js +7160 -0
  56. package/dist/chunk-BCQXEZPJ.js +154 -0
  57. package/dist/chunk-BKTQAYO2.js +418 -0
  58. package/dist/chunk-BSECIPAQ.js +80 -0
  59. package/dist/chunk-BUIMGNDD.js +52 -0
  60. package/dist/chunk-BXHV3GSB.js +10320 -0
  61. package/dist/chunk-C6ZVZCPP.js +139 -0
  62. package/dist/chunk-CEZNQVWM.js +82 -0
  63. package/dist/chunk-CGFZGR7G.js +1492 -0
  64. package/dist/chunk-CGOADM2X.js +337 -0
  65. package/dist/chunk-CI4R6P2B.js +24 -0
  66. package/dist/chunk-D2TCGRSX.js +73 -0
  67. package/dist/chunk-D4PTYFOG.js +154 -0
  68. package/dist/chunk-D54ZWG4T.js +717 -0
  69. package/dist/chunk-DEBI37QB.js +145 -0
  70. package/dist/chunk-DWROHPXP.js +1051 -0
  71. package/dist/chunk-DZ6UOJ5D.js +92 -0
  72. package/dist/chunk-EBJTVRQN.js +81 -0
  73. package/dist/chunk-EGEI2RQA.js +1167 -0
  74. package/dist/chunk-EHHUASO5.js +1305 -0
  75. package/dist/chunk-EPKTIOED.js +41 -0
  76. package/dist/chunk-ER5JW2QZ.js +5681 -0
  77. package/dist/chunk-ETHFACAB.js +315 -0
  78. package/dist/chunk-EXUE5W2F.js +112 -0
  79. package/dist/chunk-EXXICUIW.js +73 -0
  80. package/dist/chunk-FFQSZJOO.js +169 -0
  81. package/dist/chunk-FHFKPGDO.js +85 -0
  82. package/dist/chunk-FW3QKMFX.js +158 -0
  83. package/dist/chunk-GD4ZNZ7W.js +30 -0
  84. package/dist/chunk-GHVMWF2I.js +581 -0
  85. package/dist/chunk-GMKAXSYQ.js +297 -0
  86. package/dist/chunk-HBVF6BWO.js +325 -0
  87. package/dist/chunk-HV4YGVMS.js +379 -0
  88. package/dist/chunk-ILBCKAV6.js +89 -0
  89. package/dist/chunk-J4U4AW46.js +440 -0
  90. package/dist/chunk-J6ESSM47.js +1791 -0
  91. package/dist/chunk-JBZHIC6T.js +2309 -0
  92. package/dist/chunk-JCFWOULR.js +245 -0
  93. package/dist/chunk-JRF5L6OD.js +131 -0
  94. package/dist/chunk-JSS75KCG.js +2014 -0
  95. package/dist/chunk-JSXWIWWJ.js +225 -0
  96. package/dist/chunk-JYV7I2QH.js +98 -0
  97. package/dist/chunk-KIOXTQQL.js +1596 -0
  98. package/dist/chunk-KWVTXT5B.js +89 -0
  99. package/dist/chunk-KX22RD6R.js +33 -0
  100. package/dist/chunk-LA6IPTGH.js +709 -0
  101. package/dist/chunk-LH5VCGLB.js +1796 -0
  102. package/dist/chunk-LQQ6XJ2Y.js +269 -0
  103. package/dist/chunk-MJQ55WZZ.js +65 -0
  104. package/dist/chunk-ML3UKMH3.js +40 -0
  105. package/dist/chunk-MOZPLHSU.js +5526 -0
  106. package/dist/chunk-NGEWJ4DF.js +89 -0
  107. package/dist/chunk-NH6VIOAR.js +211 -0
  108. package/dist/chunk-O5VZQKCB.js +26106 -0
  109. package/dist/chunk-OHSV42QB.js +748 -0
  110. package/dist/chunk-OO3TAHO5.js +4070 -0
  111. package/dist/chunk-OTNHSAGN.js +38282 -0
  112. package/dist/chunk-OUWAC7AQ.js +442 -0
  113. package/dist/chunk-OXMIV4WE.js +270 -0
  114. package/dist/chunk-P5KJURUR.js +229 -0
  115. package/dist/chunk-P6GJOVO6.js +307 -0
  116. package/dist/chunk-QARAGUXX.js +23 -0
  117. package/dist/chunk-R43HJCDX.js +2711 -0
  118. package/dist/chunk-RAXBAWRN.js +210 -0
  119. package/dist/chunk-RK3JHWPT.js +39 -0
  120. package/dist/chunk-RWA23GMM.js +24 -0
  121. package/dist/chunk-S4K4L6IM.js +179 -0
  122. package/dist/chunk-SAXK6LVF.js +124 -0
  123. package/dist/chunk-SEPGEJNK.js +1607 -0
  124. package/dist/chunk-SUJERP5I.js +285 -0
  125. package/dist/chunk-T3ED6BY2.js +913 -0
  126. package/dist/chunk-THKDVSEC.js +90 -0
  127. package/dist/chunk-TI4GSEL5.js +52 -0
  128. package/dist/chunk-TPI73L57.js +164 -0
  129. package/dist/chunk-TVHW77C7.js +338 -0
  130. package/dist/chunk-TWNHOSDT.js +1073 -0
  131. package/dist/chunk-UFMN76QD.js +347 -0
  132. package/dist/chunk-UHGLFRZE.js +205 -0
  133. package/dist/chunk-UTZYPKBF.js +15231 -0
  134. package/dist/chunk-UXNLELW6.js +25 -0
  135. package/dist/chunk-UXXS53EA.js +166 -0
  136. package/dist/chunk-V5DHFGI7.js +171 -0
  137. package/dist/chunk-V5YIPMHV.js +2353 -0
  138. package/dist/chunk-WMUWEO2C.js +754 -0
  139. package/dist/chunk-WOE3D7JM.js +945 -0
  140. package/dist/chunk-X2IK5LVR.js +2592 -0
  141. package/dist/chunk-XU2YXCDL.js +459 -0
  142. package/dist/chunk-YJG5HIX5.js +203 -0
  143. package/dist/chunk-Z3QE7TZM.js +72 -0
  144. package/dist/chunk-Z5Z3JHJY.js +64 -0
  145. package/dist/chunk-ZDIFBJFT.js +168 -0
  146. package/dist/chunk-ZGGCEXDC.js +176 -0
  147. package/dist/chunk-ZMZCZNKU.js +263 -0
  148. package/dist/chunk-ZNC5T65N.js +17 -0
  149. package/dist/chunk-ZO3I3A7O.js +308 -0
  150. package/dist/chunk-ZQKXBUB7.js +217 -0
  151. package/dist/chunk-ZTRINFM2.js +78 -0
  152. package/dist/chunk-ZU7JZCVP.js +193 -0
  153. package/dist/chunk-ZZAWNALS.js +350 -0
  154. package/dist/cli/entry.js +54 -0
  155. package/dist/connector-L5QQTR6Y.js +26 -0
  156. package/dist/context-62DC6EC3.js +31 -0
  157. package/dist/council-convene-NJWPNBDU.js +46 -0
  158. package/dist/create-agent-config-ODSQWNVC.js +55 -0
  159. package/dist/default-registry-2PU3BTAU.js +68 -0
  160. package/dist/deliver-task-KONV4C4M.js +57 -0
  161. package/dist/delivery-gate-v2-F2YVMVGK.js +27 -0
  162. package/dist/domain-knowledge-store-DDZ3F4J7.js +11 -0
  163. package/dist/fetch-core-ZPPYDSZH.js +14 -0
  164. package/dist/galaxy-K72BBDQN.js +91 -0
  165. package/dist/goal-criteria-SCALAU2D.js +141 -0
  166. package/dist/goal-persist-XWY2GDQX.js +19 -0
  167. package/dist/goal-tracker-FXMJ2GVK.js +11 -0
  168. package/dist/headless-LSB6I7PT.js +162 -0
  169. package/dist/headless-coordinator-VDPDMVUT.js +90 -0
  170. package/dist/inject-U55SMB6A.js +39 -0
  171. package/dist/login-flow-CD4ZGXOC.js +59 -0
  172. package/dist/logs-cli-SQVYFMMN.js +283 -0
  173. package/dist/loop-ULZYBDA3.js +76 -0
  174. package/dist/main.js +4064 -0
  175. package/dist/manager-ROZBCWPA.js +171 -0
  176. package/dist/manager-Z7XFFUOM.js +10670 -0
  177. package/dist/meridian-indexer-ZIW363L7.js +21 -0
  178. package/dist/native-resolver-PN34UIVV.js +13 -0
  179. package/dist/node_modules/better-sqlite3/lib/database.js +90 -0
  180. package/dist/node_modules/better-sqlite3/lib/index.js +3 -0
  181. package/dist/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
  182. package/dist/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
  183. package/dist/node_modules/better-sqlite3/lib/methods/function.js +31 -0
  184. package/dist/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
  185. package/dist/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
  186. package/dist/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
  187. package/dist/node_modules/better-sqlite3/lib/methods/table.js +189 -0
  188. package/dist/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
  189. package/dist/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
  190. package/dist/node_modules/better-sqlite3/lib/sqlite-error.js +20 -0
  191. package/dist/node_modules/better-sqlite3/lib/util.js +12 -0
  192. package/dist/node_modules/better-sqlite3/package.json +59 -0
  193. package/dist/ownership-ledger-E3WIEKPL.js +9 -0
  194. package/dist/package.json +3 -0
  195. package/dist/path-grants-XRRXHXG7.js +40 -0
  196. package/dist/paths-LVV5IMQN.js +49 -0
  197. package/dist/plan-fact-anchors-LMVQZCW5.js +13 -0
  198. package/dist/plan-store-ODLTTPFB.js +17 -0
  199. package/dist/plan-templates-5FPFBOL6.js +109 -0
  200. package/dist/plugin-loader-ZZKX7ZE6.js +22 -0
  201. package/dist/prefix-budget-62OV5HQC.js +13 -0
  202. package/dist/project-trust-5WS5MOMU.js +31 -0
  203. package/dist/provider-cli-ALDQXIT7.js +37 -0
  204. package/dist/providers-XQYTAVPK.js +11 -0
  205. package/dist/proxy-resolver-V2Y6534D.js +15 -0
  206. package/dist/recovery-cli-KPGY6FY3.js +106 -0
  207. package/dist/review-discipline-MNSYCST5.js +55 -0
  208. package/dist/review-router-BVQZW3MX.js +18 -0
  209. package/dist/seed-capsules/seed-capsule-attack-methodology.md +62 -0
  210. package/dist/seed-capsules/seed-capsule-changgeng.md +31 -0
  211. package/dist/seed-capsules/seed-capsule-design-aesthetics.md +38 -0
  212. package/dist/seed-capsules/seed-capsule-diagnostic-ladder.md +54 -0
  213. package/dist/seed-capsules/seed-capsule-fu.md +39 -0
  214. package/dist/seed-capsules/seed-capsule-huagai.md +73 -0
  215. package/dist/seed-capsules/seed-capsule-kaiyang.md +65 -0
  216. package/dist/seed-capsules/seed-capsule-knowledge-work.md +40 -0
  217. package/dist/seed-capsules/seed-capsule-tanlang.md +47 -0
  218. package/dist/seed-capsules/seed-capsule-tianfu.md +23 -0
  219. package/dist/seed-capsules/seed-capsule-tianliang.md +63 -0
  220. package/dist/seed-capsules/seed-capsule-tianquan.md +32 -0
  221. package/dist/seed-capsules/seed-capsule-tianxuan-ccr.md +22 -0
  222. package/dist/seed-capsules/seed-capsule-tianxuan.md +64 -0
  223. package/dist/seed-capsules/seed-capsule-yaoguang.md +87 -0
  224. package/dist/serve-X2R2UTLF.js +170 -0
  225. package/dist/serve-agent-KHSU6RVV.js +965 -0
  226. package/dist/session-persist-HUUC772H.js +37 -0
  227. package/dist/session-registry-6XUD5C4Y.js +15 -0
  228. package/dist/skill-loader-CK3RMO47.js +48 -0
  229. package/dist/skills/cli-harness-sop.md +207 -0
  230. package/dist/spawn-git-4HFU65AH.js +29 -0
  231. package/dist/sse-EYUTBWUK.js +512 -0
  232. package/dist/starflow-NOFBMPFU.js +30 -0
  233. package/dist/streamableHttp-V67CNJMO.js +447 -0
  234. package/dist/task-contract-6XHBNJQK.js +32 -0
  235. package/dist/task-ledger-3K4TJIER.js +9 -0
  236. package/dist/team-orchestrate-S6NHZP7X.js +64 -0
  237. package/dist/title-extract-QX4XGCYN.js +50 -0
  238. package/dist/todo-QDXGRZZP.js +31 -0
  239. package/dist/todo-store-V3TTO5AH.js +12 -0
  240. package/dist/tool-preset-LU5BYEDZ.js +23 -0
  241. package/dist/tool-tiers-GD742YBN.js +23 -0
  242. package/dist/turndown.es-OI55K6C3.js +17373 -0
  243. package/dist/verification-attribution-EKG4CNUV.js +13 -0
  244. package/dist/volatile-git-MBTST7RA.js +15 -0
  245. package/dist/wave-checkpoint-XLIIHXQA.js +30 -0
  246. package/dist/web-cli-DUG4I35O.js +233 -0
  247. package/dist/web-search-3IK542AA.js +34 -0
  248. package/dist/workers/cpu-worker.js +45 -0
  249. package/dist/workflow-runner-HX7UBAYG.js +297 -0
  250. package/dist/worktree-baseline-O5NCU6M2.js +9 -0
  251. package/package.json +122 -0
  252. package/scripts/check-mobile-env.mjs +76 -0
  253. package/scripts/fetch-native-sqlite.js +392 -0
  254. package/scripts/prune-installed-wasms.js +45 -0
  255. package/scripts/tree-sitter-wasm-keep.js +40 -0
@@ -0,0 +1,1305 @@
1
+ #!/usr/bin/env -S node --expose-gc --max-old-space-size=4096
2
+ import { createRequire as __rivetCreateRequire } from 'node:module'; const require = __rivetCreateRequire(import.meta.url);
3
+ import {
4
+ dependencyId
5
+ } from "./chunk-4QJ3Y5JM.js";
6
+ import {
7
+ matchDomain
8
+ } from "./chunk-CGOADM2X.js";
9
+ import {
10
+ RETIRED_BUNDLED_SKILLS
11
+ } from "./chunk-OHSV42QB.js";
12
+ import {
13
+ init_esm_shims
14
+ } from "./chunk-5GGOGLSN.js";
15
+
16
+ // src/model/capability.ts
17
+ init_esm_shims();
18
+ function score(task, card) {
19
+ switch (task) {
20
+ case "repo_summarization":
21
+ return card.contextWindow / 1e6 + (card.cacheEconomics === "strong" ? 1 : 0);
22
+ case "code_edit":
23
+ return card.toolUseReliability * 0.5 + card.editSuccessRate * 0.5;
24
+ case "test_failure_diagnosis":
25
+ return card.testRepairRate * 0.7 + card.jsonStability * 0.3;
26
+ case "compaction":
27
+ return (card.cacheEconomics === "strong" ? 1 : 0.5) + card.jsonStability;
28
+ case "risky_refactor":
29
+ return card.toolUseReliability * 0.4 + card.editSuccessRate * 0.3 + card.testRepairRate * 0.3;
30
+ case "planning":
31
+ return card.toolUseReliability * 0.4 + card.jsonStability * 0.3 + (card.cacheEconomics === "strong" ? 0.3 : 0) + card.contextWindow / 1e6;
32
+ }
33
+ }
34
+ function recommendModelForTask(task, cards) {
35
+ if (cards.length === 0) throw new Error("No model capability cards configured");
36
+ return [...cards].sort((a, b) => {
37
+ const scoreA = score(task, a) + (a.recommendedTasks.includes(task) ? 0.5 : 0);
38
+ const scoreB = score(task, b) + (b.recommendedTasks.includes(task) ? 0.5 : 0);
39
+ return scoreB - scoreA;
40
+ })[0];
41
+ }
42
+ var V4_FLASH_CARD = {
43
+ toolUseReliability: 0.85,
44
+ jsonStability: 0.85,
45
+ editSuccessRate: 0.75,
46
+ testRepairRate: 0.65,
47
+ cacheEconomics: "strong",
48
+ recommendedTasks: ["code_search", "code_edit", "test_failure_diagnosis", "risky_refactor", "planning", "repo_summarization", "compaction"]
49
+ };
50
+ var GLM53_FLASH_CARD = {
51
+ toolUseReliability: 0.8,
52
+ jsonStability: 0.8,
53
+ editSuccessRate: 0.7,
54
+ testRepairRate: 0.6,
55
+ cacheEconomics: "strong",
56
+ recommendedTasks: ["code_search", "code_edit", "test_failure_diagnosis", "risky_refactor", "planning", "repo_summarization", "compaction"]
57
+ };
58
+ function capabilityCardForModel(m) {
59
+ if (m.id === "deepseek-v4-flash" || m.alias === "v4-flash") {
60
+ return { model: m.id, contextWindow: m.contextWindow, ...V4_FLASH_CARD };
61
+ }
62
+ if (m.id === "glm-5.3-flash" || m.alias === "glm-53-flash") {
63
+ return { model: m.id, contextWindow: m.contextWindow, ...GLM53_FLASH_CARD };
64
+ }
65
+ const isPro = m.id.includes("pro") || m.alias?.includes("pro");
66
+ const isFlash = m.id.includes("flash") || m.alias?.includes("flash");
67
+ if (isPro || !isFlash && !isPro) {
68
+ return {
69
+ model: m.id,
70
+ toolUseReliability: 0.8,
71
+ jsonStability: 0.8,
72
+ editSuccessRate: 0.7,
73
+ testRepairRate: 0.6,
74
+ contextWindow: m.contextWindow,
75
+ cacheEconomics: "strong",
76
+ recommendedTasks: ["code_search", "code_edit", "test_failure_diagnosis", "risky_refactor"]
77
+ };
78
+ }
79
+ return {
80
+ model: m.id,
81
+ toolUseReliability: 0.6,
82
+ jsonStability: 0.65,
83
+ editSuccessRate: 0.5,
84
+ testRepairRate: 0.45,
85
+ contextWindow: m.contextWindow,
86
+ cacheEconomics: "strong",
87
+ recommendedTasks: ["repo_summarization", "compaction"]
88
+ };
89
+ }
90
+ function buildModelCards(provider) {
91
+ return provider.models.map(capabilityCardForModel);
92
+ }
93
+
94
+ // src/agent/linucb-bandit.ts
95
+ init_esm_shims();
96
+ var DEFAULT_ALPHA = 1.5;
97
+ var DEFAULT_MIN_CONFIDENCE = 0.25;
98
+ var LinUCBBandit = class _LinUCBBandit {
99
+ arms = /* @__PURE__ */ new Map();
100
+ d;
101
+ alpha;
102
+ minConfidence;
103
+ totalPulls = 0;
104
+ constructor(config) {
105
+ this.d = config.dimension;
106
+ this.alpha = config.alpha ?? DEFAULT_ALPHA;
107
+ this.minConfidence = config.minConfidence ?? DEFAULT_MIN_CONFIDENCE;
108
+ }
109
+ /** Register an arm (action choice) */
110
+ addArm(id) {
111
+ if (this.arms.has(id)) return;
112
+ const A = new Array(this.d * this.d).fill(0);
113
+ for (let i = 0; i < this.d; i++) A[i * this.d + i] = 1;
114
+ const b = new Array(this.d).fill(0);
115
+ this.arms.set(id, { id, A, b, pulls: 0, totalReward: 0 });
116
+ }
117
+ /** Select best arm given context features */
118
+ recommend(context) {
119
+ if (this.arms.size === 0) return null;
120
+ if (context.length !== this.d) return null;
121
+ let bestArm = null;
122
+ let bestScore = -Infinity;
123
+ for (const [id, arm] of this.arms) {
124
+ const score2 = this.ucbScore(arm, context);
125
+ if (score2 > bestScore) {
126
+ bestScore = score2;
127
+ bestArm = id;
128
+ }
129
+ }
130
+ if (!bestArm) return null;
131
+ const confidence = this.totalPulls > 0 ? this.arms.get(bestArm).pulls / this.totalPulls : 0;
132
+ return { armId: bestArm, score: bestScore, confidence };
133
+ }
134
+ /** Should we suggest this action? (above min confidence threshold) */
135
+ shouldSuggest(context) {
136
+ const rec = this.recommend(context);
137
+ if (!rec) return null;
138
+ if (this.totalPulls < 10) return rec;
139
+ return rec.score > this.minConfidence ? rec : null;
140
+ }
141
+ /** Update arm with reward signal */
142
+ update(armId, context, reward) {
143
+ const arm = this.arms.get(armId);
144
+ if (!arm || context.length !== this.d) return;
145
+ for (let i = 0; i < this.d; i++) {
146
+ for (let j = 0; j < this.d; j++) {
147
+ arm.A[i * this.d + j] += context[i] * context[j];
148
+ }
149
+ }
150
+ for (let i = 0; i < this.d; i++) {
151
+ arm.b[i] += reward * context[i];
152
+ }
153
+ arm.pulls++;
154
+ arm.totalReward += reward;
155
+ this.totalPulls++;
156
+ }
157
+ /** Record accept signal (+0.75) */
158
+ accept(armId, context) {
159
+ this.update(armId, context, 0.75);
160
+ }
161
+ /** Record reject signal (-0.25) */
162
+ reject(armId, context) {
163
+ this.update(armId, context, -0.25);
164
+ }
165
+ /** Get arm statistics */
166
+ getStats() {
167
+ return [...this.arms.values()].map((a) => ({
168
+ id: a.id,
169
+ pulls: a.pulls,
170
+ avgReward: a.pulls > 0 ? a.totalReward / a.pulls : 0
171
+ }));
172
+ }
173
+ /** Serialize state for persistence */
174
+ serialize() {
175
+ return JSON.stringify({
176
+ d: this.d,
177
+ alpha: this.alpha,
178
+ totalPulls: this.totalPulls,
179
+ arms: [...this.arms.entries()]
180
+ });
181
+ }
182
+ /** Restore from serialized state */
183
+ static deserialize(json, config) {
184
+ const data = JSON.parse(json);
185
+ const bandit = new _LinUCBBandit(config);
186
+ bandit.totalPulls = data.totalPulls ?? 0;
187
+ for (const [id, arm] of data.arms) {
188
+ bandit.arms.set(id, arm);
189
+ }
190
+ return bandit;
191
+ }
192
+ /**
193
+ * In-place restore of arms + totalPulls from a serialized snapshot.
194
+ *
195
+ * Unlike `deserialize` (which builds a NEW instance), this overwrites the
196
+ * live instance's state so a long-lived bandit reference can pick up
197
+ * cross-session history. This is a REPLACE, not a merge: a freshly
198
+ * constructed bandit has no learning worth preserving, so dropping its
199
+ * cold-start arms in favor of the persisted ones is intentional.
200
+ *
201
+ * Throws on malformed JSON / shape; callers persisting non-critical state
202
+ * should wrap in try/catch.
203
+ */
204
+ importState(json) {
205
+ const data = JSON.parse(json);
206
+ this.arms = /* @__PURE__ */ new Map();
207
+ for (const [id, arm] of data.arms) {
208
+ this.arms.set(id, arm);
209
+ }
210
+ this.totalPulls = data.totalPulls ?? 0;
211
+ }
212
+ ucbScore(arm, x) {
213
+ const theta = new Array(this.d).fill(0);
214
+ for (let i = 0; i < this.d; i++) {
215
+ const aii = arm.A[i * this.d + i];
216
+ theta[i] = aii > 0 ? arm.b[i] / aii : 0;
217
+ }
218
+ let exploit = 0;
219
+ let explore = 0;
220
+ for (let i = 0; i < this.d; i++) {
221
+ exploit += theta[i] * x[i];
222
+ const aii = arm.A[i * this.d + i];
223
+ explore += aii > 0 ? x[i] * x[i] / aii : 0;
224
+ }
225
+ return exploit + this.alpha * Math.sqrt(explore);
226
+ }
227
+ };
228
+
229
+ // src/agent/skill-gate.ts
230
+ init_esm_shims();
231
+ var SKILL_CALL_RE = /skill\(\s*name\s*=\s*["'「]?([A-Za-z][\w-]*)/g;
232
+ var DIRECTIVE_RE = /(?:使用|调用|加载|遵循|按照|执行|follow(?:ing)?|use|using|load)\s*[`「"']?([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)+)[`」"']?/gi;
233
+ var SUFFIX_RE = /[`「]([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)+)[`」]\s*(?:skill|技能)/gi;
234
+ function extractRequiredSkills(planContent) {
235
+ const seen = /* @__PURE__ */ new Map();
236
+ for (const re of [SKILL_CALL_RE, DIRECTIVE_RE, SUFFIX_RE]) {
237
+ re.lastIndex = 0;
238
+ for (const m of planContent.matchAll(re)) {
239
+ const name = m[1];
240
+ const key2 = name.toLowerCase();
241
+ if (!seen.has(key2)) seen.set(key2, name);
242
+ }
243
+ }
244
+ return [...seen.values()];
245
+ }
246
+ var invokedBySession = /* @__PURE__ */ new Map();
247
+ function key(sessionId) {
248
+ return sessionId ?? "__default__";
249
+ }
250
+ function recordSkillInvoked(name, sessionId) {
251
+ const k = key(sessionId);
252
+ let set = invokedBySession.get(k);
253
+ if (!set) {
254
+ set = /* @__PURE__ */ new Set();
255
+ invokedBySession.set(k, set);
256
+ }
257
+ set.add(name.toLowerCase());
258
+ }
259
+ function getInvokedSkills(sessionId) {
260
+ return invokedBySession.get(key(sessionId)) ?? /* @__PURE__ */ new Set();
261
+ }
262
+ function clearSkillGate(sessionId) {
263
+ invokedBySession.delete(key(sessionId));
264
+ }
265
+ function isSkillGateEnabled() {
266
+ return process.env.RIVET_SKILL_GATE !== "0";
267
+ }
268
+ function evaluateSkillGate(required, opts) {
269
+ const available = new Set([...opts.availableNames].map((n) => n.toLowerCase()));
270
+ const invoked = new Set([...opts.invokedNames].map((n) => n.toLowerCase()));
271
+ const retired = new Set(RETIRED_BUNDLED_SKILLS.map((e) => e.name.toLowerCase()));
272
+ const missing = [];
273
+ const unavailable = [];
274
+ const native = [];
275
+ for (const name of required) {
276
+ const k = name.toLowerCase();
277
+ if (retired.has(k)) {
278
+ native.push(name);
279
+ } else if (!available.has(k)) {
280
+ unavailable.push(name);
281
+ } else if (!invoked.has(k)) {
282
+ missing.push(name);
283
+ }
284
+ }
285
+ return { missing, unavailable, native };
286
+ }
287
+ function formatSkillGateBlock(missing) {
288
+ return `\u6280\u80FD\u95E8\u7981\uFF1A\u8BA1\u5212\u70B9\u540D\u4E86\u6D41\u7A0B skill\u300C${missing.join("\u3001")}\u300D\uFF0C\u4F46\u672C\u4F1A\u8BDD\u5C1A\u672A\u52A0\u8F7D\u3002
289
+ \u8BA1\u5212\u70B9\u540D\u7684 skill \u662F\u8BE5\u8BA1\u5212\u4E13\u5C5E\u7684\u6267\u884C\u5951\u7EA6\uFF0C\u6B63\u6587\u53EA\u542B\u901A\u7528\u7EAA\u5F8B\u4E4B\u5916\u7684\u589E\u91CF\u6D41\u7A0B\u3002
290
+ ` + missing.map((n) => `\u5148\u6267\u884C skill(name="${n}") \u52A0\u8F7D\u5E76\u9075\u5FAA\u5176\u6D41\u7A0B\uFF0C`).join("") + `\u518D\u91CD\u65B0\u53D1\u8D77\u6267\u884C\u3002\u9003\u751F\u9600\uFF1ARIVET_SKILL_GATE=0\u3002`;
291
+ }
292
+
293
+ // src/agent/gated-influence-audit.ts
294
+ init_esm_shims();
295
+ function sanitizeEvidenceWindow(input) {
296
+ const out = {};
297
+ if (!input) return out;
298
+ for (const [key2, value] of Object.entries(input)) {
299
+ if (value === void 0) continue;
300
+ if (typeof value === "number" && !Number.isFinite(value)) continue;
301
+ out[key2] = value;
302
+ }
303
+ return out;
304
+ }
305
+ function safeTargetId(value) {
306
+ return value.replace(/[^a-zA-Z0-9:_@.-]/g, "_").slice(0, 160) || "unknown";
307
+ }
308
+ function gatedInfluenceAuditKind(event) {
309
+ return `gated_influence_audit:${event.source}:${event.sessionId}:${safeTargetId(event.targetId)}:${event.timestamp}`;
310
+ }
311
+ function buildGatedInfluenceAuditEvent(input) {
312
+ return {
313
+ schemaVersion: 1,
314
+ source: input.source,
315
+ sessionId: input.sessionId,
316
+ targetId: input.targetId,
317
+ gateOpen: input.gateOpen,
318
+ applied: input.applied,
319
+ reason: input.reason,
320
+ evidenceWindow: sanitizeEvidenceWindow(input.evidenceWindow),
321
+ vetoSignals: [...new Set(input.vetoSignals ?? [])].sort(),
322
+ timestamp: input.timestamp ?? Date.now()
323
+ };
324
+ }
325
+ function persistGatedInfluenceAudit(store, event) {
326
+ if (!store) return;
327
+ try {
328
+ store.saveBanditState(gatedInfluenceAuditKind(event), JSON.stringify(event));
329
+ } catch {
330
+ }
331
+ }
332
+
333
+ // src/agent/team-wave-telemetry.ts
334
+ init_esm_shims();
335
+ import { createHash } from "crypto";
336
+ function hashTeamObjective(input) {
337
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
338
+ }
339
+ function teamWaveTelemetryKind(event) {
340
+ return `team_wave:${event.objectiveHash}:${event.sessionId}:${event.fromWave}:${event.timestamp}`;
341
+ }
342
+ function uniqueSorted(values) {
343
+ return [...new Set(values.filter(Boolean))].sort();
344
+ }
345
+ function extractObservedChangedFilesFromArtifacts(run) {
346
+ const files = /* @__PURE__ */ new Set();
347
+ for (const result of run.results) {
348
+ for (const artifact of result.artifacts) {
349
+ if (artifact.kind !== "diff") continue;
350
+ for (const match of artifact.content.matchAll(/^\+\+\+ b\/(.+)$/gm)) {
351
+ if (match[1]) files.add(match[1]);
352
+ }
353
+ }
354
+ }
355
+ return [...files].sort();
356
+ }
357
+ function extractReportedChangedFiles(run) {
358
+ return uniqueSorted(run.results.flatMap((result) => result.changedFiles));
359
+ }
360
+ function taskIdFromWorkOrderId(workOrderId) {
361
+ if (workOrderId.startsWith("team:")) return workOrderId.slice("team:".length);
362
+ if (workOrderId.startsWith("task:")) return workOrderId.slice("task:".length);
363
+ return null;
364
+ }
365
+ function extractObservedChangedFilesFromResult(result) {
366
+ const files = /* @__PURE__ */ new Set();
367
+ for (const artifact of result.artifacts) {
368
+ if (artifact.kind !== "diff") continue;
369
+ for (const match of artifact.content.matchAll(/^\+\+\+ b\/(.+)$/gm)) {
370
+ if (match[1]) files.add(match[1]);
371
+ }
372
+ }
373
+ return [...files].sort();
374
+ }
375
+ function buildChangedFilesByTask(run, taskIds) {
376
+ const taskSet = new Set(taskIds);
377
+ const reported = /* @__PURE__ */ new Map();
378
+ const observed = /* @__PURE__ */ new Map();
379
+ for (const result of run.results) {
380
+ const taskId = taskIdFromWorkOrderId(result.workOrderId);
381
+ if (!taskId || !taskSet.has(taskId)) continue;
382
+ const reportedFiles = uniqueSorted(result.changedFiles);
383
+ if (reportedFiles.length > 0) reported.set(taskId, reportedFiles);
384
+ const observedFiles = extractObservedChangedFilesFromResult(result);
385
+ if (observedFiles.length > 0) observed.set(taskId, observedFiles);
386
+ }
387
+ return {
388
+ reportedChangedFilesByTask: [...reported.entries()].map(([taskId, files]) => ({ taskId, files })).sort((a, b) => a.taskId.localeCompare(b.taskId)),
389
+ observedChangedFilesByTask: [...observed.entries()].map(([taskId, files]) => ({ taskId, files })).sort((a, b) => a.taskId.localeCompare(b.taskId))
390
+ };
391
+ }
392
+ function buildTeamWaveTelemetry(input) {
393
+ const tasks = input.wave.taskIds.map((id) => input.taskMap.get(id)).filter((task) => Boolean(task));
394
+ const reportedChangedFiles = extractReportedChangedFiles(input.run);
395
+ const observedChangedFiles = extractObservedChangedFilesFromArtifacts(input.run);
396
+ const { reportedChangedFilesByTask, observedChangedFilesByTask } = buildChangedFilesByTask(input.run, input.wave.taskIds);
397
+ const changedFilesSource = observedChangedFiles.length > 0 ? "diff_artifact" : reportedChangedFiles.length > 0 ? "worker_result" : "unknown";
398
+ const verificationStates = input.run.results.map((result) => result.verification?.status).filter((status) => Boolean(status));
399
+ const verificationPassed = verificationStates.length > 0 ? verificationStates.every((status) => status === "passed") : void 0;
400
+ return {
401
+ schemaVersion: 1,
402
+ sessionId: input.sessionId,
403
+ objectiveHash: hashTeamObjective(input.objective),
404
+ mode: input.mode,
405
+ fromWave: input.fromWave,
406
+ waveId: input.wave.id,
407
+ waveCount: input.waves.length,
408
+ timestamp: input.timestamp ?? Date.now(),
409
+ planned: {
410
+ taskIds: [...input.wave.taskIds],
411
+ risk: input.wave.risk,
412
+ profiles: uniqueSorted(tasks.map((task) => task.profile)),
413
+ authorities: uniqueSorted(tasks.map((task) => taskAuthority(task))),
414
+ files: uniqueSorted(tasks.flatMap((task) => task.touchSet.length > 0 ? task.touchSet : task.files)),
415
+ taskDependencies: tasks.filter((task) => task.dependsOn.length > 0).map((task) => ({ taskId: task.id, dependsOn: uniqueSorted(task.dependsOn.map(dependencyId)) }))
416
+ },
417
+ outcome: {
418
+ dispatched: input.dispatched ?? input.run.results.length,
419
+ statuses: input.run.results.map((result) => ({
420
+ workOrderId: result.workOrderId,
421
+ status: result.status,
422
+ evidenceStatus: result.evidenceStatus
423
+ })),
424
+ ...verificationPassed === void 0 ? {} : { verificationPassed }
425
+ },
426
+ changedFiles: {
427
+ ...reportedChangedFiles.length > 0 ? { reportedChangedFiles } : {},
428
+ ...observedChangedFiles.length > 0 ? { observedChangedFiles } : {},
429
+ ...reportedChangedFilesByTask.length > 0 ? { reportedChangedFilesByTask } : {},
430
+ ...observedChangedFilesByTask.length > 0 ? { observedChangedFilesByTask } : {},
431
+ changedFilesSource
432
+ },
433
+ ...input.run.workerModels && input.run.workerModels.length > 0 ? { workerModels: input.run.workerModels.map((model) => ({ ...model })) } : {},
434
+ ...input.run.modelTierShadows && input.run.modelTierShadows.length > 0 ? {
435
+ workerModelTierShadows: input.run.modelTierShadows.map((event) => ({
436
+ workOrderId: event.workOrderId,
437
+ recommendedTier: event.recommendedTier,
438
+ actualTier: event.actualTier,
439
+ matched: event.matched,
440
+ reason: event.reason
441
+ }))
442
+ } : {}
443
+ };
444
+ }
445
+ function taskAuthority(task) {
446
+ if (task.profile === "patcher") return "tianliang";
447
+ if (task.profile === "reviewer" || task.profile === "adversarial_verifier") return "tianquan";
448
+ return matchDomain(task.objective) ?? "tianliang";
449
+ }
450
+ function persistTeamWaveTelemetry(store, event) {
451
+ if (!store) return;
452
+ try {
453
+ store.saveBanditState(teamWaveTelemetryKind(event), JSON.stringify(event));
454
+ } catch {
455
+ }
456
+ }
457
+
458
+ // src/agent/team-scope-health.ts
459
+ init_esm_shims();
460
+ import { createHash as createHash2 } from "crypto";
461
+ function shortHash(input) {
462
+ return createHash2("sha256").update(input).digest("hex").slice(0, 8);
463
+ }
464
+ function uniqueSorted2(values) {
465
+ return [...new Set(values.filter(Boolean))].sort();
466
+ }
467
+ function selectActualFiles(input) {
468
+ const observed = uniqueSorted2(input.observedChangedFiles ?? []);
469
+ if (observed.length > 0) return { actualFiles: observed, source: "diff_artifact", reason: "actual_files_from_observed_diff" };
470
+ const reported = uniqueSorted2(input.reportedChangedFiles ?? []);
471
+ if (reported.length > 0) return { actualFiles: reported, source: "worker_result", reason: "actual_files_from_worker_report" };
472
+ return { actualFiles: [], source: input.changedFilesSource === "mixed" ? "mixed" : "unknown", reason: "no_actual_files_observed" };
473
+ }
474
+ function isHighRiskScopePath(file) {
475
+ const normalized = file.replace(/\\/g, "/");
476
+ return /(^|\/)(package-lock\.json|package\.json|tsconfig\.json|\.env(?:\..*)?|schema|schemas|migration|migrations|prompt|prompts|security|auth|config)(\/|$|\.)/.test(normalized);
477
+ }
478
+ function computeSeverity(input) {
479
+ if (input.actualFiles.length === 0) {
480
+ input.reasons.push("no_actual_files_observed");
481
+ return "healthy";
482
+ }
483
+ if (input.plannedFiles.length === 0) {
484
+ input.reasons.push("actual_files_without_planned_scope");
485
+ return "high";
486
+ }
487
+ if (input.leakedFiles.some(isHighRiskScopePath)) {
488
+ input.reasons.push("high_risk_scope_leak");
489
+ return "high";
490
+ }
491
+ if (input.leakedFiles.length > 0) {
492
+ input.reasons.push("scope_leak_detected");
493
+ return "medium";
494
+ }
495
+ if (input.missingFiles.length > 0) {
496
+ input.reasons.push("planned_files_not_observed");
497
+ return "low";
498
+ }
499
+ input.reasons.push("scope_healthy");
500
+ return "healthy";
501
+ }
502
+ function buildTeamScopeHealth(input) {
503
+ const plannedFiles = uniqueSorted2(input.plannedFiles);
504
+ const plannedSet = new Set(plannedFiles);
505
+ const selected = selectActualFiles(input);
506
+ const actualFiles = selected.actualFiles;
507
+ const actualSet = new Set(actualFiles);
508
+ const coveredFiles = actualFiles.filter((file) => plannedSet.has(file));
509
+ const leakedFiles = actualFiles.filter((file) => !plannedSet.has(file));
510
+ const missingFiles = plannedFiles.filter((file) => !actualSet.has(file));
511
+ const reasons = [selected.reason];
512
+ const severity = computeSeverity({ plannedFiles, actualFiles, leakedFiles, missingFiles, reasons });
513
+ return {
514
+ schemaVersion: 1,
515
+ sourceKind: input.sourceKind,
516
+ sourceKey: input.sourceKey,
517
+ sessionId: input.sessionId,
518
+ objectiveHash: input.objectiveHash,
519
+ plannedFiles,
520
+ actualFiles,
521
+ coveredFiles,
522
+ leakedFiles,
523
+ missingFiles,
524
+ changedFilesSource: selected.source,
525
+ scopeLeakRate: actualFiles.length === 0 ? 0 : leakedFiles.length / actualFiles.length,
526
+ coverageRate: plannedFiles.length === 0 ? 0 : coveredFiles.length / plannedFiles.length,
527
+ severity,
528
+ reasons: uniqueSorted2(reasons),
529
+ timestamp: input.timestamp
530
+ };
531
+ }
532
+ function buildTeamWaveScopeHealth(event, options = {}) {
533
+ return buildTeamScopeHealth({
534
+ sourceKind: "team_wave",
535
+ sourceKey: teamWaveTelemetryKind(event),
536
+ sessionId: event.sessionId,
537
+ objectiveHash: event.objectiveHash,
538
+ plannedFiles: event.planned.files,
539
+ reportedChangedFiles: event.changedFiles.reportedChangedFiles,
540
+ observedChangedFiles: event.changedFiles.observedChangedFiles,
541
+ changedFilesSource: event.changedFiles.changedFilesSource,
542
+ timestamp: options.timestamp ?? event.timestamp
543
+ });
544
+ }
545
+ function buildTeamEpisodeScopeHealth(episode, options = {}) {
546
+ return buildTeamScopeHealth({
547
+ sourceKind: "team_episode",
548
+ sourceKey: episode.episodeKey,
549
+ sessionId: episode.sessionId,
550
+ objectiveHash: episode.objectiveHash,
551
+ plannedFiles: episode.planned.files,
552
+ reportedChangedFiles: episode.changedFiles.reportedChangedFiles,
553
+ observedChangedFiles: episode.changedFiles.observedChangedFiles,
554
+ changedFilesSource: episode.changedFiles.changedFilesSource,
555
+ timestamp: options.timestamp ?? episode.timestamp
556
+ });
557
+ }
558
+ function teamScopeHealthPersistKind(event) {
559
+ const sourceSeed = `${event.sourceKey}:${event.actualFiles.join("|")}:${event.plannedFiles.join("|")}`;
560
+ return `team_scope_health:${event.objectiveHash}:${event.sessionId}:${event.sourceKind}:${event.timestamp}:${shortHash(sourceSeed)}`;
561
+ }
562
+ function persistTeamScopeHealth(store, event) {
563
+ if (!store) return;
564
+ try {
565
+ store.saveBanditState(teamScopeHealthPersistKind(event), JSON.stringify(event));
566
+ } catch {
567
+ }
568
+ }
569
+
570
+ // src/agent/team-reward.ts
571
+ init_esm_shims();
572
+
573
+ // src/agent/routing-reward.ts
574
+ init_esm_shims();
575
+ var REVIEW_PASS_WEIGHT = 0.3;
576
+ var VERIFICATION_PASS_WEIGHT = 0.3;
577
+ var COST_OVER_BUDGET_WEIGHT = 0.1;
578
+ var LATENCY_SURPRISAL_WEIGHT = 0.1;
579
+ var FALSE_GREEN_WEIGHT = 0.6;
580
+ function clamp(value, min, max) {
581
+ if (!Number.isFinite(value)) return 0;
582
+ return Math.min(max, Math.max(min, value));
583
+ }
584
+ function scoreOptionalBoolean(value) {
585
+ return value === true ? 1 : 0;
586
+ }
587
+ function scorePenaltyFlag(value) {
588
+ return value === true ? 1 : 0;
589
+ }
590
+ function normalizeUnitPenalty(value) {
591
+ return clamp(value ?? 0, 0, 1);
592
+ }
593
+ function computeRoutingReward(input) {
594
+ const reviewPass = scoreOptionalBoolean(input.reviewPass);
595
+ const verificationPass = scoreOptionalBoolean(input.verificationPass);
596
+ const normalizedCostOverBudget = normalizeUnitPenalty(input.normalizedCostOverBudget);
597
+ const normalizedLatencySurprisal = normalizeUnitPenalty(input.normalizedLatencySurprisal);
598
+ const falseGreen = scorePenaltyFlag(input.falseGreen);
599
+ const reward = REVIEW_PASS_WEIGHT * reviewPass + VERIFICATION_PASS_WEIGHT * verificationPass - COST_OVER_BUDGET_WEIGHT * normalizedCostOverBudget - LATENCY_SURPRISAL_WEIGHT * normalizedLatencySurprisal - FALSE_GREEN_WEIGHT * falseGreen;
600
+ return clamp(reward, -1, 1);
601
+ }
602
+ function buildRoutingRewardRecord(input) {
603
+ const normalizedCostOverBudget = normalizeUnitPenalty(input.normalizedCostOverBudget);
604
+ const normalizedLatencySurprisal = normalizeUnitPenalty(input.normalizedLatencySurprisal);
605
+ const components = {
606
+ currentModel: input.currentModel,
607
+ hasRecommendedModel: input.recommendedModel !== void 0,
608
+ modelMatched: input.recommendedModel !== void 0 && input.currentModel === input.recommendedModel,
609
+ reviewObserved: input.reviewPass !== void 0,
610
+ reviewPass: scoreOptionalBoolean(input.reviewPass),
611
+ verificationObserved: input.verificationPass !== void 0,
612
+ verificationPass: scoreOptionalBoolean(input.verificationPass),
613
+ falseGreen: input.falseGreen === true,
614
+ normalizedCostOverBudget,
615
+ normalizedLatencySurprisal
616
+ };
617
+ if (input.recommendedModel !== void 0) components.recommendedModel = input.recommendedModel;
618
+ return {
619
+ schemaVersion: 1,
620
+ reward: computeRoutingReward({
621
+ ...input,
622
+ normalizedCostOverBudget,
623
+ normalizedLatencySurprisal
624
+ }),
625
+ components
626
+ };
627
+ }
628
+
629
+ // src/agent/team-reward.ts
630
+ var REVIEW_PASS_WEIGHT2 = 0.3;
631
+ var VERIFICATION_PASS_WEIGHT2 = 0.3;
632
+ var CONFLICT_WEIGHT = 0.15;
633
+ var REWORK_WEIGHT = 0.15;
634
+ var SCOPE_LEAK_WEIGHT = 0.15;
635
+ var COST_OVER_BUDGET_WEIGHT2 = 0.1;
636
+ var LATENCY_SURPRISAL_WEIGHT2 = 0.1;
637
+ var FALSE_GREEN_WEIGHT2 = 0.6;
638
+ function clampReward(value) {
639
+ if (!Number.isFinite(value)) return 0;
640
+ return Math.min(1, Math.max(-1, value));
641
+ }
642
+ function scoreOptionalBoolean2(value) {
643
+ return value === true ? 1 : 0;
644
+ }
645
+ function normalizeReviewVerdict(verdict) {
646
+ if (!verdict) return void 0;
647
+ const normalized = verdict.trim().toLowerCase().replace(/[\s_-]+/g, "-");
648
+ if (!normalized) return void 0;
649
+ if (["pass", "passed", "verified", "approve", "approved", "ok", "clean", "green", "no-findings"].includes(normalized)) return true;
650
+ if (["fail", "failed", "rejected", "reject", "blocked", "changes-requested", "red"].includes(normalized)) return false;
651
+ return void 0;
652
+ }
653
+ function denominator(event) {
654
+ return Math.max(event.outcome.dispatched, event.outcome.statuses.length, 1);
655
+ }
656
+ function normalizeScopeLeak(event) {
657
+ return buildTeamWaveScopeHealth(event).scopeLeakRate;
658
+ }
659
+ function hasFalseGreen(event, reviewPass) {
660
+ const statuses = event.outcome.statuses;
661
+ const hasPassedWithoutVerifiedEvidence = statuses.some(
662
+ (status) => status.status === "passed" && status.evidenceStatus !== "verified"
663
+ );
664
+ const allPassed = statuses.length > 0 && statuses.every((status) => status.status === "passed");
665
+ return hasPassedWithoutVerifiedEvidence || allPassed && event.outcome.verificationPassed === false || allPassed && reviewPass === false;
666
+ }
667
+ function computeTeamWaveReward(input) {
668
+ const reviewPass = scoreOptionalBoolean2(input.reviewPass);
669
+ const verificationPass = scoreOptionalBoolean2(input.verificationPass);
670
+ const normalizedConflict = normalizeUnitPenalty(input.normalizedConflict);
671
+ const normalizedRework = normalizeUnitPenalty(input.normalizedRework);
672
+ const normalizedScopeLeak = normalizeUnitPenalty(input.normalizedScopeLeak);
673
+ const normalizedCostOverBudget = normalizeUnitPenalty(input.normalizedCostOverBudget);
674
+ const normalizedLatencySurprisal = normalizeUnitPenalty(input.normalizedLatencySurprisal);
675
+ const falseGreen = input.falseGreen ? 1 : 0;
676
+ const reward = REVIEW_PASS_WEIGHT2 * reviewPass + VERIFICATION_PASS_WEIGHT2 * verificationPass - CONFLICT_WEIGHT * normalizedConflict - REWORK_WEIGHT * normalizedRework - SCOPE_LEAK_WEIGHT * normalizedScopeLeak - COST_OVER_BUDGET_WEIGHT2 * normalizedCostOverBudget - LATENCY_SURPRISAL_WEIGHT2 * normalizedLatencySurprisal - FALSE_GREEN_WEIGHT2 * falseGreen;
677
+ return clampReward(reward);
678
+ }
679
+ function deriveTeamWaveRewardInput(event) {
680
+ const denom = denominator(event);
681
+ const reviewPass = normalizeReviewVerdict(event.outcome.reviewVerdict);
682
+ const blockedOrEscalated = event.outcome.statuses.filter(
683
+ (status) => status.status === "blocked" || status.status === "escalated"
684
+ ).length;
685
+ const failedOrFailedEvidence = event.outcome.statuses.filter(
686
+ (status) => status.status === "failed" || status.evidenceStatus === "failed"
687
+ ).length;
688
+ return {
689
+ verificationPass: event.outcome.verificationPassed,
690
+ reviewPass,
691
+ normalizedConflict: normalizeUnitPenalty(blockedOrEscalated / denom),
692
+ normalizedRework: normalizeUnitPenalty(failedOrFailedEvidence / denom),
693
+ normalizedScopeLeak: normalizeScopeLeak(event),
694
+ // P1 has no cost/latency telemetry in TeamWaveTelemetry yet. Missing facts
695
+ // are neutral rather than guessed.
696
+ normalizedCostOverBudget: 0,
697
+ normalizedLatencySurprisal: 0,
698
+ falseGreen: hasFalseGreen(event, reviewPass)
699
+ };
700
+ }
701
+ function buildTeamWaveRewardRecord(input) {
702
+ const normalizedConflict = normalizeUnitPenalty(input.normalizedConflict);
703
+ const normalizedRework = normalizeUnitPenalty(input.normalizedRework);
704
+ const normalizedScopeLeak = normalizeUnitPenalty(input.normalizedScopeLeak);
705
+ const normalizedCostOverBudget = normalizeUnitPenalty(input.normalizedCostOverBudget);
706
+ const normalizedLatencySurprisal = normalizeUnitPenalty(input.normalizedLatencySurprisal);
707
+ return {
708
+ schemaVersion: 1,
709
+ reward: computeTeamWaveReward({
710
+ ...input,
711
+ normalizedConflict,
712
+ normalizedRework,
713
+ normalizedScopeLeak,
714
+ normalizedCostOverBudget,
715
+ normalizedLatencySurprisal
716
+ }),
717
+ components: {
718
+ reviewObserved: input.reviewPass !== void 0,
719
+ reviewPass: scoreOptionalBoolean2(input.reviewPass),
720
+ verificationObserved: input.verificationPass !== void 0,
721
+ verificationPass: scoreOptionalBoolean2(input.verificationPass),
722
+ normalizedConflict,
723
+ normalizedRework,
724
+ normalizedScopeLeak,
725
+ normalizedCostOverBudget,
726
+ normalizedLatencySurprisal,
727
+ falseGreen: input.falseGreen
728
+ }
729
+ };
730
+ }
731
+
732
+ // src/agent/team-episode.ts
733
+ init_esm_shims();
734
+ import { createHash as createHash3 } from "crypto";
735
+ function teamEpisodeKey(input) {
736
+ return `team_episode:${input.objectiveHash}:${input.sessionId}:${input.mode}:${input.waveCount}`;
737
+ }
738
+ function shortHash2(input) {
739
+ return createHash3("sha256").update(input).digest("hex").slice(0, 8);
740
+ }
741
+ function teamEpisodePersistKind(episode) {
742
+ const sourceSeed = episode.fragments.map((fragment) => fragment.sourceKey).join("|");
743
+ return `${episode.episodeKey}:${episode.timestamp}:${shortHash2(sourceSeed)}`;
744
+ }
745
+ function uniqueSorted3(values) {
746
+ return [...new Set(values.filter(Boolean))].sort();
747
+ }
748
+ function riskRank(risk) {
749
+ return risk === "high" ? 2 : risk === "medium" ? 1 : 0;
750
+ }
751
+ function maxRisk(values) {
752
+ return values.reduce((max, value) => riskRank(value) > riskRank(max) ? value : max, "low");
753
+ }
754
+ function duplicateIndexes(indexes) {
755
+ const counts = /* @__PURE__ */ new Map();
756
+ for (const index of indexes) counts.set(index, (counts.get(index) ?? 0) + 1);
757
+ return [...counts.entries()].filter(([, count]) => count > 1).map(([index]) => index).sort((a, b) => a - b);
758
+ }
759
+ function missingIndexes(indexes, waveCount) {
760
+ const observed = new Set(indexes);
761
+ const missing = [];
762
+ for (let index = 0; index < waveCount; index++) {
763
+ if (!observed.has(index)) missing.push(index);
764
+ }
765
+ return missing;
766
+ }
767
+ function aggregateVerification(values) {
768
+ if (values.some((value) => value === false)) return false;
769
+ const observed = values.filter((value) => value !== void 0);
770
+ if (observed.length > 0 && observed.length === values.length && observed.every(Boolean)) return true;
771
+ return void 0;
772
+ }
773
+ function normalizeReviewVerdict2(verdict) {
774
+ if (!verdict) return void 0;
775
+ const normalized = verdict.trim().toLowerCase().replace(/[\s_-]+/g, "-");
776
+ if (!normalized) return void 0;
777
+ if (["pass", "passed", "verified", "approve", "approved", "ok", "clean", "green", "no-findings"].includes(normalized)) return true;
778
+ if (["fail", "failed", "rejected", "reject", "blocked", "changes-requested", "red"].includes(normalized)) return false;
779
+ return void 0;
780
+ }
781
+ function aggregateReviewVerdict(values) {
782
+ const normalized = values.map(normalizeReviewVerdict2);
783
+ if (normalized.some((value) => value === false)) return { pass: false, verdict: "fail" };
784
+ const observed = normalized.filter((value) => value !== void 0);
785
+ if (observed.length > 0 && observed.length === values.length && observed.every(Boolean)) return { pass: true, verdict: "pass" };
786
+ return {};
787
+ }
788
+ function aggregateChangedFilesSource(sources) {
789
+ const unique = uniqueSorted3(sources);
790
+ if (unique.length === 0) return "unknown";
791
+ if (unique.length === 1) return unique[0];
792
+ return "mixed";
793
+ }
794
+ function buildTeamEpisode(fragments, options = {}) {
795
+ if (fragments.length === 0) throw new Error("buildTeamEpisode requires at least one fragment");
796
+ const first = fragments[0];
797
+ const mismatchReasons = [];
798
+ for (const fragment of fragments) {
799
+ if (fragment.sessionId !== first.sessionId) mismatchReasons.push(`sessionId mismatch: ${fragment.sessionId}`);
800
+ if (fragment.objectiveHash !== first.objectiveHash) mismatchReasons.push(`objectiveHash mismatch: ${fragment.objectiveHash}`);
801
+ if (fragment.mode !== first.mode) mismatchReasons.push(`mode mismatch: ${fragment.mode}`);
802
+ if (fragment.waveCount !== first.waveCount) mismatchReasons.push(`waveCount mismatch: ${fragment.waveCount}`);
803
+ }
804
+ const ordered = [...fragments].sort((a, b) => a.fromWave - b.fromWave || a.timestamp - b.timestamp);
805
+ const observedWaveIndexes = uniqueSorted3(ordered.map((fragment) => String(fragment.fromWave))).map(Number).sort((a, b) => a - b);
806
+ const duplicateWaveIndexes = duplicateIndexes(ordered.map((fragment) => fragment.fromWave));
807
+ const missingWaveIndexes = missingIndexes(observedWaveIndexes, first.waveCount);
808
+ const complete = mismatchReasons.length === 0 && duplicateWaveIndexes.length === 0 && missingWaveIndexes.length === 0 && observedWaveIndexes.length === first.waveCount;
809
+ const review = aggregateReviewVerdict(ordered.map((fragment) => fragment.outcome.reviewVerdict));
810
+ const observedChangedFiles = uniqueSorted3(ordered.flatMap((fragment) => fragment.changedFiles.observedChangedFiles ?? []));
811
+ const reportedChangedFiles = uniqueSorted3(ordered.flatMap((fragment) => fragment.changedFiles.reportedChangedFiles ?? []));
812
+ const episode = {
813
+ schemaVersion: 1,
814
+ sessionId: first.sessionId,
815
+ objectiveHash: first.objectiveHash,
816
+ mode: first.mode,
817
+ episodeKey: teamEpisodeKey({ objectiveHash: first.objectiveHash, sessionId: first.sessionId, mode: first.mode, waveCount: first.waveCount }),
818
+ complete,
819
+ waveCount: first.waveCount,
820
+ observedWaveIndexes,
821
+ missingWaveIndexes,
822
+ duplicateWaveIndexes,
823
+ fragments: ordered.map((telemetry) => ({ sourceKey: teamWaveTelemetryKind(telemetry), telemetry })),
824
+ planned: {
825
+ taskIds: uniqueSorted3(ordered.flatMap((fragment) => fragment.planned.taskIds)),
826
+ files: uniqueSorted3(ordered.flatMap((fragment) => fragment.planned.files)),
827
+ profiles: uniqueSorted3(ordered.flatMap((fragment) => fragment.planned.profiles)),
828
+ authorities: uniqueSorted3(ordered.flatMap((fragment) => fragment.planned.authorities)),
829
+ maxRisk: maxRisk(ordered.map((fragment) => fragment.planned.risk))
830
+ },
831
+ outcome: {
832
+ dispatched: ordered.reduce((sum, fragment) => sum + fragment.outcome.dispatched, 0),
833
+ statuses: ordered.flatMap((fragment) => fragment.outcome.statuses.map((status) => ({ ...status }))),
834
+ ...aggregateVerification(ordered.map((fragment) => fragment.outcome.verificationPassed)) === void 0 ? {} : { verificationPassed: aggregateVerification(ordered.map((fragment) => fragment.outcome.verificationPassed)) },
835
+ ...review.verdict ? { reviewVerdict: review.verdict } : {}
836
+ },
837
+ changedFiles: {
838
+ ...reportedChangedFiles.length > 0 ? { reportedChangedFiles } : {},
839
+ ...observedChangedFiles.length > 0 ? { observedChangedFiles } : {},
840
+ changedFilesSource: aggregateChangedFilesSource(ordered.map((fragment) => fragment.changedFiles.changedFilesSource))
841
+ },
842
+ mismatchReasons: uniqueSorted3(mismatchReasons),
843
+ timestamp: options.timestamp ?? Math.max(...ordered.map((fragment) => fragment.timestamp))
844
+ };
845
+ return episode;
846
+ }
847
+ function deriveTeamEpisodeRewardInput(episode) {
848
+ if (!episode.complete) return null;
849
+ const denom = Math.max(episode.outcome.dispatched, episode.outcome.statuses.length, 1);
850
+ const blockedOrEscalated = episode.outcome.statuses.filter(
851
+ (status) => status.status === "blocked" || status.status === "escalated"
852
+ ).length;
853
+ const failedOrFailedEvidence = episode.outcome.statuses.filter(
854
+ (status) => status.status === "failed" || status.evidenceStatus === "failed"
855
+ ).length;
856
+ const review = normalizeReviewVerdict2(episode.outcome.reviewVerdict);
857
+ const waveInputs = episode.fragments.map((fragment) => deriveTeamWaveRewardInput(fragment.telemetry));
858
+ return {
859
+ verificationPass: episode.outcome.verificationPassed,
860
+ reviewPass: review,
861
+ normalizedConflict: normalizeUnitPenalty(blockedOrEscalated / denom),
862
+ normalizedRework: normalizeUnitPenalty(failedOrFailedEvidence / denom),
863
+ normalizedScopeLeak: buildTeamEpisodeScopeHealth(episode).scopeLeakRate,
864
+ normalizedCostOverBudget: 0,
865
+ normalizedLatencySurprisal: 0,
866
+ falseGreen: waveInputs.some((input) => input.falseGreen)
867
+ };
868
+ }
869
+ function filesTouchedByMultipleWaves(episode) {
870
+ const counts = /* @__PURE__ */ new Map();
871
+ for (const fragment of episode.fragments) {
872
+ const files = fragment.telemetry.changedFiles.observedChangedFiles ?? fragment.telemetry.changedFiles.reportedChangedFiles ?? [];
873
+ for (const file of new Set(files.filter(Boolean))) counts.set(file, (counts.get(file) ?? 0) + 1);
874
+ }
875
+ return [...counts.entries()].filter(([, count]) => count > 1).map(([file]) => file).sort();
876
+ }
877
+ function formatTeamDelivery(episode) {
878
+ const lines = [];
879
+ lines.push(
880
+ `\u56E2\u961F\u4EA4\u4ED8\u7EFC\u5408 \u2014 ${episode.observedWaveIndexes.length}/${episode.waveCount} \u6CE2\uFF0C${episode.outcome.dispatched} \u4E2A worker${episode.complete ? "" : "\uFF08\u672A\u5B8C\u6210\uFF09"}`
881
+ );
882
+ for (const fragment of episode.fragments) {
883
+ const t = fragment.telemetry;
884
+ const statuses = t.outcome.statuses;
885
+ const ok = statuses.filter((s) => s.status === "passed" || s.status === "completed").length;
886
+ const total = statuses.length || t.planned.taskIds.length;
887
+ const ids = t.planned.taskIds.join(", ") || "\u2014";
888
+ lines.push(` \u6CE2 ${t.fromWave + 1}\uFF1A${ids}\uFF08${ok}/${total} \u901A\u8FC7\uFF09`);
889
+ }
890
+ const changed = episode.changedFiles.observedChangedFiles ?? episode.changedFiles.reportedChangedFiles ?? [];
891
+ lines.push(`\u6539\u52A8\u6587\u4EF6\uFF08${changed.length}\uFF09\uFF1A${changed.length > 0 ? changed.join(", ") : "\u65E0"}`);
892
+ const conflicts = filesTouchedByMultipleWaves(episode);
893
+ if (conflicts.length > 0) {
894
+ lines.push(`\u26A0 \u591A\u6CE2\u89E6\u78B0\u7684\u6587\u4EF6\uFF08\u8BF7\u5BA1\u67E5\u51B2\u7A81\uFF09\uFF1A${conflicts.join(", ")}`);
895
+ }
896
+ const verification = episode.outcome.verificationPassed;
897
+ const verdict = episode.outcome.reviewVerdict ?? (verification === true ? "verified" : verification === false ? "verification-failed" : "no-verdict");
898
+ const verifNote = verification !== void 0 ? ` verification=${verification ? "pass" : "fail"}` : "";
899
+ lines.push(`\u603B\u4F53\uFF1Areview=${verdict}${verifNote}`);
900
+ return lines.join("\n");
901
+ }
902
+ function persistTeamEpisode(store, episode) {
903
+ if (!store) return;
904
+ try {
905
+ store.saveBanditState(teamEpisodePersistKind(episode), JSON.stringify(episode));
906
+ } catch {
907
+ }
908
+ }
909
+
910
+ // src/agent/worker-episode.ts
911
+ init_esm_shims();
912
+ import { createHash as createHash4 } from "crypto";
913
+ function hashObjective(objective) {
914
+ return createHash4("sha256").update(objective).digest("hex").slice(0, 12);
915
+ }
916
+ function workerEpisodeKey(episode) {
917
+ return `worker_episode:${episode.sessionId}:${episode.orderId.replace(/:/g, "-")}:${episode.timestamp}`;
918
+ }
919
+ function buildWorkerEpisode(input) {
920
+ const { order, result, writeGate } = input;
921
+ const exhausted = result.failureReason === "timeout" || result.failureReason === "max_turns";
922
+ return {
923
+ schemaVersion: 1,
924
+ orderId: order.id,
925
+ sessionId: input.sessionId,
926
+ objectiveHash: hashObjective(order.objective),
927
+ model: input.model,
928
+ ...order.profile ? { profile: order.profile } : {},
929
+ role: input.role,
930
+ scopeFileCount: order.scope.files?.length ?? 0,
931
+ changedFileCount: result.changedFiles.length,
932
+ status: result.status,
933
+ ...result.evidenceStatus ? { evidenceStatus: result.evidenceStatus } : {},
934
+ gateOutcome: writeGate?.report.outcome ?? "not-run",
935
+ falseGreen: writeGate?.report.falseGreen === true,
936
+ repairCount: writeGate?.repairCount ?? 0,
937
+ timestamp: input.timestamp ?? Date.now(),
938
+ ...input.actuals?.toolUses !== void 0 ? { actualToolUses: input.actuals.toolUses } : {},
939
+ ...input.actuals?.durationMs !== void 0 ? { durationMs: input.actuals.durationMs } : {},
940
+ ...exhausted ? { exhausted: true } : {},
941
+ ...result.failureReason ? { failureReason: result.failureReason } : {},
942
+ ...input.actuals?.usage && (input.actuals.usage.input > 0 || input.actuals.usage.output > 0) ? { usage: input.actuals.usage } : {},
943
+ ...input.actuals?.budget ? { budget: input.actuals.budget } : {}
944
+ };
945
+ }
946
+ function persistWorkerEpisode(store, episode) {
947
+ if (!store) return;
948
+ try {
949
+ store.saveBanditState(workerEpisodeKey(episode), JSON.stringify(episode));
950
+ } catch {
951
+ }
952
+ }
953
+ function workerActualKey(objectiveHash, timestamp) {
954
+ return `worker_actual:${objectiveHash}:${timestamp}`;
955
+ }
956
+ function persistWorkerActualIndex(store, objectiveHash, row) {
957
+ if (!store) return;
958
+ try {
959
+ store.saveBanditState(workerActualKey(objectiveHash, Date.now()), JSON.stringify(row));
960
+ } catch {
961
+ }
962
+ }
963
+ function deriveWorkerEpisodeRewardInput(episode) {
964
+ if (episode.gateOutcome === "blocked") return null;
965
+ const base = { currentModel: episode.model };
966
+ if (episode.gateOutcome === "passed") return { ...base, verificationPass: true };
967
+ if (episode.gateOutcome === "failed") {
968
+ return { ...base, verificationPass: false, ...episode.falseGreen ? { falseGreen: true } : {} };
969
+ }
970
+ return base;
971
+ }
972
+
973
+ // src/agent/reward-loop.ts
974
+ init_esm_shims();
975
+ import { createHash as createHash6 } from "crypto";
976
+
977
+ // src/agent/model-routing-shadow.ts
978
+ init_esm_shims();
979
+ import { createHash as createHash5 } from "crypto";
980
+
981
+ // src/model/task-inferrer.ts
982
+ init_esm_shims();
983
+ function inferTaskType(recentCalls) {
984
+ if (recentCalls.length === 0) return null;
985
+ const names = recentCalls.map((c) => c.name);
986
+ const editCount = names.filter((n) => n === "edit_file" || n === "write_file").length;
987
+ const testCount = names.filter((n) => n === "run_tests" || n === "bash").length;
988
+ const searchCount = names.filter((n) => n === "grep" || n === "glob" || n === "read_file").length;
989
+ const hasTestFailure = recentCalls.some((c) => (c.name === "run_tests" || c.name === "bash") && c.isError);
990
+ if (testCount > 0 && hasTestFailure) {
991
+ return { task: "test_failure_diagnosis", reason: "test failure detected, diagnosis mode recommended" };
992
+ }
993
+ if (editCount >= 2 && testCount > 0) {
994
+ return { task: "risky_refactor", reason: "multi-file edit with verification, refactor-capable model recommended" };
995
+ }
996
+ if (editCount > 0) {
997
+ return { task: "code_edit", reason: "file modification, edit-capable model recommended" };
998
+ }
999
+ if (searchCount >= 3 && editCount === 0) {
1000
+ return { task: "repo_summarization", reason: "code exploration, context-capable model recommended" };
1001
+ }
1002
+ return null;
1003
+ }
1004
+
1005
+ // src/agent/model-routing-shadow.ts
1006
+ function hashObjective2(input) {
1007
+ return createHash5("sha256").update(input).digest("hex").slice(0, 16);
1008
+ }
1009
+ function routingShadowKind(event) {
1010
+ return `routing_shadow:${event.sessionId}:${event.turn}:${event.timestamp}`;
1011
+ }
1012
+ function inferLegacyRoutingRecommendation(recentCalls, modelCards) {
1013
+ if (!modelCards || modelCards.length === 0) return null;
1014
+ const inference = inferTaskType(recentCalls);
1015
+ if (!inference) return null;
1016
+ try {
1017
+ const recommended = recommendModelForTask(inference.task, modelCards);
1018
+ return {
1019
+ task: inference.task,
1020
+ model: recommended.model,
1021
+ reason: inference.reason
1022
+ };
1023
+ } catch {
1024
+ return null;
1025
+ }
1026
+ }
1027
+ function buildModelRoutingShadowEvent(input) {
1028
+ const legacyReason = input.legacyRouting ? `${input.legacyRouting.task} \xB7 ${input.legacyRouting.model} ${input.legacyRouting.reason}` : "legacy router produced no recommendation";
1029
+ return {
1030
+ schemaVersion: 1,
1031
+ sessionId: input.sessionId,
1032
+ turn: input.turn,
1033
+ objectiveHash: hashObjective2(input.objective),
1034
+ currentModel: input.currentModel,
1035
+ selectedBy: input.selectedBy ?? "config",
1036
+ ...input.legacyRouting ? { legacyRouterRecommendedModel: input.legacyRouting.model } : {},
1037
+ ...input.efeRecommendedModel ? { efeRecommendedModel: input.efeRecommendedModel } : {},
1038
+ ...input.verificationOutcome ? { verificationOutcome: input.verificationOutcome } : {},
1039
+ sensorium: {
1040
+ complexity: input.sensorium.complexity,
1041
+ pressure: input.sensorium.pressure,
1042
+ confidence: input.sensorium.confidence,
1043
+ stability: input.sensorium.stability
1044
+ },
1045
+ reason: input.reason ?? legacyReason,
1046
+ timestamp: input.timestamp ?? Date.now()
1047
+ };
1048
+ }
1049
+ function persistModelRoutingShadow(store, event) {
1050
+ if (!store) return;
1051
+ try {
1052
+ store.saveBanditState(routingShadowKind(event), JSON.stringify(event));
1053
+ } catch {
1054
+ }
1055
+ }
1056
+
1057
+ // src/agent/reward-loop.ts
1058
+ var MAX_TRACKED_SESSIONS = 512;
1059
+ var lastTimestampBySession = /* @__PURE__ */ new Map();
1060
+ function shortHash3(input) {
1061
+ return createHash6("sha256").update(input).digest("hex").slice(0, 8);
1062
+ }
1063
+ function resolveRewardClosureTimestamp(sessionId, explicitTimestamp) {
1064
+ const last = lastTimestampBySession.get(sessionId) ?? 0;
1065
+ let next;
1066
+ if (explicitTimestamp !== void 0) {
1067
+ next = explicitTimestamp;
1068
+ lastTimestampBySession.set(sessionId, Math.max(last, explicitTimestamp));
1069
+ } else {
1070
+ const now = Date.now();
1071
+ next = now > last ? now : last + 1;
1072
+ lastTimestampBySession.set(sessionId, next);
1073
+ }
1074
+ if (lastTimestampBySession.size > MAX_TRACKED_SESSIONS) {
1075
+ const oldest = lastTimestampBySession.keys().next().value;
1076
+ if (oldest !== void 0) lastTimestampBySession.delete(oldest);
1077
+ }
1078
+ return next;
1079
+ }
1080
+ function rewardClosureHashSeed(record) {
1081
+ return `${record.sourceKind}:${record.sourceKey}:${record.timestamp}`;
1082
+ }
1083
+ function rewardClosureKind(record) {
1084
+ return `reward_closure:${record.sourceKind}:${record.sessionId}:${record.timestamp}:${shortHash3(rewardClosureHashSeed(record))}`;
1085
+ }
1086
+ function buildRewardClosureRecord(input) {
1087
+ const timestamp = resolveRewardClosureTimestamp(input.sessionId, input.timestamp);
1088
+ const id = `${input.sourceKind}:${input.sourceKey}:${timestamp}:${shortHash3(`${input.sourceKind}:${input.sourceKey}:${timestamp}`)}`;
1089
+ return {
1090
+ schemaVersion: 1,
1091
+ id,
1092
+ sourceKind: input.sourceKind,
1093
+ sourceKey: input.sourceKey,
1094
+ ...input.objectiveHash ? { objectiveHash: input.objectiveHash } : {},
1095
+ sessionId: input.sessionId,
1096
+ reward: input.reward,
1097
+ components: input.components,
1098
+ timestamp
1099
+ };
1100
+ }
1101
+ function routingRewardInputFromEvent(event) {
1102
+ return {
1103
+ currentModel: event.currentModel,
1104
+ recommendedModel: event.efeRecommendedModel ?? event.legacyRouterRecommendedModel,
1105
+ // W4-D2: main-loop verification outcome flows into the shadow reward.
1106
+ // 'blocked' stays undefined — environment-neutral, never a capability signal.
1107
+ ...event.verificationOutcome === "passed" ? { verificationPass: true } : {},
1108
+ ...event.verificationOutcome === "failed" ? { verificationPass: false } : {},
1109
+ normalizedCostOverBudget: 0,
1110
+ normalizedLatencySurprisal: 0
1111
+ };
1112
+ }
1113
+ function buildRewardClosureRecordFromRoutingShadow(event, options) {
1114
+ const rewardInput = routingRewardInputFromEvent(event);
1115
+ const rewardRecord = buildRoutingRewardRecord(rewardInput);
1116
+ return buildRewardClosureRecord({
1117
+ sourceKind: "routing_shadow",
1118
+ sourceKey: routingShadowKind(event),
1119
+ objectiveHash: event.objectiveHash,
1120
+ sessionId: event.sessionId,
1121
+ reward: rewardRecord.reward,
1122
+ components: {
1123
+ ...rewardRecord.components,
1124
+ turn: event.turn,
1125
+ selectedBy: event.selectedBy,
1126
+ sensoriumComplexity: event.sensorium.complexity,
1127
+ sensoriumPressure: event.sensorium.pressure,
1128
+ sensoriumConfidence: event.sensorium.confidence,
1129
+ sensoriumStability: event.sensorium.stability
1130
+ },
1131
+ timestamp: options?.timestamp
1132
+ });
1133
+ }
1134
+ function buildRewardClosureRecordFromTeamWave(event, options) {
1135
+ const rewardInput = deriveTeamWaveRewardInput(event);
1136
+ const rewardRecord = buildTeamWaveRewardRecord(rewardInput);
1137
+ return buildRewardClosureRecord({
1138
+ sourceKind: "team_wave",
1139
+ sourceKey: teamWaveTelemetryKind(event),
1140
+ objectiveHash: event.objectiveHash,
1141
+ sessionId: event.sessionId,
1142
+ reward: rewardRecord.reward,
1143
+ components: {
1144
+ ...rewardRecord.components,
1145
+ mode: event.mode,
1146
+ fromWave: event.fromWave,
1147
+ waveId: event.waveId,
1148
+ waveCount: event.waveCount,
1149
+ dispatched: event.outcome.dispatched,
1150
+ ...event.outcome.reviewVerdict ? { reviewVerdict: event.outcome.reviewVerdict } : {},
1151
+ changedFilesSource: event.changedFiles.changedFilesSource,
1152
+ workerModelCount: event.workerModels?.length ?? 0,
1153
+ tierShadowCount: event.workerModelTierShadows?.length ?? 0,
1154
+ tierShadowMatchedCount: event.workerModelTierShadows?.filter((shadow) => shadow.matched).length ?? 0,
1155
+ ...event.workerModels?.length === 1 ? { workerModel: event.workerModels[0].model } : {}
1156
+ },
1157
+ timestamp: options?.timestamp
1158
+ });
1159
+ }
1160
+ function buildRewardClosureRecordFromTeamEpisode(episode, options) {
1161
+ const rewardInput = deriveTeamEpisodeRewardInput(episode);
1162
+ if (!rewardInput) return null;
1163
+ const rewardRecord = buildTeamWaveRewardRecord(rewardInput);
1164
+ return buildRewardClosureRecord({
1165
+ sourceKind: "team_episode",
1166
+ sourceKey: teamEpisodeKey(episode),
1167
+ objectiveHash: episode.objectiveHash,
1168
+ sessionId: episode.sessionId,
1169
+ reward: rewardRecord.reward,
1170
+ components: {
1171
+ ...rewardRecord.components,
1172
+ mode: episode.mode,
1173
+ waveCount: episode.waveCount,
1174
+ observedWaveCount: episode.observedWaveIndexes.length,
1175
+ dispatched: episode.outcome.dispatched,
1176
+ ...episode.outcome.reviewVerdict ? { reviewVerdict: episode.outcome.reviewVerdict } : {},
1177
+ changedFilesSource: episode.changedFiles.changedFilesSource,
1178
+ fragmentCount: episode.fragments.length,
1179
+ maxRisk: episode.planned.maxRisk
1180
+ },
1181
+ timestamp: options?.timestamp
1182
+ });
1183
+ }
1184
+ function buildRewardClosureRecordFromWorkerEpisode(episode, options) {
1185
+ const rewardInput = deriveWorkerEpisodeRewardInput(episode);
1186
+ if (!rewardInput) return null;
1187
+ const rewardRecord = buildRoutingRewardRecord(rewardInput);
1188
+ return buildRewardClosureRecord({
1189
+ sourceKind: "worker_episode",
1190
+ sourceKey: workerEpisodeKey(episode),
1191
+ objectiveHash: episode.objectiveHash,
1192
+ sessionId: episode.sessionId,
1193
+ reward: rewardRecord.reward,
1194
+ components: {
1195
+ ...rewardRecord.components,
1196
+ workerModel: episode.model,
1197
+ orderId: episode.orderId,
1198
+ role: episode.role,
1199
+ ...episode.profile ? { profile: episode.profile } : {},
1200
+ status: episode.status,
1201
+ gateOutcome: episode.gateOutcome,
1202
+ repairCount: episode.repairCount,
1203
+ changedFileCount: episode.changedFileCount
1204
+ },
1205
+ timestamp: options?.timestamp
1206
+ });
1207
+ }
1208
+ function persistRewardClosure(store, record) {
1209
+ if (!store) return;
1210
+ try {
1211
+ store.saveBanditState(rewardClosureKind(record), JSON.stringify(record));
1212
+ } catch {
1213
+ }
1214
+ }
1215
+ function recordRoutingRewardClosure(store, event, options) {
1216
+ const record = buildRewardClosureRecordFromRoutingShadow(event, options);
1217
+ persistRewardClosure(store, record);
1218
+ return record;
1219
+ }
1220
+ function recordTeamWaveRewardClosure(store, event, options) {
1221
+ const record = buildRewardClosureRecordFromTeamWave(event, options);
1222
+ persistRewardClosure(store, record);
1223
+ return record;
1224
+ }
1225
+ function recordWorkerEpisodeClosure(store, episode, options) {
1226
+ persistWorkerEpisode(store, episode);
1227
+ const record = buildRewardClosureRecordFromWorkerEpisode(episode, options);
1228
+ if (!record) return null;
1229
+ persistRewardClosure(store, record);
1230
+ return record;
1231
+ }
1232
+ function recordTeamEpisodeRewardClosure(store, episode, options) {
1233
+ const record = buildRewardClosureRecordFromTeamEpisode(episode, options);
1234
+ if (!record) return null;
1235
+ persistRewardClosure(store, record);
1236
+ return record;
1237
+ }
1238
+ function parseWaveFragment(json) {
1239
+ try {
1240
+ const parsed = JSON.parse(json);
1241
+ if (parsed?.schemaVersion !== 1 || typeof parsed.fromWave !== "number") return null;
1242
+ return parsed;
1243
+ } catch {
1244
+ return null;
1245
+ }
1246
+ }
1247
+ function buildTeamEpisodeFromStore(store, lastFragment) {
1248
+ const byWave = /* @__PURE__ */ new Map();
1249
+ if (store?.loadBanditStatesByPrefix) {
1250
+ const prefix = `team_wave:${lastFragment.objectiveHash}:${lastFragment.sessionId}:`;
1251
+ try {
1252
+ for (const row of store.loadBanditStatesByPrefix(prefix, 200)) {
1253
+ const fragment = parseWaveFragment(row.json);
1254
+ if (!fragment) continue;
1255
+ if (fragment.mode !== lastFragment.mode || fragment.waveCount !== lastFragment.waveCount) continue;
1256
+ const existing = byWave.get(fragment.fromWave);
1257
+ if (!existing || fragment.timestamp > existing.timestamp) byWave.set(fragment.fromWave, fragment);
1258
+ }
1259
+ } catch {
1260
+ }
1261
+ }
1262
+ byWave.set(lastFragment.fromWave, lastFragment);
1263
+ return buildTeamEpisode([...byWave.values()]);
1264
+ }
1265
+ function recordTeamEpisodeClosureFromStore(store, lastFragment) {
1266
+ const episode = buildTeamEpisodeFromStore(store, lastFragment);
1267
+ const persistStore = store?.saveBanditState ? { saveBanditState: store.saveBanditState.bind(store) } : null;
1268
+ persistTeamEpisode(persistStore, episode);
1269
+ return recordTeamEpisodeRewardClosure(persistStore, episode);
1270
+ }
1271
+
1272
+ export {
1273
+ recommendModelForTask,
1274
+ buildModelCards,
1275
+ LinUCBBandit,
1276
+ inferTaskType,
1277
+ extractRequiredSkills,
1278
+ recordSkillInvoked,
1279
+ getInvokedSkills,
1280
+ clearSkillGate,
1281
+ isSkillGateEnabled,
1282
+ evaluateSkillGate,
1283
+ formatSkillGateBlock,
1284
+ buildGatedInfluenceAuditEvent,
1285
+ persistGatedInfluenceAudit,
1286
+ inferLegacyRoutingRecommendation,
1287
+ buildModelRoutingShadowEvent,
1288
+ persistModelRoutingShadow,
1289
+ hashTeamObjective,
1290
+ buildTeamWaveTelemetry,
1291
+ persistTeamWaveTelemetry,
1292
+ buildTeamWaveScopeHealth,
1293
+ persistTeamScopeHealth,
1294
+ computeTeamWaveReward,
1295
+ deriveTeamWaveRewardInput,
1296
+ formatTeamDelivery,
1297
+ hashObjective,
1298
+ buildWorkerEpisode,
1299
+ persistWorkerActualIndex,
1300
+ recordRoutingRewardClosure,
1301
+ recordTeamWaveRewardClosure,
1302
+ recordWorkerEpisodeClosure,
1303
+ buildTeamEpisodeFromStore,
1304
+ recordTeamEpisodeClosureFromStore
1305
+ };