oh-my-knowledge 0.30.0 → 0.32.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 (1084) hide show
  1. package/README.md +33 -672
  2. package/README.zh.md +45 -684
  3. package/dist/analysis/coverage-analyzer.d.ts.map +1 -0
  4. package/dist/analysis/coverage-analyzer.js.map +1 -0
  5. package/dist/analysis/failure-clusterer.d.ts.map +1 -0
  6. package/dist/analysis/failure-clusterer.js.map +1 -0
  7. package/dist/analysis/gap-analyzer.d.ts +121 -0
  8. package/dist/analysis/gap-analyzer.d.ts.map +1 -0
  9. package/dist/analysis/gap-analyzer.js +471 -0
  10. package/dist/analysis/gap-analyzer.js.map +1 -0
  11. package/dist/analysis/hedging-classifier.d.ts.map +1 -0
  12. package/dist/analysis/hedging-classifier.js.map +1 -0
  13. package/dist/analysis/report-diagnostics.d.ts +34 -0
  14. package/dist/analysis/report-diagnostics.d.ts.map +1 -0
  15. package/dist/analysis/report-diagnostics.js +753 -0
  16. package/dist/analysis/report-diagnostics.js.map +1 -0
  17. package/dist/analysis/sample-diagnostics.d.ts.map +1 -0
  18. package/dist/analysis/sample-diagnostics.js.map +1 -0
  19. package/dist/analysis/saturation.d.ts.map +1 -0
  20. package/dist/analysis/saturation.js.map +1 -0
  21. package/dist/authoring/evolver.d.ts +102 -0
  22. package/dist/authoring/evolver.d.ts.map +1 -0
  23. package/dist/authoring/evolver.js +681 -0
  24. package/dist/authoring/evolver.js.map +1 -0
  25. package/dist/authoring/generator.d.ts +38 -0
  26. package/dist/authoring/generator.d.ts.map +1 -0
  27. package/dist/authoring/generator.js +720 -0
  28. package/dist/authoring/generator.js.map +1 -0
  29. package/dist/authoring/sample-fixer.d.ts +51 -0
  30. package/dist/authoring/sample-fixer.d.ts.map +1 -0
  31. package/dist/authoring/sample-fixer.js +213 -0
  32. package/dist/authoring/sample-fixer.js.map +1 -0
  33. package/dist/cli/commands/doctor.d.ts +26 -0
  34. package/dist/cli/commands/doctor.d.ts.map +1 -0
  35. package/dist/cli/commands/doctor.js +302 -0
  36. package/dist/cli/commands/doctor.js.map +1 -0
  37. package/dist/cli/commands/eval/gold/compare.d.ts +17 -0
  38. package/dist/cli/commands/eval/gold/compare.d.ts.map +1 -0
  39. package/dist/cli/commands/eval/gold/compare.js +91 -0
  40. package/dist/cli/commands/eval/gold/compare.js.map +1 -0
  41. package/dist/cli/commands/eval/gold/index.d.ts +9 -0
  42. package/dist/cli/commands/eval/gold/index.d.ts.map +1 -0
  43. package/dist/cli/commands/eval/gold/index.js +34 -0
  44. package/dist/cli/commands/eval/gold/index.js.map +1 -0
  45. package/dist/cli/commands/eval/gold/init.d.ts +11 -0
  46. package/dist/cli/commands/eval/gold/init.d.ts.map +1 -0
  47. package/dist/cli/commands/eval/gold/init.js +51 -0
  48. package/dist/cli/commands/eval/gold/init.js.map +1 -0
  49. package/dist/cli/commands/eval/gold/validate.d.ts +12 -0
  50. package/dist/cli/commands/eval/gold/validate.d.ts.map +1 -0
  51. package/dist/cli/commands/eval/gold/validate.js +46 -0
  52. package/dist/cli/commands/eval/gold/validate.js.map +1 -0
  53. package/dist/cli/commands/eval/index.d.ts +54 -0
  54. package/dist/cli/commands/eval/index.d.ts.map +1 -0
  55. package/dist/cli/commands/eval/index.js +465 -0
  56. package/dist/cli/commands/eval/index.js.map +1 -0
  57. package/dist/cli/commands/evolve.d.ts +37 -0
  58. package/dist/cli/commands/evolve.d.ts.map +1 -0
  59. package/dist/cli/commands/evolve.js +283 -0
  60. package/dist/cli/commands/evolve.js.map +1 -0
  61. package/dist/cli/commands/init.d.ts +16 -0
  62. package/dist/cli/commands/init.d.ts.map +1 -0
  63. package/dist/cli/commands/init.js +152 -0
  64. package/dist/cli/commands/init.js.map +1 -0
  65. package/dist/cli/commands/observe/inbox.d.ts +23 -0
  66. package/dist/cli/commands/observe/inbox.d.ts.map +1 -0
  67. package/dist/cli/commands/observe/inbox.js +260 -0
  68. package/dist/cli/commands/observe/inbox.js.map +1 -0
  69. package/dist/cli/commands/observe/index.d.ts +22 -0
  70. package/dist/cli/commands/observe/index.d.ts.map +1 -0
  71. package/dist/cli/commands/observe/index.js +118 -0
  72. package/dist/cli/commands/observe/index.js.map +1 -0
  73. package/dist/cli/commands/observe/ingest.d.ts +13 -0
  74. package/dist/cli/commands/observe/ingest.d.ts.map +1 -0
  75. package/dist/cli/commands/observe/ingest.js +71 -0
  76. package/dist/cli/commands/observe/ingest.js.map +1 -0
  77. package/dist/cli/commands/observe/show.d.ts +13 -0
  78. package/dist/cli/commands/observe/show.d.ts.map +1 -0
  79. package/dist/cli/commands/observe/show.js +49 -0
  80. package/dist/cli/commands/observe/show.js.map +1 -0
  81. package/dist/cli/commands/sample.d.ts +38 -0
  82. package/dist/cli/commands/sample.d.ts.map +1 -0
  83. package/dist/cli/commands/sample.js +487 -0
  84. package/dist/cli/commands/sample.js.map +1 -0
  85. package/dist/cli/commands/studio.d.ts +23 -0
  86. package/dist/cli/commands/studio.d.ts.map +1 -0
  87. package/dist/cli/commands/studio.js +158 -0
  88. package/dist/cli/commands/studio.js.map +1 -0
  89. package/dist/cli/index.d.ts.map +1 -0
  90. package/dist/cli/index.js +29 -0
  91. package/dist/cli/index.js.map +1 -0
  92. package/dist/cli/lib/cli-exit.d.ts +16 -0
  93. package/dist/cli/lib/cli-exit.d.ts.map +1 -0
  94. package/dist/cli/lib/cli-exit.js +20 -0
  95. package/dist/cli/lib/cli-exit.js.map +1 -0
  96. package/dist/cli/lib/cmd-flags.d.ts +224 -0
  97. package/dist/cli/lib/cmd-flags.d.ts.map +1 -0
  98. package/dist/cli/lib/cmd-flags.js +46 -0
  99. package/dist/cli/lib/cmd-flags.js.map +1 -0
  100. package/dist/cli/lib/i18n-dict/common.d.ts +4 -0
  101. package/dist/cli/lib/i18n-dict/common.d.ts.map +1 -0
  102. package/dist/cli/lib/i18n-dict/common.js +67 -0
  103. package/dist/cli/lib/i18n-dict/common.js.map +1 -0
  104. package/dist/cli/lib/i18n-dict/evolve.d.ts +4 -0
  105. package/dist/cli/lib/i18n-dict/evolve.d.ts.map +1 -0
  106. package/dist/cli/lib/i18n-dict/evolve.js +43 -0
  107. package/dist/cli/lib/i18n-dict/evolve.js.map +1 -0
  108. package/dist/cli/lib/i18n-dict/gen.d.ts +4 -0
  109. package/dist/cli/lib/i18n-dict/gen.d.ts.map +1 -0
  110. package/dist/cli/lib/i18n-dict/gen.js +63 -0
  111. package/dist/cli/lib/i18n-dict/gen.js.map +1 -0
  112. package/dist/cli/lib/i18n-dict/help.d.ts +4 -0
  113. package/dist/cli/lib/i18n-dict/help.d.ts.map +1 -0
  114. package/dist/cli/lib/i18n-dict/help.js +281 -0
  115. package/dist/cli/lib/i18n-dict/help.js.map +1 -0
  116. package/dist/cli/lib/i18n-dict/init.d.ts +4 -0
  117. package/dist/cli/lib/i18n-dict/init.d.ts.map +1 -0
  118. package/dist/cli/lib/i18n-dict/init.js +27 -0
  119. package/dist/cli/lib/i18n-dict/init.js.map +1 -0
  120. package/dist/cli/lib/i18n-dict/run.d.ts +4 -0
  121. package/dist/cli/lib/i18n-dict/run.d.ts.map +1 -0
  122. package/dist/cli/lib/i18n-dict/run.js +143 -0
  123. package/dist/cli/lib/i18n-dict/run.js.map +1 -0
  124. package/dist/cli/lib/i18n-dict/types.d.ts +5 -0
  125. package/dist/cli/lib/i18n-dict/types.d.ts.map +1 -0
  126. package/dist/cli/lib/i18n-dict/types.js +2 -0
  127. package/dist/cli/lib/i18n-dict/types.js.map +1 -0
  128. package/dist/cli/lib/i18n-dict.d.ts +63 -0
  129. package/dist/cli/lib/i18n-dict.d.ts.map +1 -0
  130. package/dist/cli/lib/i18n-dict.js +67 -0
  131. package/dist/cli/lib/i18n-dict.js.map +1 -0
  132. package/dist/cli/lib/i18n.d.ts +29 -0
  133. package/dist/cli/lib/i18n.d.ts.map +1 -0
  134. package/dist/cli/lib/i18n.js +58 -0
  135. package/dist/cli/lib/i18n.js.map +1 -0
  136. package/dist/cli/lib/parse-run-config/judge-models.d.ts +24 -0
  137. package/dist/cli/lib/parse-run-config/judge-models.d.ts.map +1 -0
  138. package/dist/cli/lib/parse-run-config/judge-models.js +55 -0
  139. package/dist/cli/lib/parse-run-config/judge-models.js.map +1 -0
  140. package/dist/cli/lib/parse-run-config/samples-discovery.d.ts +19 -0
  141. package/dist/cli/lib/parse-run-config/samples-discovery.d.ts.map +1 -0
  142. package/dist/cli/lib/parse-run-config/samples-discovery.js +53 -0
  143. package/dist/cli/lib/parse-run-config/samples-discovery.js.map +1 -0
  144. package/dist/cli/lib/parse-run-config/variant-resolution.d.ts +18 -0
  145. package/dist/cli/lib/parse-run-config/variant-resolution.d.ts.map +1 -0
  146. package/dist/cli/lib/parse-run-config/variant-resolution.js +59 -0
  147. package/dist/cli/lib/parse-run-config/variant-resolution.js.map +1 -0
  148. package/dist/cli/lib/parse-run-config.d.ts +93 -0
  149. package/dist/cli/lib/parse-run-config.d.ts.map +1 -0
  150. package/dist/cli/lib/parse-run-config.js +157 -0
  151. package/dist/cli/lib/parse-run-config.js.map +1 -0
  152. package/dist/cli/lib/progress.d.ts.map +1 -0
  153. package/dist/cli/lib/progress.js.map +1 -0
  154. package/dist/cli/lib/resolve-skill-input.d.ts +8 -0
  155. package/dist/cli/lib/resolve-skill-input.d.ts.map +1 -0
  156. package/dist/cli/lib/resolve-skill-input.js +38 -0
  157. package/dist/cli/lib/resolve-skill-input.js.map +1 -0
  158. package/dist/cli/lib/run-tally.d.ts +18 -0
  159. package/dist/cli/lib/run-tally.d.ts.map +1 -0
  160. package/dist/cli/lib/run-tally.js.map +1 -0
  161. package/dist/cli/lib/shared.d.ts +12 -0
  162. package/dist/cli/lib/shared.d.ts.map +1 -0
  163. package/dist/cli/lib/shared.js +26 -0
  164. package/dist/cli/lib/shared.js.map +1 -0
  165. package/dist/cli/lib/update-check.d.ts +9 -0
  166. package/dist/cli/lib/update-check.d.ts.map +1 -0
  167. package/dist/cli/lib/update-check.js +113 -0
  168. package/dist/cli/lib/update-check.js.map +1 -0
  169. package/dist/cli/oclif/base-command.d.ts +8 -0
  170. package/dist/cli/oclif/base-command.d.ts.map +1 -0
  171. package/dist/cli/oclif/base-command.js +27 -0
  172. package/dist/cli/oclif/base-command.js.map +1 -0
  173. package/dist/cli/oclif/help.d.ts +16 -0
  174. package/dist/cli/oclif/help.d.ts.map +1 -0
  175. package/dist/cli/oclif/help.js +33 -0
  176. package/dist/cli/oclif/help.js.map +1 -0
  177. package/dist/cli/oclif/i18n.d.ts +16 -0
  178. package/dist/cli/oclif/i18n.d.ts.map +1 -0
  179. package/dist/cli/oclif/i18n.js +62 -0
  180. package/dist/cli/oclif/i18n.js.map +1 -0
  181. package/dist/cli/oclif/parsers.d.ts +10 -0
  182. package/dist/cli/oclif/parsers.d.ts.map +1 -0
  183. package/dist/cli/oclif/parsers.js +102 -0
  184. package/dist/cli/oclif/parsers.js.map +1 -0
  185. package/dist/cli/oclif/projection.d.ts +23 -0
  186. package/dist/cli/oclif/projection.d.ts.map +1 -0
  187. package/dist/cli/oclif/projection.js +52 -0
  188. package/dist/cli/oclif/projection.js.map +1 -0
  189. package/dist/cli/oclif/run.d.ts +2 -0
  190. package/dist/cli/oclif/run.d.ts.map +1 -0
  191. package/dist/cli/oclif/run.js +11 -0
  192. package/dist/cli/oclif/run.js.map +1 -0
  193. package/dist/diagnosis/observe-mapper.d.ts +68 -0
  194. package/dist/diagnosis/observe-mapper.d.ts.map +1 -0
  195. package/dist/diagnosis/observe-mapper.js +276 -0
  196. package/dist/diagnosis/observe-mapper.js.map +1 -0
  197. package/dist/diagnosis/observe-producer.d.ts +9 -0
  198. package/dist/diagnosis/observe-producer.d.ts.map +1 -0
  199. package/dist/diagnosis/observe-producer.js +199 -0
  200. package/dist/diagnosis/observe-producer.js.map +1 -0
  201. package/dist/diagnosis/studio-projection.d.ts +7 -0
  202. package/dist/diagnosis/studio-projection.d.ts.map +1 -0
  203. package/dist/diagnosis/studio-projection.js +84 -0
  204. package/dist/diagnosis/studio-projection.js.map +1 -0
  205. package/dist/diagnosis/types.d.ts +4 -0
  206. package/dist/diagnosis/types.d.ts.map +1 -0
  207. package/dist/diagnosis/types.js +20 -0
  208. package/dist/diagnosis/types.js.map +1 -0
  209. package/dist/doctor/fixer.d.ts +12 -0
  210. package/dist/doctor/fixer.d.ts.map +1 -0
  211. package/dist/doctor/fixer.js +326 -0
  212. package/dist/doctor/fixer.js.map +1 -0
  213. package/dist/doctor/health/builtin-dimensions.d.ts.map +1 -0
  214. package/dist/doctor/health/builtin-dimensions.js.map +1 -0
  215. package/dist/doctor/health/composer.d.ts.map +1 -0
  216. package/dist/doctor/health/composer.js +296 -0
  217. package/dist/doctor/health/composer.js.map +1 -0
  218. package/dist/doctor/health/dimension-registry.d.ts.map +1 -0
  219. package/dist/doctor/health/dimension-registry.js.map +1 -0
  220. package/dist/doctor/health/dimension-spec.d.ts +47 -0
  221. package/dist/doctor/health/dimension-spec.d.ts.map +1 -0
  222. package/dist/doctor/health/dimension-spec.js.map +1 -0
  223. package/dist/doctor/health/parser.d.ts +26 -0
  224. package/dist/doctor/health/parser.d.ts.map +1 -0
  225. package/dist/doctor/health/parser.js +228 -0
  226. package/dist/doctor/health/parser.js.map +1 -0
  227. package/dist/doctor/health/prompt-builder.d.ts +3 -0
  228. package/dist/doctor/health/prompt-builder.d.ts.map +1 -0
  229. package/dist/doctor/health/prompt-builder.js +2 -0
  230. package/dist/doctor/health/prompt-builder.js.map +1 -0
  231. package/dist/doctor/health/register.d.ts.map +1 -0
  232. package/dist/doctor/health/register.js.map +1 -0
  233. package/dist/doctor/index.d.ts.map +1 -0
  234. package/dist/doctor/index.js +246 -0
  235. package/dist/doctor/index.js.map +1 -0
  236. package/dist/doctor/messages.d.ts +21 -0
  237. package/dist/doctor/messages.d.ts.map +1 -0
  238. package/dist/doctor/messages.js +217 -0
  239. package/dist/doctor/messages.js.map +1 -0
  240. package/dist/doctor/preflight.d.ts.map +1 -0
  241. package/dist/doctor/preflight.js.map +1 -0
  242. package/dist/doctor/renderer.d.ts +17 -0
  243. package/dist/doctor/renderer.d.ts.map +1 -0
  244. package/dist/doctor/renderer.js +122 -0
  245. package/dist/doctor/renderer.js.map +1 -0
  246. package/dist/doctor/rules.d.ts.map +1 -0
  247. package/dist/doctor/rules.js +289 -0
  248. package/dist/doctor/rules.js.map +1 -0
  249. package/dist/eval-core/bootstrap.d.ts.map +1 -0
  250. package/dist/eval-core/bootstrap.js.map +1 -0
  251. package/dist/eval-core/cache.d.ts.map +1 -0
  252. package/dist/eval-core/cache.js.map +1 -0
  253. package/dist/eval-core/comparability.d.ts.map +1 -0
  254. package/dist/eval-core/comparability.js.map +1 -0
  255. package/dist/eval-core/dependency-checker.d.ts +37 -0
  256. package/dist/eval-core/dependency-checker.d.ts.map +1 -0
  257. package/dist/eval-core/dependency-checker.js.map +1 -0
  258. package/dist/eval-core/evaluation-execution.d.ts.map +1 -0
  259. package/dist/eval-core/evaluation-execution.js.map +1 -0
  260. package/dist/eval-core/evaluation-job.d.ts.map +1 -0
  261. package/dist/eval-core/evaluation-job.js.map +1 -0
  262. package/dist/eval-core/evaluation-reporting.d.ts.map +1 -0
  263. package/dist/eval-core/evaluation-reporting.js.map +1 -0
  264. package/dist/eval-core/execution-strategy.d.ts.map +1 -0
  265. package/dist/eval-core/execution-strategy.js +165 -0
  266. package/dist/eval-core/execution-strategy.js.map +1 -0
  267. package/dist/eval-core/fact-checker.d.ts.map +1 -0
  268. package/dist/eval-core/fact-checker.js.map +1 -0
  269. package/dist/eval-core/layer-gates.d.ts.map +1 -0
  270. package/dist/eval-core/layer-gates.js.map +1 -0
  271. package/dist/eval-core/mock-hook.cjs +215 -0
  272. package/dist/eval-core/mocks-runtime.d.ts +100 -0
  273. package/dist/eval-core/mocks-runtime.d.ts.map +1 -0
  274. package/dist/eval-core/mocks-runtime.js +559 -0
  275. package/dist/eval-core/mocks-runtime.js.map +1 -0
  276. package/dist/eval-core/schema.d.ts.map +1 -0
  277. package/dist/eval-core/schema.js.map +1 -0
  278. package/dist/eval-core/statistics.d.ts.map +1 -0
  279. package/dist/eval-core/statistics.js.map +1 -0
  280. package/dist/eval-core/task-planner.d.ts.map +1 -0
  281. package/dist/eval-core/task-planner.js.map +1 -0
  282. package/dist/eval-core/verdict.d.ts.map +1 -0
  283. package/dist/eval-core/verdict.js.map +1 -0
  284. package/dist/eval-workflows/batch-evaluation-workflow.d.ts.map +1 -0
  285. package/dist/eval-workflows/batch-evaluation-workflow.js.map +1 -0
  286. package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.d.ts +22 -0
  287. package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.d.ts.map +1 -0
  288. package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js +67 -0
  289. package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js.map +1 -0
  290. package/dist/eval-workflows/evaluation-pipeline/report-finalize.d.ts +32 -0
  291. package/dist/eval-workflows/evaluation-pipeline/report-finalize.d.ts.map +1 -0
  292. package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +57 -0
  293. package/dist/eval-workflows/evaluation-pipeline/report-finalize.js.map +1 -0
  294. package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +60 -0
  295. package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts.map +1 -0
  296. package/dist/eval-workflows/evaluation-pipeline/run-state.js +88 -0
  297. package/dist/eval-workflows/evaluation-pipeline/run-state.js.map +1 -0
  298. package/dist/eval-workflows/evaluation-pipeline/test-set-hash.d.ts +21 -0
  299. package/dist/eval-workflows/evaluation-pipeline/test-set-hash.d.ts.map +1 -0
  300. package/dist/eval-workflows/evaluation-pipeline/test-set-hash.js +46 -0
  301. package/dist/eval-workflows/evaluation-pipeline/test-set-hash.js.map +1 -0
  302. package/dist/eval-workflows/evaluation-pipeline.d.ts +97 -0
  303. package/dist/eval-workflows/evaluation-pipeline.d.ts.map +1 -0
  304. package/dist/eval-workflows/evaluation-pipeline.js +179 -0
  305. package/dist/eval-workflows/evaluation-pipeline.js.map +1 -0
  306. package/dist/eval-workflows/evaluation-preparation.d.ts.map +1 -0
  307. package/dist/eval-workflows/evaluation-preparation.js.map +1 -0
  308. package/dist/eval-workflows/messages.d.ts +4 -0
  309. package/dist/eval-workflows/messages.d.ts.map +1 -0
  310. package/dist/eval-workflows/messages.js +30 -0
  311. package/dist/eval-workflows/messages.js.map +1 -0
  312. package/dist/eval-workflows/run-evaluation.d.ts.map +1 -0
  313. package/dist/eval-workflows/run-evaluation.js +528 -0
  314. package/dist/eval-workflows/run-evaluation.js.map +1 -0
  315. package/dist/executors/anthropic-api.d.ts.map +1 -0
  316. package/dist/executors/anthropic-api.js.map +1 -0
  317. package/dist/executors/claude-cli.d.ts.map +1 -0
  318. package/dist/executors/claude-cli.js +181 -0
  319. package/dist/executors/claude-cli.js.map +1 -0
  320. package/dist/executors/claude-sdk-trace.d.ts.map +1 -0
  321. package/dist/executors/claude-sdk-trace.js.map +1 -0
  322. package/dist/executors/claude-sdk.d.ts.map +1 -0
  323. package/dist/executors/claude-sdk.js.map +1 -0
  324. package/dist/executors/codex-cli-trace.d.ts.map +1 -0
  325. package/dist/executors/codex-cli-trace.js.map +1 -0
  326. package/dist/executors/codex-cli.d.ts.map +1 -0
  327. package/dist/executors/codex-cli.js.map +1 -0
  328. package/dist/executors/codex-sdk.d.ts.map +1 -0
  329. package/dist/executors/codex-sdk.js.map +1 -0
  330. package/dist/executors/gemini.d.ts.map +1 -0
  331. package/dist/executors/gemini.js.map +1 -0
  332. package/dist/executors/index.d.ts.map +1 -0
  333. package/dist/executors/index.js.map +1 -0
  334. package/dist/executors/openai-api.d.ts.map +1 -0
  335. package/dist/executors/openai-api.js.map +1 -0
  336. package/dist/executors/runtime-fingerprint.d.ts.map +1 -0
  337. package/dist/executors/runtime-fingerprint.js.map +1 -0
  338. package/dist/executors/script.d.ts.map +1 -0
  339. package/dist/executors/script.js.map +1 -0
  340. package/dist/executors/shared.d.ts +194 -0
  341. package/dist/executors/shared.d.ts.map +1 -0
  342. package/dist/executors/shared.js +253 -0
  343. package/dist/executors/shared.js.map +1 -0
  344. package/dist/grading/assertions.d.ts.map +1 -0
  345. package/dist/grading/assertions.js.map +1 -0
  346. package/dist/grading/debias-validate.d.ts.map +1 -0
  347. package/dist/grading/debias-validate.js.map +1 -0
  348. package/dist/grading/diagnostic.d.ts.map +1 -0
  349. package/dist/grading/diagnostic.js.map +1 -0
  350. package/dist/grading/gold-cli.d.ts.map +1 -0
  351. package/dist/grading/gold-cli.js.map +1 -0
  352. package/dist/grading/gold-dataset.d.ts.map +1 -0
  353. package/dist/grading/gold-dataset.js.map +1 -0
  354. package/dist/grading/human-gold.d.ts.map +1 -0
  355. package/dist/grading/human-gold.js.map +1 -0
  356. package/dist/grading/index.d.ts.map +1 -0
  357. package/dist/grading/index.js.map +1 -0
  358. package/dist/grading/judge.d.ts.map +1 -0
  359. package/dist/grading/judge.js.map +1 -0
  360. package/dist/grading/layered-scores.d.ts.map +1 -0
  361. package/dist/grading/layered-scores.js.map +1 -0
  362. package/dist/inputs/eval-config.d.ts.map +1 -0
  363. package/dist/inputs/eval-config.js.map +1 -0
  364. package/dist/inputs/load-samples.d.ts.map +1 -0
  365. package/dist/inputs/load-samples.js.map +1 -0
  366. package/dist/inputs/mcp-resolver.d.ts.map +1 -0
  367. package/dist/inputs/mcp-resolver.js.map +1 -0
  368. package/dist/inputs/skill-loader.d.ts.map +1 -0
  369. package/dist/inputs/skill-loader.js +261 -0
  370. package/dist/inputs/skill-loader.js.map +1 -0
  371. package/dist/inputs/url-fetcher.d.ts.map +1 -0
  372. package/dist/inputs/url-fetcher.js.map +1 -0
  373. package/dist/observability/experience-frontmatter.d.ts +37 -0
  374. package/dist/observability/experience-frontmatter.d.ts.map +1 -0
  375. package/dist/observability/experience-frontmatter.js +100 -0
  376. package/dist/observability/experience-frontmatter.js.map +1 -0
  377. package/dist/observability/experience.d.ts +23 -0
  378. package/dist/observability/experience.d.ts.map +1 -0
  379. package/dist/observability/experience.js +3438 -0
  380. package/dist/observability/experience.js.map +1 -0
  381. package/dist/observability/feedback-matchers.d.ts +37 -0
  382. package/dist/observability/feedback-matchers.d.ts.map +1 -0
  383. package/dist/observability/feedback-matchers.js +232 -0
  384. package/dist/observability/feedback-matchers.js.map +1 -0
  385. package/dist/observability/feedback-projection.d.ts +21 -0
  386. package/dist/observability/feedback-projection.d.ts.map +1 -0
  387. package/dist/observability/feedback-projection.js +20 -0
  388. package/dist/observability/feedback-projection.js.map +1 -0
  389. package/dist/observability/inbox-view-model.d.ts +43 -0
  390. package/dist/observability/inbox-view-model.d.ts.map +1 -0
  391. package/dist/observability/inbox-view-model.js +180 -0
  392. package/dist/observability/inbox-view-model.js.map +1 -0
  393. package/dist/observability/inbox.d.ts +28 -0
  394. package/dist/observability/inbox.d.ts.map +1 -0
  395. package/dist/observability/inbox.js +765 -0
  396. package/dist/observability/inbox.js.map +1 -0
  397. package/dist/observability/problem-patterns.d.ts +12 -0
  398. package/dist/observability/problem-patterns.d.ts.map +1 -0
  399. package/dist/observability/problem-patterns.js +244 -0
  400. package/dist/observability/problem-patterns.js.map +1 -0
  401. package/dist/observability/prompts/llm-enhanced-review.prompt.md +224 -0
  402. package/dist/observability/resolved-review.d.ts +37 -0
  403. package/dist/observability/resolved-review.d.ts.map +1 -0
  404. package/dist/observability/resolved-review.js +552 -0
  405. package/dist/observability/resolved-review.js.map +1 -0
  406. package/dist/observability/review-state.d.ts +20 -0
  407. package/dist/observability/review-state.d.ts.map +1 -0
  408. package/dist/observability/review-state.js +169 -0
  409. package/dist/observability/review-state.js.map +1 -0
  410. package/dist/observability/skill-chain-advisories.d.ts +16 -0
  411. package/dist/observability/skill-chain-advisories.d.ts.map +1 -0
  412. package/dist/observability/skill-chain-advisories.js +69 -0
  413. package/dist/observability/skill-chain-advisories.js.map +1 -0
  414. package/dist/observability/skill-chain.d.ts +7 -0
  415. package/dist/observability/skill-chain.d.ts.map +1 -0
  416. package/dist/observability/skill-chain.js +474 -0
  417. package/dist/observability/skill-chain.js.map +1 -0
  418. package/dist/observability/skill-health-analyzer.d.ts.map +1 -0
  419. package/dist/observability/skill-health-analyzer.js.map +1 -0
  420. package/dist/observability/soft-standards/constants.d.ts +5 -0
  421. package/dist/observability/soft-standards/constants.d.ts.map +1 -0
  422. package/dist/observability/soft-standards/constants.js +10 -0
  423. package/dist/observability/soft-standards/constants.js.map +1 -0
  424. package/dist/observability/soft-standards/index.d.ts +16 -0
  425. package/dist/observability/soft-standards/index.d.ts.map +1 -0
  426. package/dist/observability/soft-standards/index.js +15 -0
  427. package/dist/observability/soft-standards/index.js.map +1 -0
  428. package/dist/observability/soft-standards/llm-extractor.d.ts +6 -0
  429. package/dist/observability/soft-standards/llm-extractor.d.ts.map +1 -0
  430. package/dist/observability/soft-standards/llm-extractor.js +692 -0
  431. package/dist/observability/soft-standards/llm-extractor.js.map +1 -0
  432. package/dist/observability/soft-standards/runtime-evaluator.d.ts +12 -0
  433. package/dist/observability/soft-standards/runtime-evaluator.d.ts.map +1 -0
  434. package/dist/observability/soft-standards/runtime-evaluator.js +385 -0
  435. package/dist/observability/soft-standards/runtime-evaluator.js.map +1 -0
  436. package/dist/observability/soft-standards/skill-standards-store.d.ts +11 -0
  437. package/dist/observability/soft-standards/skill-standards-store.d.ts.map +1 -0
  438. package/dist/observability/soft-standards/skill-standards-store.js +158 -0
  439. package/dist/observability/soft-standards/skill-standards-store.js.map +1 -0
  440. package/dist/observability/soft-standards/types.d.ts +212 -0
  441. package/dist/observability/soft-standards/types.d.ts.map +1 -0
  442. package/dist/observability/soft-standards/types.js +2 -0
  443. package/dist/observability/soft-standards/types.js.map +1 -0
  444. package/dist/observability/text-signals.d.ts +14 -0
  445. package/dist/observability/text-signals.d.ts.map +1 -0
  446. package/dist/observability/text-signals.js +116 -0
  447. package/dist/observability/text-signals.js.map +1 -0
  448. package/dist/observability/trace-adapter.d.ts.map +1 -0
  449. package/dist/observability/trace-adapter.js.map +1 -0
  450. package/dist/observability/trace-attribution.d.ts +56 -0
  451. package/dist/observability/trace-attribution.d.ts.map +1 -0
  452. package/dist/observability/trace-attribution.js +200 -0
  453. package/dist/observability/trace-attribution.js.map +1 -0
  454. package/dist/observability/trace-segmenter.d.ts +52 -0
  455. package/dist/observability/trace-segmenter.d.ts.map +1 -0
  456. package/dist/observability/trace-segmenter.js +322 -0
  457. package/dist/observability/trace-segmenter.js.map +1 -0
  458. package/dist/observability/trace-source.d.ts +92 -0
  459. package/dist/observability/trace-source.d.ts.map +1 -0
  460. package/dist/observability/trace-source.js +502 -0
  461. package/dist/observability/trace-source.js.map +1 -0
  462. package/dist/renderer/html-renderer.d.ts.map +1 -0
  463. package/dist/renderer/html-renderer.js.map +1 -0
  464. package/dist/renderer/layout.d.ts.map +1 -0
  465. package/dist/renderer/layout.js.map +1 -0
  466. package/dist/renderer/observation-inbox/helpers.d.ts +14 -0
  467. package/dist/renderer/observation-inbox/helpers.d.ts.map +1 -0
  468. package/dist/renderer/observation-inbox/helpers.js +75 -0
  469. package/dist/renderer/observation-inbox/helpers.js.map +1 -0
  470. package/dist/renderer/observation-inbox/styles.d.ts +2 -0
  471. package/dist/renderer/observation-inbox/styles.d.ts.map +1 -0
  472. package/dist/renderer/observation-inbox/styles.js +5184 -0
  473. package/dist/renderer/observation-inbox/styles.js.map +1 -0
  474. package/dist/renderer/observation-inbox-renderer.d.ts +5 -0
  475. package/dist/renderer/observation-inbox-renderer.d.ts.map +1 -0
  476. package/dist/renderer/observation-inbox-renderer.js +7182 -0
  477. package/dist/renderer/observation-inbox-renderer.js.map +1 -0
  478. package/dist/renderer/skill-detail-renderer.d.ts +13 -0
  479. package/dist/renderer/skill-detail-renderer.d.ts.map +1 -0
  480. package/dist/renderer/skill-detail-renderer.js +1244 -0
  481. package/dist/renderer/skill-detail-renderer.js.map +1 -0
  482. package/dist/renderer/skill-health-renderer.d.ts.map +1 -0
  483. package/dist/renderer/skill-health-renderer.js.map +1 -0
  484. package/dist/renderer/skill-list-renderer.d.ts +3 -0
  485. package/dist/renderer/skill-list-renderer.d.ts.map +1 -0
  486. package/dist/renderer/skill-list-renderer.js +334 -0
  487. package/dist/renderer/skill-list-renderer.js.map +1 -0
  488. package/dist/renderer/summary.d.ts +68 -0
  489. package/dist/renderer/summary.d.ts.map +1 -0
  490. package/dist/renderer/summary.js +1896 -0
  491. package/dist/renderer/summary.js.map +1 -0
  492. package/dist/renderer/table.d.ts.map +1 -0
  493. package/dist/renderer/table.js.map +1 -0
  494. package/dist/renderer/test-view.d.ts.map +1 -0
  495. package/dist/renderer/test-view.js +905 -0
  496. package/dist/renderer/test-view.js.map +1 -0
  497. package/dist/renderer/trends.d.ts.map +1 -0
  498. package/dist/renderer/trends.js.map +1 -0
  499. package/dist/server/job-store.d.ts.map +1 -0
  500. package/dist/server/job-store.js.map +1 -0
  501. package/dist/server/report-server.d.ts.map +1 -0
  502. package/dist/server/report-server.js +892 -0
  503. package/dist/server/report-server.js.map +1 -0
  504. package/dist/server/report-store.d.ts.map +1 -0
  505. package/dist/server/report-store.js.map +1 -0
  506. package/dist/server/skill-index.d.ts +14 -0
  507. package/dist/server/skill-index.d.ts.map +1 -0
  508. package/dist/server/skill-index.js +381 -0
  509. package/dist/server/skill-index.js.map +1 -0
  510. package/dist/server/skill-insights.d.ts +32 -0
  511. package/dist/server/skill-insights.d.ts.map +1 -0
  512. package/dist/server/skill-insights.js +788 -0
  513. package/dist/server/skill-insights.js.map +1 -0
  514. package/dist/shared/hard-rules.d.ts +42 -0
  515. package/dist/shared/hard-rules.d.ts.map +1 -0
  516. package/dist/shared/hard-rules.js +242 -0
  517. package/dist/shared/hard-rules.js.map +1 -0
  518. package/dist/shared/llm-prompts/index.d.ts +14 -0
  519. package/dist/shared/llm-prompts/index.d.ts.map +1 -0
  520. package/dist/shared/llm-prompts/index.js +22 -0
  521. package/dist/shared/llm-prompts/index.js.map +1 -0
  522. package/dist/shared/llm-prompts/skill-health.d.ts +22 -0
  523. package/dist/shared/llm-prompts/skill-health.d.ts.map +1 -0
  524. package/dist/shared/llm-prompts/skill-health.js +170 -0
  525. package/dist/shared/llm-prompts/skill-health.js.map +1 -0
  526. package/dist/shared/time.d.ts.map +1 -0
  527. package/dist/shared/time.js.map +1 -0
  528. package/dist/shared/tool-search.d.ts.map +1 -0
  529. package/dist/shared/tool-search.js.map +1 -0
  530. package/dist/types/dependencies.d.ts +23 -0
  531. package/dist/types/dependencies.d.ts.map +1 -0
  532. package/dist/types/dependencies.js +2 -0
  533. package/dist/types/dependencies.js.map +1 -0
  534. package/dist/types/diagnosis.d.ts +88 -0
  535. package/dist/types/diagnosis.d.ts.map +1 -0
  536. package/dist/types/diagnosis.js +2 -0
  537. package/dist/types/diagnosis.js.map +1 -0
  538. package/dist/types/doctor.d.ts +157 -0
  539. package/dist/types/doctor.d.ts.map +1 -0
  540. package/dist/types/doctor.js.map +1 -0
  541. package/dist/types/eval.d.ts +377 -0
  542. package/dist/types/eval.d.ts.map +1 -0
  543. package/dist/types/eval.js.map +1 -0
  544. package/dist/types/executor.d.ts.map +1 -0
  545. package/dist/types/executor.js.map +1 -0
  546. package/dist/types/index.d.ts +12 -0
  547. package/dist/types/index.d.ts.map +1 -0
  548. package/dist/types/index.js +12 -0
  549. package/dist/types/index.js.map +1 -0
  550. package/dist/types/judge.d.ts.map +1 -0
  551. package/dist/types/judge.js.map +1 -0
  552. package/dist/types/observability.d.ts +865 -0
  553. package/dist/types/observability.d.ts.map +1 -0
  554. package/dist/types/observability.js +14 -0
  555. package/dist/types/observability.js.map +1 -0
  556. package/dist/types/report.d.ts +560 -0
  557. package/dist/types/report.d.ts.map +1 -0
  558. package/dist/types/report.js.map +1 -0
  559. package/dist/types/shared.d.ts.map +1 -0
  560. package/dist/types/shared.js.map +1 -0
  561. package/dist/types/skill-index.d.ts +142 -0
  562. package/dist/types/skill-index.d.ts.map +1 -0
  563. package/dist/types/skill-index.js +2 -0
  564. package/dist/types/skill-index.js.map +1 -0
  565. package/dist/types/storage.d.ts.map +1 -0
  566. package/dist/types/storage.js.map +1 -0
  567. package/dist/util/safe-slice.d.ts +23 -0
  568. package/dist/util/safe-slice.d.ts.map +1 -0
  569. package/dist/util/safe-slice.js +33 -0
  570. package/dist/util/safe-slice.js.map +1 -0
  571. package/package.json +40 -8
  572. package/dist/src/analysis/coverage-analyzer.d.ts.map +0 -1
  573. package/dist/src/analysis/coverage-analyzer.js.map +0 -1
  574. package/dist/src/analysis/failure-clusterer.d.ts.map +0 -1
  575. package/dist/src/analysis/failure-clusterer.js.map +0 -1
  576. package/dist/src/analysis/gap-analyzer.d.ts +0 -121
  577. package/dist/src/analysis/gap-analyzer.d.ts.map +0 -1
  578. package/dist/src/analysis/gap-analyzer.js +0 -471
  579. package/dist/src/analysis/gap-analyzer.js.map +0 -1
  580. package/dist/src/analysis/hedging-classifier.d.ts.map +0 -1
  581. package/dist/src/analysis/hedging-classifier.js.map +0 -1
  582. package/dist/src/analysis/report-diagnostics.d.ts +0 -34
  583. package/dist/src/analysis/report-diagnostics.d.ts.map +0 -1
  584. package/dist/src/analysis/report-diagnostics.js +0 -753
  585. package/dist/src/analysis/report-diagnostics.js.map +0 -1
  586. package/dist/src/analysis/sample-diagnostics.d.ts.map +0 -1
  587. package/dist/src/analysis/sample-diagnostics.js.map +0 -1
  588. package/dist/src/analysis/saturation.d.ts.map +0 -1
  589. package/dist/src/analysis/saturation.js.map +0 -1
  590. package/dist/src/authoring/evolver.d.ts +0 -78
  591. package/dist/src/authoring/evolver.d.ts.map +0 -1
  592. package/dist/src/authoring/evolver.js +0 -320
  593. package/dist/src/authoring/evolver.js.map +0 -1
  594. package/dist/src/authoring/generator.d.ts +0 -35
  595. package/dist/src/authoring/generator.d.ts.map +0 -1
  596. package/dist/src/authoring/generator.js +0 -704
  597. package/dist/src/authoring/generator.js.map +0 -1
  598. package/dist/src/authoring/sample-fixer.d.ts +0 -45
  599. package/dist/src/authoring/sample-fixer.d.ts.map +0 -1
  600. package/dist/src/authoring/sample-fixer.js +0 -186
  601. package/dist/src/authoring/sample-fixer.js.map +0 -1
  602. package/dist/src/cli/cli-exit.d.ts +0 -15
  603. package/dist/src/cli/cli-exit.d.ts.map +0 -1
  604. package/dist/src/cli/cli-exit.js +0 -19
  605. package/dist/src/cli/cli-exit.js.map +0 -1
  606. package/dist/src/cli/commands/_shared.d.ts +0 -12
  607. package/dist/src/cli/commands/_shared.d.ts.map +0 -1
  608. package/dist/src/cli/commands/_shared.js +0 -26
  609. package/dist/src/cli/commands/_shared.js.map +0 -1
  610. package/dist/src/cli/commands/doctor.d.ts +0 -2
  611. package/dist/src/cli/commands/doctor.d.ts.map +0 -1
  612. package/dist/src/cli/commands/doctor.js +0 -174
  613. package/dist/src/cli/commands/doctor.js.map +0 -1
  614. package/dist/src/cli/commands/eval-gold.d.ts +0 -2
  615. package/dist/src/cli/commands/eval-gold.d.ts.map +0 -1
  616. package/dist/src/cli/commands/eval-gold.js +0 -137
  617. package/dist/src/cli/commands/eval-gold.js.map +0 -1
  618. package/dist/src/cli/commands/eval-runner.d.ts +0 -2
  619. package/dist/src/cli/commands/eval-runner.d.ts.map +0 -1
  620. package/dist/src/cli/commands/eval-runner.js +0 -299
  621. package/dist/src/cli/commands/eval-runner.js.map +0 -1
  622. package/dist/src/cli/commands/eval.d.ts +0 -2
  623. package/dist/src/cli/commands/eval.d.ts.map +0 -1
  624. package/dist/src/cli/commands/eval.js +0 -11
  625. package/dist/src/cli/commands/eval.js.map +0 -1
  626. package/dist/src/cli/commands/evolve.d.ts +0 -2
  627. package/dist/src/cli/commands/evolve.d.ts.map +0 -1
  628. package/dist/src/cli/commands/evolve.js +0 -132
  629. package/dist/src/cli/commands/evolve.js.map +0 -1
  630. package/dist/src/cli/commands/init.d.ts +0 -2
  631. package/dist/src/cli/commands/init.d.ts.map +0 -1
  632. package/dist/src/cli/commands/init.js +0 -110
  633. package/dist/src/cli/commands/init.js.map +0 -1
  634. package/dist/src/cli/commands/observe.d.ts +0 -2
  635. package/dist/src/cli/commands/observe.d.ts.map +0 -1
  636. package/dist/src/cli/commands/observe.js +0 -247
  637. package/dist/src/cli/commands/observe.js.map +0 -1
  638. package/dist/src/cli/commands/registry.d.ts +0 -11
  639. package/dist/src/cli/commands/registry.d.ts.map +0 -1
  640. package/dist/src/cli/commands/registry.js +0 -32
  641. package/dist/src/cli/commands/registry.js.map +0 -1
  642. package/dist/src/cli/commands/sample.d.ts +0 -15
  643. package/dist/src/cli/commands/sample.d.ts.map +0 -1
  644. package/dist/src/cli/commands/sample.js +0 -415
  645. package/dist/src/cli/commands/sample.js.map +0 -1
  646. package/dist/src/cli/commands/studio.d.ts +0 -2
  647. package/dist/src/cli/commands/studio.d.ts.map +0 -1
  648. package/dist/src/cli/commands/studio.js +0 -84
  649. package/dist/src/cli/commands/studio.js.map +0 -1
  650. package/dist/src/cli/i18n-dict.d.ts +0 -54
  651. package/dist/src/cli/i18n-dict.d.ts.map +0 -1
  652. package/dist/src/cli/i18n-dict.js +0 -1125
  653. package/dist/src/cli/i18n-dict.js.map +0 -1
  654. package/dist/src/cli/i18n.d.ts +0 -24
  655. package/dist/src/cli/i18n.d.ts.map +0 -1
  656. package/dist/src/cli/i18n.js +0 -53
  657. package/dist/src/cli/i18n.js.map +0 -1
  658. package/dist/src/cli/index.d.ts.map +0 -1
  659. package/dist/src/cli/index.js +0 -61
  660. package/dist/src/cli/index.js.map +0 -1
  661. package/dist/src/cli/parse-run-config.d.ts +0 -90
  662. package/dist/src/cli/parse-run-config.d.ts.map +0 -1
  663. package/dist/src/cli/parse-run-config.js +0 -302
  664. package/dist/src/cli/parse-run-config.js.map +0 -1
  665. package/dist/src/cli/parse-strict.d.ts +0 -7
  666. package/dist/src/cli/parse-strict.d.ts.map +0 -1
  667. package/dist/src/cli/parse-strict.js +0 -26
  668. package/dist/src/cli/parse-strict.js.map +0 -1
  669. package/dist/src/cli/progress.d.ts.map +0 -1
  670. package/dist/src/cli/progress.js.map +0 -1
  671. package/dist/src/cli/run-tally.d.ts +0 -18
  672. package/dist/src/cli/run-tally.d.ts.map +0 -1
  673. package/dist/src/cli/run-tally.js.map +0 -1
  674. package/dist/src/cli/update-check.d.ts +0 -3
  675. package/dist/src/cli/update-check.d.ts.map +0 -1
  676. package/dist/src/cli/update-check.js +0 -37
  677. package/dist/src/cli/update-check.js.map +0 -1
  678. package/dist/src/doctor/health/builtin-dimensions.d.ts.map +0 -1
  679. package/dist/src/doctor/health/builtin-dimensions.js.map +0 -1
  680. package/dist/src/doctor/health/composer.d.ts.map +0 -1
  681. package/dist/src/doctor/health/composer.js +0 -293
  682. package/dist/src/doctor/health/composer.js.map +0 -1
  683. package/dist/src/doctor/health/dimension-registry.d.ts.map +0 -1
  684. package/dist/src/doctor/health/dimension-registry.js.map +0 -1
  685. package/dist/src/doctor/health/dimension-spec.d.ts +0 -47
  686. package/dist/src/doctor/health/dimension-spec.d.ts.map +0 -1
  687. package/dist/src/doctor/health/dimension-spec.js.map +0 -1
  688. package/dist/src/doctor/health/parser.d.ts +0 -27
  689. package/dist/src/doctor/health/parser.d.ts.map +0 -1
  690. package/dist/src/doctor/health/parser.js +0 -191
  691. package/dist/src/doctor/health/parser.js.map +0 -1
  692. package/dist/src/doctor/health/prompt-builder.d.ts +0 -22
  693. package/dist/src/doctor/health/prompt-builder.d.ts.map +0 -1
  694. package/dist/src/doctor/health/prompt-builder.js +0 -170
  695. package/dist/src/doctor/health/prompt-builder.js.map +0 -1
  696. package/dist/src/doctor/health/register.d.ts.map +0 -1
  697. package/dist/src/doctor/health/register.js.map +0 -1
  698. package/dist/src/doctor/html-renderer.d.ts +0 -20
  699. package/dist/src/doctor/html-renderer.d.ts.map +0 -1
  700. package/dist/src/doctor/html-renderer.js +0 -376
  701. package/dist/src/doctor/html-renderer.js.map +0 -1
  702. package/dist/src/doctor/index.d.ts.map +0 -1
  703. package/dist/src/doctor/index.js +0 -245
  704. package/dist/src/doctor/index.js.map +0 -1
  705. package/dist/src/doctor/preflight.d.ts.map +0 -1
  706. package/dist/src/doctor/preflight.js.map +0 -1
  707. package/dist/src/doctor/renderer.d.ts +0 -17
  708. package/dist/src/doctor/renderer.d.ts.map +0 -1
  709. package/dist/src/doctor/renderer.js +0 -123
  710. package/dist/src/doctor/renderer.js.map +0 -1
  711. package/dist/src/doctor/rules.d.ts.map +0 -1
  712. package/dist/src/doctor/rules.js +0 -289
  713. package/dist/src/doctor/rules.js.map +0 -1
  714. package/dist/src/eval-core/bootstrap.d.ts.map +0 -1
  715. package/dist/src/eval-core/bootstrap.js.map +0 -1
  716. package/dist/src/eval-core/cache.d.ts.map +0 -1
  717. package/dist/src/eval-core/cache.js.map +0 -1
  718. package/dist/src/eval-core/comparability.d.ts.map +0 -1
  719. package/dist/src/eval-core/comparability.js.map +0 -1
  720. package/dist/src/eval-core/dependency-checker.d.ts +0 -58
  721. package/dist/src/eval-core/dependency-checker.d.ts.map +0 -1
  722. package/dist/src/eval-core/dependency-checker.js.map +0 -1
  723. package/dist/src/eval-core/evaluation-execution.d.ts.map +0 -1
  724. package/dist/src/eval-core/evaluation-execution.js.map +0 -1
  725. package/dist/src/eval-core/evaluation-job.d.ts.map +0 -1
  726. package/dist/src/eval-core/evaluation-job.js.map +0 -1
  727. package/dist/src/eval-core/evaluation-reporting.d.ts.map +0 -1
  728. package/dist/src/eval-core/evaluation-reporting.js.map +0 -1
  729. package/dist/src/eval-core/execution-strategy.d.ts.map +0 -1
  730. package/dist/src/eval-core/execution-strategy.js +0 -165
  731. package/dist/src/eval-core/execution-strategy.js.map +0 -1
  732. package/dist/src/eval-core/fact-checker.d.ts.map +0 -1
  733. package/dist/src/eval-core/fact-checker.js.map +0 -1
  734. package/dist/src/eval-core/layer-gates.d.ts.map +0 -1
  735. package/dist/src/eval-core/layer-gates.js.map +0 -1
  736. package/dist/src/eval-core/mock-hook.cjs +0 -203
  737. package/dist/src/eval-core/mocks-runtime.d.ts +0 -96
  738. package/dist/src/eval-core/mocks-runtime.d.ts.map +0 -1
  739. package/dist/src/eval-core/mocks-runtime.js +0 -323
  740. package/dist/src/eval-core/mocks-runtime.js.map +0 -1
  741. package/dist/src/eval-core/schema.d.ts.map +0 -1
  742. package/dist/src/eval-core/schema.js.map +0 -1
  743. package/dist/src/eval-core/statistics.d.ts.map +0 -1
  744. package/dist/src/eval-core/statistics.js.map +0 -1
  745. package/dist/src/eval-core/task-planner.d.ts.map +0 -1
  746. package/dist/src/eval-core/task-planner.js.map +0 -1
  747. package/dist/src/eval-core/verdict.d.ts.map +0 -1
  748. package/dist/src/eval-core/verdict.js.map +0 -1
  749. package/dist/src/eval-workflows/batch-evaluation-workflow.d.ts.map +0 -1
  750. package/dist/src/eval-workflows/batch-evaluation-workflow.js.map +0 -1
  751. package/dist/src/eval-workflows/evaluation-pipeline.d.ts +0 -109
  752. package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +0 -1
  753. package/dist/src/eval-workflows/evaluation-pipeline.js +0 -373
  754. package/dist/src/eval-workflows/evaluation-pipeline.js.map +0 -1
  755. package/dist/src/eval-workflows/evaluation-preparation.d.ts.map +0 -1
  756. package/dist/src/eval-workflows/evaluation-preparation.js.map +0 -1
  757. package/dist/src/eval-workflows/run-evaluation.d.ts.map +0 -1
  758. package/dist/src/eval-workflows/run-evaluation.js +0 -528
  759. package/dist/src/eval-workflows/run-evaluation.js.map +0 -1
  760. package/dist/src/executors/anthropic-api.d.ts.map +0 -1
  761. package/dist/src/executors/anthropic-api.js.map +0 -1
  762. package/dist/src/executors/claude-cli.d.ts.map +0 -1
  763. package/dist/src/executors/claude-cli.js +0 -179
  764. package/dist/src/executors/claude-cli.js.map +0 -1
  765. package/dist/src/executors/claude-sdk-trace.d.ts.map +0 -1
  766. package/dist/src/executors/claude-sdk-trace.js.map +0 -1
  767. package/dist/src/executors/claude-sdk.d.ts.map +0 -1
  768. package/dist/src/executors/claude-sdk.js.map +0 -1
  769. package/dist/src/executors/codex-cli-trace.d.ts.map +0 -1
  770. package/dist/src/executors/codex-cli-trace.js.map +0 -1
  771. package/dist/src/executors/codex-cli.d.ts.map +0 -1
  772. package/dist/src/executors/codex-cli.js.map +0 -1
  773. package/dist/src/executors/codex-sdk.d.ts.map +0 -1
  774. package/dist/src/executors/codex-sdk.js.map +0 -1
  775. package/dist/src/executors/gemini.d.ts.map +0 -1
  776. package/dist/src/executors/gemini.js.map +0 -1
  777. package/dist/src/executors/index.d.ts.map +0 -1
  778. package/dist/src/executors/index.js.map +0 -1
  779. package/dist/src/executors/openai-api.d.ts.map +0 -1
  780. package/dist/src/executors/openai-api.js.map +0 -1
  781. package/dist/src/executors/runtime-fingerprint.d.ts.map +0 -1
  782. package/dist/src/executors/runtime-fingerprint.js.map +0 -1
  783. package/dist/src/executors/script.d.ts.map +0 -1
  784. package/dist/src/executors/script.js.map +0 -1
  785. package/dist/src/executors/shared.d.ts +0 -194
  786. package/dist/src/executors/shared.d.ts.map +0 -1
  787. package/dist/src/executors/shared.js +0 -256
  788. package/dist/src/executors/shared.js.map +0 -1
  789. package/dist/src/grading/assertions.d.ts.map +0 -1
  790. package/dist/src/grading/assertions.js.map +0 -1
  791. package/dist/src/grading/debias-validate.d.ts.map +0 -1
  792. package/dist/src/grading/debias-validate.js.map +0 -1
  793. package/dist/src/grading/diagnostic.d.ts.map +0 -1
  794. package/dist/src/grading/diagnostic.js.map +0 -1
  795. package/dist/src/grading/gold-cli.d.ts.map +0 -1
  796. package/dist/src/grading/gold-cli.js.map +0 -1
  797. package/dist/src/grading/gold-dataset.d.ts.map +0 -1
  798. package/dist/src/grading/gold-dataset.js.map +0 -1
  799. package/dist/src/grading/human-gold.d.ts.map +0 -1
  800. package/dist/src/grading/human-gold.js.map +0 -1
  801. package/dist/src/grading/index.d.ts.map +0 -1
  802. package/dist/src/grading/index.js.map +0 -1
  803. package/dist/src/grading/judge.d.ts.map +0 -1
  804. package/dist/src/grading/judge.js.map +0 -1
  805. package/dist/src/grading/layered-scores.d.ts.map +0 -1
  806. package/dist/src/grading/layered-scores.js.map +0 -1
  807. package/dist/src/inputs/eval-config.d.ts.map +0 -1
  808. package/dist/src/inputs/eval-config.js.map +0 -1
  809. package/dist/src/inputs/load-samples.d.ts.map +0 -1
  810. package/dist/src/inputs/load-samples.js.map +0 -1
  811. package/dist/src/inputs/mcp-resolver.d.ts.map +0 -1
  812. package/dist/src/inputs/mcp-resolver.js.map +0 -1
  813. package/dist/src/inputs/skill-loader.d.ts.map +0 -1
  814. package/dist/src/inputs/skill-loader.js +0 -253
  815. package/dist/src/inputs/skill-loader.js.map +0 -1
  816. package/dist/src/inputs/url-fetcher.d.ts.map +0 -1
  817. package/dist/src/inputs/url-fetcher.js.map +0 -1
  818. package/dist/src/observability/experience.d.ts +0 -260
  819. package/dist/src/observability/experience.d.ts.map +0 -1
  820. package/dist/src/observability/experience.js +0 -1233
  821. package/dist/src/observability/experience.js.map +0 -1
  822. package/dist/src/observability/inbox-view-model.d.ts +0 -27
  823. package/dist/src/observability/inbox-view-model.d.ts.map +0 -1
  824. package/dist/src/observability/inbox-view-model.js +0 -135
  825. package/dist/src/observability/inbox-view-model.js.map +0 -1
  826. package/dist/src/observability/inbox.d.ts +0 -125
  827. package/dist/src/observability/inbox.d.ts.map +0 -1
  828. package/dist/src/observability/inbox.js +0 -741
  829. package/dist/src/observability/inbox.js.map +0 -1
  830. package/dist/src/observability/problem-patterns.d.ts +0 -51
  831. package/dist/src/observability/problem-patterns.d.ts.map +0 -1
  832. package/dist/src/observability/problem-patterns.js +0 -205
  833. package/dist/src/observability/problem-patterns.js.map +0 -1
  834. package/dist/src/observability/review-state.d.ts +0 -54
  835. package/dist/src/observability/review-state.d.ts.map +0 -1
  836. package/dist/src/observability/review-state.js +0 -131
  837. package/dist/src/observability/review-state.js.map +0 -1
  838. package/dist/src/observability/skill-chain-advisories.d.ts +0 -25
  839. package/dist/src/observability/skill-chain-advisories.d.ts.map +0 -1
  840. package/dist/src/observability/skill-chain-advisories.js +0 -69
  841. package/dist/src/observability/skill-chain-advisories.js.map +0 -1
  842. package/dist/src/observability/skill-chain.d.ts +0 -61
  843. package/dist/src/observability/skill-chain.d.ts.map +0 -1
  844. package/dist/src/observability/skill-chain.js +0 -233
  845. package/dist/src/observability/skill-chain.js.map +0 -1
  846. package/dist/src/observability/skill-health-analyzer.d.ts.map +0 -1
  847. package/dist/src/observability/skill-health-analyzer.js.map +0 -1
  848. package/dist/src/observability/text-signals.d.ts +0 -7
  849. package/dist/src/observability/text-signals.d.ts.map +0 -1
  850. package/dist/src/observability/text-signals.js +0 -22
  851. package/dist/src/observability/text-signals.js.map +0 -1
  852. package/dist/src/observability/trace-adapter.d.ts.map +0 -1
  853. package/dist/src/observability/trace-adapter.js.map +0 -1
  854. package/dist/src/observability/trace-attribution.d.ts +0 -56
  855. package/dist/src/observability/trace-attribution.d.ts.map +0 -1
  856. package/dist/src/observability/trace-attribution.js +0 -200
  857. package/dist/src/observability/trace-attribution.js.map +0 -1
  858. package/dist/src/observability/trace-segmenter.d.ts +0 -52
  859. package/dist/src/observability/trace-segmenter.d.ts.map +0 -1
  860. package/dist/src/observability/trace-segmenter.js +0 -320
  861. package/dist/src/observability/trace-segmenter.js.map +0 -1
  862. package/dist/src/observability/trace-source.d.ts +0 -99
  863. package/dist/src/observability/trace-source.d.ts.map +0 -1
  864. package/dist/src/observability/trace-source.js +0 -492
  865. package/dist/src/observability/trace-source.js.map +0 -1
  866. package/dist/src/renderer/html-renderer.d.ts.map +0 -1
  867. package/dist/src/renderer/html-renderer.js.map +0 -1
  868. package/dist/src/renderer/layout.d.ts.map +0 -1
  869. package/dist/src/renderer/layout.js.map +0 -1
  870. package/dist/src/renderer/observation-inbox-renderer.d.ts +0 -4
  871. package/dist/src/renderer/observation-inbox-renderer.d.ts.map +0 -1
  872. package/dist/src/renderer/observation-inbox-renderer.js +0 -5376
  873. package/dist/src/renderer/observation-inbox-renderer.js.map +0 -1
  874. package/dist/src/renderer/skill-detail-renderer.d.ts +0 -15
  875. package/dist/src/renderer/skill-detail-renderer.d.ts.map +0 -1
  876. package/dist/src/renderer/skill-detail-renderer.js +0 -1234
  877. package/dist/src/renderer/skill-detail-renderer.js.map +0 -1
  878. package/dist/src/renderer/skill-health-renderer.d.ts.map +0 -1
  879. package/dist/src/renderer/skill-health-renderer.js.map +0 -1
  880. package/dist/src/renderer/skill-list-renderer.d.ts +0 -4
  881. package/dist/src/renderer/skill-list-renderer.d.ts.map +0 -1
  882. package/dist/src/renderer/skill-list-renderer.js +0 -327
  883. package/dist/src/renderer/skill-list-renderer.js.map +0 -1
  884. package/dist/src/renderer/summary.d.ts +0 -68
  885. package/dist/src/renderer/summary.d.ts.map +0 -1
  886. package/dist/src/renderer/summary.js +0 -1896
  887. package/dist/src/renderer/summary.js.map +0 -1
  888. package/dist/src/renderer/table.d.ts.map +0 -1
  889. package/dist/src/renderer/table.js.map +0 -1
  890. package/dist/src/renderer/test-view.d.ts.map +0 -1
  891. package/dist/src/renderer/test-view.js +0 -905
  892. package/dist/src/renderer/test-view.js.map +0 -1
  893. package/dist/src/renderer/trends.d.ts.map +0 -1
  894. package/dist/src/renderer/trends.js.map +0 -1
  895. package/dist/src/server/job-store.d.ts.map +0 -1
  896. package/dist/src/server/job-store.js.map +0 -1
  897. package/dist/src/server/report-server.d.ts.map +0 -1
  898. package/dist/src/server/report-server.js +0 -801
  899. package/dist/src/server/report-server.js.map +0 -1
  900. package/dist/src/server/report-store.d.ts.map +0 -1
  901. package/dist/src/server/report-store.js.map +0 -1
  902. package/dist/src/server/skill-index.d.ts +0 -77
  903. package/dist/src/server/skill-index.d.ts.map +0 -1
  904. package/dist/src/server/skill-index.js +0 -331
  905. package/dist/src/server/skill-index.js.map +0 -1
  906. package/dist/src/server/skill-insights.d.ts +0 -92
  907. package/dist/src/server/skill-insights.d.ts.map +0 -1
  908. package/dist/src/server/skill-insights.js +0 -676
  909. package/dist/src/server/skill-insights.js.map +0 -1
  910. package/dist/src/shared/hard-rules.d.ts +0 -40
  911. package/dist/src/shared/hard-rules.d.ts.map +0 -1
  912. package/dist/src/shared/hard-rules.js +0 -200
  913. package/dist/src/shared/hard-rules.js.map +0 -1
  914. package/dist/src/shared/time.d.ts.map +0 -1
  915. package/dist/src/shared/time.js.map +0 -1
  916. package/dist/src/shared/tool-search.d.ts.map +0 -1
  917. package/dist/src/shared/tool-search.js.map +0 -1
  918. package/dist/src/types/doctor.d.ts +0 -155
  919. package/dist/src/types/doctor.d.ts.map +0 -1
  920. package/dist/src/types/doctor.js.map +0 -1
  921. package/dist/src/types/eval.d.ts +0 -372
  922. package/dist/src/types/eval.d.ts.map +0 -1
  923. package/dist/src/types/eval.js.map +0 -1
  924. package/dist/src/types/executor.d.ts.map +0 -1
  925. package/dist/src/types/executor.js.map +0 -1
  926. package/dist/src/types/index.d.ts +0 -8
  927. package/dist/src/types/index.d.ts.map +0 -1
  928. package/dist/src/types/index.js +0 -8
  929. package/dist/src/types/index.js.map +0 -1
  930. package/dist/src/types/judge.d.ts.map +0 -1
  931. package/dist/src/types/judge.js.map +0 -1
  932. package/dist/src/types/report.d.ts +0 -551
  933. package/dist/src/types/report.d.ts.map +0 -1
  934. package/dist/src/types/report.js.map +0 -1
  935. package/dist/src/types/shared.d.ts.map +0 -1
  936. package/dist/src/types/shared.js.map +0 -1
  937. package/dist/src/types/storage.d.ts.map +0 -1
  938. package/dist/src/types/storage.js.map +0 -1
  939. package/dist/src/util/safe-slice.d.ts +0 -23
  940. package/dist/src/util/safe-slice.d.ts.map +0 -1
  941. package/dist/src/util/safe-slice.js +0 -33
  942. package/dist/src/util/safe-slice.js.map +0 -1
  943. /package/dist/{src/analysis → analysis}/coverage-analyzer.d.ts +0 -0
  944. /package/dist/{src/analysis → analysis}/coverage-analyzer.js +0 -0
  945. /package/dist/{src/analysis → analysis}/failure-clusterer.d.ts +0 -0
  946. /package/dist/{src/analysis → analysis}/failure-clusterer.js +0 -0
  947. /package/dist/{src/analysis → analysis}/hedging-classifier.d.ts +0 -0
  948. /package/dist/{src/analysis → analysis}/hedging-classifier.js +0 -0
  949. /package/dist/{src/analysis → analysis}/sample-diagnostics.d.ts +0 -0
  950. /package/dist/{src/analysis → analysis}/sample-diagnostics.js +0 -0
  951. /package/dist/{src/analysis → analysis}/saturation.d.ts +0 -0
  952. /package/dist/{src/analysis → analysis}/saturation.js +0 -0
  953. /package/dist/{src/cli → cli}/index.d.ts +0 -0
  954. /package/dist/{src/cli → cli/lib}/progress.d.ts +0 -0
  955. /package/dist/{src/cli → cli/lib}/progress.js +0 -0
  956. /package/dist/{src/cli → cli/lib}/run-tally.js +0 -0
  957. /package/dist/{src/doctor → doctor}/health/builtin-dimensions.d.ts +0 -0
  958. /package/dist/{src/doctor → doctor}/health/builtin-dimensions.js +0 -0
  959. /package/dist/{src/doctor → doctor}/health/composer.d.ts +0 -0
  960. /package/dist/{src/doctor → doctor}/health/dimension-registry.d.ts +0 -0
  961. /package/dist/{src/doctor → doctor}/health/dimension-registry.js +0 -0
  962. /package/dist/{src/doctor → doctor}/health/dimension-spec.js +0 -0
  963. /package/dist/{src/doctor → doctor}/health/register.d.ts +0 -0
  964. /package/dist/{src/doctor → doctor}/health/register.js +0 -0
  965. /package/dist/{src/doctor → doctor}/index.d.ts +0 -0
  966. /package/dist/{src/doctor → doctor}/preflight.d.ts +0 -0
  967. /package/dist/{src/doctor → doctor}/preflight.js +0 -0
  968. /package/dist/{src/doctor → doctor}/rules.d.ts +0 -0
  969. /package/dist/{src/eval-core → eval-core}/bootstrap.d.ts +0 -0
  970. /package/dist/{src/eval-core → eval-core}/bootstrap.js +0 -0
  971. /package/dist/{src/eval-core → eval-core}/cache.d.ts +0 -0
  972. /package/dist/{src/eval-core → eval-core}/cache.js +0 -0
  973. /package/dist/{src/eval-core → eval-core}/comparability.d.ts +0 -0
  974. /package/dist/{src/eval-core → eval-core}/comparability.js +0 -0
  975. /package/dist/{src/eval-core → eval-core}/dependency-checker.js +0 -0
  976. /package/dist/{src/eval-core → eval-core}/evaluation-execution.d.ts +0 -0
  977. /package/dist/{src/eval-core → eval-core}/evaluation-execution.js +0 -0
  978. /package/dist/{src/eval-core → eval-core}/evaluation-job.d.ts +0 -0
  979. /package/dist/{src/eval-core → eval-core}/evaluation-job.js +0 -0
  980. /package/dist/{src/eval-core → eval-core}/evaluation-reporting.d.ts +0 -0
  981. /package/dist/{src/eval-core → eval-core}/evaluation-reporting.js +0 -0
  982. /package/dist/{src/eval-core → eval-core}/execution-strategy.d.ts +0 -0
  983. /package/dist/{src/eval-core → eval-core}/fact-checker.d.ts +0 -0
  984. /package/dist/{src/eval-core → eval-core}/fact-checker.js +0 -0
  985. /package/dist/{src/eval-core → eval-core}/layer-gates.d.ts +0 -0
  986. /package/dist/{src/eval-core → eval-core}/layer-gates.js +0 -0
  987. /package/dist/{src/eval-core → eval-core}/schema.d.ts +0 -0
  988. /package/dist/{src/eval-core → eval-core}/schema.js +0 -0
  989. /package/dist/{src/eval-core → eval-core}/statistics.d.ts +0 -0
  990. /package/dist/{src/eval-core → eval-core}/statistics.js +0 -0
  991. /package/dist/{src/eval-core → eval-core}/task-planner.d.ts +0 -0
  992. /package/dist/{src/eval-core → eval-core}/task-planner.js +0 -0
  993. /package/dist/{src/eval-core → eval-core}/verdict.d.ts +0 -0
  994. /package/dist/{src/eval-core → eval-core}/verdict.js +0 -0
  995. /package/dist/{src/eval-workflows → eval-workflows}/batch-evaluation-workflow.d.ts +0 -0
  996. /package/dist/{src/eval-workflows → eval-workflows}/batch-evaluation-workflow.js +0 -0
  997. /package/dist/{src/eval-workflows → eval-workflows}/evaluation-preparation.d.ts +0 -0
  998. /package/dist/{src/eval-workflows → eval-workflows}/evaluation-preparation.js +0 -0
  999. /package/dist/{src/eval-workflows → eval-workflows}/run-evaluation.d.ts +0 -0
  1000. /package/dist/{src/executors → executors}/anthropic-api.d.ts +0 -0
  1001. /package/dist/{src/executors → executors}/anthropic-api.js +0 -0
  1002. /package/dist/{src/executors → executors}/claude-cli.d.ts +0 -0
  1003. /package/dist/{src/executors → executors}/claude-sdk-trace.d.ts +0 -0
  1004. /package/dist/{src/executors → executors}/claude-sdk-trace.js +0 -0
  1005. /package/dist/{src/executors → executors}/claude-sdk.d.ts +0 -0
  1006. /package/dist/{src/executors → executors}/claude-sdk.js +0 -0
  1007. /package/dist/{src/executors → executors}/codex-cli-trace.d.ts +0 -0
  1008. /package/dist/{src/executors → executors}/codex-cli-trace.js +0 -0
  1009. /package/dist/{src/executors → executors}/codex-cli.d.ts +0 -0
  1010. /package/dist/{src/executors → executors}/codex-cli.js +0 -0
  1011. /package/dist/{src/executors → executors}/codex-sdk.d.ts +0 -0
  1012. /package/dist/{src/executors → executors}/codex-sdk.js +0 -0
  1013. /package/dist/{src/executors → executors}/gemini.d.ts +0 -0
  1014. /package/dist/{src/executors → executors}/gemini.js +0 -0
  1015. /package/dist/{src/executors → executors}/index.d.ts +0 -0
  1016. /package/dist/{src/executors → executors}/index.js +0 -0
  1017. /package/dist/{src/executors → executors}/openai-api.d.ts +0 -0
  1018. /package/dist/{src/executors → executors}/openai-api.js +0 -0
  1019. /package/dist/{src/executors → executors}/runtime-fingerprint.d.ts +0 -0
  1020. /package/dist/{src/executors → executors}/runtime-fingerprint.js +0 -0
  1021. /package/dist/{src/executors → executors}/script.d.ts +0 -0
  1022. /package/dist/{src/executors → executors}/script.js +0 -0
  1023. /package/dist/{src/grading → grading}/assertions.d.ts +0 -0
  1024. /package/dist/{src/grading → grading}/assertions.js +0 -0
  1025. /package/dist/{src/grading → grading}/debias-validate.d.ts +0 -0
  1026. /package/dist/{src/grading → grading}/debias-validate.js +0 -0
  1027. /package/dist/{src/grading → grading}/diagnostic.d.ts +0 -0
  1028. /package/dist/{src/grading → grading}/diagnostic.js +0 -0
  1029. /package/dist/{src/grading → grading}/gold-cli.d.ts +0 -0
  1030. /package/dist/{src/grading → grading}/gold-cli.js +0 -0
  1031. /package/dist/{src/grading → grading}/gold-dataset.d.ts +0 -0
  1032. /package/dist/{src/grading → grading}/gold-dataset.js +0 -0
  1033. /package/dist/{src/grading → grading}/human-gold.d.ts +0 -0
  1034. /package/dist/{src/grading → grading}/human-gold.js +0 -0
  1035. /package/dist/{src/grading → grading}/index.d.ts +0 -0
  1036. /package/dist/{src/grading → grading}/index.js +0 -0
  1037. /package/dist/{src/grading → grading}/judge.d.ts +0 -0
  1038. /package/dist/{src/grading → grading}/judge.js +0 -0
  1039. /package/dist/{src/grading → grading}/layered-scores.d.ts +0 -0
  1040. /package/dist/{src/grading → grading}/layered-scores.js +0 -0
  1041. /package/dist/{src/inputs → inputs}/eval-config.d.ts +0 -0
  1042. /package/dist/{src/inputs → inputs}/eval-config.js +0 -0
  1043. /package/dist/{src/inputs → inputs}/load-samples.d.ts +0 -0
  1044. /package/dist/{src/inputs → inputs}/load-samples.js +0 -0
  1045. /package/dist/{src/inputs → inputs}/mcp-resolver.d.ts +0 -0
  1046. /package/dist/{src/inputs → inputs}/mcp-resolver.js +0 -0
  1047. /package/dist/{src/inputs → inputs}/skill-loader.d.ts +0 -0
  1048. /package/dist/{src/inputs → inputs}/url-fetcher.d.ts +0 -0
  1049. /package/dist/{src/inputs → inputs}/url-fetcher.js +0 -0
  1050. /package/dist/{src/observability → observability}/skill-health-analyzer.d.ts +0 -0
  1051. /package/dist/{src/observability → observability}/skill-health-analyzer.js +0 -0
  1052. /package/dist/{src/observability → observability}/trace-adapter.d.ts +0 -0
  1053. /package/dist/{src/observability → observability}/trace-adapter.js +0 -0
  1054. /package/dist/{src/renderer → renderer}/html-renderer.d.ts +0 -0
  1055. /package/dist/{src/renderer → renderer}/html-renderer.js +0 -0
  1056. /package/dist/{src/renderer → renderer}/layout.d.ts +0 -0
  1057. /package/dist/{src/renderer → renderer}/layout.js +0 -0
  1058. /package/dist/{src/renderer → renderer}/skill-health-renderer.d.ts +0 -0
  1059. /package/dist/{src/renderer → renderer}/skill-health-renderer.js +0 -0
  1060. /package/dist/{src/renderer → renderer}/table.d.ts +0 -0
  1061. /package/dist/{src/renderer → renderer}/table.js +0 -0
  1062. /package/dist/{src/renderer → renderer}/test-view.d.ts +0 -0
  1063. /package/dist/{src/renderer → renderer}/trends.d.ts +0 -0
  1064. /package/dist/{src/renderer → renderer}/trends.js +0 -0
  1065. /package/dist/{src/server → server}/job-store.d.ts +0 -0
  1066. /package/dist/{src/server → server}/job-store.js +0 -0
  1067. /package/dist/{src/server → server}/report-server.d.ts +0 -0
  1068. /package/dist/{src/server → server}/report-store.d.ts +0 -0
  1069. /package/dist/{src/server → server}/report-store.js +0 -0
  1070. /package/dist/{src/shared → shared}/time.d.ts +0 -0
  1071. /package/dist/{src/shared → shared}/time.js +0 -0
  1072. /package/dist/{src/shared → shared}/tool-search.d.ts +0 -0
  1073. /package/dist/{src/shared → shared}/tool-search.js +0 -0
  1074. /package/dist/{src/types → types}/doctor.js +0 -0
  1075. /package/dist/{src/types → types}/eval.js +0 -0
  1076. /package/dist/{src/types → types}/executor.d.ts +0 -0
  1077. /package/dist/{src/types → types}/executor.js +0 -0
  1078. /package/dist/{src/types → types}/judge.d.ts +0 -0
  1079. /package/dist/{src/types → types}/judge.js +0 -0
  1080. /package/dist/{src/types → types}/report.js +0 -0
  1081. /package/dist/{src/types → types}/shared.d.ts +0 -0
  1082. /package/dist/{src/types → types}/shared.js +0 -0
  1083. /package/dist/{src/types → types}/storage.d.ts +0 -0
  1084. /package/dist/{src/types → types}/storage.js +0 -0
@@ -0,0 +1,3438 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { buildExperienceProblemPatterns, mergeExperienceProblemPatterns, } from './problem-patterns.js';
3
+ import { observationMetricAnnotationVerdict, observationReviewStateKey } from './review-state.js';
4
+ import { extractCommandEnvelopeText, stripCommandEnvelopeText } from './trace-attribution.js';
5
+ import { hasAssistantDeliverableArtifactText, hasAssistantDeliverySignalText, hasUserHardRuleText, isAssistantProgressUpdateText, isAssistantProtocolReplyText, isRuntimeProtocolPromptText, isSyntheticUserMessageText, isToolResultFailureText, isUserInteractionMetricText } from './text-signals.js';
6
+ import { durationMsBetween } from '../shared/time.js';
7
+ import { loadExpectedToolsForSkill, loadFrontmatterSkillType, loadSkillDeclarationCheck, } from './experience-frontmatter.js';
8
+ import { findNegativeFeedbackMatches, findPositiveFeedbackMatches, findUserCorrectionMatches, findUserGoalShiftMatches, hasNegativeFeedbackSignal, hasPositiveFeedbackSignal, hasUserCorrectionSignal, hasUserGoalShiftSignal, } from './feedback-matchers.js';
9
+ export { findNegativeFeedbackMatches, findPositiveFeedbackMatches, findUserCorrectionMatches, findUserGoalShiftMatches, hasNegativeFeedbackSignal, hasPositiveFeedbackSignal, hasUserCorrectionSignal, hasUserGoalShiftSignal, } from './feedback-matchers.js';
10
+ export function aggregateExperienceChecklistItemStatus(statuses) {
11
+ if (statuses.includes('degraded'))
12
+ return 'degraded';
13
+ if (statuses.includes('failed'))
14
+ return 'failed';
15
+ if (statuses.includes('unknown'))
16
+ return 'unknown';
17
+ if (statuses.includes('not_declared'))
18
+ return 'not_declared';
19
+ if (statuses.includes('passed'))
20
+ return 'passed';
21
+ return 'not_applicable';
22
+ }
23
+ const USER_INTERRUPTION_RE = /\[Request interrupted by user(?: for tool use)?\]|interrupted by user|用户中断|停止任务|停一下|先别|别动|等一下|等下|等等|取消(?:任务|执行)?|先暂停|暂停一下/i;
24
+ const TIMELINE_PREVIEW_EVENT_LIMIT = 240;
25
+ const ZERO_INDICATORS = {
26
+ userMessageCount: 0,
27
+ userFollowUpCount: 0,
28
+ userCorrectionCount: 0,
29
+ userInterruptionCount: 0,
30
+ sessionInterruptedCount: 0,
31
+ negativeFeedbackCount: 0,
32
+ positiveFeedbackCount: 0,
33
+ userGoalShiftCount: 0,
34
+ hardRuleTextHitCount: 0,
35
+ assistantDeliverySignalCount: 0,
36
+ deliverableArtifactSignalCount: 0,
37
+ routerDownstreamCompleted: 0,
38
+ routerDownstreamFailed: 0,
39
+ selfCorrectionCount: 0,
40
+ repeatedExecutionCount: 0,
41
+ toolCallCount: 0,
42
+ toolFailureCount: 0,
43
+ highObservationCount: 0,
44
+ mediumObservationCount: 0,
45
+ hedgingCount: 0,
46
+ explicitMarkerCount: 0,
47
+ };
48
+ export function buildObservationExperienceReport(input) {
49
+ const sessionsBySourceTrace = new Map(input.sessions.map((session) => [session.sourcePath, session]));
50
+ const sessionGroupsById = groupSessionsByLogicalId(input.sessions);
51
+ const sessionGroupsByKey = groupSessionsByExperienceKey(input.sessions);
52
+ const goalSlices = [];
53
+ const invocations = [];
54
+ for (const segment of input.segments) {
55
+ if (segment.skillName === 'general')
56
+ continue;
57
+ const session = sessionsBySourceTrace.get(segment.sourceTrace ?? '') ?? sessionGroupsById.get(segment.sessionId)?.[0];
58
+ const sourceTrace = segment.sourceTrace ?? session?.sourcePath ?? '';
59
+ const sessionGroupKey = session ? experienceSessionGroupKey(session) : `trace:${sourceTrace || segment.sessionId}`;
60
+ const relatedItems = relatedObservationItems(segment, input.items);
61
+ const bounds = session ? segmentRecordBounds(session, segment) : { start: 0, end: 0 };
62
+ const timeline = session ? buildTimeline(session, bounds.start, bounds.end) : [];
63
+ const metricScopeId = hashParts('session', segment.skillName, sessionGroupKey);
64
+ const userRefs = timeline.filter((event) => event.kind === 'user_message');
65
+ const indicators = indicatorsForSegment(segment, relatedItems, timeline, metricScopeId, input.reviewState);
66
+ const observationRefs = relatedItems.map(observationEvidenceRef);
67
+ const evidenceChain = evidenceChainForTimeline(timeline, observationRefs);
68
+ const ruleFindings = ruleFindingsForEvidence(indicators, timeline, observationRefs, evidenceChain, metricScopeId, input.reviewState);
69
+ const assistiveInference = assistiveInferenceForEvidence(indicators, evidenceChain, ruleFindings);
70
+ const problemPatterns = buildExperienceProblemPatterns({
71
+ skillName: segment.skillName,
72
+ sessionId: segment.sessionId,
73
+ timeline,
74
+ metricScopeId,
75
+ reviewState: input.reviewState,
76
+ });
77
+ const hasGoalShift = userRefs.some((ref) => hasUserGoalShiftSignal(ref.snippet ?? ''));
78
+ // 包含 sourceTrace 防止 main + subagent 因 segmentIndex 各自从 0 计数而撞 hash
79
+ // (segmenter 给 segment.sessionId = sessionGroupId,main 和 subagent 共享)
80
+ const goalSliceId = hashParts('goal', segment.sessionId, sourceTrace, segment.skillName, String(segment.segmentIndex));
81
+ const invocationId = hashParts('invocation', segment.sessionId, sourceTrace, segment.skillName, String(segment.segmentIndex));
82
+ goalSlices.push({
83
+ id: goalSliceId,
84
+ skillName: segment.skillName,
85
+ sessionId: segment.sessionId,
86
+ sourceTrace,
87
+ cwd: segment.cwd,
88
+ startTimestamp: segment.startTimestamp,
89
+ endTimestamp: segment.endTimestamp,
90
+ sliceReasonCode: hasGoalShift ? 'explicit_user_goal_shift' : 'skill_segment_boundary',
91
+ sliceConfidence: hasGoalShift ? 'medium' : 'high',
92
+ inferredUserGoal: inferUserGoal(userRefs),
93
+ userMessageRefs: userRefs.slice(0, 8).map(evidenceRefFromTimeline),
94
+ });
95
+ invocations.push({
96
+ id: invocationId,
97
+ skillName: segment.skillName,
98
+ sessionId: segment.sessionId,
99
+ sessionGroupKey,
100
+ sourceTrace,
101
+ sourceKind: segment.sourceKind ?? sourceKindForPath(sourceTrace),
102
+ entrypoint: session ? session.entrypoint ?? inferEntrypointFromRecords(session) : undefined,
103
+ sourceMetadata: session?.sourceMetadata,
104
+ cwd: segment.cwd,
105
+ segmentIndex: segment.segmentIndex,
106
+ goalSliceId,
107
+ startTimestamp: segment.startTimestamp,
108
+ endTimestamp: segment.endTimestamp,
109
+ attribution: {
110
+ source: segment.attribution?.source ?? 'unknown',
111
+ confidence: segment.attribution?.confidence ?? 0.3,
112
+ rawSkillRef: segment.attribution?.rawSkillRef,
113
+ pluginName: segment.attribution?.pluginName,
114
+ commandName: segment.attribution?.commandName,
115
+ },
116
+ metrics: segment.metrics,
117
+ toolCounts: countTools(segment),
118
+ indicators,
119
+ evidenceChain,
120
+ ruleFindings,
121
+ assistiveInference,
122
+ problemPatterns,
123
+ relatedObservationIds: relatedItems.map((item) => item.id),
124
+ evidenceRefs: [
125
+ ...observationRefs,
126
+ ...userRefs.slice(0, 5).map(evidenceRefFromTimeline),
127
+ ],
128
+ timeline,
129
+ });
130
+ }
131
+ const sessions = summarizeExperienceSessions(invocations, sessionGroupsByKey, input.generatedAt, input.reviewState);
132
+ const skills = summarizeExperienceSkills(sessions, invocations);
133
+ return {
134
+ kind: 'observe-experience',
135
+ schemaVersion: 1,
136
+ scope: 'evidence-only',
137
+ generatedAt: input.generatedAt,
138
+ meta: {
139
+ sessionCount: sessions.length,
140
+ skillCount: skills.length,
141
+ invocationCount: invocations.length,
142
+ goalSliceCount: goalSlices.length,
143
+ noteCodes: ['no_llm_judge', 'no_auto_verdict', 'default_goal_slice_is_allowed', 'deterministic_assistive_inference'],
144
+ },
145
+ goalSlices,
146
+ invocations,
147
+ sessions,
148
+ skills,
149
+ };
150
+ }
151
+ function relatedObservationItems(segment, items) {
152
+ return items.filter((item) => item.skillName === segment.skillName
153
+ && (item.sessionId === segment.sessionId || item.recentSessionIds.includes(segment.sessionId))
154
+ && timestampsOverlap(item.firstSeen, item.lastSeen, segment.startTimestamp, segment.endTimestamp));
155
+ }
156
+ function logicalSessionId(session) {
157
+ return session.sessionGroupId ?? session.sessionId;
158
+ }
159
+ function experienceSessionGroupKey(session) {
160
+ const logicalId = logicalSessionId(session);
161
+ if (session.traceRole && session.traceRole !== 'standalone' && session.sessionGroupPath) {
162
+ return `group:${session.sessionGroupPath}\u0000${logicalId}`;
163
+ }
164
+ return `trace:${session.sourcePath}`;
165
+ }
166
+ function groupSessionsByLogicalId(sessions) {
167
+ const groups = new Map();
168
+ for (const session of sessions) {
169
+ const key = logicalSessionId(session);
170
+ const group = groups.get(key) ?? [];
171
+ group.push(session);
172
+ groups.set(key, group);
173
+ }
174
+ for (const [key, group] of groups.entries()) {
175
+ groups.set(key, group.sort(compareSessionsForTimeline));
176
+ }
177
+ return groups;
178
+ }
179
+ function groupSessionsByExperienceKey(sessions) {
180
+ const groups = new Map();
181
+ for (const session of sessions) {
182
+ const key = experienceSessionGroupKey(session);
183
+ const group = groups.get(key) ?? [];
184
+ group.push(session);
185
+ groups.set(key, group);
186
+ }
187
+ for (const [key, group] of groups.entries()) {
188
+ groups.set(key, group.sort(compareSessionsForTimeline));
189
+ }
190
+ return groups;
191
+ }
192
+ function compareSessionsForTimeline(a, b) {
193
+ const roleRank = (session) => session.traceRole === 'main' ? 0 : session.traceRole === 'standalone' ? 1 : 2;
194
+ const rank = roleRank(a) - roleRank(b);
195
+ if (rank !== 0)
196
+ return rank;
197
+ const time = (a.startTimestamp ?? '').localeCompare(b.startTimestamp ?? '');
198
+ if (time !== 0)
199
+ return time;
200
+ return a.sourcePath.localeCompare(b.sourcePath);
201
+ }
202
+ function timestampsOverlap(aStart, aEnd, bStart, bEnd) {
203
+ if (!aStart || !aEnd || !bStart || !bEnd)
204
+ return true;
205
+ return aStart <= bEnd && bStart <= aEnd;
206
+ }
207
+ function segmentRecordBounds(session, segment) {
208
+ if (typeof segment.startRecordIndex === 'number' && typeof segment.endRecordIndex === 'number') {
209
+ const rawStart = clampRecordIndex(session, segment.startRecordIndex);
210
+ const rawEnd = clampRecordIndex(session, Math.max(segment.startRecordIndex, segment.endRecordIndex));
211
+ const humanStart = Math.min(rawStart, previousHumanUserRecordIndex(session, rawStart) ?? rawStart);
212
+ return {
213
+ start: includeLeadingRuntimeContext(session, humanStart),
214
+ end: includeTrailingDeliveryContext(session, Math.min(session.records.length, rawEnd + 1)),
215
+ };
216
+ }
217
+ const indexes = segment.toolCalls
218
+ .map((toolCall) => toolCall.messageIndex)
219
+ .filter((index) => typeof index === 'number' && index >= 0);
220
+ if (indexes.length > 0) {
221
+ const start = Math.max(0, Math.min(...indexes) - 3);
222
+ return {
223
+ start: Math.min(start, previousHumanUserRecordIndex(session, start) ?? start),
224
+ end: includeTrailingDeliveryContext(session, Math.min(session.records.length, Math.max(...indexes) + 5)),
225
+ };
226
+ }
227
+ const timestampIndexes = [];
228
+ session.records.forEach((record, index) => {
229
+ const ts = timestampOf(record);
230
+ if (ts && ts >= segment.startTimestamp && ts <= segment.endTimestamp)
231
+ timestampIndexes.push(index);
232
+ });
233
+ if (timestampIndexes.length === 0)
234
+ return { start: 0, end: Math.min(session.records.length, 12) };
235
+ const start = Math.max(0, Math.min(...timestampIndexes) - 2);
236
+ return {
237
+ start: Math.min(start, previousHumanUserRecordIndex(session, start) ?? start),
238
+ end: includeTrailingDeliveryContext(session, Math.min(session.records.length, Math.max(...timestampIndexes) + 3)),
239
+ };
240
+ }
241
+ function clampRecordIndex(session, index) {
242
+ return Math.max(0, Math.min(session.records.length - 1, index));
243
+ }
244
+ function includeLeadingRuntimeContext(session, start) {
245
+ let nextStart = start;
246
+ for (let index = start - 1; index >= 0; index -= 1) {
247
+ const events = timelineEventsFromRecord(session, session.records[index], index);
248
+ if (events.length === 0)
249
+ continue;
250
+ if (events.some((event) => event.kind === 'runtime_context')) {
251
+ nextStart = index;
252
+ continue;
253
+ }
254
+ break;
255
+ }
256
+ return nextStart;
257
+ }
258
+ function includeTrailingDeliveryContext(session, end) {
259
+ const safeEnd = Math.min(session.records.length, Math.max(0, end));
260
+ const lookaheadEnd = Math.min(session.records.length, safeEnd + 8);
261
+ for (let index = safeEnd; index < lookaheadEnd; index += 1) {
262
+ const events = timelineEventsFromRecord(session, session.records[index], index);
263
+ if (events.some((event) => event.kind === 'user_message'))
264
+ break;
265
+ if (events.some(isAssistantDeliveryEvent))
266
+ return index + 1;
267
+ }
268
+ return safeEnd;
269
+ }
270
+ function previousHumanUserRecordIndex(session, start) {
271
+ for (let index = Math.min(start, session.records.length - 1); index >= 0; index -= 1) {
272
+ const events = timelineEventsFromRecord(session, session.records[index], index);
273
+ if (events.some((event) => event.kind === 'user_message'))
274
+ return index;
275
+ }
276
+ return undefined;
277
+ }
278
+ function isAssistantDeliveryEvent(event) {
279
+ if (event.kind !== 'assistant_message')
280
+ return false;
281
+ const text = event.fullText ?? event.snippet ?? '';
282
+ return hasAssistantDeliverySignalText(text);
283
+ }
284
+ function isAssistantDeliverableArtifactEvent(event) {
285
+ if (event.kind !== 'assistant_message')
286
+ return false;
287
+ const text = event.fullText ?? event.snippet ?? '';
288
+ return hasAssistantDeliverableArtifactText(text);
289
+ }
290
+ function isAssistantProgressUpdateEvent(event) {
291
+ if (event.kind !== 'assistant_message')
292
+ return false;
293
+ const text = event.fullText ?? event.snippet ?? '';
294
+ return isAssistantProgressUpdateText(text);
295
+ }
296
+ function hasSelfCorrectionSignal(event) {
297
+ if (event.kind !== 'assistant_message')
298
+ return false;
299
+ const text = event.fullText ?? event.snippet ?? '';
300
+ return /刚才.*(?:不对|错了|有误)|发现.*(?:不对|错了|问题|遗漏)|重新(?:检查|分析|执行|生成|整理|补(?:充)?(?:结论|答案|回答))|改用|换成|修正|我再(?:检查|重新|看)|跑偏|偏(?:题|了|向)|没(?:有)?(?:完整|完全)?(?:回答|覆盖)|漏(?:了|掉)|不完整|我刚追问了一版|按原问题(?:重新)?(?:补|回|答)|\b(?:recheck|retry|rerun|mistake|wrong)\b/i.test(text);
301
+ }
302
+ function hasRepeatedExecutionSignal(event) {
303
+ if (event.kind !== 'assistant_message' && event.kind !== 'tool_use')
304
+ return false;
305
+ const text = `${event.label ?? ''} ${event.toolName ?? ''} ${event.fullText ?? event.snippet ?? ''}`;
306
+ return /重复(?:执行|尝试|读取|搜索|调用)|再次(?:执行|读取|搜索|调用)|重新(?:执行|读取|搜索|调用|跑|运行)|再(?:执行|读取|搜索|调用|跑)一遍|重试|\b(?:retry|rerun)\b/i.test(text);
307
+ }
308
+ function buildTimeline(session, start, end) {
309
+ return buildTimelineWindow(session, start, end).slice(0, TIMELINE_PREVIEW_EVENT_LIMIT);
310
+ }
311
+ function buildTimelineWindow(session, start, end) {
312
+ const events = [];
313
+ const safeEnd = Math.min(session.records.length, Math.max(start, end));
314
+ for (let index = start; index < safeEnd; index += 1) {
315
+ events.push(...timelineEventsFromRecord(session, session.records[index], index));
316
+ }
317
+ return events
318
+ .sort((a, b) => a.order - b.order);
319
+ }
320
+ function timelineEventsFromRecord(session, record, messageIndex) {
321
+ if (!record || typeof record !== 'object')
322
+ return [];
323
+ const rec = record;
324
+ const uuid = typeof rec.uuid === 'string' ? rec.uuid : undefined;
325
+ const timestamp = timestampOf(record);
326
+ const base = {
327
+ sourceTrace: session.sourcePath,
328
+ sessionId: logicalSessionId(session),
329
+ traceRole: session.traceRole,
330
+ traceLabel: session.traceLabel,
331
+ messageIndex,
332
+ logicalMessageIndex: messageIndex,
333
+ sourceLineIndex: messageIndex,
334
+ messageUuid: uuid,
335
+ timestamp,
336
+ };
337
+ if (rec.type === 'user') {
338
+ const user = rec;
339
+ return userEvents(user, base, messageIndex);
340
+ }
341
+ if (rec.type === 'assistant') {
342
+ const assistant = rec;
343
+ return assistantEvents(assistant, base, messageIndex);
344
+ }
345
+ return sessionEventFromRecord(rec, base, messageIndex);
346
+ }
347
+ function sessionEventFromRecord(record, base, messageIndex) {
348
+ const type = typeof record.type === 'string' ? record.type : '';
349
+ const rawText = safeRecordText(record);
350
+ if (!isSessionBoundaryType(type) && !hasAssistantTurnFailedText(rawText))
351
+ return [];
352
+ return [timelineEvent({
353
+ ...base,
354
+ kind: 'runtime_context',
355
+ role: 'other',
356
+ order: messageIndex * 10,
357
+ snippet: snippet(rawText || type, 700),
358
+ fullText: fullText(rawText || type),
359
+ label: type || 'session event',
360
+ })];
361
+ }
362
+ function isSessionBoundaryType(type) {
363
+ return /^session[._-](?:started|ended)$/i.test(type);
364
+ }
365
+ function safeRecordText(record) {
366
+ try {
367
+ return JSON.stringify(record);
368
+ }
369
+ catch {
370
+ return String(record ?? '');
371
+ }
372
+ }
373
+ function hasAssistantTurnFailedText(value) {
374
+ return /\[assistant turn failed\]|assistant turn failed|assistant_turn_failed|turn failed/i.test(value);
375
+ }
376
+ function userEvents(record, base, messageIndex) {
377
+ const content = record.message.content;
378
+ const events = [];
379
+ if (typeof content === 'string') {
380
+ events.push(...userTextEventsFromText(record, content, base, messageIndex, 0));
381
+ return events;
382
+ }
383
+ let textIndex = 0;
384
+ let resultIndex = 0;
385
+ for (const part of content) {
386
+ if (part.type === 'text') {
387
+ const nextEvents = userTextEventsFromText(record, part.text, base, messageIndex, textIndex);
388
+ events.push(...nextEvents);
389
+ if (nextEvents.length > 0)
390
+ textIndex += nextEvents.length;
391
+ }
392
+ else if (part.type === 'tool_result') {
393
+ const full = fullText(part.content);
394
+ const text = snippet(part.content, 900);
395
+ const failed = part.is_error === true || isToolResultFailureText(part.content);
396
+ events.push(timelineEvent({
397
+ ...base,
398
+ kind: 'tool_result',
399
+ role: 'tool',
400
+ order: messageIndex * 10 + 5 + resultIndex,
401
+ toolUseId: part.tool_use_id,
402
+ isError: failed,
403
+ snippet: text,
404
+ fullText: full,
405
+ label: failed ? 'tool result error' : 'tool result',
406
+ }));
407
+ resultIndex += 1;
408
+ }
409
+ }
410
+ return events;
411
+ }
412
+ function userTextEventsFromText(record, rawText, base, messageIndex, offset) {
413
+ const events = [];
414
+ const commandEnvelope = extractCommandEnvelopeText(rawText);
415
+ if (commandEnvelope) {
416
+ events.push(timelineEvent({
417
+ ...base,
418
+ kind: 'runtime_context',
419
+ role: 'tool',
420
+ order: messageIndex * 10 + offset,
421
+ snippet: snippet(commandEnvelope, 700),
422
+ fullText: fullText(commandEnvelope),
423
+ label: 'command envelope',
424
+ }));
425
+ }
426
+ const humanText = commandEnvelope ? stripCommandEnvelopeText(rawText) : rawText;
427
+ const full = fullText(humanText);
428
+ const text = snippet(humanText, 700);
429
+ if (text) {
430
+ const kind = userTextEventKind(record, text);
431
+ events.push(timelineEvent({
432
+ ...base,
433
+ kind,
434
+ role: kind === 'user_message' ? 'user' : kind === 'synthetic_user_event' ? 'other' : 'tool',
435
+ order: messageIndex * 10 + offset + (commandEnvelope ? 1 : 0),
436
+ snippet: text,
437
+ fullText: full,
438
+ label: userTextEventLabel(kind),
439
+ }));
440
+ }
441
+ return events;
442
+ }
443
+ function assistantEvents(record, base, messageIndex) {
444
+ const events = [];
445
+ const textParts = [];
446
+ let index = 0;
447
+ for (const part of record.message.content) {
448
+ if (part.type === 'text' && part.text)
449
+ textParts.push(part.text);
450
+ if (part.type === 'tool_use' && part.id && part.name) {
451
+ const inputText = JSON.stringify(part.input ?? {});
452
+ events.push(timelineEvent({
453
+ ...base,
454
+ kind: 'tool_use',
455
+ role: 'assistant',
456
+ order: messageIndex * 10 + 5 + index,
457
+ toolUseId: part.id,
458
+ toolName: part.name,
459
+ snippet: snippet(inputText, 900),
460
+ fullText: fullText(inputText),
461
+ label: `tool_use ${part.name}`,
462
+ }));
463
+ index += 1;
464
+ }
465
+ }
466
+ const rawText = textParts.join('\n');
467
+ const text = snippet(rawText, 700);
468
+ if (text) {
469
+ const protocolReply = isAssistantProtocolReplyText(rawText);
470
+ events.unshift(timelineEvent({
471
+ ...base,
472
+ kind: protocolReply ? 'runtime_context' : 'assistant_message',
473
+ role: 'assistant',
474
+ order: messageIndex * 10,
475
+ snippet: text,
476
+ fullText: fullText(rawText),
477
+ label: protocolReply ? 'assistant protocol reply' : 'assistant message',
478
+ }));
479
+ }
480
+ return events;
481
+ }
482
+ function timelineEvent(input) {
483
+ return {
484
+ ...input,
485
+ id: hashParts(input.sourceTrace, input.sessionId, String(input.messageIndex ?? ''), input.kind, input.toolUseId ?? '', input.snippet ?? ''),
486
+ };
487
+ }
488
+ function userTextEventKind(record, text) {
489
+ if (isSkillContextRecord(record, text))
490
+ return 'skill_context';
491
+ if (isRuntimeContextRecord(record, text))
492
+ return 'runtime_context';
493
+ if (isSyntheticUserMessageText(text))
494
+ return 'synthetic_user_event';
495
+ return 'user_message';
496
+ }
497
+ function userTextEventLabel(kind) {
498
+ if (kind === 'skill_context')
499
+ return 'skill context';
500
+ if (kind === 'runtime_context')
501
+ return 'runtime context';
502
+ if (kind === 'synthetic_user_event')
503
+ return 'synthetic user event';
504
+ return 'user message';
505
+ }
506
+ function observationEvidenceRef(item) {
507
+ return {
508
+ id: hashParts('observation', item.id),
509
+ kind: 'observation',
510
+ sourceTrace: item.sourceTrace,
511
+ sessionId: item.sessionId,
512
+ messageIndex: item.evidence.messageIndex,
513
+ logicalMessageIndex: item.evidence.messageIndex,
514
+ sourceLineIndex: item.evidence.messageIndex,
515
+ messageUuid: item.evidence.messageUuid,
516
+ toolUseId: item.evidence.toolUseId,
517
+ timestamp: item.evidence.segmentTimestamp,
518
+ role: 'other',
519
+ label: `${item.signalType}/${item.signalSubtype}`,
520
+ snippet: snippet(item.evidence.query || item.evidence.path || item.evidence.assistantSnippet || item.evidence.outputSnippet || item.evidence.markerToken, 700),
521
+ };
522
+ }
523
+ function evidenceRefFromTimeline(event) {
524
+ return {
525
+ id: event.id,
526
+ kind: event.kind,
527
+ sourceTrace: event.sourceTrace,
528
+ sessionId: event.sessionId,
529
+ traceRole: event.traceRole,
530
+ traceLabel: event.traceLabel,
531
+ messageIndex: event.messageIndex,
532
+ logicalMessageIndex: event.logicalMessageIndex ?? event.messageIndex,
533
+ sourceLineIndex: event.sourceLineIndex ?? event.messageIndex,
534
+ messageUuid: event.messageUuid,
535
+ toolUseId: event.toolUseId,
536
+ timestamp: event.timestamp,
537
+ role: event.role,
538
+ label: event.label,
539
+ snippet: event.snippet,
540
+ };
541
+ }
542
+ function evidenceChainForTimeline(timeline, observationRefs) {
543
+ const events = uniqueTimelineEvents(timeline).sort(compareTimelineEvents);
544
+ const userEvents = events.filter((event) => event.kind === 'user_message' && !isSyntheticUserMessageText(event.snippet ?? ''));
545
+ const runtimeEvents = events.filter((event) => event.kind === 'runtime_context');
546
+ const skillEvents = events.filter((event) => event.kind === 'skill_context');
547
+ const assistantEvents = events.filter((event) => event.kind === 'assistant_message');
548
+ const toolUseEvents = events.filter((event) => event.kind === 'tool_use');
549
+ const toolResultEvents = events.filter((event) => event.kind === 'tool_result');
550
+ const toolFailureEvents = toolResultEvents.filter(isToolFailureEvent);
551
+ return {
552
+ userMessageCount: userEvents.length,
553
+ runtimeContextCount: runtimeEvents.length,
554
+ skillContextCount: skillEvents.length,
555
+ assistantMessageCount: assistantEvents.length,
556
+ toolUseCount: toolUseEvents.length,
557
+ toolResultCount: toolResultEvents.length,
558
+ toolFailureResultCount: toolFailureEvents.length,
559
+ observationCount: observationRefs.length,
560
+ firstUserMessage: userEvents[0] ? evidenceRefFromTimeline(userEvents[0]) : undefined,
561
+ firstRuntimeContext: runtimeEvents[0] ? evidenceRefFromTimeline(runtimeEvents[0]) : undefined,
562
+ firstSkillContext: skillEvents[0] ? evidenceRefFromTimeline(skillEvents[0]) : undefined,
563
+ firstToolUse: toolUseEvents[0] ? evidenceRefFromTimeline(toolUseEvents[0]) : undefined,
564
+ firstToolFailure: toolFailureEvents[0] ? evidenceRefFromTimeline(toolFailureEvents[0]) : undefined,
565
+ lastAssistantMessage: assistantEvents.at(-1) ? evidenceRefFromTimeline(assistantEvents.at(-1)) : undefined,
566
+ };
567
+ }
568
+ function ruleFindingsForEvidence(indicators, timeline, observationRefs, evidenceChain, metricScopeId, reviewState) {
569
+ const events = uniqueTimelineEvents(timeline);
570
+ const userEvents = events.filter((event) => event.kind === 'user_message');
571
+ const metricUserEvents = userEvents.filter((event) => isUserInteractionMetricText(event.snippet ?? ''));
572
+ const refs = (matches) => matches.slice(0, 5).map(evidenceRefFromTimeline);
573
+ const findings = [];
574
+ const push = (code, level, count, evidenceRefs = []) => {
575
+ if (count <= 0)
576
+ return;
577
+ findings.push({ code, level, count, evidenceRefs: uniqueEvidenceRefs(evidenceRefs).slice(0, 5) });
578
+ };
579
+ push('high_observation_seen', 'attention', indicators.highObservationCount, observationRefs);
580
+ push('user_correction_seen', 'attention', indicators.userCorrectionCount, refs(metricUserEvents.filter((event) => metricIsActive(event, 'user_correction', hasUserCorrectionSignal(event.snippet ?? ''), reviewState, metricScopeId))));
581
+ push('user_interruption_seen', 'attention', indicators.userInterruptionCount, refs(metricUserEvents.filter((event) => metricIsActive(event, 'user_interruption', USER_INTERRUPTION_RE.test(event.snippet ?? ''), reviewState, metricScopeId))));
582
+ push('session_interrupted_seen', 'attention', indicators.sessionInterruptedCount, refs(sessionInterruptedEvents(events)));
583
+ push('negative_feedback_seen', 'attention', indicators.negativeFeedbackCount, refs(metricUserEvents.filter((event) => metricIsActive(event, 'negative_feedback', hasNegativeFeedbackSignal(event.snippet ?? ''), reviewState))));
584
+ push('tool_failure_seen', 'sample', indicators.toolFailureCount, refs(events.filter(isToolFailureEvent)));
585
+ push('medium_observation_seen', 'sample', indicators.mediumObservationCount, observationRefs);
586
+ push('hedging_seen', 'sample', indicators.hedgingCount, observationRefs);
587
+ push('explicit_marker_seen', 'sample', indicators.explicitMarkerCount, observationRefs);
588
+ push('hard_rule_seen', 'sample', indicators.hardRuleTextHitCount, refs(metricUserEvents.filter((event) => metricIsActive(event, 'hard_rule', hasUserHardRuleText(event.snippet ?? ''), reviewState, metricScopeId))));
589
+ push('positive_feedback_seen', 'normal', indicators.positiveFeedbackCount, refs(metricUserEvents.filter((event) => metricIsActive(event, 'positive_feedback', hasPositiveFeedbackSignal(event.snippet ?? ''), reviewState))));
590
+ push('user_goal_shift_seen', 'normal', indicators.userGoalShiftCount, refs(metricUserEvents.filter((event) => metricIsActive(event, 'user_goal_shift', hasUserGoalShiftSignal(event.snippet ?? ''), reviewState, metricScopeId))));
591
+ push('runtime_context_excluded', 'normal', evidenceChain.runtimeContextCount, evidenceChain.firstRuntimeContext ? [evidenceChain.firstRuntimeContext] : []);
592
+ push('skill_context_excluded', 'normal', evidenceChain.skillContextCount, evidenceChain.firstSkillContext ? [evidenceChain.firstSkillContext] : []);
593
+ if (findings.filter((finding) => finding.level !== 'normal').length === 0) {
594
+ findings.push({ code: 'no_priority_signal', level: 'normal', count: 1, evidenceRefs: [] });
595
+ }
596
+ return findings;
597
+ }
598
+ function assistiveInferenceForEvidence(indicators, evidenceChain, ruleFindings) {
599
+ const attentionFindings = ruleFindings.filter((finding) => finding.level === 'attention');
600
+ const sampleFindings = ruleFindings.filter((finding) => finding.level === 'sample');
601
+ const normalFindings = ruleFindings.filter((finding) => finding.level === 'normal');
602
+ const basisRuleCodes = unique([
603
+ ...attentionFindings.map((finding) => finding.code),
604
+ ...sampleFindings.map((finding) => finding.code),
605
+ ...normalFindings
606
+ .filter((finding) => finding.code === 'positive_feedback_seen' || finding.code === 'hard_rule_seen' || finding.code === 'user_goal_shift_seen' || finding.code === 'no_priority_signal')
607
+ .map((finding) => finding.code),
608
+ ]);
609
+ const cautionCodes = ['no_llm_judge', 'rule_only'];
610
+ if (evidenceChain.runtimeContextCount > 0)
611
+ cautionCodes.push('runtime_context_excluded');
612
+ if (evidenceChain.skillContextCount > 0)
613
+ cautionCodes.push('skill_context_excluded');
614
+ if (evidenceChain.userMessageCount === 0)
615
+ cautionCodes.push('no_human_user_message');
616
+ if (evidenceChain.toolUseCount + evidenceChain.toolResultCount + evidenceChain.assistantMessageCount > 24)
617
+ cautionCodes.push('limited_timeline_window');
618
+ let code;
619
+ if (attentionFindings.length > 0) {
620
+ code = 'review_recommended';
621
+ }
622
+ else if (sampleFindings.length > 0) {
623
+ code = 'sample_recommended';
624
+ }
625
+ else if (indicators.positiveFeedbackCount > 0) {
626
+ code = 'positive_signal_observed';
627
+ }
628
+ else if (indicators.userGoalShiftCount > 0) {
629
+ code = 'user_switched_topic_neutral';
630
+ }
631
+ else if (evidenceChain.userMessageCount === 0 && evidenceChain.toolUseCount === 0 && evidenceChain.observationCount === 0) {
632
+ code = 'insufficient_human_context';
633
+ }
634
+ else if (evidenceChain.userMessageCount === 0 && indicators.hardRuleTextHitCount === 0) {
635
+ code = 'insufficient_human_context';
636
+ }
637
+ else {
638
+ code = 'no_obvious_issue_from_rules';
639
+ }
640
+ const confidence = attentionFindings.length > 0 || indicators.positiveFeedbackCount > 0
641
+ ? 'high'
642
+ : sampleFindings.length > 0 || indicators.userGoalShiftCount > 0 || evidenceChain.userMessageCount > 0 || evidenceChain.toolUseCount > 0
643
+ ? 'medium'
644
+ : 'low';
645
+ const evidenceRefs = uniqueEvidenceRefs([
646
+ ...attentionFindings.flatMap((finding) => finding.evidenceRefs),
647
+ ...sampleFindings.flatMap((finding) => finding.evidenceRefs),
648
+ ...(evidenceChain.firstUserMessage ? [evidenceChain.firstUserMessage] : []),
649
+ ...(evidenceChain.firstToolUse ? [evidenceChain.firstToolUse] : []),
650
+ ...(evidenceChain.firstToolFailure ? [evidenceChain.firstToolFailure] : []),
651
+ ]).slice(0, 8);
652
+ return {
653
+ mode: 'deterministic_rules_only',
654
+ code,
655
+ confidence,
656
+ basisRuleCodes,
657
+ cautionCodes: unique(cautionCodes),
658
+ evidenceRefs,
659
+ };
660
+ }
661
+ function indicatorsForSegment(segment, relatedItems, timeline, metricScopeId, reviewState) {
662
+ const userRefs = timeline.filter((event) => event.kind === 'user_message');
663
+ const humanUserRefs = userRefs.filter((ref) => Boolean(ref.snippet) && !isSyntheticUserMessageText(ref.snippet ?? ''));
664
+ const interactionUserRefs = humanUserRefs.filter((ref) => isUserInteractionMetricText(ref.snippet ?? ''));
665
+ return {
666
+ userMessageCount: humanUserRefs.length,
667
+ userFollowUpCount: interactionUserRefs.reduce((sum, ref, index) => sum + (metricIsActive(ref, 'user_follow_up', index > 0 && !hasUserGoalShiftSignal(ref.snippet ?? ''), reviewState, metricScopeId) ? 1 : 0), 0),
668
+ userCorrectionCount: interactionUserRefs.reduce((sum, ref) => sum + metricCount(ref, 'user_correction', findUserCorrectionMatches(ref.snippet ?? '').length, reviewState, metricScopeId), 0),
669
+ userInterruptionCount: interactionUserRefs.reduce((sum, ref) => sum + (metricIsActive(ref, 'user_interruption', USER_INTERRUPTION_RE.test(ref.snippet ?? ''), reviewState, metricScopeId) ? 1 : 0), 0),
670
+ sessionInterruptedCount: sessionInterruptedEvents(timeline).length,
671
+ negativeFeedbackCount: interactionUserRefs.reduce((sum, ref) => sum + metricCount(ref, 'negative_feedback', findNegativeFeedbackMatches(ref.snippet ?? '').length, reviewState), 0),
672
+ positiveFeedbackCount: interactionUserRefs.reduce((sum, ref) => sum + metricCount(ref, 'positive_feedback', findPositiveFeedbackMatches(ref.snippet ?? '').length, reviewState), 0),
673
+ userGoalShiftCount: interactionUserRefs.reduce((sum, ref) => sum + metricCount(ref, 'user_goal_shift', findUserGoalShiftMatches(ref.snippet ?? '').length, reviewState, metricScopeId), 0),
674
+ hardRuleTextHitCount: interactionUserRefs.reduce((sum, ref) => sum + (metricIsActive(ref, 'hard_rule', hasUserHardRuleText(ref.snippet ?? ''), reviewState, metricScopeId) ? 1 : 0), 0),
675
+ assistantDeliverySignalCount: timeline.filter(isAssistantDeliveryEvent).length,
676
+ deliverableArtifactSignalCount: timeline.reduce((sum, ref) => sum + (metricIsActive(ref, 'deliverable_artifact', isAssistantDeliverableArtifactEvent(ref), reviewState) ? 1 : 0), 0),
677
+ routerDownstreamCompleted: 0,
678
+ routerDownstreamFailed: 0,
679
+ selfCorrectionCount: timeline.reduce((sum, ref) => sum + (metricIsActive(ref, 'self_correction', hasSelfCorrectionSignal(ref), reviewState) ? 1 : 0), 0),
680
+ repeatedExecutionCount: timeline.reduce((sum, ref) => sum + (metricIsActive(ref, 'repeated_execution', hasRepeatedExecutionSignal(ref), reviewState) ? 1 : 0), 0),
681
+ toolCallCount: segment.metrics.numToolCalls,
682
+ toolFailureCount: Math.max(segment.metrics.numToolFailures, timeline.filter(isToolFailureEvent).length),
683
+ highObservationCount: relatedItems.filter((item) => item.severity === 'high').length,
684
+ mediumObservationCount: relatedItems.filter((item) => item.severity === 'medium').length,
685
+ hedgingCount: relatedItems.filter((item) => item.signalType === 'hedging').reduce((sum, item) => sum + item.occurrences, 0),
686
+ explicitMarkerCount: relatedItems.filter((item) => item.signalType === 'explicit_marker').reduce((sum, item) => sum + item.occurrences, 0),
687
+ };
688
+ }
689
+ function isToolFailureEvent(event) {
690
+ return event.kind === 'tool_result' && (event.isError === true || isToolResultFailureText(event.fullText ?? event.snippet ?? ''));
691
+ }
692
+ function sessionInterruptedEvents(timeline) {
693
+ const events = uniqueTimelineEvents(timeline).sort(compareTimelineEvents);
694
+ const interrupted = [];
695
+ for (let index = 0; index < events.length; index += 1) {
696
+ const event = events[index];
697
+ const text = `${event.label ?? ''}\n${event.snippet ?? ''}\n${event.fullText ?? ''}`;
698
+ if (hasAssistantTurnFailedText(text)) {
699
+ interrupted.push(event);
700
+ continue;
701
+ }
702
+ if (/^session[._-]ended$/i.test(event.label ?? '')) {
703
+ const next = events.slice(index + 1).find((candidate) => /^session[._-]started$/i.test(candidate.label ?? ''));
704
+ if (next)
705
+ interrupted.push(event);
706
+ }
707
+ }
708
+ return uniqueTimelineEvents(interrupted);
709
+ }
710
+ function metricIsActive(ref, metricKey, ruleDetected, reviewState, metricScopeId) {
711
+ const verdict = observationMetricAnnotationVerdict(reviewState, { ...ref, metricScopeId }, metricKey);
712
+ if (verdict === 'confirmed')
713
+ return true;
714
+ if (verdict === 'rejected')
715
+ return false;
716
+ return ruleDetected;
717
+ }
718
+ function metricCount(ref, metricKey, ruleCount, reviewState, metricScopeId) {
719
+ const verdict = observationMetricAnnotationVerdict(reviewState, { ...ref, metricScopeId }, metricKey);
720
+ if (verdict === 'confirmed')
721
+ return Math.max(1, ruleCount);
722
+ if (verdict === 'rejected')
723
+ return 0;
724
+ return ruleCount;
725
+ }
726
+ function summarizeExperienceSessions(invocations, sessionGroupsByKey, generatedAt, reviewState) {
727
+ const byKey = new Map();
728
+ const canonicalEpisodesBySessionGroup = new Map();
729
+ for (const invocation of invocations) {
730
+ const key = `${invocation.skillName}\u0000${invocation.sessionGroupKey}`;
731
+ const group = byKey.get(key) ?? [];
732
+ group.push(invocation);
733
+ byKey.set(key, group);
734
+ }
735
+ return Array.from(byKey.values()).map((group) => {
736
+ const first = group[0];
737
+ const indicators = sumIndicators(group.map((invocation) => invocation.indicators));
738
+ const relatedObservationIds = unique(group.flatMap((invocation) => invocation.relatedObservationIds));
739
+ const reviewBasisCodes = basisCodesForIndicators(indicators);
740
+ const reviewPriorityScore = scoreForIndicators(indicators);
741
+ const timeline = uniqueTimelineEvents(group.flatMap((invocation) => invocation.timeline)).sort(compareTimelineEvents);
742
+ const sessionGroup = sessionGroupsByKey.get(first.sessionGroupKey) ?? [];
743
+ const sourceSessionStartTimestamp = minString(sessionGroup.map((session) => session.startTimestamp));
744
+ const sourceSessionEndTimestamp = maxString(sessionGroup.map((session) => session.endTimestamp));
745
+ const timelineTree = sessionGroup.length > 0 ? buildSessionTimelineTree(first.sessionId, sessionGroup) : undefined;
746
+ const fullSessionTimeline = timelineTree
747
+ ? uniqueTimelineEvents([
748
+ ...timelineTree.main,
749
+ ...timelineTree.branches.flatMap((branch) => branch.events),
750
+ ]).sort(compareTimelineEvents)
751
+ : timeline;
752
+ const fullSessionEventCount = fullSessionTimeline.length;
753
+ const previewEvents = timeline.slice(0, TIMELINE_PREVIEW_EVENT_LIMIT);
754
+ const previewIndexes = previewEvents
755
+ .map((event) => event.messageIndex)
756
+ .filter((index) => typeof index === 'number');
757
+ const groupStartRecordIndex = minDefined(group.map((invocation) => minDefined(invocation.timeline.map((event) => event.messageIndex))));
758
+ const groupEndRecordIndex = maxDefined(group.map((invocation) => maxDefined(invocation.timeline.map((event) => event.messageIndex))));
759
+ const previewStartRecordIndex = minDefined(previewIndexes);
760
+ const previewEndRecordIndex = maxDefined(previewIndexes);
761
+ const fullSessionIndexes = fullSessionTimeline
762
+ .map((event) => event.messageIndex)
763
+ .filter((index) => typeof index === 'number');
764
+ const sessionStartRecordIndex = minDefined(fullSessionIndexes) ?? 0;
765
+ const sessionEndRecordIndex = maxDefined(fullSessionIndexes) ?? Math.max(0, (sessionGroup[0]?.records.length ?? 1) - 1);
766
+ const omittedBeforeCount = previewStartRecordIndex === undefined
767
+ ? 0
768
+ : fullSessionTimeline.filter((event) => typeof event.messageIndex === 'number' && event.messageIndex < previewStartRecordIndex).length;
769
+ const omittedAfterCount = previewEndRecordIndex === undefined
770
+ ? 0
771
+ : fullSessionTimeline.filter((event) => typeof event.messageIndex === 'number' && event.messageIndex > previewEndRecordIndex).length;
772
+ const observationRefs = uniqueEvidenceRefs(group.flatMap((invocation) => invocation.evidenceRefs.filter((ref) => ref.kind === 'observation')));
773
+ const evidenceChain = evidenceChainForTimeline(timeline, observationRefs);
774
+ const metricScopeId = hashParts('session', first.skillName, first.sessionGroupKey);
775
+ const ruleFindings = ruleFindingsForEvidence(indicators, timeline, observationRefs, evidenceChain, metricScopeId);
776
+ const assistiveInference = assistiveInferenceForEvidence(indicators, evidenceChain, ruleFindings);
777
+ const problemPatterns = mergeExperienceProblemPatterns(group.flatMap((invocation) => invocation.problemPatterns));
778
+ const storyInvocations = invocations.filter((invocation) => invocation.sessionGroupKey === first.sessionGroupKey);
779
+ const baseSession = {
780
+ id: metricScopeId,
781
+ skillName: first.skillName,
782
+ sessionId: first.sessionId,
783
+ sourceTrace: first.sourceTrace,
784
+ sourceKind: first.sourceKind,
785
+ entrypoint: first.entrypoint,
786
+ sourceMetadata: mergeSourceMetadata(group.map((invocation) => invocation.sourceMetadata)),
787
+ cwd: first.cwd,
788
+ sourceSessionStartTimestamp,
789
+ sourceSessionEndTimestamp,
790
+ sourceSessionDurationMs: durationMsBetween(sourceSessionStartTimestamp, sourceSessionEndTimestamp),
791
+ startTimestamp: group.reduce((min, invocation) => invocation.startTimestamp < min ? invocation.startTimestamp : min, first.startTimestamp),
792
+ endTimestamp: group.reduce((max, invocation) => invocation.endTimestamp > max ? invocation.endTimestamp : max, first.endTimestamp),
793
+ invocationIds: group.map((invocation) => invocation.id),
794
+ goalSliceIds: unique(group.map((invocation) => invocation.goalSliceId)),
795
+ reviewPriority: priorityForScore(reviewPriorityScore),
796
+ reviewPriorityScore,
797
+ reviewBasisCodes,
798
+ indicators,
799
+ evidenceChain,
800
+ ruleFindings,
801
+ assistiveInference,
802
+ problemPatterns,
803
+ relatedObservationIds,
804
+ timelinePreview: previewEvents,
805
+ fullSessionTimeline,
806
+ timelineTree,
807
+ timelineScope: {
808
+ mode: 'skill_segment_window',
809
+ segmentStartRecordIndex: groupStartRecordIndex,
810
+ segmentEndRecordIndex: groupEndRecordIndex,
811
+ previewStartRecordIndex,
812
+ previewEndRecordIndex,
813
+ sessionStartRecordIndex,
814
+ sessionEndRecordIndex,
815
+ previewEventCount: previewEvents.length,
816
+ fullSessionEventCount,
817
+ truncated: timeline.length > previewEvents.length || omittedBeforeCount > 0 || omittedAfterCount > 0,
818
+ omittedBeforeCount,
819
+ omittedAfterCount,
820
+ },
821
+ attributionSources: unique(group.map((invocation) => invocation.attribution.source).filter(Boolean)).sort(),
822
+ pluginNames: unique(group.map((invocation) => invocation.attribution.pluginName).filter((value) => Boolean(value))).sort(),
823
+ rawSkillRefs: unique(group.map((invocation) => invocation.attribution.rawSkillRef).filter((value) => Boolean(value))).sort(),
824
+ commandNames: unique(group.map((invocation) => invocation.attribution.commandName).filter((value) => Boolean(value))).sort(),
825
+ };
826
+ const sessionStory = buildSessionStory(baseSession, storyInvocations, canonicalEpisodesBySessionGroup.get(first.sessionGroupKey), reviewState);
827
+ if (!canonicalEpisodesBySessionGroup.has(first.sessionGroupKey)) {
828
+ canonicalEpisodesBySessionGroup.set(first.sessionGroupKey, sessionStory.episodes ?? []);
829
+ }
830
+ const sessionWithStoryBase = {
831
+ ...baseSession,
832
+ sessionStory,
833
+ };
834
+ const sessionWithStory = {
835
+ ...sessionWithStoryBase,
836
+ indicators: enrichRouterDownstreamIndicators(sessionWithStoryBase),
837
+ };
838
+ const reviewerReport = buildReviewerReport(sessionWithStory, group, generatedAt, reviewState, storyInvocations, sessionStory);
839
+ return {
840
+ ...sessionWithStory,
841
+ reviewPriority: priorityForReviewerFindings(sessionWithStory, reviewerReport.findings),
842
+ reviewerReport,
843
+ };
844
+ }).sort((a, b) => {
845
+ const priorityDiff = experiencePriorityRank(b.reviewPriority) - experiencePriorityRank(a.reviewPriority);
846
+ if (priorityDiff !== 0)
847
+ return priorityDiff;
848
+ if (b.reviewPriorityScore !== a.reviewPriorityScore)
849
+ return b.reviewPriorityScore - a.reviewPriorityScore;
850
+ return b.endTimestamp.localeCompare(a.endTimestamp);
851
+ });
852
+ }
853
+ function experiencePriorityRank(priority) {
854
+ if (priority === 'review_first')
855
+ return 2;
856
+ if (priority === 'sample_review')
857
+ return 1;
858
+ return 0;
859
+ }
860
+ const REVIEWER_REPORT_RULE_VERSION = 'reviewer-report.v1';
861
+ function buildReviewerReport(session, invocations, generatedAt, reviewState, storyInvocations = invocations, sessionStory = buildSessionStory(session, storyInvocations)) {
862
+ const indicators = session.indicators;
863
+ const scopeReasons = reviewerScopeReasonCodes(session);
864
+ const scopeKind = scopeReasons.length === 0 ? 'single_skill_single_goal' : 'degraded_complex';
865
+ const findings = reviewerFindingsForSession(session, reviewState);
866
+ const attentionCount = findings.filter((finding) => finding.level === 'attention').length;
867
+ const possibleFalsePositiveCount = findings.filter((finding) => finding.level === 'possible_false_positive').length;
868
+ const tokenUsage = sumTokenUsage(invocations);
869
+ const title = reviewerTitle(session, attentionCount, possibleFalsePositiveCount);
870
+ const expectedToolCheck = expectedToolCheckForSession(session);
871
+ const feedbackCounts = canonicalFeedbackCountsForSession(session, reviewState);
872
+ const traceLinks = uniqueEvidenceRefs([
873
+ ...(session.evidenceChain.firstUserMessage ? [session.evidenceChain.firstUserMessage] : []),
874
+ ...(session.evidenceChain.firstToolUse ? [session.evidenceChain.firstToolUse] : []),
875
+ ...(session.evidenceChain.firstToolFailure ? [session.evidenceChain.firstToolFailure] : []),
876
+ ...(session.evidenceChain.lastAssistantMessage ? [session.evidenceChain.lastAssistantMessage] : []),
877
+ ...findings.flatMap((finding) => finding.evidenceRefs),
878
+ ]).slice(0, 10);
879
+ return {
880
+ schemaVersion: 1,
881
+ mode: 'deterministic_session_story',
882
+ generatedAt,
883
+ title,
884
+ summary: reviewerSummary(session, scopeKind, attentionCount, possibleFalsePositiveCount),
885
+ scope: {
886
+ kind: scopeKind,
887
+ reasonCodes: scopeReasons,
888
+ },
889
+ chainSteps: [
890
+ reviewerStep(1, '用户期待', userGoalStepText(session), session.evidenceChain.firstUserMessage ? 'ok' : 'unknown', session.evidenceChain.firstUserMessage ? [session.evidenceChain.firstUserMessage] : []),
891
+ reviewerStep(2, '选择能力', skillSelectionStepText(session), 'ok', [session.evidenceChain.firstSkillContext, session.evidenceChain.firstToolUse].filter((ref) => Boolean(ref))),
892
+ reviewerStep(3, '执行流程', executionStepText(session, expectedToolCheck), executionStepStatus(session, expectedToolCheck), session.evidenceChain.firstToolUse ? [session.evidenceChain.firstToolUse] : []),
893
+ reviewerStep(4, '结果 / 产物', deliveryStepText(session), userFacingClosureForSession(session).deliveryCount > 0 ? 'ok' : 'unknown', userFacingClosureForSession(session).evidenceRefs),
894
+ reviewerStep(5, '用户反馈', userFeedbackStepText(session, reviewState), userFeedbackStepStatus(session, reviewState), userFeedbackEvidenceRefs(session)),
895
+ ],
896
+ findings,
897
+ oneLookMetrics: {
898
+ toolCallCount: indicators.toolCallCount,
899
+ toolFailureCount: indicators.toolFailureCount,
900
+ userMessageCount: indicators.userMessageCount,
901
+ userFollowUpCount: feedbackCounts.userFollowUpCount,
902
+ assistantDeliverySignalCount: indicators.assistantDeliverySignalCount,
903
+ deliverableArtifactSignalCount: indicators.deliverableArtifactSignalCount,
904
+ routerDownstreamCompleted: indicators.routerDownstreamCompleted,
905
+ routerDownstreamFailed: indicators.routerDownstreamFailed,
906
+ assistantProgressUpdateCount: assistantProgressUpdateEvents(session).length,
907
+ selfCorrectionCount: indicators.selfCorrectionCount,
908
+ repeatedExecutionCount: indicators.repeatedExecutionCount,
909
+ finalDeliverySignalCount: assistantFinalDeliveryEvents(session).length,
910
+ traceEventCount: session.fullSessionTimeline.length || session.timelinePreview.length,
911
+ tokenUsage: {
912
+ ...tokenUsage,
913
+ attribution: 'skill_segment',
914
+ },
915
+ },
916
+ sessionStory,
917
+ authorSuggestions: reviewerAuthorSuggestions(session, findings),
918
+ traceLinks,
919
+ };
920
+ }
921
+ function buildSessionStory(session, invocations, canonicalEpisodes, reviewState) {
922
+ const nodes = [];
923
+ const push = (kind, label, status, text, evidenceRefs = []) => {
924
+ nodes.push({
925
+ id: hashParts('session-story-node', session.id, kind, String(nodes.length), text),
926
+ order: nodes.length + 1,
927
+ kind,
928
+ label,
929
+ status,
930
+ text,
931
+ evidenceRefs: uniqueEvidenceRefs(evidenceRefs).slice(0, 5),
932
+ });
933
+ return nodes[nodes.length - 1];
934
+ };
935
+ const goalSlices = sessionStoryGoalSlices(session, invocations);
936
+ const subagentDispatches = sessionStorySubagentDispatches(session);
937
+ const skillLinks = sessionStorySkillLinks(session, invocations);
938
+ const progressUpdates = assistantProgressUpdateEvents(session);
939
+ const finalDeliveries = assistantFinalDeliveryEvents(session);
940
+ const userGoalNode = push('user_goal', '用户提出目标', session.evidenceChain.firstUserMessage ? 'ok' : 'unknown', goalSlices.length > 1
941
+ ? `识别到 ${goalSlices.length} 个目标段:${goalSlices.map((goal) => goal.inferredUserGoal ?? '未提取到明确目标').slice(0, 3).join(';')}${goalSlices.length > 3 ? ';...' : ''}`
942
+ : session.evidenceChain.firstUserMessage?.snippet
943
+ ? `用户目标:${session.evidenceChain.firstUserMessage.snippet}`
944
+ : '没有看到明确人工用户目标;当前只能按运行证据还原链路。', session.evidenceChain.firstUserMessage ? [session.evidenceChain.firstUserMessage] : []);
945
+ const roleSummary = skillLinks.map((link) => `${link.skillName}:${skillRoleLabel(link.role)}`).join(';');
946
+ const invocationText = skillLinks.length > 1
947
+ ? `本次链路识别到 ${skillLinks.length} 个能力:${roleSummary}。`
948
+ : skillLinks[0]
949
+ ? `本次使用能力:${skillLinks[0].skillName},角色判断:${skillRoleLabel(skillLinks[0].role)}。`
950
+ : `本次使用能力:${session.skillName}。`;
951
+ const invocationNode = push('skill_invocation', '能力介入', 'ok', invocationText, uniqueEvidenceRefs([session.evidenceChain.firstSkillContext, session.evidenceChain.firstToolUse].filter((ref) => Boolean(ref))));
952
+ let subagentNode;
953
+ if (subagentDispatches.length > 0) {
954
+ subagentNode = push('subagent_branch', '分支 / 子任务', 'unknown', `检测到 ${subagentDispatches.length} 条分支或子任务执行线;主线和分支已单独列出,仍需要结合原文确认真实委派关系。`, subagentDispatches.flatMap((dispatch) => dispatch.evidenceRefs.slice(0, 1)));
955
+ }
956
+ const executionNode = push('tool_execution', '执行过程', session.indicators.toolFailureCount > 0 ? 'attention' : session.indicators.toolCallCount > 0 ? 'ok' : 'unknown', session.indicators.toolCallCount > 0
957
+ ? `执行中看到 ${session.indicators.toolCallCount} 次工具调用${session.indicators.toolFailureCount > 0 ? `,其中失败 ${session.indicators.toolFailureCount} 次` : ''}。`
958
+ : '没有看到明确工具调用;只能根据消息上下文复盘。', uniqueEvidenceRefs([
959
+ session.evidenceChain.firstToolUse,
960
+ session.evidenceChain.firstToolFailure,
961
+ ].filter((ref) => Boolean(ref))));
962
+ const deliveryNode = push('delivery', '交付产物', finalDeliveries.length > 0 ? 'ok' : 'attention', deliveryStepText(session), uniqueEvidenceRefs([
963
+ ...finalDeliveries.slice(-2).map(evidenceRefFromTimeline),
964
+ ...progressUpdates.slice(-2).map(evidenceRefFromTimeline),
965
+ session.evidenceChain.lastAssistantMessage,
966
+ ].filter((ref) => Boolean(ref))));
967
+ const feedbackNode = push('user_feedback', '用户反馈', userFeedbackStepStatus(session, reviewState), userFeedbackStepText(session, reviewState), userFeedbackEvidenceRefs(session));
968
+ if (session.indicators.userGoalShiftCount > 0) {
969
+ push('goal_shift', '目标切换', 'unknown', `用户中途切换了 ${session.indicators.userGoalShiftCount} 次目标,后续诉求可能不属于当前 skill。`, userFeedbackEvidenceRefs(session));
970
+ }
971
+ const episodes = canonicalEpisodes ?? sessionStoryEpisodes(session, invocations, goalSlices, subagentDispatches, skillLinks);
972
+ const goalChecklistItems = checklistItemsForAnswer(session, 'goal_satisfaction', episodes, reviewState);
973
+ const declaredBehaviorChecklistItems = checklistItemsForAnswer(session, 'declared_behavior_fit', episodes);
974
+ const userFeelingChecklistItems = checklistItemsForAnswer(session, 'user_feeling', episodes, reviewState);
975
+ const answers = [
976
+ sessionStoryAnswer('goal_satisfaction', '用户目标有没有被满足', goalChecklistItems, [
977
+ session.evidenceChain.firstUserMessage,
978
+ session.evidenceChain.lastAssistantMessage,
979
+ ...userFeedbackEvidenceRefs(session),
980
+ ]),
981
+ sessionStoryAnswer('declared_behavior_fit', '行为是否符合能力用途', declaredBehaviorChecklistItems, [
982
+ session.evidenceChain.firstSkillContext,
983
+ session.evidenceChain.firstToolUse,
984
+ session.evidenceChain.firstToolFailure,
985
+ ]),
986
+ sessionStoryAnswer('user_feeling', '用户是否觉得有用或绕路', userFeelingChecklistItems, userFeedbackEvidenceRefs(session)),
987
+ ];
988
+ const summary = answers.some((answer) => answer.status === 'degraded')
989
+ ? '这次数据本身有可信度问题(比如 skill 没真的被加载),先确认数据再下判断。'
990
+ : answers.some((answer) => answer.status === 'attention')
991
+ ? '这次有几条需要看一眼的事项,从红色标记的事实和原文开始看。'
992
+ : answers.every((answer) => answer.status === 'ok')
993
+ ? '这次从目标、执行到反馈都没有明显异常,进入常规抽样。'
994
+ : '这次链路已按语义节点展开,但部分结论仍需要人工结合原文判断。';
995
+ return {
996
+ schemaVersion: 1,
997
+ summary,
998
+ invocationCount: invocations.length,
999
+ goalSliceCount: session.goalSliceIds.length,
1000
+ branchCount: subagentDispatches.length,
1001
+ progressUpdateCount: progressUpdates.length,
1002
+ finalDeliverySignalCount: finalDeliveries.length,
1003
+ mainlineNodeIds: [
1004
+ userGoalNode.id,
1005
+ invocationNode.id,
1006
+ ...(subagentNode ? [subagentNode.id] : []),
1007
+ executionNode.id,
1008
+ deliveryNode.id,
1009
+ feedbackNode.id,
1010
+ ],
1011
+ goalSlices,
1012
+ subagentDispatches,
1013
+ skillLinks,
1014
+ episodes,
1015
+ graph: sessionStoryGraph(nodes, skillLinks),
1016
+ nodes,
1017
+ answers,
1018
+ };
1019
+ }
1020
+ function sessionStoryEpisodes(session, invocations, goalSlices, subagentDispatches, skillLinks) {
1021
+ const baseSkillSegments = sessionStorySkillSegments(session, invocations, skillLinks);
1022
+ const baseEpisodeId = hashParts('session-story-episode', session.id, '0');
1023
+ const orchestrationEdges = sessionStoryOrchestrationEdges(baseEpisodeId, baseSkillSegments, subagentDispatches, session, invocations);
1024
+ const skillSegments = skillSegmentsWithOrchestrationRoles(baseSkillSegments, orchestrationEdges);
1025
+ const feedbackSignals = sessionStoryFeedbackSignals(session, invocations, skillSegments, orchestrationEdges);
1026
+ const artifacts = sessionStoryArtifacts(invocations);
1027
+ const closure = sessionStoryOutcomeClosure(session, artifacts);
1028
+ const timeline = session.fullSessionTimeline.length > 0 ? session.fullSessionTimeline : session.timelinePreview;
1029
+ const ranges = sessionStoryEpisodeRanges(session, timeline);
1030
+ return ranges.map((range, index) => {
1031
+ const episodeId = hashParts('session-story-episode', session.id, String(index));
1032
+ const episodeSkillSegments = skillSegments.filter((segment) => (segment.messageRanges ?? []).some((messageRange) => messageRangeOverlapsEpisodeRange(messageRange, range)));
1033
+ const segmentIds = new Set(episodeSkillSegments.map((segment) => segment.id));
1034
+ const episodeEdges = orchestrationEdges
1035
+ .filter((edge) => {
1036
+ if (edge.edgeKind === 'internal_skill' && edge.parentSkillSegmentId && edge.executorSkillSegmentId) {
1037
+ return segmentIds.has(edge.parentSkillSegmentId) && segmentIds.has(edge.executorSkillSegmentId);
1038
+ }
1039
+ return (edge.parentSkillSegmentId && segmentIds.has(edge.parentSkillSegmentId))
1040
+ || edge.evidenceRefs.some((ref) => episodeRangeContainsRef(range, ref));
1041
+ })
1042
+ .map((edge) => ({ ...edge, episodeId }));
1043
+ const episodeFeedbackSignals = feedbackSignals
1044
+ .filter((signal) => episodeRangeContainsRef(range, signal.evidenceRef))
1045
+ .map((signal) => {
1046
+ const attributions = (signal.canonicalAttributions ?? signal.attributions).filter((attribution) => episodeFeedbackAttributionBelongsToEpisode(attribution, segmentIds, episodeEdges));
1047
+ return {
1048
+ ...signal,
1049
+ canonicalAttributions: attributions,
1050
+ attributions,
1051
+ };
1052
+ })
1053
+ .filter((signal) => signal.attributions.length > 0);
1054
+ const episodeArtifacts = artifacts.filter((artifact) => episodeRangeContainsRef(range, artifact.evidenceRef));
1055
+ const episodeGoalSlices = goalSlices.filter((goal) => goal.evidenceRefs.some((ref) => episodeRangeContainsRef(range, ref)));
1056
+ const episodeTimeline = timeline.filter((event) => episodeRangeContainsRef(range, event));
1057
+ const startRef = episodeTimeline[0] ? evidenceRefFromTimeline(episodeTimeline[0]) : session.evidenceChain.firstUserMessage;
1058
+ const endRef = episodeTimeline.at(-1) ? evidenceRefFromTimeline(episodeTimeline.at(-1)) : session.evidenceChain.lastAssistantMessage;
1059
+ const primaryGoal = episodeGoalSlices[0]?.inferredUserGoal
1060
+ ?? episodeTimeline.find((event) => event.kind === 'user_message')?.snippet
1061
+ ?? goalSlices[0]?.inferredUserGoal
1062
+ ?? session.evidenceChain.firstUserMessage?.snippet;
1063
+ const episodeClosure = index === ranges.length - 1 ? closure : 'unknown';
1064
+ return {
1065
+ id: episodeId,
1066
+ order: index + 1,
1067
+ sessionId: session.sessionId,
1068
+ primaryGoal,
1069
+ goalEvidenceRefs: [
1070
+ ...episodeGoalSlices.map((goal) => ({
1071
+ kind: 'goal_slice',
1072
+ goalSliceId: goal.id,
1073
+ evidenceRef: goal.evidenceRefs[0],
1074
+ label: goal.inferredUserGoal ?? `目标段 ${goal.order}`,
1075
+ })),
1076
+ ...(episodeTimeline.find((event) => event.kind === 'user_message') ? [{
1077
+ kind: 'user_message',
1078
+ evidenceRef: evidenceRefFromTimeline(episodeTimeline.find((event) => event.kind === 'user_message')),
1079
+ label: episodeTimeline.find((event) => event.kind === 'user_message')?.snippet,
1080
+ }] : []),
1081
+ ],
1082
+ startTimestamp: minString([startRef?.timestamp, ...episodeSkillSegments.map((segment) => segment.startTimestamp)]) ?? session.startTimestamp,
1083
+ endTimestamp: maxString([endRef?.timestamp, ...episodeSkillSegments.map((segment) => segment.endTimestamp)]) ?? session.endTimestamp,
1084
+ startRef,
1085
+ endRef,
1086
+ boundaryReason: range.boundaryReason ?? sessionStoryEpisodeBoundaryReason(session, subagentDispatches, episodeEdges, episodeClosure),
1087
+ skillSegments: episodeSkillSegments,
1088
+ orchestrationEdges: episodeEdges,
1089
+ feedbackSignals: episodeFeedbackSignals,
1090
+ outcome: {
1091
+ closure: episodeClosure,
1092
+ artifacts: episodeArtifacts,
1093
+ verdict: session.reviewPriority,
1094
+ acceptanceCriteria: sessionStoryAcceptanceCriteria(session, episodeFeedbackSignals, episodeArtifacts),
1095
+ },
1096
+ };
1097
+ });
1098
+ }
1099
+ function episodeFeedbackAttributionBelongsToEpisode(attribution, segmentIds, episodeEdges) {
1100
+ if (!attribution.skillSegmentId || !segmentIds.has(attribution.skillSegmentId))
1101
+ return false;
1102
+ if (attribution.reasonCode !== 'orchestration_edge')
1103
+ return true;
1104
+ return episodeEdges.some((edge) => edge.parentSkillSegmentId === attribution.skillSegmentId
1105
+ || edge.executorSkillSegmentId === attribution.skillSegmentId);
1106
+ }
1107
+ function skillSegmentsWithOrchestrationRoles(skillSegments, orchestrationEdges) {
1108
+ if (orchestrationEdges.length === 0)
1109
+ return skillSegments;
1110
+ const parentIds = new Set(orchestrationEdges.map((edge) => edge.parentSkillSegmentId).filter((value) => Boolean(value)));
1111
+ const executorIds = new Set(orchestrationEdges.map((edge) => edge.executorSkillSegmentId).filter((value) => Boolean(value)));
1112
+ return skillSegments.map((segment) => {
1113
+ if (parentIds.has(segment.id) && segment.skillType === 'unknown') {
1114
+ return {
1115
+ ...segment,
1116
+ skillType: 'router',
1117
+ skillTypeSource: 'trace',
1118
+ traceInferredSkillType: 'router',
1119
+ episodeRole: 'router',
1120
+ };
1121
+ }
1122
+ if (executorIds.has(segment.id) && segment.episodeRole === 'supporting') {
1123
+ return {
1124
+ ...segment,
1125
+ skillType: segment.skillType === 'unknown' ? 'executor' : segment.skillType,
1126
+ skillTypeSource: segment.skillType === 'unknown' ? 'trace' : segment.skillTypeSource,
1127
+ traceInferredSkillType: segment.traceInferredSkillType ?? 'executor',
1128
+ episodeRole: 'main_executor',
1129
+ };
1130
+ }
1131
+ return segment;
1132
+ });
1133
+ }
1134
+ function sessionStoryEpisodeRanges(session, timeline) {
1135
+ const primarySourceTrace = primarySourceTraceForSession(session);
1136
+ const rangeTimeline = timeline.filter((event) => !primarySourceTrace || !event.sourceTrace || event.sourceTrace === primarySourceTrace);
1137
+ const indexes = rangeTimeline
1138
+ .map((event) => event.messageIndex)
1139
+ .filter((value) => typeof value === 'number');
1140
+ const sessionStart = minDefined(indexes) ?? session.timelineScope.sessionStartRecordIndex ?? 0;
1141
+ const sessionEnd = maxDefined(indexes) ?? session.timelineScope.sessionEndRecordIndex ?? sessionStart;
1142
+ const goalShiftStarts = unique(timeline
1143
+ .filter((event) => event.kind === 'user_message'
1144
+ && typeof event.messageIndex === 'number'
1145
+ && event.messageIndex > sessionStart
1146
+ && (!primarySourceTrace || !event.sourceTrace || event.sourceTrace === primarySourceTrace))
1147
+ .filter((event) => hasUserGoalShiftSignal(event.snippet ?? event.fullText ?? ''))
1148
+ .map((event) => event.messageIndex))
1149
+ .sort((a, b) => a - b);
1150
+ const starts = [sessionStart, ...goalShiftStarts];
1151
+ return starts.map((start, index) => ({
1152
+ startMessageIndex: start,
1153
+ endMessageIndex: (starts[index + 1] ?? (sessionEnd + 1)) - 1,
1154
+ sourceTrace: primarySourceTrace,
1155
+ sessionId: session.sessionId,
1156
+ boundaryReason: index < starts.length - 1 ? 'goal_shift' : undefined,
1157
+ }));
1158
+ }
1159
+ function primarySourceTraceForSession(session) {
1160
+ return session.sourceTrace
1161
+ ?? session.evidenceChain.firstUserMessage?.sourceTrace
1162
+ ?? session.fullSessionTimeline.find((event) => event.traceRole === 'main')?.sourceTrace
1163
+ ?? session.fullSessionTimeline[0]?.sourceTrace
1164
+ ?? session.timelinePreview[0]?.sourceTrace;
1165
+ }
1166
+ function episodeRangeContainsRef(range, ref) {
1167
+ if (!ref || typeof ref.messageIndex !== 'number')
1168
+ return false;
1169
+ // New observations carry sourceTrace and are compared strictly. Older persisted
1170
+ // observations may not, so they keep the historical messageIndex-only fallback.
1171
+ if (range.sourceTrace && ref.sourceTrace && range.sourceTrace !== ref.sourceTrace)
1172
+ return false;
1173
+ if (range.sessionId && ref.sessionId && range.sessionId !== ref.sessionId)
1174
+ return false;
1175
+ return ref.messageIndex >= range.startMessageIndex && ref.messageIndex <= range.endMessageIndex;
1176
+ }
1177
+ function messageRangeOverlapsEpisodeRange(messageRange, range) {
1178
+ if (range.sourceTrace && messageRange.sourceTrace && range.sourceTrace !== messageRange.sourceTrace)
1179
+ return false;
1180
+ if (range.sessionId && messageRange.sessionId && range.sessionId !== messageRange.sessionId)
1181
+ return false;
1182
+ return messageRange.endMessageIndex >= range.startMessageIndex
1183
+ && messageRange.startMessageIndex <= range.endMessageIndex;
1184
+ }
1185
+ function sessionStoryEpisodeBoundaryReason(session, subagentDispatches, orchestrationEdges, closure) {
1186
+ if (session.indicators.userGoalShiftCount > 0)
1187
+ return 'goal_shift';
1188
+ if (subagentDispatches.length > 0)
1189
+ return 'checkpoint_or_subagent';
1190
+ if (orchestrationEdges.length > 0 && closure === 'closed')
1191
+ return 'downstream_closed';
1192
+ return 'session_end';
1193
+ }
1194
+ function sessionStorySkillSegments(session, invocations, skillLinks) {
1195
+ const invocationById = new Map(invocations.map((invocation) => [invocation.id, invocation]));
1196
+ return skillLinks.map((link, index) => {
1197
+ const group = link.invocationIds.map((id) => invocationById.get(id)).filter((value) => Boolean(value));
1198
+ const declaredSkillType = loadFrontmatterSkillType(link.skillName, session.cwd);
1199
+ const traceInferredSkillType = traceInferredSkillTypeForLink(link);
1200
+ const skillType = declaredSkillType ?? traceInferredSkillType ?? 'unknown';
1201
+ const evidenceRefs = uniqueEvidenceRefs([
1202
+ ...link.evidenceRefs,
1203
+ ...group.flatMap((invocation) => invocation.evidenceRefs.slice(0, 2)),
1204
+ ]).slice(0, 6);
1205
+ const messageRanges = group
1206
+ .map((invocation) => {
1207
+ const timelineWithIndex = invocation.timeline.filter((event) => typeof event.messageIndex === 'number');
1208
+ const indexes = timelineWithIndex
1209
+ .map((event) => event.messageIndex)
1210
+ .filter((value) => typeof value === 'number');
1211
+ const startMessageIndex = minDefined(indexes);
1212
+ const endMessageIndex = maxDefined(indexes);
1213
+ const sourceTrace = invocation.sourceTrace ?? timelineWithIndex[0]?.sourceTrace;
1214
+ const sessionId = invocation.sessionId ?? timelineWithIndex[0]?.sessionId;
1215
+ return typeof startMessageIndex === 'number' && typeof endMessageIndex === 'number'
1216
+ ? { startMessageIndex, endMessageIndex, sourceTrace, sessionId }
1217
+ : undefined;
1218
+ })
1219
+ .filter((value) => Boolean(value));
1220
+ const messageIndexes = messageRanges.flatMap((range) => [range.startMessageIndex, range.endMessageIndex]);
1221
+ return {
1222
+ id: hashParts('session-story-skill-segment', session.id, link.skillName, String(index)),
1223
+ order: index + 1,
1224
+ skillName: link.skillName,
1225
+ skillType,
1226
+ skillTypeSource: declaredSkillType ? 'frontmatter' : traceInferredSkillType ? 'trace' : 'unknown',
1227
+ declaredSkillType,
1228
+ traceInferredSkillType,
1229
+ episodeRole: episodeRoleForLink(link, session, skillType),
1230
+ skillInvocationIds: link.invocationIds,
1231
+ startMessageIndex: minDefined(messageIndexes),
1232
+ endMessageIndex: maxDefined(messageIndexes),
1233
+ messageRanges,
1234
+ startTimestamp: minString(group.map((invocation) => invocation.startTimestamp)) ?? session.startTimestamp,
1235
+ endTimestamp: maxString(group.map((invocation) => invocation.endTimestamp)) ?? session.endTimestamp,
1236
+ typeSpecificChecklist: [],
1237
+ evidenceRefs,
1238
+ };
1239
+ });
1240
+ }
1241
+ function traceInferredSkillTypeForLink(link) {
1242
+ if (link.role === 'router')
1243
+ return 'router';
1244
+ if (link.role === 'executor' || link.role === 'mixed')
1245
+ return 'executor';
1246
+ return undefined;
1247
+ }
1248
+ function episodeRoleForLink(link, session, resolvedSkillType) {
1249
+ const skillType = resolvedSkillType ?? loadFrontmatterSkillType(link.skillName, session.cwd) ?? traceInferredSkillTypeForLink(link) ?? 'unknown';
1250
+ if (skillType === 'router')
1251
+ return 'router';
1252
+ if (skillType === 'delegation')
1253
+ return 'delegator';
1254
+ if (skillType === 'executor')
1255
+ return 'main_executor';
1256
+ if (skillType === 'advisory')
1257
+ return 'observer';
1258
+ if (skillType === 'workflow_owner')
1259
+ return 'router';
1260
+ if (link.role === 'router')
1261
+ return 'router';
1262
+ if (link.role === 'executor' || link.role === 'mixed')
1263
+ return 'main_executor';
1264
+ return 'supporting';
1265
+ }
1266
+ function sessionStoryOrchestrationEdges(episodeId, skillSegments, subagentDispatches, session, invocations) {
1267
+ const edges = [];
1268
+ const router = skillSegments.find((segment) => segment.episodeRole === 'router');
1269
+ const delegator = skillSegments.find((segment) => segment.episodeRole === 'delegator' || segment.skillType === 'delegation');
1270
+ const timeline = uniqueTimelineEvents(invocations.flatMap((invocation) => invocation.timeline))
1271
+ .sort(compareTimelineEvents);
1272
+ const runnerEvent = timeline
1273
+ .find(isOrchestrationRuntimeEvent);
1274
+ const runnerRef = runnerEvent ? evidenceRefFromTimeline(runnerEvent) : undefined;
1275
+ const runnerOwner = runnerRef ? skillSegmentForEvidenceRef(runnerRef, skillSegments) : undefined;
1276
+ const mentionedUpstream = runnerOwner
1277
+ ? mentionedUpstreamSkillSegmentForRuntime(runnerOwner, runnerEvent, timeline, skillSegments)
1278
+ : undefined;
1279
+ const runnerText = `${runnerEvent?.toolName ?? ''} ${runnerEvent?.snippet ?? ''} ${runnerEvent?.fullText ?? ''}`;
1280
+ const runnerExecutor = runnerText
1281
+ ? (mentionedUpstream && runnerOwner && runnerOwner.id !== mentionedUpstream.id
1282
+ ? runnerOwner
1283
+ : skillSegments.find((segment) => {
1284
+ if (segment.id === runnerOwner?.id || segment.id === mentionedUpstream?.id)
1285
+ return false;
1286
+ const name = segment.skillName.toLowerCase();
1287
+ return name.includes('apply-cc') && /apply-cc|runner\.js|send-input\.js/i.test(runnerText);
1288
+ }))
1289
+ : undefined;
1290
+ const priorUpstreamCandidate = runnerOwner
1291
+ ? bestPriorUpstreamSkillSegmentForRuntime(runnerOwner, runnerEvent, timeline, skillSegments)
1292
+ : undefined;
1293
+ const parentSegment = priorUpstreamCandidate
1294
+ ?? mentionedUpstream
1295
+ ?? router
1296
+ ?? (runnerOwner && (runnerOwner.skillType === 'router' || runnerOwner.skillType === 'delegation' || runnerOwner.episodeRole === 'delegator') ? runnerOwner : undefined)
1297
+ ?? delegator;
1298
+ const executor = runnerExecutor
1299
+ ?? (runnerOwner && parentSegment?.id !== runnerOwner.id ? runnerOwner : undefined)
1300
+ ?? (parentSegment?.id === router?.id ? skillSegments.find((segment) => segment.id !== parentSegment?.id
1301
+ && segment.id !== router?.id
1302
+ && segment.episodeRole === 'main_executor') : undefined);
1303
+ if (parentSegment && (executor || runnerEvent) && parentSegment.id !== executor?.id) {
1304
+ const edgeKind = executor && skillSegmentsShareSourceTrace(parentSegment, executor)
1305
+ ? 'internal_skill'
1306
+ : 'external_child_session';
1307
+ edges.push({
1308
+ id: hashParts('session-story-edge', episodeId, parentSegment.id, executor?.id ?? 'downstream', '0'),
1309
+ episodeId,
1310
+ edgeKind,
1311
+ parentSkillSegmentId: parentSegment.id,
1312
+ executorSkillSegmentId: executor?.id,
1313
+ childSessionId: sessionStoryChildSessionId(runnerEvent),
1314
+ runnerStartedRef: runnerRef,
1315
+ status: runnerEvent || subagentDispatches.length > 0 ? 'started' : 'unknown',
1316
+ evidenceRefs: uniqueEvidenceRefs([
1317
+ ...parentSegment.evidenceRefs.slice(0, 2),
1318
+ ...(executor?.evidenceRefs.slice(0, 2) ?? []),
1319
+ ...(runnerRef ? [runnerRef] : []),
1320
+ ...subagentDispatches.flatMap((dispatch) => dispatch.evidenceRefs.slice(0, 1)),
1321
+ ]).slice(0, 6),
1322
+ });
1323
+ }
1324
+ if (edges.length === 0) {
1325
+ const fallbackEdge = fallbackOrchestrationEdgeFromRuntime(episodeId, skillSegments, invocations);
1326
+ if (fallbackEdge)
1327
+ edges.push(fallbackEdge);
1328
+ }
1329
+ for (const dispatch of subagentDispatches) {
1330
+ const parentSegment = delegator ?? router;
1331
+ const executor = skillSegments.find((segment) => segment.id !== parentSegment?.id && segment.episodeRole === 'main_executor');
1332
+ edges.push({
1333
+ id: hashParts('session-story-edge', episodeId, dispatch.id),
1334
+ episodeId,
1335
+ edgeKind: executor ? 'internal_skill' : 'external_child_session',
1336
+ parentSkillSegmentId: parentSegment?.id,
1337
+ executorSkillSegmentId: executor?.id,
1338
+ childSessionId: dispatch.branchId,
1339
+ status: 'started',
1340
+ evidenceRefs: dispatch.evidenceRefs,
1341
+ });
1342
+ }
1343
+ return edges;
1344
+ }
1345
+ function bestPriorUpstreamSkillSegmentForRuntime(runnerOwner, runnerEvent, timeline, skillSegments) {
1346
+ const runnerMessageIndex = runnerEvent?.messageIndex;
1347
+ const contextText = timeline
1348
+ .filter((event) => {
1349
+ if (!timelineEventSharesTraceScope(event, runnerEvent))
1350
+ return false;
1351
+ if (typeof runnerMessageIndex !== 'number' || typeof event.messageIndex !== 'number')
1352
+ return true;
1353
+ return event.messageIndex <= runnerMessageIndex && event.messageIndex >= Math.max(0, runnerMessageIndex - 20);
1354
+ })
1355
+ .map((event) => `${event.toolName ?? ''} ${event.snippet ?? ''} ${event.fullText ?? ''}`)
1356
+ .join('\n');
1357
+ return skillSegments
1358
+ .filter((segment) => segment.id !== runnerOwner.id && segment.order < runnerOwner.order)
1359
+ .sort((a, b) => upstreamParentScore(b, runnerOwner, contextText) - upstreamParentScore(a, runnerOwner, contextText)
1360
+ || b.order - a.order)[0];
1361
+ }
1362
+ function mentionedUpstreamSkillSegmentForRuntime(runnerOwner, runnerEvent, timeline, skillSegments) {
1363
+ if (!runnerEvent)
1364
+ return undefined;
1365
+ const runnerMessageIndex = runnerEvent.messageIndex;
1366
+ const nearbyText = timeline
1367
+ .filter((event) => {
1368
+ if (!timelineEventSharesTraceScope(event, runnerEvent))
1369
+ return false;
1370
+ if (typeof runnerMessageIndex !== 'number' || typeof event.messageIndex !== 'number')
1371
+ return true;
1372
+ return event.messageIndex <= runnerMessageIndex && event.messageIndex >= Math.max(0, runnerMessageIndex - 8);
1373
+ })
1374
+ .map((event) => `${event.toolName ?? ''} ${event.snippet ?? ''} ${event.fullText ?? ''}`)
1375
+ .join('\n');
1376
+ return skillSegments
1377
+ .filter((segment) => segment.id !== runnerOwner.id)
1378
+ .filter((segment) => segment.order < runnerOwner.order)
1379
+ .filter((segment) => {
1380
+ const name = segment.skillName.toLowerCase();
1381
+ const lowerText = nearbyText.toLowerCase();
1382
+ const compactName = compactObjectText(name);
1383
+ const compactText = compactObjectText(lowerText);
1384
+ const mentionIndex = name.length >= 4 ? lowerText.indexOf(name) : -1;
1385
+ const localContext = mentionIndex >= 0
1386
+ ? lowerText.slice(Math.max(0, mentionIndex - 40), mentionIndex + name.length + 100)
1387
+ : lowerText;
1388
+ const explicitlyMentioned = mentionIndex >= 0;
1389
+ const compactMentioned = compactName.length >= 6 && compactText.includes(compactName);
1390
+ if (!explicitlyMentioned && !compactMentioned)
1391
+ return false;
1392
+ return /skill|技能|流程|工作流|按|根据|启动|触发|\/consult|\/tech-solution|\/prd_start|runner\.js/i.test(localContext);
1393
+ })
1394
+ .sort((a, b) => upstreamParentScore(b, runnerOwner, nearbyText) - upstreamParentScore(a, runnerOwner, nearbyText)
1395
+ || Math.abs(a.order - runnerOwner.order) - Math.abs(b.order - runnerOwner.order))[0];
1396
+ }
1397
+ function upstreamParentScore(segment, runnerOwner, contextText) {
1398
+ const lowerName = segment.skillName.toLowerCase();
1399
+ const lowerText = contextText.toLowerCase();
1400
+ let score = 0;
1401
+ if (segment.episodeRole === 'router' || segment.skillType === 'router')
1402
+ score += 80;
1403
+ if (segment.episodeRole === 'delegator' || segment.skillType === 'delegation')
1404
+ score += 70;
1405
+ if (segment.episodeRole === 'observer' || segment.skillType === 'advisory')
1406
+ score += 45;
1407
+ if (/dev[-_\s]*lifecycle|aiprd[-_\s]*task[-_\s]*runner|omk[-_\s]*reviewer/i.test(lowerName))
1408
+ score += 35;
1409
+ if (/yuque|web[-_\s]*fetch|fetch|search/i.test(lowerName))
1410
+ score -= 35;
1411
+ const nameIndex = lowerText.indexOf(lowerName);
1412
+ if (nameIndex >= 0) {
1413
+ const localContext = lowerText.slice(Math.max(0, nameIndex - 50), nameIndex + lowerName.length + 120);
1414
+ if (/按|根据|流程|工作流|skill|技能|启动|触发/.test(localContext))
1415
+ score += 30;
1416
+ if (/读取|搜索|文档|链接|知识库/.test(localContext))
1417
+ score -= 10;
1418
+ }
1419
+ if (segment.order < runnerOwner.order)
1420
+ score += Math.max(0, 20 - (runnerOwner.order - segment.order) * 3);
1421
+ return score;
1422
+ }
1423
+ function fallbackOrchestrationEdgeFromRuntime(episodeId, skillSegments, invocations) {
1424
+ const timeline = uniqueTimelineEvents(invocations.flatMap((invocation) => invocation.timeline)).sort(compareTimelineEvents);
1425
+ const runtimeEvent = timeline.find((event) => {
1426
+ const text = `${event.toolName ?? ''} ${event.snippet ?? ''} ${event.fullText ?? ''}`;
1427
+ return /skills\/[\w.-]+\/scripts\/|runner\.js|send-input\.js|check-session\.js|session:\s*claude-|ttydUrl/i.test(text);
1428
+ });
1429
+ if (!runtimeEvent)
1430
+ return undefined;
1431
+ const runtimeRef = evidenceRefFromTimeline(runtimeEvent);
1432
+ const executor = skillSegmentForEvidenceRef(runtimeRef, skillSegments);
1433
+ if (!executor)
1434
+ return undefined;
1435
+ const parent = skillSegments
1436
+ .filter((segment) => segment.id !== executor.id && segment.order < executor.order)
1437
+ .sort((a, b) => b.order - a.order)[0];
1438
+ if (!parent)
1439
+ return undefined;
1440
+ const edgeKind = skillSegmentsShareSourceTrace(parent, executor) ? 'internal_skill' : 'external_child_session';
1441
+ return {
1442
+ id: hashParts('session-story-edge', episodeId, parent.id, executor.id, 'fallback'),
1443
+ episodeId,
1444
+ edgeKind,
1445
+ parentSkillSegmentId: parent.id,
1446
+ executorSkillSegmentId: executor.id,
1447
+ childSessionId: sessionStoryChildSessionId(runtimeEvent),
1448
+ runnerStartedRef: runtimeRef,
1449
+ status: 'started',
1450
+ evidenceRefs: uniqueEvidenceRefs([
1451
+ ...parent.evidenceRefs.slice(0, 2),
1452
+ ...executor.evidenceRefs.slice(0, 2),
1453
+ runtimeRef,
1454
+ ]).slice(0, 6),
1455
+ };
1456
+ }
1457
+ function isOrchestrationRuntimeEvent(event) {
1458
+ const text = `${event.toolName ?? ''} ${event.snippet ?? ''} ${event.fullText ?? ''}`;
1459
+ if (event.kind === 'tool_use') {
1460
+ return /runner\.js|send-input\.js|check-session\.js/i.test(text);
1461
+ }
1462
+ if (event.kind === 'tool_result') {
1463
+ const trimmed = text.trim();
1464
+ if (/^---\s*\n?\s*name:/i.test(trimmed) || /^---\s+name:/i.test(trimmed))
1465
+ return false;
1466
+ return /"event"\s*:\s*"started"|Command still running \(session|Process exited with code|Process exited with signal|session:\s*claude-|\bclaude-[a-z0-9_-]+\b/i.test(text);
1467
+ }
1468
+ if (event.kind === 'assistant_message') {
1469
+ return /子\s*Claude|ttyd|session:\s*claude-|已启动.*Claude|Claude\s*执行窗口/i.test(text);
1470
+ }
1471
+ return false;
1472
+ }
1473
+ function sessionStoryChildSessionId(event) {
1474
+ const text = `${event?.snippet ?? ''} ${event?.fullText ?? ''}`;
1475
+ return text.match(/\bclaude-[a-z0-9_-]+\b/i)?.[0];
1476
+ }
1477
+ function sessionStoryFeedbackSignals(session, invocations, skillSegments, orchestrationEdges) {
1478
+ const timeline = uniqueTimelineEvents(invocations.flatMap((invocation) => invocation.timeline))
1479
+ .sort(compareTimelineEvents);
1480
+ const promises = sessionStoryPromiseOwners(timeline, skillSegments);
1481
+ const userEvents = timeline.filter((event) => event.kind === 'user_message'
1482
+ && isUserInteractionMetricText(event.snippet ?? event.fullText ?? ''));
1483
+ return userEvents
1484
+ .map((event, index) => {
1485
+ const text = event.snippet ?? event.fullText ?? '';
1486
+ const type = feedbackSignalType(text, index);
1487
+ if (type === 'unknown' && index === 0)
1488
+ return undefined;
1489
+ const evidenceRef = evidenceRefFromTimeline(event);
1490
+ const canonicalAttributions = feedbackAttributionsForText(text, evidenceRef, skillSegments, orchestrationEdges, timeline, promises);
1491
+ return {
1492
+ id: hashParts('session-story-feedback', session.id, event.id, String(index)),
1493
+ order: index + 1,
1494
+ type,
1495
+ text,
1496
+ targetObject: feedbackTargetObject(text, skillSegments),
1497
+ sourceWindow: orchestrationEdges.length > 0 && /有结论|进度|没返回|为什么|停止|中断|跑偏|不对|组件.*pr|master/i.test(text)
1498
+ ? 'episode'
1499
+ : 'skill_invocation',
1500
+ evidenceRef,
1501
+ canonicalAttributions,
1502
+ attributions: canonicalAttributions,
1503
+ };
1504
+ })
1505
+ .filter((value) => Boolean(value));
1506
+ }
1507
+ function feedbackSignalType(text, index) {
1508
+ if (USER_INTERRUPTION_RE.test(text))
1509
+ return 'interruption';
1510
+ if (hasUserCorrectionSignal(text) || /不是|不对|错了|跑偏|漏了|组件.*pr|master/i.test(text))
1511
+ return 'correction';
1512
+ if (hasNegativeFeedbackSignal(text) || /烦|失望|怎么.*还|为什么.*没|没返回|有结论吗/i.test(text))
1513
+ return 'frustration';
1514
+ if (hasPositiveFeedbackSignal(text))
1515
+ return 'positive';
1516
+ if (index > 0)
1517
+ return 'follow_up';
1518
+ return 'unknown';
1519
+ }
1520
+ function feedbackTargetObject(text, skillSegments) {
1521
+ const skillOwner = targetObjectSkillOwner(text, skillSegments);
1522
+ if (skillOwner)
1523
+ return skillOwner.skillName;
1524
+ if (/pr|pull request/i.test(text))
1525
+ return 'PR';
1526
+ if (/有结论|进度|没返回|通知|返回/i.test(text))
1527
+ return '异步结果';
1528
+ if (/停止|暂停|中断|别动/i.test(text))
1529
+ return '执行流程';
1530
+ if (/产物|文档|报告|demo/i.test(text))
1531
+ return '产物';
1532
+ return undefined;
1533
+ }
1534
+ function feedbackAttributionsForText(text, evidenceRef, skillSegments, orchestrationEdges, timeline, promises) {
1535
+ const lower = text.toLowerCase();
1536
+ const targetOwner = targetObjectSkillOwner(text, skillSegments);
1537
+ const promiseOwner = promiseOwnerForFeedback(evidenceRef, text, promises);
1538
+ const promisePrimaryOwner = promiseOwner && shouldPromiseOwnerReceivePrimaryFeedback(text)
1539
+ ? upstreamPromiseOwnerForFeedback(promiseOwner, orchestrationEdges, skillSegments) ?? promiseOwner
1540
+ : promiseOwner;
1541
+ const actionOwner = actionOwnerForFeedback(evidenceRef, text, timeline, skillSegments);
1542
+ const windowMatched = skillSegmentForEvidenceRef(evidenceRef, skillSegments);
1543
+ const windowOwner = windowMatched && shouldUseWindowFeedbackOwner(windowMatched, text) ? windowMatched : undefined;
1544
+ const promiseLike = isAsyncPromiseFeedbackText(text);
1545
+ const explicitTargetOwner = targetOwner && isExplicitSkillTargetText(text, targetOwner);
1546
+ const ownerDecision = explicitTargetOwner
1547
+ ? { segment: targetOwner, reason: 'object_match' }
1548
+ : promisePrimaryOwner && promiseLike
1549
+ ? { segment: promisePrimaryOwner, reason: 'promise_match' }
1550
+ : targetOwner
1551
+ ? { segment: targetOwner, reason: 'object_match' }
1552
+ : actionOwner
1553
+ ? { segment: actionOwner, reason: 'action_match' }
1554
+ : windowOwner
1555
+ ? { segment: windowOwner, reason: feedbackAttributionReasonForText(lower) }
1556
+ : undefined;
1557
+ const primary = ownerDecision?.segment;
1558
+ const attributions = [];
1559
+ if (ownerDecision) {
1560
+ attributions.push({
1561
+ skillName: ownerDecision.segment.skillName,
1562
+ skillSegmentId: ownerDecision.segment.id,
1563
+ attributionRole: 'primary_fault',
1564
+ reasonCode: ownerDecision.reason,
1565
+ evidenceRefs: [evidenceRef],
1566
+ });
1567
+ }
1568
+ if (promiseOwner && promiseOwner.id !== primary?.id && shouldPromiseOwnerReceivePrimaryFeedback(text)) {
1569
+ attributions.push({
1570
+ skillName: promiseOwner.skillName,
1571
+ skillSegmentId: promiseOwner.id,
1572
+ attributionRole: promisePrimaryOwner?.id !== promiseOwner.id ? 'context_only' : 'primary_fault',
1573
+ reasonCode: 'promise_match',
1574
+ evidenceRefs: [evidenceRef],
1575
+ });
1576
+ }
1577
+ const downstreamParents = primary
1578
+ ? downstreamRelatedParentsForPrimary(primary, skillSegments, orchestrationEdges)
1579
+ : promiseOwner
1580
+ ? downstreamRelatedParentsForPrimary(promiseOwner, skillSegments, orchestrationEdges)
1581
+ : [];
1582
+ for (const { segment, edge } of downstreamParents) {
1583
+ attributions.push({
1584
+ skillName: segment.skillName,
1585
+ skillSegmentId: segment.id,
1586
+ attributionRole: 'downstream_related',
1587
+ reasonCode: 'orchestration_edge',
1588
+ evidenceRefs: [evidenceRef, ...edge.evidenceRefs.slice(0, 2)].slice(0, 3),
1589
+ });
1590
+ }
1591
+ if (attributions.length === 0 && windowMatched) {
1592
+ attributions.push({
1593
+ skillName: windowMatched.skillName,
1594
+ skillSegmentId: windowMatched.id,
1595
+ attributionRole: 'context_only',
1596
+ reasonCode: 'episode_context',
1597
+ evidenceRefs: [evidenceRef],
1598
+ });
1599
+ }
1600
+ else if (attributions.length === 0 && skillSegments[0]) {
1601
+ attributions.push({
1602
+ skillName: skillSegments[0].skillName,
1603
+ skillSegmentId: skillSegments[0].id,
1604
+ attributionRole: 'context_only',
1605
+ reasonCode: 'episode_context',
1606
+ evidenceRefs: [evidenceRef],
1607
+ });
1608
+ }
1609
+ return dedupeFeedbackAttributions(attributions);
1610
+ }
1611
+ function upstreamPromiseOwnerForFeedback(promiseOwner, orchestrationEdges, skillSegments) {
1612
+ const segmentById = new Map(skillSegments.map((segment) => [segment.id, segment]));
1613
+ const parents = orchestrationEdges
1614
+ .filter((edge) => edge.executorSkillSegmentId === promiseOwner.id && edge.parentSkillSegmentId && edge.parentSkillSegmentId !== promiseOwner.id)
1615
+ .map((edge) => segmentById.get(edge.parentSkillSegmentId))
1616
+ .filter((segment) => Boolean(segment));
1617
+ return parents
1618
+ .sort((a, b) => upstreamPromiseOwnerScore(b) - upstreamPromiseOwnerScore(a))[0];
1619
+ }
1620
+ function upstreamPromiseOwnerScore(segment) {
1621
+ let score = 0;
1622
+ if (segment.episodeRole === 'router' || segment.skillType === 'router')
1623
+ score += 80;
1624
+ if (segment.episodeRole === 'delegator' || segment.skillType === 'delegation')
1625
+ score += 70;
1626
+ if (/aiprd[-_\s]*task[-_\s]*runner|dev[-_\s]*lifecycle|omk[-_\s]*reviewer/i.test(segment.skillName))
1627
+ score += 30;
1628
+ return score;
1629
+ }
1630
+ function downstreamRelatedParentsForPrimary(primary, skillSegments, orchestrationEdges) {
1631
+ const segmentById = new Map(skillSegments.map((segment) => [segment.id, segment]));
1632
+ return orchestrationEdges
1633
+ .filter((edge) => edge.parentSkillSegmentId
1634
+ && edge.executorSkillSegmentId === primary.id
1635
+ && edge.parentSkillSegmentId !== primary.id)
1636
+ .map((edge) => {
1637
+ const segment = segmentById.get(edge.parentSkillSegmentId);
1638
+ return segment ? { segment, edge } : undefined;
1639
+ })
1640
+ .filter((value) => Boolean(value));
1641
+ }
1642
+ function isAsyncPromiseFeedbackText(text) {
1643
+ return /有结论|进度|怎么样了|跑完|完成了吗|没返回|为什么.*(?:没|不).*?(?:通知|返回|同步)|通知|返回|同步|查看地址/i.test(text);
1644
+ }
1645
+ function shouldPromiseOwnerReceivePrimaryFeedback(text) {
1646
+ return /为什么.*(?:没|不).*?(?:通知|返回|同步)|没返回|没有.*(?:通知|返回|同步)|有结论吗|怎么.*还没/i.test(text);
1647
+ }
1648
+ function isExplicitSkillTargetText(text, owner) {
1649
+ const lower = text.toLowerCase();
1650
+ const name = owner.skillName.toLowerCase();
1651
+ const compact = compactObjectText(lower);
1652
+ const compactName = compactObjectText(name);
1653
+ return lower.includes(name) || (compactName.length >= 4 && compact.includes(compactName));
1654
+ }
1655
+ function sessionStoryPromiseOwners(timeline, skillSegments) {
1656
+ return timeline
1657
+ .filter((event) => event.kind === 'assistant_message')
1658
+ .map((event) => {
1659
+ const text = `${event.snippet ?? ''} ${event.fullText ?? ''}`;
1660
+ if (!/有结果.*同步|完成.*(?:通知|同步|回复|转回)|跑完.*(?:告诉|通知|同步)|我会等.*(?:分析完|完成)|我会.*(?:转回|同步|回复)|有结论.*(?:同步|回复)/i.test(text))
1661
+ return undefined;
1662
+ const evidenceRef = evidenceRefFromTimeline(event);
1663
+ const segment = skillSegmentForEvidenceRef(evidenceRef, skillSegments);
1664
+ if (!segment || typeof evidenceRef.messageIndex !== 'number')
1665
+ return undefined;
1666
+ return { messageIndex: evidenceRef.messageIndex, segment, evidenceRef };
1667
+ })
1668
+ .filter((value) => Boolean(value));
1669
+ }
1670
+ function promiseOwnerForFeedback(evidenceRef, text, promises) {
1671
+ if (typeof evidenceRef.messageIndex !== 'number')
1672
+ return undefined;
1673
+ const feedbackMessageIndex = evidenceRef.messageIndex;
1674
+ if (!/有结论|进度|怎么样了|跑完|完成了吗|没返回|为什么.*(?:没|不).*?(?:通知|返回|同步)|通知|返回|同步/i.test(text))
1675
+ return undefined;
1676
+ return promises
1677
+ .filter((promise) => evidenceRefsShareTraceScope(promise.evidenceRef, evidenceRef))
1678
+ .filter((promise) => promise.messageIndex <= feedbackMessageIndex)
1679
+ .sort((a, b) => b.messageIndex - a.messageIndex)[0]?.segment;
1680
+ }
1681
+ function targetObjectSkillOwner(text, skillSegments) {
1682
+ const lower = text.toLowerCase();
1683
+ const compact = compactObjectText(lower);
1684
+ const explicitSkill = skillSegments.find((segment) => {
1685
+ const name = segment.skillName.toLowerCase();
1686
+ const compactName = compactObjectText(name);
1687
+ return lower.includes(name)
1688
+ || (compactName.length >= 4 && compact.includes(compactName));
1689
+ });
1690
+ if (explicitSkill)
1691
+ return explicitSkill;
1692
+ const byName = (patterns) => skillSegments.find((segment) => patterns.some((pattern) => pattern.test(segment.skillName.toLowerCase())));
1693
+ if (/skill\s*extract|soft[-_\s]*standard|llm[-_\s]*enhanced|omk[-_\s]*reviewer|skill\.md|这个skill|执行流程|依赖.*脚本|删除.*脚本|review/.test(lower)) {
1694
+ return byName([/omk[-_\s]*reviewer/, /reviewer/]);
1695
+ }
1696
+ if (/preview|预览|可预览|端口|localhost|127\.0\.0\.1|链接发给我|url/.test(lower)) {
1697
+ return byName([/ai[-_\s]*worker[-_\s]*webtools/, /webtools/, /preview/]);
1698
+ }
1699
+ if (/runner|child|子\s*claude|claude\s*session|ttyd|执行窗口/.test(lower)) {
1700
+ return byName([/apply[-_\s]*cc/]);
1701
+ }
1702
+ if (/\bpr\b|pull request|merge|分支|拉下|拉取|代码|项目分枝|项目分支|master/.test(lower)) {
1703
+ return byName([/dev[-_\s]*lifecycle/, /antcode/, /git/]);
1704
+ }
1705
+ if (/日报|每天早上|每天晚上|复盘报告/.test(lower)) {
1706
+ return byName([/damai[-_\s]*daily/, /daily/]);
1707
+ }
1708
+ return undefined;
1709
+ }
1710
+ function compactObjectText(value) {
1711
+ return value.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '');
1712
+ }
1713
+ function uniqueBy(values, keyOf) {
1714
+ const seen = new Set();
1715
+ const out = [];
1716
+ for (const value of values) {
1717
+ const key = keyOf(value);
1718
+ if (seen.has(key))
1719
+ continue;
1720
+ seen.add(key);
1721
+ out.push(value);
1722
+ }
1723
+ return out;
1724
+ }
1725
+ function actionOwnerForFeedback(evidenceRef, text, timeline, skillSegments) {
1726
+ const category = feedbackActionCategory(text);
1727
+ if (!category || typeof evidenceRef.messageIndex !== 'number')
1728
+ return undefined;
1729
+ const feedbackMessageIndex = evidenceRef.messageIndex;
1730
+ const nextRuntimeEvent = timeline.find((event) => {
1731
+ if (typeof event.messageIndex !== 'number')
1732
+ return false;
1733
+ if (!evidenceRefsShareTraceScope(event, evidenceRef))
1734
+ return false;
1735
+ if (event.messageIndex <= feedbackMessageIndex || event.messageIndex > feedbackMessageIndex + 16)
1736
+ return false;
1737
+ if (event.kind !== 'tool_use' && event.kind !== 'assistant_message')
1738
+ return false;
1739
+ const eventText = `${event.toolName ?? ''} ${event.snippet ?? ''} ${event.fullText ?? ''}`;
1740
+ return actionCategoryMatchesRuntimeEvent(category, eventText);
1741
+ });
1742
+ return nextRuntimeEvent ? skillSegmentForEvidenceRef(evidenceRefFromTimeline(nextRuntimeEvent), skillSegments) : undefined;
1743
+ }
1744
+ function feedbackActionCategory(text) {
1745
+ if (/删除|删掉|remove|delete|rm\s/.test(text))
1746
+ return 'delete';
1747
+ if (/拉下|拉取|pull|fetch|checkout|分支/.test(text))
1748
+ return 'pull';
1749
+ if (/预览|链接|端口|打开|url/.test(text))
1750
+ return 'preview';
1751
+ if (/看下|review|检查|确认|否决|补充|更新|执行流程|skill/.test(text))
1752
+ return 'review';
1753
+ if (/停止|暂停|中断|stop|cancel/.test(text))
1754
+ return 'stop';
1755
+ return undefined;
1756
+ }
1757
+ function actionCategoryMatchesRuntimeEvent(category, eventText) {
1758
+ if (!category)
1759
+ return false;
1760
+ const lower = eventText.toLowerCase();
1761
+ if (category === 'delete')
1762
+ return /\brm\b|delete|remove|unlink|删除/.test(lower);
1763
+ if (category === 'pull')
1764
+ return /git\s+(?:pull|fetch|checkout|switch)|拉取|拉下|分支/.test(lower);
1765
+ if (category === 'preview')
1766
+ return /preview|localhost|127\.0\.0\.1|端口|server|vite|python3.*server|npm.*dev/.test(lower);
1767
+ if (category === 'review')
1768
+ return /skill|review|grep|rg|read|sed|cat|检查|确认|否决|补充|更新/.test(lower);
1769
+ if (category === 'stop')
1770
+ return /kill|stop|cancel|interrupt|停止|中断/.test(lower);
1771
+ return false;
1772
+ }
1773
+ function shouldUseWindowFeedbackOwner(segment, text) {
1774
+ if (segment.skillType !== 'delegation' && segment.episodeRole !== 'delegator')
1775
+ return true;
1776
+ return /子\s*claude|child|runner|ttyd|session|claude|有结论|进度|怎么样了|没返回|通知|同步|跑完|完成了吗/i.test(text);
1777
+ }
1778
+ function dedupeFeedbackAttributions(attributions) {
1779
+ const seen = new Set();
1780
+ const out = [];
1781
+ for (const attribution of attributions) {
1782
+ const key = `${attribution.skillSegmentId ?? attribution.skillName ?? ''}:${attribution.attributionRole}:${attribution.reasonCode}`;
1783
+ if (seen.has(key))
1784
+ continue;
1785
+ seen.add(key);
1786
+ out.push(attribution);
1787
+ }
1788
+ return out;
1789
+ }
1790
+ function feedbackAttributionReasonForText(lowerText) {
1791
+ if (/pr|pull request|master|分支|组件|产物|文档|报告|demo|链接|地址|文件|项目|skill|agent|claude/.test(lowerText)) {
1792
+ return 'object_match';
1793
+ }
1794
+ if (/有结论|进度|没返回|通知|返回|同步|发给|给我|怎么样了|还在线|完成了吗|跑完/.test(lowerText)) {
1795
+ return 'promise_match';
1796
+ }
1797
+ if (/停止|暂停|中断|别动|删除|补充|追加|继续|重跑|重新|修正|更新|拉下|看下|执行|检查|确认|否决|采用|弃用/.test(lowerText)) {
1798
+ return 'action_match';
1799
+ }
1800
+ return 'episode_context';
1801
+ }
1802
+ function skillSegmentForEvidenceRef(evidenceRef, skillSegments, preferredSkillName) {
1803
+ const messageIndex = evidenceRef.messageIndex;
1804
+ if (typeof messageIndex !== 'number')
1805
+ return undefined;
1806
+ const candidates = skillSegments
1807
+ .map((segment) => {
1808
+ const ranges = segment.messageRanges?.length
1809
+ ? segment.messageRanges
1810
+ : typeof segment.startMessageIndex === 'number' && typeof segment.endMessageIndex === 'number'
1811
+ ? [{ startMessageIndex: segment.startMessageIndex, endMessageIndex: segment.endMessageIndex }]
1812
+ : [];
1813
+ const matchedRange = ranges.find((range) => messageRangeContainsEvidenceRef(range, evidenceRef));
1814
+ return matchedRange ? {
1815
+ segment,
1816
+ rangeSize: matchedRange.endMessageIndex - matchedRange.startMessageIndex,
1817
+ traceSpecificity: messageRangeTraceSpecificity(matchedRange, evidenceRef),
1818
+ preferred: preferredSkillName ? segment.skillName === preferredSkillName : false,
1819
+ startsHere: messageIndex === matchedRange.startMessageIndex,
1820
+ } : undefined;
1821
+ })
1822
+ .filter((value) => Boolean(value))
1823
+ .sort((a, b) => Number(b.preferred) - Number(a.preferred)
1824
+ || b.traceSpecificity - a.traceSpecificity
1825
+ || Number(b.startsHere) - Number(a.startsHere)
1826
+ || a.rangeSize - b.rangeSize
1827
+ || a.segment.order - b.segment.order);
1828
+ return candidates[0]?.segment;
1829
+ }
1830
+ function messageRangeContainsEvidenceRef(range, ref) {
1831
+ if (typeof ref.messageIndex !== 'number')
1832
+ return false;
1833
+ if (range.sourceTrace && ref.sourceTrace && range.sourceTrace !== ref.sourceTrace)
1834
+ return false;
1835
+ if (range.sessionId && ref.sessionId && range.sessionId !== ref.sessionId)
1836
+ return false;
1837
+ return ref.messageIndex >= range.startMessageIndex && ref.messageIndex <= range.endMessageIndex;
1838
+ }
1839
+ function messageRangeTraceSpecificity(range, ref) {
1840
+ return (range.sourceTrace && ref.sourceTrace && range.sourceTrace === ref.sourceTrace ? 2 : 0)
1841
+ + (range.sessionId && ref.sessionId && range.sessionId === ref.sessionId ? 1 : 0);
1842
+ }
1843
+ function evidenceRefsShareTraceScope(a, b) {
1844
+ if (!a || !b)
1845
+ return true;
1846
+ if (a.sourceTrace && b.sourceTrace && a.sourceTrace !== b.sourceTrace)
1847
+ return false;
1848
+ if (a.sessionId && b.sessionId && a.sessionId !== b.sessionId)
1849
+ return false;
1850
+ return true;
1851
+ }
1852
+ function timelineEventSharesTraceScope(event, anchor) {
1853
+ return evidenceRefsShareTraceScope(event, anchor);
1854
+ }
1855
+ function skillSegmentsShareSourceTrace(a, b) {
1856
+ const aTraces = new Set((a.messageRanges ?? []).map((range) => range.sourceTrace).filter((value) => Boolean(value)));
1857
+ const bTraces = new Set((b.messageRanges ?? []).map((range) => range.sourceTrace).filter((value) => Boolean(value)));
1858
+ if (aTraces.size === 0 || bTraces.size === 0)
1859
+ return true;
1860
+ return Array.from(aTraces).some((trace) => bTraces.has(trace));
1861
+ }
1862
+ function sessionStoryArtifacts(invocations) {
1863
+ const timeline = uniqueTimelineEvents(invocations.flatMap((invocation) => invocation.timeline))
1864
+ .sort(compareTimelineEvents);
1865
+ return timeline
1866
+ .filter((event) => event.kind === 'assistant_message' && hasAssistantDeliverableArtifactText(event.fullText ?? event.snippet ?? ''))
1867
+ .slice(-5)
1868
+ .map((event) => {
1869
+ const text = event.snippet ?? event.fullText ?? '';
1870
+ const pathOrUrl = text.match(/https?:\/\/\S+/)?.[0]
1871
+ ?? text.match(/(?:outputs|reports|dist|docs|artifacts|\/tmp|\/Users)\/[^\s`,。))]+/i)?.[0];
1872
+ return {
1873
+ kind: pathOrUrl?.startsWith('http') ? 'url' : pathOrUrl ? 'path' : 'unknown',
1874
+ label: pathOrUrl ?? text.slice(0, 80),
1875
+ pathOrUrl,
1876
+ artifactGoalMatch: 'unknown',
1877
+ evidenceRef: evidenceRefFromTimeline(event),
1878
+ };
1879
+ });
1880
+ }
1881
+ function sessionStoryOutcomeClosure(session, artifacts) {
1882
+ if (session.indicators.userInterruptionCount > 0)
1883
+ return 'abandoned';
1884
+ if (session.indicators.assistantDeliverySignalCount > 0 || artifacts.length > 0)
1885
+ return 'closed';
1886
+ if (session.indicators.userFollowUpCount > 0 || session.indicators.negativeFeedbackCount > 0 || session.indicators.userCorrectionCount > 0)
1887
+ return 'unresolved';
1888
+ return 'unknown';
1889
+ }
1890
+ function sessionStoryAcceptanceCriteria(session, feedbackSignals, artifacts) {
1891
+ if (feedbackSignals.some((signal) => signal.attributions.some((attribution) => attribution.attributionRole === 'primary_fault'))) {
1892
+ return '下次同类任务中,主要归因的用户反馈应消失,且对应 skill 段能看到明确闭环或阻塞原因。';
1893
+ }
1894
+ if (artifacts.length === 0 && session.indicators.assistantDeliverySignalCount === 0) {
1895
+ return '下次同类任务中,应看到明确最终答复、产物路径,或清晰的阻塞说明。';
1896
+ }
1897
+ return undefined;
1898
+ }
1899
+ function assistantFinalDeliveryEvents(session) {
1900
+ return (session.fullSessionTimeline.length > 0 ? session.fullSessionTimeline : session.timelinePreview)
1901
+ .filter(isAssistantDeliveryEvent);
1902
+ }
1903
+ function assistantProgressUpdateEvents(session) {
1904
+ return (session.fullSessionTimeline.length > 0 ? session.fullSessionTimeline : session.timelinePreview)
1905
+ .filter(isAssistantProgressUpdateEvent);
1906
+ }
1907
+ function sessionStoryGoalSlices(session, invocations) {
1908
+ const byId = new Map();
1909
+ for (const invocation of invocations) {
1910
+ const group = byId.get(invocation.goalSliceId) ?? [];
1911
+ group.push(invocation);
1912
+ byId.set(invocation.goalSliceId, group);
1913
+ }
1914
+ return Array.from(byId.entries()).map(([id, group], index) => {
1915
+ const timeline = uniqueTimelineEvents(group.flatMap((invocation) => invocation.timeline)).sort(compareTimelineEvents);
1916
+ const userEvents = timeline.filter((event) => event.kind === 'user_message');
1917
+ const startTimestamp = minString(group.map((invocation) => invocation.startTimestamp)) ?? session.startTimestamp;
1918
+ const endTimestamp = maxString(group.map((invocation) => invocation.endTimestamp)) ?? session.endTimestamp;
1919
+ const hasGoalShift = userEvents.some((event) => hasUserGoalShiftSignal(event.snippet ?? ''));
1920
+ const reasonCode = hasGoalShift
1921
+ ? 'explicit_user_goal_shift'
1922
+ : group.length > 1 ? 'skill_segment_boundary' : 'default_session_slice';
1923
+ return {
1924
+ id,
1925
+ order: index + 1,
1926
+ skillNames: unique(group.map((invocation) => invocation.skillName)).sort(),
1927
+ startTimestamp,
1928
+ endTimestamp,
1929
+ reasonCode,
1930
+ inferredUserGoal: inferUserGoal(userEvents),
1931
+ evidenceRefs: userEvents.slice(0, 3).map(evidenceRefFromTimeline),
1932
+ };
1933
+ }).sort((a, b) => a.startTimestamp.localeCompare(b.startTimestamp));
1934
+ }
1935
+ function sessionStorySubagentDispatches(session) {
1936
+ const branches = session.timelineTree?.branches ?? [];
1937
+ return branches.map((branch, index) => ({
1938
+ id: hashParts('session-story-dispatch', session.id, branch.id),
1939
+ order: index + 1,
1940
+ branchId: branch.id,
1941
+ label: branch.label,
1942
+ sourceTrace: branch.sourceTrace,
1943
+ attachTo: branch.attachTo ? {
1944
+ messageIndex: branch.attachTo.messageIndex,
1945
+ toolUseId: branch.attachTo.toolUseId,
1946
+ label: branch.attachTo.label,
1947
+ } : undefined,
1948
+ eventCount: branch.events.length,
1949
+ evidenceRefs: branch.events.slice(0, 3).map(evidenceRefFromTimeline),
1950
+ }));
1951
+ }
1952
+ function sessionStorySkillLinks(session, invocations) {
1953
+ const bySkill = new Map();
1954
+ for (const invocation of invocations) {
1955
+ const group = bySkill.get(invocation.skillName) ?? [];
1956
+ group.push(invocation);
1957
+ bySkill.set(invocation.skillName, group);
1958
+ }
1959
+ return Array.from(bySkill.entries()).map(([skillName, group], index) => {
1960
+ const role = inferSkillRole(group, invocations, session);
1961
+ return {
1962
+ id: hashParts('session-story-skill-link', session.id, skillName),
1963
+ order: index + 1,
1964
+ skillName,
1965
+ role,
1966
+ invocationIds: group.map((invocation) => invocation.id),
1967
+ goalSliceIds: unique(group.map((invocation) => invocation.goalSliceId)),
1968
+ evidenceRefs: uniqueEvidenceRefs(group.flatMap((invocation) => [
1969
+ invocation.evidenceChain.firstSkillContext,
1970
+ invocation.evidenceChain.firstToolUse,
1971
+ ...routingEvidenceEvents(invocation).map(evidenceRefFromTimeline),
1972
+ ].filter((ref) => Boolean(ref)))).slice(0, 5),
1973
+ };
1974
+ }).sort((a, b) => a.order - b.order);
1975
+ }
1976
+ function inferSkillRole(group, allInvocations, session) {
1977
+ const hasRoutingSignal = group.some((invocation) => routingEvidenceEvents(invocation).length > 0);
1978
+ const hasBranchDispatch = (session.timelineTree?.branches.length ?? 0) > 0 && group.some((invocation) => invocation.timeline.some((event) => event.kind === 'tool_use' && /^(Task|Agent|Skill)$/i.test(event.toolName ?? '')));
1979
+ const hasExecutionSignal = group.some((invocation) => invocation.timeline.some((event) => event.kind === 'tool_use' && !/^(Task|Agent|Skill)$/i.test(event.toolName ?? '')));
1980
+ if ((hasRoutingSignal || hasBranchDispatch) && allInvocations.length > group.length)
1981
+ return hasExecutionSignal ? 'mixed' : 'router';
1982
+ if (hasRoutingSignal || hasBranchDispatch)
1983
+ return 'router';
1984
+ if (hasExecutionSignal)
1985
+ return 'executor';
1986
+ return 'unknown';
1987
+ }
1988
+ function routingEvidenceEvents(invocation) {
1989
+ return invocation.timeline.filter((event) => {
1990
+ if (event.kind !== 'assistant_message' && event.kind !== 'tool_use')
1991
+ return false;
1992
+ const text = event.fullText ?? event.snippet ?? '';
1993
+ return /子\s*Claude|subagent|子任务|分发|委派|调用.+skill|走\s*`?[\w-]+`?\s*skill|\/consult|task-runner/i.test(text);
1994
+ }).slice(0, 3);
1995
+ }
1996
+ function skillRoleLabel(role) {
1997
+ if (role === 'router')
1998
+ return '路由';
1999
+ if (role === 'executor')
2000
+ return '执行';
2001
+ if (role === 'mixed')
2002
+ return '路由 + 执行';
2003
+ return '未确认';
2004
+ }
2005
+ function sessionStoryGraph(nodes, skillLinks) {
2006
+ const graphNodes = nodes.map((node) => ({
2007
+ id: node.id,
2008
+ label: node.label,
2009
+ kind: node.kind,
2010
+ status: node.status,
2011
+ detailNodeId: node.id,
2012
+ }));
2013
+ for (const link of skillLinks) {
2014
+ graphNodes.push({
2015
+ id: link.id,
2016
+ label: `${link.skillName}(${skillRoleLabel(link.role)})`,
2017
+ kind: 'skill_invocation',
2018
+ status: link.role === 'unknown' ? 'unknown' : 'ok',
2019
+ role: link.role,
2020
+ });
2021
+ }
2022
+ const edges = [];
2023
+ for (let index = 1; index < nodes.length; index += 1) {
2024
+ edges.push({ fromId: nodes[index - 1].id, toId: nodes[index].id, label: '下一步' });
2025
+ }
2026
+ const goalNode = nodes.find((node) => node.kind === 'user_goal');
2027
+ const executionNode = nodes.find((node) => node.kind === 'tool_execution');
2028
+ if (goalNode && executionNode) {
2029
+ for (const link of skillLinks) {
2030
+ edges.push({ fromId: goalNode.id, toId: link.id, label: '触发' });
2031
+ edges.push({ fromId: link.id, toId: executionNode.id, label: skillRoleLabel(link.role) });
2032
+ }
2033
+ }
2034
+ return { nodes: graphNodes, edges };
2035
+ }
2036
+ function sessionStoryAnswer(key, label, checklistItems, evidenceRefs) {
2037
+ const folded = foldExperienceChecklistItems(checklistItems);
2038
+ return {
2039
+ key,
2040
+ label,
2041
+ status: folded.status,
2042
+ reason: folded.reason,
2043
+ sourceItemKeys: folded.sourceItemKeys,
2044
+ text: sessionStoryAnswerText(key, folded.reason),
2045
+ evidenceRefs: uniqueEvidenceRefs(evidenceRefs.filter((ref) => Boolean(ref))).slice(0, 5),
2046
+ checklistItems,
2047
+ };
2048
+ }
2049
+ function sessionStoryAnswerText(key, reason) {
2050
+ const texts = {
2051
+ goal_satisfaction: {
2052
+ data_degraded: '这次没看到 skill 真的被加载,先确认数据,再判断目标是否满足。',
2053
+ blocking_failed: '用户出现了不满或叫停,目标没真的满足。',
2054
+ attention_accumulated: '有几条要看一眼,打开原文确认目标是否真的达成。',
2055
+ unknown_dominant: '现有证据不够,判断不了目标是否满足。',
2056
+ all_passed: '关键信号都没问题,看起来目标已满足。',
2057
+ not_applicable: '当前场景不适合回答这个问题。',
2058
+ },
2059
+ declared_behavior_fit: {
2060
+ data_degraded: '没看到 skill 真的被加载,先确认这次任务是不是真的命中了你的 skill。',
2061
+ blocking_failed: '关键执行项没过,skill 行为需要优先看一眼。',
2062
+ attention_accumulated: 'SKILL.md 声明的标准流程、硬性规则或核心工具有几条要看一眼。',
2063
+ unknown_dominant: 'SKILL.md 声明不全或执行证据不够,判不出行为是否符合用途。',
2064
+ all_passed: '执行情况看起来符合 skill 声明的用途。',
2065
+ not_applicable: '当前场景不适合回答这个问题。',
2066
+ },
2067
+ user_feeling: {
2068
+ data_degraded: '这次数据本身有问题,用户感受判断先放一放。',
2069
+ blocking_failed: '看到用户不满或主动叫停,可能觉得没用或绕路了。',
2070
+ attention_accumulated: '看到用户纠正或追问,结合原文判断用户感受。',
2071
+ unknown_dominant: '用户没给明确反馈,判断不了是否觉得有用。',
2072
+ all_passed: '没看到明显不满,用户体感看起来正常。',
2073
+ not_applicable: '当前场景不适合回答这个问题。',
2074
+ },
2075
+ };
2076
+ return texts[key][reason];
2077
+ }
2078
+ function checklistItemsForAnswer(session, key, episodes, reviewState) {
2079
+ if (key === 'goal_satisfaction')
2080
+ return goalSatisfactionChecklistItems(session, episodes, reviewState);
2081
+ if (key === 'declared_behavior_fit')
2082
+ return declaredBehaviorChecklistItems(session, episodes);
2083
+ return userFeelingChecklistItems(session, episodes, reviewState);
2084
+ }
2085
+ function attributionSourcesToLabel(sources) {
2086
+ if (sources.length === 0)
2087
+ return '未识别(旧数据可能没记录来源)';
2088
+ const map = {
2089
+ 'skill-tool': 'assistant 调用 Skill 工具',
2090
+ 'command-name': '用户用 slash command',
2091
+ 'business-action': '用户用业务动作块',
2092
+ [legacyBusinessActionSource()]: '用户用业务动作块',
2093
+ 'skill-script': '跑了 skills/<name>/scripts 脚本',
2094
+ 'read-skill-md': 'LLM 主动 Read SKILL.md',
2095
+ unknown: '未知',
2096
+ };
2097
+ return sources.map((s) => map[s] ?? s).join(' + ');
2098
+ }
2099
+ export function isExperienceTraceInProgress(session) {
2100
+ if (session.indicators.assistantDeliverySignalCount > 0)
2101
+ return false;
2102
+ if (session.indicators.deliverableArtifactSignalCount > 0)
2103
+ return false;
2104
+ // 用户已经主动表达不满(纠正 / 负向 / 中断)属于「被打断」, 不是「在途中」, 仍需触发 final_delivery_absent。
2105
+ if (session.indicators.userCorrectionCount > 0)
2106
+ return false;
2107
+ if (session.indicators.negativeFeedbackCount > 0)
2108
+ return false;
2109
+ if (session.indicators.userInterruptionCount > 0)
2110
+ return false;
2111
+ const last = session.evidenceChain.lastAssistantMessage?.snippet ?? '';
2112
+ // 完全没有最后助手回复时不强判 in-progress, 走原 failed / unknown 路径。
2113
+ if (!last.trim())
2114
+ return false;
2115
+ if (isAssistantProgressUpdateText(last))
2116
+ return true;
2117
+ // 预备语(让我看 / 先看看 / 先读 / 接下来)也算 in-progress, 仍未给出最终回复。
2118
+ return /让我(?:先|来|看|读|检查|分析|确认|拉|获取|继续)|先(?:看(?:看|一?下)|读(?:取|一下)?|确认|检查|分析|拉取|获取)|接下来/i.test(last);
2119
+ }
2120
+ function checklistItem(input) {
2121
+ const { statusCandidates, ...item } = input;
2122
+ return {
2123
+ ...item,
2124
+ status: aggregateExperienceChecklistItemStatus([input.status, ...(statusCandidates ?? [])]),
2125
+ source: input.source ?? 'deterministic_rule',
2126
+ evidenceRefs: uniqueEvidenceRefs((input.evidenceRefs ?? []).filter((ref) => Boolean(ref))).slice(0, 5),
2127
+ };
2128
+ }
2129
+ export function hasRecognizableUserGoalText(value) {
2130
+ const text = value?.trim() ?? '';
2131
+ if (!text)
2132
+ return false;
2133
+ if (/^(嗯+|啊+|好的|好|继续|可以|收到|ok|OK|yes|no|不用|不需要)[。.!!??\s]*$/.test(text))
2134
+ return false;
2135
+ if (/(帮我|请|需要|想要|我要|给我|看下|看一下|基于|根据|重新|继续|先|把|将)/.test(text)
2136
+ && /(生成|创建|写|实现|修复|优化|调整|修改|新增|删除|分析|review|评价|检查|看|拉取|执行|运行|验证|整理|总结|回复|评论|设计|拆分|合并|标注|定位|排查|上传|导出|发布|查询|对齐|沉淀|补充|改|做)/i.test(text)) {
2137
+ return true;
2138
+ }
2139
+ if (/(生成|创建|写一个|实现|修复|优化|调整|修改|新增|分析|review|检查|排查|验证|总结|设计|标注|定位|查询|对齐|补充|改一下|做一个)/i.test(text))
2140
+ return true;
2141
+ return text.length >= 12 && /[??]/.test(text) && !/^(为什么|怎么|哪里)[??]?$/.test(text);
2142
+ }
2143
+ function hasRecognizableUserGoal(ref) {
2144
+ return hasRecognizableUserGoalText(ref?.snippet);
2145
+ }
2146
+ function goalSatisfactionChecklistItems(session, episodes, reviewState) {
2147
+ const feedbackRefs = userFeedbackEvidenceRefs(session);
2148
+ const feedbackCounts = canonicalFeedbackCountsForSession(session, reviewState);
2149
+ const goalIdentified = hasRecognizableUserGoal(session.evidenceChain.firstUserMessage);
2150
+ const inProgress = isExperienceTraceInProgress(session);
2151
+ const closure = userFacingClosureForSession(session, episodes);
2152
+ const hasDelivery = closure.deliveryCount > 0;
2153
+ const hasArtifact = closure.artifactCount > 0;
2154
+ return [
2155
+ checklistItem({
2156
+ key: 'goal_identified',
2157
+ label: goalIdentified ? '目标已识别' : '目标不明确',
2158
+ status: goalIdentified ? 'passed' : 'unknown',
2159
+ contribution: 'informational',
2160
+ reason: goalIdentified
2161
+ ? '真实用户原文里能识别出目标动作或明确请求。'
2162
+ : session.evidenceChain.firstUserMessage ? '看到真实用户原文,但目标动作不够明确。' : '没有看到真实用户目标原文。',
2163
+ evidenceRefs: [session.evidenceChain.firstUserMessage],
2164
+ }),
2165
+ checklistItem({
2166
+ key: 'completion_result_present',
2167
+ label: hasDelivery ? '给了用户最终答复' : inProgress ? '会话进行中' : '没给用户最终答复',
2168
+ status: hasDelivery ? 'passed' : inProgress ? 'not_applicable' : 'failed',
2169
+ contribution: hasDelivery ? 'attention' : inProgress ? 'neutral' : 'attention',
2170
+ reason: hasDelivery
2171
+ ? '看到 assistant 给出了明确的完成话术或结果反馈。'
2172
+ : inProgress
2173
+ ? '最后一句还是过程态(「先看看」「让我」),任务还没收尾,先不判定。'
2174
+ : '没看到 assistant 给用户明确的完成话术或结果反馈。',
2175
+ evidenceRefs: [session.evidenceChain.lastAssistantMessage],
2176
+ suggestionKey: hasDelivery || inProgress ? undefined : 'final_delivery_absent',
2177
+ }),
2178
+ checklistItem({
2179
+ key: 'deliverable_artifact_present',
2180
+ label: hasArtifact ? '给了可点开的产物' : inProgress ? '会话进行中' : '没给可点开的产物',
2181
+ status: hasArtifact ? 'passed' : inProgress ? 'not_applicable' : 'unknown',
2182
+ contribution: hasArtifact ? 'informational' : inProgress ? 'neutral' : 'informational',
2183
+ reason: hasArtifact
2184
+ ? '看到 assistant 回复里附了链接、路径、代码块或文件。'
2185
+ : inProgress
2186
+ ? '任务还没收尾,先不判定产物。'
2187
+ : '没看到明确的链接、路径、代码块或文件;不一定失败,得按 skill 目标判断。',
2188
+ evidenceRefs: [session.evidenceChain.lastAssistantMessage],
2189
+ suggestionKey: hasArtifact || inProgress ? undefined : 'artifact_absent',
2190
+ }),
2191
+ checklistItem({
2192
+ key: 'negative_feedback_seen',
2193
+ label: feedbackCounts.negativeFeedbackCount > 0 ? '看到用户负向反馈' : '未见用户负向反馈',
2194
+ status: feedbackCounts.negativeFeedbackCount > 0 ? 'failed' : 'passed',
2195
+ contribution: 'blocking',
2196
+ reason: feedbackCounts.negativeFeedbackCount > 0 ? '看到用户负向表达,不能直接认为目标已满足。' : '没有看到用户负向表达。',
2197
+ evidenceRefs: feedbackRefs,
2198
+ suggestionKey: feedbackCounts.negativeFeedbackCount > 0 ? 'negative_feedback_review' : undefined,
2199
+ }),
2200
+ checklistItem({
2201
+ key: 'user_correction_seen',
2202
+ label: feedbackCounts.userCorrectionCount > 0 ? '看到用户纠正' : '未见用户纠正',
2203
+ status: feedbackCounts.userCorrectionCount > 0 ? 'failed' : 'passed',
2204
+ contribution: 'attention',
2205
+ reason: feedbackCounts.userCorrectionCount > 0 ? '用户中途纠正了方向,目标是否满足要打开原文看。' : '没有看到用户纠正。',
2206
+ evidenceRefs: feedbackRefs,
2207
+ suggestionKey: feedbackCounts.userCorrectionCount > 0 ? 'user_correction_review' : undefined,
2208
+ }),
2209
+ checklistItem({
2210
+ key: 'user_interruption_seen',
2211
+ label: feedbackCounts.userInterruptionCount > 0 ? '看到用户中断' : '未见用户中断',
2212
+ status: feedbackCounts.userInterruptionCount > 0 ? 'failed' : 'passed',
2213
+ contribution: 'blocking',
2214
+ reason: feedbackCounts.userInterruptionCount > 0 ? '看到用户中断或停止任务信号,不能认为执行链路自然完成。' : '没有看到用户中断信号。',
2215
+ evidenceRefs: feedbackRefs,
2216
+ suggestionKey: feedbackCounts.userInterruptionCount > 0 ? 'user_interruption_review' : undefined,
2217
+ }),
2218
+ checklistItem({
2219
+ key: 'goal_shift_seen',
2220
+ label: session.indicators.userGoalShiftCount > 0 ? '看到目标切换' : '未见目标切换',
2221
+ status: session.indicators.userGoalShiftCount > 0 ? 'failed' : 'passed',
2222
+ contribution: 'attention',
2223
+ reason: session.indicators.userGoalShiftCount > 0 ? '用户中途切换了目标,后续诉求可能不属于这个 skill。' : '没有看到目标切换。',
2224
+ evidenceRefs: feedbackRefs,
2225
+ suggestionKey: session.indicators.userGoalShiftCount > 0 ? 'goal_shift_review' : undefined,
2226
+ }),
2227
+ ...skillTypeClosureChecklistItems(session, 'goal_satisfaction', episodes),
2228
+ ];
2229
+ }
2230
+ function declaredBehaviorChecklistItems(session, episodes) {
2231
+ const expectedToolCheck = expectedToolCheckForSession(session);
2232
+ const declarations = loadSkillDeclarationCheck(session.skillName, session.cwd);
2233
+ const hasSkillRead = session.evidenceChain.skillContextCount > 0;
2234
+ const attributionLabel = attributionSourcesToLabel(session.attributionSources ?? []);
2235
+ const items = [
2236
+ checklistItem({
2237
+ key: 'attribution_source',
2238
+ label: hasSkillRead
2239
+ ? 'LLM 读了 SKILL.md'
2240
+ : `skill 判定来源:${attributionLabel}`,
2241
+ status: 'passed',
2242
+ contribution: 'informational',
2243
+ reason: hasSkillRead
2244
+ ? `看到 ${session.evidenceChain.skillContextCount} 次 SKILL.md 加载事件,可作为能力归因证据。`
2245
+ : `日志里没看到 LLM 主动 Read SKILL.md。这次 skill 归因来自:${attributionLabel}。OpenClaw 脚本 / Skill 工具 / slash command 触发时这是常态,不代表 skill 没用上。`,
2246
+ evidenceRefs: [session.evidenceChain.firstSkillContext, session.evidenceChain.firstToolUse],
2247
+ }),
2248
+ checklistItem({
2249
+ key: 'workflow_declared',
2250
+ label: declarations.workflows.declared ? '标准流程已声明' : '标准流程未声明',
2251
+ status: declarations.workflows.declared ? 'passed' : 'not_declared',
2252
+ contribution: declarations.workflows.declared ? 'informational' : 'attention',
2253
+ reason: declarations.workflows.declared ? `SKILL.md 里声明了 ${declarations.workflows.count} 个标准流程节点。` : 'SKILL.md 没声明标准流程,运行时只能猜流程是否完整。',
2254
+ suggestionKey: declarations.workflows.declared ? undefined : 'workflow_not_declared',
2255
+ }),
2256
+ ];
2257
+ if (declarations.workflows.declared) {
2258
+ const executed = session.indicators.toolCallCount > 0;
2259
+ items.push(checklistItem({
2260
+ key: 'workflow_executed',
2261
+ label: executed ? '标准流程已执行' : '标准流程未执行',
2262
+ status: executed ? 'unknown' : 'failed',
2263
+ contribution: 'attention',
2264
+ reason: executed ? '看到了工具调用,但是不是真的覆盖了完整流程,还要打开原文确认。' : '声明了标准流程,但没看到工具执行的证据。',
2265
+ evidenceRefs: [session.evidenceChain.firstToolUse],
2266
+ suggestionKey: 'workflow_execution_review',
2267
+ }));
2268
+ }
2269
+ items.push(checklistItem({
2270
+ key: 'hardrule_declared',
2271
+ label: declarations.hardRules.declared ? '硬性规则已声明' : '硬性规则未声明',
2272
+ status: declarations.hardRules.declared ? 'passed' : 'not_declared',
2273
+ contribution: declarations.hardRules.declared ? 'informational' : 'attention',
2274
+ reason: declarations.hardRules.declared ? `SKILL.md 里声明了 ${declarations.hardRules.count} 条硬性规则。` : 'SKILL.md 没声明硬性规则。',
2275
+ suggestionKey: declarations.hardRules.declared ? undefined : 'hardrule_not_declared',
2276
+ }));
2277
+ if (declarations.hardRules.declared) {
2278
+ items.push(checklistItem({
2279
+ key: 'hardrule_executed',
2280
+ label: '硬性规则执行情况需打开原文看',
2281
+ status: 'unknown',
2282
+ contribution: 'attention',
2283
+ reason: '声明了硬性规则,但当前规则没法完整证明每条都执行了,要打开原文看。',
2284
+ suggestionKey: 'hardrule_execution_review',
2285
+ }));
2286
+ }
2287
+ items.push(checklistItem({
2288
+ key: 'core_tools_declared',
2289
+ label: expectedToolCheck.declared ? '核心工具已声明' : '核心工具未声明',
2290
+ status: expectedToolCheck.declared ? 'passed' : 'not_declared',
2291
+ contribution: expectedToolCheck.declared ? 'informational' : 'attention',
2292
+ reason: expectedToolCheck.declared ? `SKILL.md 声明的核心工具:${expectedToolCheck.expectedTools.join('、')}。` : 'SKILL.md 没声明核心工具,分不出「真用上了 skill 工具」还是「只是随便调了个工具」。',
2293
+ suggestionKey: expectedToolCheck.declared ? undefined : 'expected_tools_not_declared',
2294
+ }));
2295
+ if (expectedToolCheck.declared) {
2296
+ const hit = expectedToolCheck.matchedTools.length > 0;
2297
+ items.push(checklistItem({
2298
+ key: 'core_tools_hit',
2299
+ label: hit ? '核心工具用上了' : '核心工具没用上',
2300
+ status: hit ? 'passed' : 'failed',
2301
+ contribution: 'blocking',
2302
+ reason: hit ? `用上了核心工具:${expectedToolCheck.matchedTools.join('、')}。` : '没用上 SKILL.md 声明的核心工具。',
2303
+ evidenceRefs: [session.evidenceChain.firstToolUse],
2304
+ suggestionKey: hit ? undefined : 'expected_tools_missed',
2305
+ }));
2306
+ }
2307
+ items.push(...skillTypeClosureChecklistItems(session, 'declared_behavior_fit', episodes));
2308
+ return items;
2309
+ }
2310
+ function userFeelingChecklistItems(session, episodes, reviewState) {
2311
+ const feedbackRefs = userFeedbackEvidenceRefs(session);
2312
+ const feedbackCounts = canonicalFeedbackCountsForSession(session, reviewState);
2313
+ const hasAnyFeedback = feedbackCounts.positiveFeedbackCount > 0
2314
+ || feedbackCounts.negativeFeedbackCount > 0
2315
+ || feedbackCounts.userCorrectionCount > 0
2316
+ || feedbackCounts.userInterruptionCount > 0
2317
+ || feedbackCounts.userFollowUpCount > 0
2318
+ || session.indicators.userGoalShiftCount > 0;
2319
+ return [
2320
+ checklistItem({
2321
+ key: 'user_feedback_signal_present',
2322
+ label: hasAnyFeedback ? '看到用户反馈信号' : '未见用户反馈信号',
2323
+ status: hasAnyFeedback ? 'passed' : 'unknown',
2324
+ contribution: 'informational',
2325
+ reason: hasAnyFeedback ? '看到至少一种用户反馈或后续行为信号。' : '没有看到明确用户反馈信号。',
2326
+ evidenceRefs: feedbackRefs,
2327
+ }),
2328
+ checklistItem({
2329
+ key: 'positive_feedback_seen',
2330
+ label: feedbackCounts.positiveFeedbackCount > 0 ? '看到用户正向反馈' : '未见用户正向反馈',
2331
+ status: feedbackCounts.positiveFeedbackCount > 0 ? 'passed' : 'unknown',
2332
+ contribution: feedbackCounts.positiveFeedbackCount > 0 ? 'positive' : 'neutral',
2333
+ reason: feedbackCounts.positiveFeedbackCount > 0 ? '看到用户认可或正向反馈。' : '没有看到明确正向反馈。',
2334
+ evidenceRefs: feedbackRefs,
2335
+ }),
2336
+ checklistItem({
2337
+ key: 'negative_feedback_seen',
2338
+ label: feedbackCounts.negativeFeedbackCount > 0 ? '看到用户负向反馈' : '未见用户负向反馈',
2339
+ status: feedbackCounts.negativeFeedbackCount > 0 ? 'failed' : 'passed',
2340
+ contribution: feedbackCounts.negativeFeedbackCount > 0 ? 'blocking' : 'neutral',
2341
+ reason: feedbackCounts.negativeFeedbackCount > 0 ? '看到用户负向表达。' : '没有看到用户负向表达。',
2342
+ evidenceRefs: feedbackRefs,
2343
+ suggestionKey: feedbackCounts.negativeFeedbackCount > 0 ? 'negative_feedback_review' : undefined,
2344
+ }),
2345
+ checklistItem({
2346
+ key: 'user_correction_seen',
2347
+ label: feedbackCounts.userCorrectionCount > 0 ? '看到用户纠正' : '未见用户纠正',
2348
+ status: feedbackCounts.userCorrectionCount > 0 ? 'failed' : 'passed',
2349
+ contribution: feedbackCounts.userCorrectionCount > 0 ? 'attention' : 'neutral',
2350
+ reason: feedbackCounts.userCorrectionCount > 0 ? '看到用户重新解释或要求修正。' : '没有看到用户纠正信号。',
2351
+ evidenceRefs: feedbackRefs,
2352
+ suggestionKey: feedbackCounts.userCorrectionCount > 0 ? 'user_correction_review' : undefined,
2353
+ }),
2354
+ checklistItem({
2355
+ key: 'user_follow_up_seen',
2356
+ label: feedbackCounts.userFollowUpCount > 0 ? '看到用户追问' : '未见用户追问',
2357
+ status: feedbackCounts.userFollowUpCount > 0 ? 'unknown' : 'passed',
2358
+ contribution: feedbackCounts.userFollowUpCount > 0 ? 'informational' : 'neutral',
2359
+ reason: feedbackCounts.userFollowUpCount > 0 ? '看到用户追问;需要结合上下文区分推进使用还是不满意。' : '没有看到用户追问。',
2360
+ evidenceRefs: feedbackRefs,
2361
+ suggestionKey: feedbackCounts.userFollowUpCount > 0 ? 'follow_up_review' : undefined,
2362
+ }),
2363
+ checklistItem({
2364
+ key: 'user_interruption_seen',
2365
+ label: feedbackCounts.userInterruptionCount > 0 ? '看到用户中断' : '未见用户中断',
2366
+ status: feedbackCounts.userInterruptionCount > 0 ? 'failed' : 'passed',
2367
+ contribution: feedbackCounts.userInterruptionCount > 0 ? 'blocking' : 'neutral',
2368
+ reason: feedbackCounts.userInterruptionCount > 0 ? '看到用户中断或停止任务信号。' : '没有看到用户中断信号。',
2369
+ evidenceRefs: feedbackRefs,
2370
+ suggestionKey: feedbackCounts.userInterruptionCount > 0 ? 'user_interruption_review' : undefined,
2371
+ }),
2372
+ ...skillTypeClosureChecklistItems(session, 'user_feeling', episodes),
2373
+ ];
2374
+ }
2375
+ function skillTypeClosureChecklistItems(session, answerKey, episodes) {
2376
+ const runtime = currentSkillRuntimeModel(session, episodes);
2377
+ if (!runtime)
2378
+ return [];
2379
+ if (runtime.skillType === 'workflow_owner')
2380
+ return workflowOwnerClosureChecklistItems(session, runtime, answerKey, episodes);
2381
+ if (runtime.skillType === 'router' || runtime.hasDownstreamEdges)
2382
+ return routerClosureChecklistItems(session, runtime, answerKey, episodes);
2383
+ if (runtime.skillType === 'delegation' || runtime.isDelegator)
2384
+ return delegationClosureChecklistItems(session, runtime, answerKey, episodes);
2385
+ if (runtime.skillType === 'executor')
2386
+ return executorClosureChecklistItems(session, runtime, answerKey);
2387
+ if (runtime.skillType === 'advisory')
2388
+ return advisoryClosureChecklistItems(session, runtime, answerKey);
2389
+ return [];
2390
+ }
2391
+ function currentSkillRuntimeModel(session, episodesOverride) {
2392
+ const episodes = episodesOverride ?? session.sessionStory?.episodes ?? [];
2393
+ const segments = uniqueBy(episodes.flatMap((episode) => episode.skillSegments).filter((segment) => segment.skillName === session.skillName), (segment) => segment.id);
2394
+ if (segments.length === 0)
2395
+ return undefined;
2396
+ const segmentIds = new Set(segments.map((segment) => segment.id));
2397
+ const downstreamEdges = episodes.flatMap((episode) => episode.orchestrationEdges)
2398
+ .filter((edge) => edge.parentSkillSegmentId && segmentIds.has(edge.parentSkillSegmentId));
2399
+ const signals = episodes.flatMap((episode) => episode.feedbackSignals ?? []);
2400
+ const signalsForRole = (role) => signals.filter((signal) => (signal.canonicalAttributions ?? signal.attributions ?? []).some((attribution) => attribution.skillName === session.skillName && attribution.attributionRole === role));
2401
+ const declaredType = segments.map((segment) => segment.skillType).find((type) => type !== 'unknown') ?? 'unknown';
2402
+ const inferredType = declaredType !== 'unknown'
2403
+ ? declaredType
2404
+ : downstreamEdges.length > 0
2405
+ ? 'router'
2406
+ : segments.some((segment) => segment.episodeRole === 'delegator')
2407
+ ? 'delegation'
2408
+ : 'unknown';
2409
+ return {
2410
+ skillType: inferredType,
2411
+ isDelegator: segments.some((segment) => segment.episodeRole === 'delegator'),
2412
+ hasDownstreamEdges: downstreamEdges.length > 0,
2413
+ segments,
2414
+ downstreamEdges,
2415
+ downstreamSignals: signalsForRole('downstream_related'),
2416
+ primarySignals: signalsForRole('primary_fault'),
2417
+ contextSignals: signalsForRole('context_only'),
2418
+ };
2419
+ }
2420
+ function workflowOwnerClosureChecklistItems(session, runtime, answerKey, episodes) {
2421
+ if (answerKey === 'declared_behavior_fit') {
2422
+ const hasStages = runtime.segments.some((segment) => (segment.typeSpecificChecklist ?? []).some((item) => /stage|阶段|workflow/i.test(`${item.key} ${item.label}`)));
2423
+ return [
2424
+ checklistItem({
2425
+ key: 'workflow_owner_stage_matrix_declared',
2426
+ label: hasStages ? '看到阶段矩阵线索' : '未看到阶段矩阵声明',
2427
+ status: hasStages ? 'passed' : 'unknown',
2428
+ contribution: 'attention',
2429
+ reason: hasStages
2430
+ ? '当前 workflow_owner skill 有阶段化检查线索。'
2431
+ : 'workflow_owner 需要声明标准阶段矩阵,才能复盘每个阶段是否闭环。',
2432
+ evidenceRefs: runtime.segments.flatMap((segment) => segment.evidenceRefs),
2433
+ suggestionKey: hasStages ? undefined : 'workflow_owner_stage_matrix_absent',
2434
+ }),
2435
+ ];
2436
+ }
2437
+ if (answerKey === 'goal_satisfaction') {
2438
+ const closure = userFacingClosureForSession(session, episodes);
2439
+ const hasClosure = closure.deliveryCount > 0 || closure.artifactCount > 0;
2440
+ return [
2441
+ checklistItem({
2442
+ key: 'workflow_owner_stage_closure',
2443
+ label: hasClosure ? '看到流程闭环线索' : '未看到流程闭环线索',
2444
+ status: hasClosure ? 'passed' : runtime.primarySignals.length > 0 || runtime.downstreamSignals.length > 0 ? 'failed' : 'unknown',
2445
+ contribution: 'attention',
2446
+ reason: hasClosure
2447
+ ? '看到最终答复或产物线索。'
2448
+ : 'workflow_owner 需要回收各阶段状态,说明哪些阶段完成、失败或跳过。',
2449
+ evidenceRefs: [session.evidenceChain.lastAssistantMessage, ...runtime.primarySignals.map((signal) => signal.evidenceRef), ...runtime.downstreamSignals.map((signal) => signal.evidenceRef)],
2450
+ suggestionKey: hasClosure ? undefined : 'workflow_owner_stage_closure_absent',
2451
+ }),
2452
+ ];
2453
+ }
2454
+ if (answerKey === 'user_feeling') {
2455
+ return downstreamFeedbackRiskChecklistItems(runtime, 'workflow_owner_stage_feedback_seen', '流程阶段里出现用户追问或纠正');
2456
+ }
2457
+ return [];
2458
+ }
2459
+ function routerClosureChecklistItems(session, runtime, answerKey, episodes) {
2460
+ if (answerKey === 'declared_behavior_fit') {
2461
+ return [
2462
+ checklistItem({
2463
+ key: 'router_route_selected',
2464
+ label: runtime.hasDownstreamEdges ? '路由已派发下游' : '未看到下游派发',
2465
+ status: runtime.hasDownstreamEdges ? 'passed' : 'unknown',
2466
+ contribution: 'attention',
2467
+ reason: runtime.hasDownstreamEdges
2468
+ ? '看到当前 skill 和下游执行 skill / child session 的链路。'
2469
+ : '没有看到当前 router skill 把任务派发到下游执行链路。',
2470
+ evidenceRefs: runtime.downstreamEdges.flatMap((edge) => edge.evidenceRefs),
2471
+ suggestionKey: runtime.hasDownstreamEdges ? undefined : 'router_downstream_link_absent',
2472
+ }),
2473
+ checklistItem({
2474
+ key: 'router_goal_preserved',
2475
+ label: '用户目标保真需复核',
2476
+ status: 'unknown',
2477
+ contribution: 'informational',
2478
+ reason: '规则层只能确认发生了派发,child prompt 是否完整保留用户目标需要结合原文或模型识别。',
2479
+ evidenceRefs: [session.evidenceChain.firstUserMessage, ...runtime.downstreamEdges.flatMap((edge) => edge.evidenceRefs)],
2480
+ suggestionKey: 'router_goal_preservation_review',
2481
+ }),
2482
+ ];
2483
+ }
2484
+ if (answerKey === 'goal_satisfaction') {
2485
+ const hasDownstreamRisk = runtime.downstreamSignals.length > 0;
2486
+ const closure = userFacingClosureForSession(session, episodes);
2487
+ const hasClosure = closure.deliveryCount > 0 || closure.artifactCount > 0;
2488
+ return [
2489
+ checklistItem({
2490
+ key: 'router_downstream_completed',
2491
+ label: hasClosure ? '看到用户侧闭环线索' : '未看到用户侧闭环线索',
2492
+ status: hasClosure ? 'passed' : hasDownstreamRisk ? 'failed' : 'unknown',
2493
+ contribution: 'attention',
2494
+ reason: hasClosure
2495
+ ? '看到当前链路里有最终答复或产物线索。'
2496
+ : hasDownstreamRisk
2497
+ ? '下游调用链路出现用户追问 / 纠正 / 中断,但当前路由能力视角没看到清晰闭环。'
2498
+ : '已看到派发,但还不能确认下游是否完成并回传。',
2499
+ evidenceRefs: [
2500
+ session.evidenceChain.lastAssistantMessage,
2501
+ ...runtime.downstreamSignals.map((signal) => signal.evidenceRef),
2502
+ ],
2503
+ suggestionKey: hasClosure ? undefined : 'router_user_facing_closure_absent',
2504
+ }),
2505
+ ];
2506
+ }
2507
+ if (answerKey === 'user_feeling') {
2508
+ return downstreamFeedbackRiskChecklistItems(runtime, 'router_downstream_feedback_seen', '下游调用链路用户有追问');
2509
+ }
2510
+ return [];
2511
+ }
2512
+ function delegationClosureChecklistItems(session, runtime, answerKey, episodes) {
2513
+ if (answerKey === 'declared_behavior_fit') {
2514
+ const hasChild = runtime.hasDownstreamEdges || session.timelineTree?.branches.length;
2515
+ return [
2516
+ checklistItem({
2517
+ key: 'delegation_child_lifecycle_tracked',
2518
+ label: hasChild ? '看到 child / 下游生命周期' : '未看到 child 生命周期',
2519
+ status: hasChild ? 'passed' : 'unknown',
2520
+ contribution: 'attention',
2521
+ reason: hasChild ? '看到 child session、下游 skill 或分支执行线索。' : '没有看到明确 child session 或下游执行线索。',
2522
+ evidenceRefs: runtime.downstreamEdges.flatMap((edge) => edge.evidenceRefs),
2523
+ suggestionKey: hasChild ? undefined : 'delegation_child_lifecycle_absent',
2524
+ }),
2525
+ ];
2526
+ }
2527
+ if (answerKey === 'goal_satisfaction') {
2528
+ const closure = userFacingClosureForSession(session, episodes);
2529
+ const hasResult = closure.deliveryCount > 0 || closure.artifactCount > 0;
2530
+ return [
2531
+ checklistItem({
2532
+ key: 'delegation_result_recovered',
2533
+ label: hasResult ? '已回收结果给用户' : '未看到结果回收',
2534
+ status: hasResult ? 'passed' : runtime.primarySignals.length > 0 ? 'failed' : 'unknown',
2535
+ contribution: 'attention',
2536
+ reason: hasResult ? '看到最终答复或产物线索。' : 'delegation skill 需要把 child 结果回收并告知用户。',
2537
+ evidenceRefs: [session.evidenceChain.lastAssistantMessage, ...runtime.primarySignals.map((signal) => signal.evidenceRef)],
2538
+ suggestionKey: hasResult ? undefined : 'delegation_result_recovery_absent',
2539
+ }),
2540
+ ];
2541
+ }
2542
+ if (answerKey === 'user_feeling') {
2543
+ return downstreamFeedbackRiskChecklistItems(runtime, 'delegation_downstream_feedback_seen', 'child 调用链路用户有反馈');
2544
+ }
2545
+ return [];
2546
+ }
2547
+ function executorClosureChecklistItems(session, _runtime, answerKey) {
2548
+ if (answerKey !== 'goal_satisfaction')
2549
+ return [];
2550
+ const hasExecution = session.indicators.toolCallCount > 0;
2551
+ const hasResult = session.indicators.assistantDeliverySignalCount > 0 || session.indicators.deliverableArtifactSignalCount > 0;
2552
+ return [
2553
+ checklistItem({
2554
+ key: 'executor_execution_to_result',
2555
+ label: hasExecution && hasResult ? '执行后有结果' : hasExecution ? '执行后结果不明确' : '未看到执行证据',
2556
+ status: hasExecution && hasResult ? 'passed' : hasExecution ? 'unknown' : 'failed',
2557
+ contribution: 'attention',
2558
+ reason: hasExecution && hasResult
2559
+ ? '看到工具执行和最终答复 / 产物线索。'
2560
+ : hasExecution
2561
+ ? '看到工具执行,但结果或产物闭环不清晰。'
2562
+ : 'executor 类型 skill 应能看到执行证据。',
2563
+ evidenceRefs: [session.evidenceChain.firstToolUse, session.evidenceChain.lastAssistantMessage],
2564
+ suggestionKey: hasExecution && hasResult ? undefined : 'executor_result_closure_review',
2565
+ }),
2566
+ ];
2567
+ }
2568
+ function advisoryClosureChecklistItems(session, _runtime, answerKey) {
2569
+ if (answerKey !== 'goal_satisfaction')
2570
+ return [];
2571
+ const hasAnswer = session.indicators.assistantDeliverySignalCount > 0;
2572
+ return [
2573
+ checklistItem({
2574
+ key: 'advisory_answer_present',
2575
+ label: hasAnswer ? '已给分析结论' : '未看到分析结论',
2576
+ status: hasAnswer ? 'passed' : 'failed',
2577
+ contribution: 'attention',
2578
+ reason: hasAnswer ? '看到面向用户的分析结论或收尾回复。' : 'advisory 类型 skill 应给出清晰分析结论或阻塞说明。',
2579
+ evidenceRefs: [session.evidenceChain.lastAssistantMessage],
2580
+ suggestionKey: hasAnswer ? undefined : 'advisory_answer_absent',
2581
+ }),
2582
+ ];
2583
+ }
2584
+ function downstreamFeedbackRiskChecklistItems(runtime, key, presentLabel) {
2585
+ const riskSignals = runtime.downstreamSignals.filter((signal) => signal.type === 'follow_up'
2586
+ || signal.type === 'correction'
2587
+ || signal.type === 'frustration'
2588
+ || signal.type === 'interruption');
2589
+ return [
2590
+ checklistItem({
2591
+ key,
2592
+ label: riskSignals.length > 0 ? presentLabel : '未见下游反馈风险',
2593
+ status: riskSignals.length > 0 ? 'failed' : 'passed',
2594
+ contribution: riskSignals.length > 0 ? 'attention' : 'neutral',
2595
+ reason: riskSignals.length > 0
2596
+ ? `看到 ${riskSignals.length} 条下游相关的用户追问、纠正或中断;这不是当前 skill 的硬失败,但需要 owner 看下闭环。`
2597
+ : '没有看到下游相关的用户反馈风险。',
2598
+ evidenceRefs: riskSignals.map((signal) => signal.evidenceRef),
2599
+ suggestionKey: riskSignals.length > 0 ? 'downstream_feedback_review' : undefined,
2600
+ }),
2601
+ ];
2602
+ }
2603
+ export function foldExperienceChecklistItems(items) {
2604
+ const relevant = items.filter((item) => item.contribution !== 'neutral');
2605
+ const active = relevant.length > 0 ? relevant : items;
2606
+ const degraded = active.filter((item) => item.status === 'degraded');
2607
+ if (degraded.length > 0)
2608
+ return { status: 'degraded', reason: 'data_degraded', sourceItemKeys: degraded.map((item) => item.key) };
2609
+ const blockingFailed = active.filter((item) => item.contribution === 'blocking' && item.status === 'failed');
2610
+ if (blockingFailed.length > 0)
2611
+ return { status: 'attention', reason: 'blocking_failed', sourceItemKeys: blockingFailed.map((item) => item.key) };
2612
+ const attentionFailed = active.filter((item) => item.contribution === 'attention' && item.status === 'failed');
2613
+ if (attentionFailed.length > 0)
2614
+ return { status: 'attention', reason: 'attention_accumulated', sourceItemKeys: attentionFailed.map((item) => item.key) };
2615
+ const unknown = active.filter((item) => item.status === 'unknown' || item.status === 'not_declared');
2616
+ const positivePassed = active.filter((item) => item.status === 'passed' && item.contribution === 'positive');
2617
+ const decisivePassed = active.filter((item) => item.status === 'passed' && (item.contribution === 'blocking' || item.contribution === 'attention' || item.contribution === 'positive'));
2618
+ const informationalPassed = active.filter((item) => item.status === 'passed' && item.contribution === 'informational');
2619
+ const passed = [...positivePassed, ...decisivePassed.filter((item) => item.contribution !== 'positive'), ...informationalPassed];
2620
+ if (unknown.length > 0 && (decisivePassed.length === 0 || unknown.length >= passed.length)) {
2621
+ return { status: 'unknown', reason: 'unknown_dominant', sourceItemKeys: unknown.map((item) => item.key) };
2622
+ }
2623
+ if (passed.length > 0)
2624
+ return { status: 'ok', reason: 'all_passed', sourceItemKeys: passed.map((item) => item.key) };
2625
+ return { status: 'not_applicable', reason: 'not_applicable', sourceItemKeys: active.map((item) => item.key) };
2626
+ }
2627
+ function reviewerScopeReasonCodes(session) {
2628
+ const reasons = [];
2629
+ if (session.invocationIds.length !== 1)
2630
+ reasons.push('multiple_invocations');
2631
+ if (session.goalSliceIds.length !== 1)
2632
+ reasons.push('multiple_goal_slices');
2633
+ if ((session.timelineTree?.branches.length ?? 0) > 0)
2634
+ reasons.push('subagent_branches_present');
2635
+ if (session.pluginNames.length > 1 || session.commandNames.length > 1)
2636
+ reasons.push('multiple_skill_entrypoints');
2637
+ return reasons;
2638
+ }
2639
+ function reviewerStep(order, label, text, status, evidenceRefs = []) {
2640
+ return {
2641
+ order,
2642
+ label,
2643
+ status,
2644
+ text,
2645
+ evidenceRefs: uniqueEvidenceRefs(evidenceRefs).slice(0, 4),
2646
+ };
2647
+ }
2648
+ function userGoalStepText(session) {
2649
+ const goal = session.evidenceChain.firstUserMessage?.snippet;
2650
+ if (!goal)
2651
+ return '没有看到明确人工用户目标;当前只能按运行证据做常规复盘。';
2652
+ return `用户目标:${goal}`;
2653
+ }
2654
+ function skillSelectionStepText(session) {
2655
+ const entrypoint = session.commandNames.length > 0 ? `,入口 ${session.commandNames.join('、')}` : session.entrypoint ? `,入口 ${session.entrypoint}` : '';
2656
+ return `本次使用的能力:${session.skillName}${entrypoint}。`;
2657
+ }
2658
+ function executionStepStatus(session, expectedToolCheck) {
2659
+ if (session.indicators.toolFailureCount > 0 || expectedToolCheck.declared && expectedToolCheck.matchedTools.length === 0)
2660
+ return 'attention';
2661
+ if (session.indicators.toolCallCount > 0)
2662
+ return 'ok';
2663
+ return 'unknown';
2664
+ }
2665
+ function executionStepText(session, expectedToolCheck = expectedToolCheckForSession(session)) {
2666
+ const failures = session.indicators.toolFailureCount > 0 ? `,其中失败 ${session.indicators.toolFailureCount} 次` : '';
2667
+ const expected = expectedToolCheck.declared
2668
+ ? expectedToolCheck.matchedTools.length > 0
2669
+ ? `命中声明的核心工具:${expectedToolCheck.matchedTools.join('、')}。`
2670
+ : `但没有命中能力声明的核心工具:${expectedToolCheck.expectedTools.join('、')}。`
2671
+ : '';
2672
+ return `执行中看到 ${session.indicators.toolCallCount} 次工具调用${failures}。${expected}`;
2673
+ }
2674
+ function deliveryStepText(session) {
2675
+ const closure = userFacingClosureForSession(session);
2676
+ if (closure.deliveryCount > 0) {
2677
+ const artifact = closure.artifactCount > 0
2678
+ ? `,其中 ${closure.artifactCount} 次包含具体产物线索`
2679
+ : ',但未看到明确产物线索';
2680
+ return `看到 ${closure.deliveryCount} 次完成态或结果反馈${artifact}。`;
2681
+ }
2682
+ return '没有发现最后结果反馈;当前不能把过程进展当成完成。';
2683
+ }
2684
+ function userFacingClosureForSession(session, episodesOverride) {
2685
+ const runtime = currentSkillRuntimeModel(session, episodesOverride);
2686
+ const canUseDownstream = Boolean(runtime && (runtime.skillType === 'router' || runtime.skillType === 'delegation' || runtime.hasDownstreamEdges || runtime.isDelegator));
2687
+ if (!canUseDownstream) {
2688
+ return {
2689
+ deliveryCount: session.indicators.assistantDeliverySignalCount,
2690
+ artifactCount: session.indicators.deliverableArtifactSignalCount,
2691
+ evidenceRefs: uniqueEvidenceRefs([
2692
+ session.evidenceChain.lastAssistantMessage,
2693
+ ].filter((ref) => Boolean(ref))),
2694
+ };
2695
+ }
2696
+ const primarySourceTrace = primarySourceTraceForSession(session);
2697
+ const finalDeliveryEvents = assistantFinalDeliveryEvents(session)
2698
+ .filter((event) => isMainlineEvidenceRef(event, primarySourceTrace));
2699
+ const episodes = episodesOverride ?? session.sessionStory?.episodes ?? [];
2700
+ const artifacts = episodes
2701
+ .flatMap((episode) => episode.outcome.artifacts ?? [])
2702
+ .filter((artifact) => isMainlineEvidenceRef(artifact.evidenceRef, primarySourceTrace));
2703
+ const deliveryRefs = finalDeliveryEvents.map(evidenceRefFromTimeline);
2704
+ const artifactRefs = artifacts.map((artifact) => artifact.evidenceRef);
2705
+ return {
2706
+ deliveryCount: finalDeliveryEvents.length,
2707
+ artifactCount: artifacts.length,
2708
+ evidenceRefs: uniqueEvidenceRefs([...deliveryRefs, ...artifactRefs, session.evidenceChain.lastAssistantMessage].filter((ref) => Boolean(ref))).slice(0, 5),
2709
+ };
2710
+ }
2711
+ function isMainlineEvidenceRef(ref, primarySourceTrace) {
2712
+ if (!ref || !primarySourceTrace || !ref.sourceTrace)
2713
+ return true;
2714
+ return ref.sourceTrace === primarySourceTrace;
2715
+ }
2716
+ function enrichRouterDownstreamIndicators(session) {
2717
+ const runtime = currentSkillRuntimeModel(session);
2718
+ if (!runtime || !(runtime.skillType === 'router' || runtime.hasDownstreamEdges))
2719
+ return session.indicators;
2720
+ const closure = userFacingClosureForSession(session);
2721
+ const completedByEdge = runtime.downstreamEdges.some((edge) => edge.status === 'completed' || edge.runnerCompletedRef);
2722
+ const failedByEdge = runtime.downstreamEdges.some((edge) => edge.status === 'failed');
2723
+ const completed = closure.deliveryCount > 0 || closure.artifactCount > 0 || completedByEdge;
2724
+ const failed = failedByEdge || (!completed && runtime.downstreamSignals.length > 0);
2725
+ return {
2726
+ ...session.indicators,
2727
+ routerDownstreamCompleted: completed ? 1 : 0,
2728
+ routerDownstreamFailed: failed ? 1 : 0,
2729
+ };
2730
+ }
2731
+ function canonicalFeedbackCountsForSession(session, reviewState) {
2732
+ const signals = session.sessionStory?.episodes?.flatMap((episode) => episode.feedbackSignals ?? []) ?? [];
2733
+ if (signals.length === 0) {
2734
+ return {
2735
+ userFollowUpCount: session.indicators.userFollowUpCount,
2736
+ userCorrectionCount: session.indicators.userCorrectionCount,
2737
+ userInterruptionCount: session.indicators.userInterruptionCount,
2738
+ negativeFeedbackCount: session.indicators.negativeFeedbackCount,
2739
+ positiveFeedbackCount: session.indicators.positiveFeedbackCount,
2740
+ };
2741
+ }
2742
+ const includeDownstream = shouldIncludeDownstreamFeedbackForSession(session);
2743
+ const owned = signals.filter((signal) => (signal.canonicalAttributions ?? signal.attributions ?? []).some((attribution) => attribution.skillName === session.skillName
2744
+ && (attribution.attributionRole === 'primary_fault'
2745
+ || includeDownstream && attribution.attributionRole === 'downstream_related'))
2746
+ && feedbackSignalIsActiveForSession(signal, session, reviewState));
2747
+ return {
2748
+ userFollowUpCount: owned.filter((signal) => signal.type === 'follow_up').length,
2749
+ userCorrectionCount: owned.filter((signal) => signal.type === 'correction').length,
2750
+ userInterruptionCount: owned.filter((signal) => signal.type === 'interruption').length,
2751
+ negativeFeedbackCount: owned.filter((signal) => signal.type === 'frustration').length,
2752
+ positiveFeedbackCount: owned.filter((signal) => signal.type === 'positive').length,
2753
+ };
2754
+ }
2755
+ function feedbackSignalIsActiveForSession(signal, session, reviewState) {
2756
+ const metricKey = metricKeyForFeedbackSignal(signal);
2757
+ if (!metricKey)
2758
+ return true;
2759
+ const verdict = observationMetricAnnotationVerdict(reviewState, { ...signal.evidenceRef, metricScopeId: session.id }, metricKey);
2760
+ if (verdict === 'confirmed')
2761
+ return true;
2762
+ if (verdict === 'rejected')
2763
+ return false;
2764
+ return true;
2765
+ }
2766
+ function metricKeyForFeedbackSignal(signal) {
2767
+ if (signal.type === 'follow_up')
2768
+ return 'user_follow_up';
2769
+ if (signal.type === 'correction')
2770
+ return 'user_correction';
2771
+ if (signal.type === 'interruption')
2772
+ return 'user_interruption';
2773
+ if (signal.type === 'frustration')
2774
+ return 'negative_feedback';
2775
+ if (signal.type === 'positive')
2776
+ return 'positive_feedback';
2777
+ return undefined;
2778
+ }
2779
+ function shouldIncludeDownstreamFeedbackForSession(session) {
2780
+ const runtime = currentSkillRuntimeModel(session);
2781
+ return Boolean(runtime && (runtime.skillType === 'router' || runtime.skillType === 'delegation' || runtime.hasDownstreamEdges || runtime.isDelegator));
2782
+ }
2783
+ function userFeedbackStepText(session, reviewState) {
2784
+ const feedbackCounts = canonicalFeedbackCountsForSession(session, reviewState);
2785
+ const parts = [
2786
+ feedbackCounts.userFollowUpCount > 0 ? `追问/补充 ${feedbackCounts.userFollowUpCount} 次` : '',
2787
+ feedbackCounts.userCorrectionCount > 0 ? `纠正 ${feedbackCounts.userCorrectionCount} 次` : '',
2788
+ feedbackCounts.negativeFeedbackCount > 0 ? `负向反馈 ${feedbackCounts.negativeFeedbackCount} 次` : '',
2789
+ feedbackCounts.positiveFeedbackCount > 0 ? `正向反馈 ${feedbackCounts.positiveFeedbackCount} 次` : '',
2790
+ session.indicators.userGoalShiftCount > 0 ? `目标切换 ${session.indicators.userGoalShiftCount} 次` : '',
2791
+ ].filter(Boolean);
2792
+ return parts.length > 0 ? `用户反馈信号:${parts.join(',')}。` : '原始记录里没有看到人工追问、纠正、负向反馈或目标切换。';
2793
+ }
2794
+ function userFeedbackStepStatus(session, reviewState) {
2795
+ const feedbackCounts = canonicalFeedbackCountsForSession(session, reviewState);
2796
+ if (feedbackCounts.userCorrectionCount > 0 || feedbackCounts.negativeFeedbackCount > 0 || feedbackCounts.userInterruptionCount > 0)
2797
+ return 'attention';
2798
+ if (feedbackCounts.positiveFeedbackCount > 0)
2799
+ return 'ok';
2800
+ return 'unknown';
2801
+ }
2802
+ function userFeedbackEvidenceRefs(session) {
2803
+ const includeDownstream = shouldIncludeDownstreamFeedbackForSession(session);
2804
+ const refs = uniqueEvidenceRefs((session.sessionStory?.episodes ?? []).flatMap((episode) => (episode.feedbackSignals ?? []).filter((signal) => (signal.canonicalAttributions ?? signal.attributions ?? []).some((attribution) => attribution.skillName === session.skillName
2805
+ && (attribution.attributionRole === 'primary_fault'
2806
+ || includeDownstream && attribution.attributionRole === 'downstream_related'))).map((signal) => signal.evidenceRef)));
2807
+ if (refs.length > 0)
2808
+ return refs.slice(0, 5);
2809
+ return uniqueEvidenceRefs(session.ruleFindings
2810
+ .filter((finding) => finding.code === 'user_correction_seen' || finding.code === 'negative_feedback_seen' || finding.code === 'positive_feedback_seen' || finding.code === 'user_goal_shift_seen' || finding.code === 'user_interruption_seen')
2811
+ .flatMap((finding) => finding.evidenceRefs)).slice(0, 5);
2812
+ }
2813
+ function expectedToolCheckForSession(session) {
2814
+ const expectedTools = loadExpectedToolsForSkill(session.skillName, session.cwd);
2815
+ if (expectedTools.length === 0)
2816
+ return { expectedTools: [], matchedTools: [], declared: false };
2817
+ const events = session.fullSessionTimeline.length > 0 ? session.fullSessionTimeline : session.timelinePreview;
2818
+ const matchedTools = expectedTools.filter((tool) => events.some((event) => eventMatchesExpectedTool(event, tool)));
2819
+ return {
2820
+ expectedTools,
2821
+ matchedTools,
2822
+ declared: true,
2823
+ };
2824
+ }
2825
+ function eventMatchesExpectedTool(event, expectedTool) {
2826
+ if (event.kind !== 'tool_use')
2827
+ return false;
2828
+ const text = `${event.toolName ?? ''}\n${event.label ?? ''}\n${event.snippet ?? ''}\n${event.fullText ?? ''}`.toLowerCase();
2829
+ const normalized = expectedTool.toLowerCase().trim();
2830
+ const aliases = unique([
2831
+ normalized,
2832
+ normalized.replace(/[-_]?cli$/, ''),
2833
+ ].filter(Boolean));
2834
+ return aliases.some((alias) => new RegExp(`(^|[^a-z0-9_-])${escapeRegExp(alias)}([^a-z0-9_-]|$)`, 'i').test(text));
2835
+ }
2836
+ function escapeRegExp(value) {
2837
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2838
+ }
2839
+ function reviewerFindingsForSession(session, reviewState) {
2840
+ const findings = [];
2841
+ const push = (level, title, body, ruleSource, evidenceRefs = []) => {
2842
+ const id = hashParts('reviewer-finding', session.id, ruleSource, title);
2843
+ const judgmentId = hashParts('reviewer-judgment', session.id, ruleSource, title, evidenceRefs.map((ref) => ref.id).join('|'));
2844
+ const reviewEntry = reviewState?.entries[observationReviewStateKey('reviewer_judgment', judgmentId)];
2845
+ findings.push({
2846
+ id,
2847
+ judgmentId,
2848
+ source: 'deterministic_rule',
2849
+ level,
2850
+ title,
2851
+ body,
2852
+ ruleSource,
2853
+ ruleVersion: REVIEWER_REPORT_RULE_VERSION,
2854
+ evidenceRefs: uniqueEvidenceRefs(evidenceRefs).slice(0, 5),
2855
+ reviewStateRef: {
2856
+ targetType: 'reviewer_judgment',
2857
+ targetId: judgmentId,
2858
+ ...(reviewEntry?.verdict ? { verdict: reviewEntry.verdict } : {}),
2859
+ ...(reviewEntry?.reason ? { reason: reviewEntry.reason } : {}),
2860
+ ...(reviewEntry?.note ? { note: reviewEntry.note } : {}),
2861
+ ...(reviewEntry?.reviewedAt ? { reviewedAt: reviewEntry.reviewedAt } : {}),
2862
+ },
2863
+ });
2864
+ };
2865
+ const findingRefs = (code) => session.ruleFindings.filter((finding) => finding.code === code).flatMap((finding) => finding.evidenceRefs);
2866
+ const expectedToolCheck = expectedToolCheckForSession(session);
2867
+ if (session.indicators.toolFailureCount > 0) {
2868
+ push('attention', `工具调用失败 ${session.indicators.toolFailureCount} 次`, '执行中遇到工具报错。看下失败的步骤是否在 SKILL.md 里写明了重试或回退方式。', 'tool_error_recovery', findingRefs('tool_failure_seen'));
2869
+ }
2870
+ const closure = userFacingClosureForSession(session);
2871
+ const runtime = currentSkillRuntimeModel(session);
2872
+ if (closure.deliveryCount === 0) {
2873
+ const isUpstreamOrchestration = Boolean(runtime && (runtime.skillType === 'router' || runtime.skillType === 'delegation' || runtime.hasDownstreamEdges || runtime.isDelegator));
2874
+ push('attention', isUpstreamOrchestration ? '下游结果没有回传给用户' : '没看到给用户的最终答复', isUpstreamOrchestration
2875
+ ? '这个 skill 已经把任务派发到下游,但没有看到下游结果被清楚回传给用户。需要确认 child 是否完成、结果是否匹配原目标、是否主动通知用户。'
2876
+ : 'assistant 没说「完成 / 结果如下」这种收尾,可能任务还没跑完,或收尾文案不够清楚让用户知道事情结束了。', isUpstreamOrchestration ? 'router_user_facing_closure_absent' : 'final_delivery_absent', closure.evidenceRefs.length > 0 ? closure.evidenceRefs : session.evidenceChain.lastAssistantMessage ? [session.evidenceChain.lastAssistantMessage] : []);
2877
+ }
2878
+ if (session.indicators.sessionInterruptedCount > 0) {
2879
+ push('attention', `会话异常断开 ${session.indicators.sessionInterruptedCount} 次`, '任务中途被异常中断或重启。如果是网络/超时,看是否要在 skill 里加重试;如果是程序原因,跟开发反馈。', 'session_interrupted', findingRefs('session_interrupted_seen'));
2880
+ }
2881
+ if (expectedToolCheck.declared && expectedToolCheck.matchedTools.length === 0) {
2882
+ push('attention', '没用上 SKILL.md 声明的核心工具', `SKILL.md 里声明 ${expectedToolCheck.expectedTools.join('、')} 是核心工具,但这次没看到调用。要么 description 指引不够清楚,要么用户的诉求不属于这个 skill 的场景。`, 'expected_tools_missed', session.evidenceChain.firstToolUse ? [session.evidenceChain.firstToolUse] : []);
2883
+ }
2884
+ if (session.indicators.userCorrectionCount > 0) {
2885
+ push('attention', `用户纠正 ${session.indicators.userCorrectionCount} 次`, '用户在过程中纠正了方向。看原文确认是 skill 理解偏差,还是 skill 不该处理这种诉求。', 'user_correction', findingRefs('user_correction_seen'));
2886
+ }
2887
+ if (session.indicators.userInterruptionCount > 0) {
2888
+ push('attention', `用户手动叫停 ${session.indicators.userInterruptionCount} 次`, '用户主动喊停了执行。常见原因:跑偏 / 太慢 / 用错工具。看原文定位是哪一步触发的。', 'user_interruption', findingRefs('user_interruption_seen'));
2889
+ }
2890
+ if (session.indicators.negativeFeedbackCount > 0) {
2891
+ push('attention', `用户说了 ${session.indicators.negativeFeedbackCount} 次不满意`, '用户出现了「不对 / 错了 / 不行」等负向表达。先看是 skill 给的结果不达预期,还是用户对方向本身有疑问。', 'negative_feedback', findingRefs('negative_feedback_seen'));
2892
+ }
2893
+ if (session.indicators.hardRuleTextHitCount > 0) {
2894
+ push('note', `用户提了 ${session.indicators.hardRuleTextHitCount} 次硬性要求`, '用户在对话里强调了某些必须做/不能做的规则。如果同类要求反复出现,可以沉淀到 SKILL.md 的 hardRules。', 'user_hard_rule', findingRefs('hard_rule_seen'));
2895
+ }
2896
+ if (reviewerScopeReasonCodes(session).length > 0) {
2897
+ push('note', '复杂链路降级展示', '本次不是严格的 1 次会话 × 1 个目标 × 1 个能力场景。当前先做通用展示,不强行拆分多能力、子任务或目标切换。', 'complex_scope_degraded', []);
2898
+ }
2899
+ if (findings.length === 0) {
2900
+ push('note', '未命中优先问题信号', '没有看到需要优先关注的纠正、中断、负向反馈、工具失败或没收尾的信号。', 'no_priority_signal', []);
2901
+ }
2902
+ return findings;
2903
+ }
2904
+ function reviewerTitle(session, attentionCount, possibleFalsePositiveCount) {
2905
+ const suffix = possibleFalsePositiveCount > 0 ? ` · ${possibleFalsePositiveCount} 项疑似误判` : '';
2906
+ if (attentionCount > 0)
2907
+ return `${session.skillName} · ${attentionCount} 项要看一眼${suffix}`;
2908
+ if (session.indicators.assistantDeliverySignalCount > 0)
2909
+ return `${session.skillName} · 看起来有结果 · 常规抽样${suffix}`;
2910
+ return `${session.skillName} · 常规抽样 · 未见高优先级信号${suffix}`;
2911
+ }
2912
+ function reviewerSummary(session, scopeKind, attentionCount, possibleFalsePositiveCount) {
2913
+ const scopeText = scopeKind === 'single_skill_single_goal'
2914
+ ? '本次属于单个能力 / 单个目标报告范围。'
2915
+ : '本次是复杂链路,当前先做降级展示,不强行拆分语义分支。';
2916
+ const reviewText = attentionCount > 0
2917
+ ? `发现 ${attentionCount} 条事实层复核点。`
2918
+ : '没有发现优先级较高的事实层复核点。';
2919
+ const falsePositiveText = possibleFalsePositiveCount > 0 ? `另有 ${possibleFalsePositiveCount} 条疑似误判需要人工确认。` : '';
2920
+ return [scopeText, reviewText, falsePositiveText].filter(Boolean).join(' ');
2921
+ }
2922
+ function reviewerAuthorSuggestions(session, findings) {
2923
+ const suggestions = new Map();
2924
+ const pushSuggestion = (key, text, severity) => {
2925
+ const existing = suggestions.get(key);
2926
+ if (!existing || severity > existing.severity)
2927
+ suggestions.set(key, { text, severity });
2928
+ };
2929
+ for (const answer of session.sessionStory?.answers ?? []) {
2930
+ for (const item of answer.checklistItems ?? []) {
2931
+ if (!item.suggestionKey)
2932
+ continue;
2933
+ const text = suggestionTextForChecklistItem(item.suggestionKey);
2934
+ if (!text)
2935
+ continue;
2936
+ pushSuggestion(item.suggestionKey, text, severityForChecklistStatus(item.status));
2937
+ }
2938
+ }
2939
+ if (findings.some((finding) => finding.ruleSource === 'router_user_facing_closure_absent')) {
2940
+ pushSuggestion('router_user_facing_closure_absent', '补充下游结果回传和异步闭环规范,避免路由能力只负责启动、不负责结果回收。', 4);
2941
+ }
2942
+ else if (findings.some((finding) => finding.ruleSource === 'final_delivery_absent')) {
2943
+ pushSuggestion('final_delivery_absent', '补充明确的产物交付表达或交付标记,避免过程进展被当成完成。', 4);
2944
+ }
2945
+ if (findings.some((finding) => finding.ruleSource === 'tool_error_recovery')) {
2946
+ pushSuggestion('tool_error_recovery', '复查失败工具调用前后的执行流程,必要时把稳定路径写入能力说明文档。', 4);
2947
+ }
2948
+ if (findings.some((finding) => finding.ruleSource === 'session_interrupted')) {
2949
+ pushSuggestion('session_interrupted', '复查会话异常中断前后的上下文,确认是否需要补充中断恢复或重跑策略。', 4);
2950
+ }
2951
+ if (findings.some((finding) => finding.ruleSource === 'expected_tools_missed')) {
2952
+ pushSuggestion('expected_tools_missed', '如果能力依赖核心工具,请在能力定义里维护 expected_tools,并确认运行链路实际命中这些工具。', 4);
2953
+ }
2954
+ if (session.indicators.hardRuleTextHitCount > 0) {
2955
+ pushSuggestion('user_hard_rule', '把反复出现的用户硬性要求沉淀为能力规则,并在后续观测中追踪是否减少纠偏。', 2);
2956
+ }
2957
+ if (session.indicators.userCorrectionCount > 0 || session.indicators.negativeFeedbackCount > 0) {
2958
+ pushSuggestion('user_negative_review', '优先打开原始片段,确认用户纠正/负向反馈发生在交付前还是交付后。', 4);
2959
+ }
2960
+ if (reviewerScopeReasonCodes(session).length > 0) {
2961
+ pushSuggestion('complex_scope_review', '复杂链路暂按降级报告处理;后续再拆多能力、子任务或目标切换。', 1);
2962
+ }
2963
+ if (suggestions.size === 0)
2964
+ pushSuggestion('routine_sample', '进入常规抽样池,保留 evidenceRef 以便人工抽查。', 0);
2965
+ return Array.from(suggestions.values())
2966
+ .sort((a, b) => b.severity - a.severity || a.text.localeCompare(b.text))
2967
+ .map((entry) => entry.text);
2968
+ }
2969
+ function severityForChecklistStatus(status) {
2970
+ if (status === 'degraded')
2971
+ return 5;
2972
+ if (status === 'failed')
2973
+ return 4;
2974
+ if (status === 'unknown')
2975
+ return 3;
2976
+ if (status === 'not_declared')
2977
+ return 2;
2978
+ if (status === 'passed')
2979
+ return 1;
2980
+ return 0;
2981
+ }
2982
+ function suggestionTextForChecklistItem(key) {
2983
+ const suggestions = {
2984
+ final_delivery_absent: '在最后回复里加上「已完成 / 结果如下」之类的明确收尾,让用户知道任务跑完了。',
2985
+ router_user_facing_closure_absent: '在路由 / 调度能力里写清楚:下游完成后必须回收结果并同步给用户;如果未完成,要说明当前状态和下一步。',
2986
+ artifact_absent: '如果 skill 应该产出文档、demo、代码或报告,最终回复里要附上文件路径、链接或代码块。',
2987
+ goal_shift_review: '用户中途切了目标,后续追问不属于这个 skill。看下是否要在 description 里说清楚 skill 的边界。',
2988
+ user_negative_or_interrupted: '用户出现了不满 / 纠正 / 叫停。先看原文是哪一步触发的,再决定改 description、补标准流程还是补硬性规则。',
2989
+ workflow_not_declared: '在 SKILL.md 里补一个标准流程声明,把这个 skill 的执行步骤写清楚。否则报告只能猜流程是否完整。',
2990
+ workflow_execution_review: '声明了标准流程但执行证据不够。补一下每个步骤的输出形态,让运行时能验证是否真的跑过。',
2991
+ hardrule_not_declared: '在 SKILL.md 里补硬性规则声明,把那些「必须做 / 不能做」的约束写明。',
2992
+ hardrule_execution_review: '声明了硬性规则但执行证据不够。补一下每条规则的触发场景,让运行时能验证。',
2993
+ expected_tools_not_declared: '在 SKILL.md frontmatter 里声明 expected_tools。否则报告分不出「真用上了 skill 工具」还是「只是随便调了个工具」。',
2994
+ expected_tools_missed: '声明了核心工具但没用上。先确认 description 是否清楚指引到这些工具,或者用户的诉求不属于这个 skill。',
2995
+ negative_feedback_review: '打开用户负向反馈的原文,看问题出在理解目标、执行过程还是最后没收尾。',
2996
+ user_correction_review: '用户纠正了多次。把纠正内容沉淀到 SKILL.md 的标准流程或硬性规则,避免下次同类返工。',
2997
+ follow_up_review: '用户追问比较多。看是围绕产物继续推进(好事),还是因为没拿到结果而反复问(要改)。',
2998
+ user_interruption_review: '用户叫停了执行。看下断的那一步是不是 skill 没声明标准流程导致跑偏。',
2999
+ downstream_feedback_review: '这次任务的下游执行链路被用户追问、纠正或中断。路由 / 调度类 skill 需要把下游状态、结果回收和异常通知写清楚,避免只负责启动、不负责闭环。',
3000
+ };
3001
+ return suggestions[key];
3002
+ }
3003
+ function sumTokenUsage(invocations) {
3004
+ return invocations.reduce((sum, invocation) => ({
3005
+ inputTokens: sum.inputTokens + (invocation.metrics.inputTokens ?? 0),
3006
+ outputTokens: sum.outputTokens + (invocation.metrics.outputTokens ?? 0),
3007
+ cacheReadTokens: sum.cacheReadTokens + (invocation.metrics.cacheReadTokens ?? 0),
3008
+ cacheCreationTokens: sum.cacheCreationTokens + (invocation.metrics.cacheCreationTokens ?? 0),
3009
+ }), {
3010
+ inputTokens: 0,
3011
+ outputTokens: 0,
3012
+ cacheReadTokens: 0,
3013
+ cacheCreationTokens: 0,
3014
+ });
3015
+ }
3016
+ function summarizeExperienceSkills(sessions, invocations) {
3017
+ const bySkill = new Map();
3018
+ for (const session of sessions) {
3019
+ const group = bySkill.get(session.skillName) ?? [];
3020
+ group.push(session);
3021
+ bySkill.set(session.skillName, group);
3022
+ }
3023
+ const invocationCountBySkill = invocations.reduce((acc, invocation) => {
3024
+ acc[invocation.skillName] = (acc[invocation.skillName] ?? 0) + 1;
3025
+ return acc;
3026
+ }, {});
3027
+ const invocationGroupBySkill = invocations.reduce((acc, invocation) => {
3028
+ const group = acc.get(invocation.skillName) ?? [];
3029
+ group.push(invocation);
3030
+ acc.set(invocation.skillName, group);
3031
+ return acc;
3032
+ }, new Map());
3033
+ return Array.from(bySkill.entries()).map(([skillName, group]) => {
3034
+ const first = group[0];
3035
+ const skillInvocations = invocationGroupBySkill.get(skillName) ?? [];
3036
+ const indicators = sumIndicators(group.map((session) => session.indicators));
3037
+ const evidenceChain = sumEvidenceChains(group.map((session) => session.evidenceChain));
3038
+ const ruleFindings = mergeRuleFindings(group.flatMap((session) => session.ruleFindings));
3039
+ const problemPatterns = mergeExperienceProblemPatterns(group.flatMap((session) => session.problemPatterns));
3040
+ return {
3041
+ skillName,
3042
+ invocationCount: invocationCountBySkill[skillName] ?? 0,
3043
+ sessionCount: group.length,
3044
+ sourceKinds: unique(group.map((session) => session.sourceKind)).sort(),
3045
+ entrypoints: unique(group.map((session) => session.entrypoint).filter((value) => Boolean(value))).sort(),
3046
+ entrypointCounts: countBy(skillInvocations.map((invocation) => invocation.entrypoint ?? invocation.sourceKind ?? 'unknown')),
3047
+ sourceMetadataCounts: summarizeSourceMetadataCounts(skillInvocations.map((invocation) => invocation.sourceMetadata)),
3048
+ attributionCounts: countBy(skillInvocations.map((invocation) => invocation.attribution.source || 'unknown')),
3049
+ pluginNames: unique(skillInvocations.map((invocation) => invocation.attribution.pluginName).filter((value) => Boolean(value))).sort(),
3050
+ rawSkillRefs: unique(skillInvocations.map((invocation) => invocation.attribution.rawSkillRef).filter((value) => Boolean(value))).sort(),
3051
+ commandNames: unique(skillInvocations.map((invocation) => invocation.attribution.commandName).filter((value) => Boolean(value))).sort(),
3052
+ toolCounts: sumRecordCounts(skillInvocations.map((invocation) => invocation.toolCounts)),
3053
+ firstSeen: group.reduce((min, session) => session.startTimestamp < min ? session.startTimestamp : min, first.startTimestamp),
3054
+ lastSeen: group.reduce((max, session) => session.endTimestamp > max ? session.endTimestamp : max, first.endTimestamp),
3055
+ reviewFirstSessionCount: group.filter((session) => session.reviewPriority === 'review_first').length,
3056
+ sampleReviewSessionCount: group.filter((session) => session.reviewPriority === 'sample_review').length,
3057
+ indicators,
3058
+ evidenceChain,
3059
+ ruleFindings,
3060
+ assistiveInference: assistiveInferenceForEvidence(indicators, evidenceChain, ruleFindings),
3061
+ problemPatterns,
3062
+ relatedObservationIds: unique(group.flatMap((session) => session.relatedObservationIds)),
3063
+ };
3064
+ }).sort((a, b) => {
3065
+ const aScore = a.reviewFirstSessionCount * 100 + a.sampleReviewSessionCount * 10 + a.indicators.highObservationCount;
3066
+ const bScore = b.reviewFirstSessionCount * 100 + b.sampleReviewSessionCount * 10 + b.indicators.highObservationCount;
3067
+ if (bScore !== aScore)
3068
+ return bScore - aScore;
3069
+ return b.invocationCount - a.invocationCount;
3070
+ });
3071
+ }
3072
+ function mergeSourceMetadata(values) {
3073
+ const channels = unique(values.map((value) => value?.channel).filter((value) => Boolean(value)));
3074
+ const senders = unique(values.map((value) => value?.sender).filter((value) => Boolean(value)));
3075
+ const senderIds = unique(values.map((value) => value?.senderId).filter((value) => Boolean(value)));
3076
+ const providers = unique(values.map((value) => value?.provider).filter((value) => Boolean(value)));
3077
+ const models = unique(values.map((value) => value?.model).filter((value) => Boolean(value)));
3078
+ const modelApis = unique(values.map((value) => value?.modelApi).filter((value) => Boolean(value)));
3079
+ const businessActions = unique(values.flatMap((value) => sourceBusinessActions(value)));
3080
+ const merged = {};
3081
+ if (channels.length > 0)
3082
+ merged.channel = channels.join(', ');
3083
+ if (senders.length > 0)
3084
+ merged.sender = senders.join(', ');
3085
+ if (senderIds.length > 0)
3086
+ merged.senderId = senderIds.join(', ');
3087
+ if (providers.length > 0)
3088
+ merged.provider = providers.join(', ');
3089
+ if (models.length > 0)
3090
+ merged.model = models.join(', ');
3091
+ if (modelApis.length > 0)
3092
+ merged.modelApi = modelApis.join(', ');
3093
+ if (businessActions.length > 0)
3094
+ merged.businessActions = businessActions.sort();
3095
+ return Object.keys(merged).length > 0 ? merged : undefined;
3096
+ }
3097
+ function summarizeSourceMetadataCounts(values) {
3098
+ return {
3099
+ channels: countBy(values.map((value) => value?.channel).filter((value) => Boolean(value))),
3100
+ senders: countBy(values.map((value) => sourceSenderLabel(value)).filter((value) => Boolean(value))),
3101
+ businessActions: countBy(values.flatMap((value) => sourceBusinessActions(value))),
3102
+ providers: countBy(values.map((value) => value?.provider).filter((value) => Boolean(value))),
3103
+ models: countBy(values.map((value) => value?.model).filter((value) => Boolean(value))),
3104
+ };
3105
+ }
3106
+ function sourceBusinessActions(value) {
3107
+ if (!value)
3108
+ return [];
3109
+ const legacyKey = ['ai', 'maCommands'].join('');
3110
+ const legacy = value[legacyKey];
3111
+ if (Array.isArray(legacy))
3112
+ return unique([...(value.businessActions ?? []), ...legacy.filter((item) => typeof item === 'string')]);
3113
+ return value.businessActions ?? [];
3114
+ }
3115
+ function legacyBusinessActionSource() {
3116
+ return ['ai', 'ma-cmd'].join('');
3117
+ }
3118
+ function sourceSenderLabel(value) {
3119
+ if (!value?.sender && !value?.senderId)
3120
+ return undefined;
3121
+ if (value.sender && value.senderId)
3122
+ return `${value.sender}(${value.senderId})`;
3123
+ return value.sender ?? value.senderId;
3124
+ }
3125
+ function scoreForIndicators(indicators) {
3126
+ return indicators.highObservationCount * 3
3127
+ + indicators.mediumObservationCount
3128
+ + indicators.userCorrectionCount * 2
3129
+ + indicators.userInterruptionCount * 2
3130
+ + indicators.sessionInterruptedCount * 2
3131
+ + indicators.negativeFeedbackCount * 2
3132
+ + indicators.hardRuleTextHitCount
3133
+ + indicators.toolFailureCount
3134
+ + indicators.routerDownstreamFailed * 2
3135
+ + indicators.hedgingCount
3136
+ + indicators.explicitMarkerCount * 2;
3137
+ }
3138
+ function priorityForScore(score) {
3139
+ if (score >= 3)
3140
+ return 'review_first';
3141
+ if (score > 0)
3142
+ return 'sample_review';
3143
+ return 'routine_sample';
3144
+ }
3145
+ function priorityForReviewerFindings(session, findings) {
3146
+ const fallback = priorityForScore(session.reviewPriorityScore);
3147
+ const attentionFindings = findings.filter((finding) => finding.level === 'attention');
3148
+ if (attentionFindings.length === 0)
3149
+ return fallback;
3150
+ const criticalMissing = attentionFindings.some((finding) => finding.ruleSource === 'final_delivery_absent'
3151
+ || finding.ruleSource === 'router_user_facing_closure_absent'
3152
+ || finding.ruleSource === 'session_interrupted'
3153
+ || finding.ruleSource === 'expected_tools_missed')
3154
+ || session.indicators.userMessageCount === 0
3155
+ || session.indicators.toolCallCount === 0;
3156
+ if (criticalMissing)
3157
+ return 'review_first';
3158
+ return fallback === 'routine_sample' ? 'sample_review' : fallback;
3159
+ }
3160
+ function basisCodesForIndicators(indicators) {
3161
+ const codes = [];
3162
+ if (indicators.highObservationCount > 0)
3163
+ codes.push('has_high_observation');
3164
+ if (indicators.mediumObservationCount > 0)
3165
+ codes.push('has_medium_observation');
3166
+ if (indicators.userCorrectionCount > 0)
3167
+ codes.push('user_correction');
3168
+ if (indicators.userInterruptionCount > 0)
3169
+ codes.push('user_interruption');
3170
+ if (indicators.sessionInterruptedCount > 0)
3171
+ codes.push('session_interrupted');
3172
+ if (indicators.negativeFeedbackCount > 0)
3173
+ codes.push('negative_feedback');
3174
+ if (indicators.hardRuleTextHitCount > 0)
3175
+ codes.push('hard_rule_text_hit');
3176
+ if (indicators.toolFailureCount > 0)
3177
+ codes.push('tool_failure');
3178
+ if (indicators.hedgingCount > 0)
3179
+ codes.push('hedging_signal');
3180
+ if (indicators.explicitMarkerCount > 0)
3181
+ codes.push('explicit_marker');
3182
+ return codes;
3183
+ }
3184
+ function countTools(segment) {
3185
+ const counts = {};
3186
+ for (const toolCall of segment.toolCalls) {
3187
+ counts[toolCall.tool] = (counts[toolCall.tool] ?? 0) + 1;
3188
+ }
3189
+ return counts;
3190
+ }
3191
+ function countBy(values) {
3192
+ const counts = {};
3193
+ for (const value of values) {
3194
+ counts[value] = (counts[value] ?? 0) + 1;
3195
+ }
3196
+ return counts;
3197
+ }
3198
+ function sumRecordCounts(values) {
3199
+ const counts = {};
3200
+ for (const value of values) {
3201
+ for (const [key, count] of Object.entries(value)) {
3202
+ counts[key] = (counts[key] ?? 0) + count;
3203
+ }
3204
+ }
3205
+ return counts;
3206
+ }
3207
+ function sumIndicators(values) {
3208
+ return values.reduce((acc, value) => ({
3209
+ userMessageCount: acc.userMessageCount + value.userMessageCount,
3210
+ userFollowUpCount: acc.userFollowUpCount + value.userFollowUpCount,
3211
+ userCorrectionCount: acc.userCorrectionCount + value.userCorrectionCount,
3212
+ userInterruptionCount: acc.userInterruptionCount + value.userInterruptionCount,
3213
+ sessionInterruptedCount: acc.sessionInterruptedCount + (value.sessionInterruptedCount ?? 0),
3214
+ negativeFeedbackCount: acc.negativeFeedbackCount + (value.negativeFeedbackCount ?? 0),
3215
+ positiveFeedbackCount: acc.positiveFeedbackCount + (value.positiveFeedbackCount ?? 0),
3216
+ userGoalShiftCount: acc.userGoalShiftCount + (value.userGoalShiftCount ?? 0),
3217
+ hardRuleTextHitCount: acc.hardRuleTextHitCount + value.hardRuleTextHitCount,
3218
+ assistantDeliverySignalCount: acc.assistantDeliverySignalCount + (value.assistantDeliverySignalCount ?? 0),
3219
+ deliverableArtifactSignalCount: acc.deliverableArtifactSignalCount + (value.deliverableArtifactSignalCount ?? 0),
3220
+ routerDownstreamCompleted: acc.routerDownstreamCompleted + (value.routerDownstreamCompleted ?? 0),
3221
+ routerDownstreamFailed: acc.routerDownstreamFailed + (value.routerDownstreamFailed ?? 0),
3222
+ selfCorrectionCount: acc.selfCorrectionCount + (value.selfCorrectionCount ?? 0),
3223
+ repeatedExecutionCount: acc.repeatedExecutionCount + (value.repeatedExecutionCount ?? 0),
3224
+ toolCallCount: acc.toolCallCount + value.toolCallCount,
3225
+ toolFailureCount: acc.toolFailureCount + value.toolFailureCount,
3226
+ highObservationCount: acc.highObservationCount + value.highObservationCount,
3227
+ mediumObservationCount: acc.mediumObservationCount + value.mediumObservationCount,
3228
+ hedgingCount: acc.hedgingCount + value.hedgingCount,
3229
+ explicitMarkerCount: acc.explicitMarkerCount + value.explicitMarkerCount,
3230
+ }), { ...ZERO_INDICATORS });
3231
+ }
3232
+ function sumEvidenceChains(values) {
3233
+ return values.reduce((acc, value) => ({
3234
+ userMessageCount: acc.userMessageCount + (value?.userMessageCount ?? 0),
3235
+ runtimeContextCount: acc.runtimeContextCount + (value?.runtimeContextCount ?? 0),
3236
+ skillContextCount: acc.skillContextCount + (value?.skillContextCount ?? 0),
3237
+ assistantMessageCount: acc.assistantMessageCount + (value?.assistantMessageCount ?? 0),
3238
+ toolUseCount: acc.toolUseCount + (value?.toolUseCount ?? 0),
3239
+ toolResultCount: acc.toolResultCount + (value?.toolResultCount ?? 0),
3240
+ toolFailureResultCount: acc.toolFailureResultCount + (value?.toolFailureResultCount ?? 0),
3241
+ observationCount: acc.observationCount + (value?.observationCount ?? 0),
3242
+ firstUserMessage: acc.firstUserMessage ?? value?.firstUserMessage,
3243
+ firstRuntimeContext: acc.firstRuntimeContext ?? value?.firstRuntimeContext,
3244
+ firstSkillContext: acc.firstSkillContext ?? value?.firstSkillContext,
3245
+ firstToolUse: acc.firstToolUse ?? value?.firstToolUse,
3246
+ firstToolFailure: acc.firstToolFailure ?? value?.firstToolFailure,
3247
+ lastAssistantMessage: value?.lastAssistantMessage ?? acc.lastAssistantMessage,
3248
+ }), {
3249
+ userMessageCount: 0,
3250
+ runtimeContextCount: 0,
3251
+ skillContextCount: 0,
3252
+ assistantMessageCount: 0,
3253
+ toolUseCount: 0,
3254
+ toolResultCount: 0,
3255
+ toolFailureResultCount: 0,
3256
+ observationCount: 0,
3257
+ });
3258
+ }
3259
+ function mergeRuleFindings(values) {
3260
+ const byCode = new Map();
3261
+ for (const value of values) {
3262
+ const existing = byCode.get(value.code);
3263
+ if (existing) {
3264
+ existing.count += value.count;
3265
+ existing.evidenceRefs = uniqueEvidenceRefs([...existing.evidenceRefs, ...value.evidenceRefs]).slice(0, 5);
3266
+ }
3267
+ else {
3268
+ byCode.set(value.code, { ...value, evidenceRefs: uniqueEvidenceRefs(value.evidenceRefs).slice(0, 5) });
3269
+ }
3270
+ }
3271
+ return Array.from(byCode.values()).sort((a, b) => {
3272
+ const rank = { attention: 0, sample: 1, normal: 2 };
3273
+ if (rank[a.level] !== rank[b.level])
3274
+ return rank[a.level] - rank[b.level];
3275
+ return b.count - a.count;
3276
+ });
3277
+ }
3278
+ function uniqueTimelineEvents(events) {
3279
+ const byId = new Map();
3280
+ for (const event of events) {
3281
+ byId.set(event.id, event);
3282
+ }
3283
+ return Array.from(byId.values());
3284
+ }
3285
+ function compareTimelineEvents(a, b) {
3286
+ const ta = a.timestamp;
3287
+ const tb = b.timestamp;
3288
+ // 双方都有非空 timestamp 且不同 → 按时间穿插(主线 + subagent 真实交互序)
3289
+ if (ta && tb && ta !== tb) {
3290
+ return ta.localeCompare(tb);
3291
+ }
3292
+ // 同一条 trace 内 → 按 messageIndex 派生的 order(跨 trace 比 order 没意义)
3293
+ if (a.sourceTrace === b.sourceTrace) {
3294
+ return a.order - b.order;
3295
+ }
3296
+ // 跨 trace 且 timestamp 不可比 → 主线优先(避免缺 timestamp 时 subagent 顶到最前)
3297
+ const roleRank = (event) => event.traceRole === 'main' || event.traceRole === 'standalone' ? 0 : 1;
3298
+ const rankDiff = roleRank(a) - roleRank(b);
3299
+ if (rankDiff !== 0)
3300
+ return rankDiff;
3301
+ // 同 traceRole 跨文件兜底:sourceTrace 字典序保证稳定
3302
+ return a.sourceTrace.localeCompare(b.sourceTrace);
3303
+ }
3304
+ function buildSessionTimelineTree(sessionId, sessions) {
3305
+ const mainSession = sessions.find((session) => session.traceRole === 'main')
3306
+ ?? sessions.find((session) => session.traceRole === 'standalone');
3307
+ const main = mainSession ? buildTimelineWindow(mainSession, 0, mainSession.records.length) : [];
3308
+ const branches = sessions
3309
+ .filter((session) => !mainSession || session !== mainSession)
3310
+ .map((session) => {
3311
+ const events = buildTimelineWindow(session, 0, session.records.length);
3312
+ const attachTo = inferSubagentAttachment(main, session);
3313
+ return {
3314
+ id: hashParts('timeline-branch', session.sourcePath),
3315
+ label: session.traceLabel ?? session.sourcePath.split('/').pop() ?? 'subagent',
3316
+ sourceTrace: session.sourcePath,
3317
+ traceRole: session.traceRole ?? 'subagent',
3318
+ attachTo,
3319
+ events,
3320
+ };
3321
+ });
3322
+ return {
3323
+ sessionId,
3324
+ main,
3325
+ branches,
3326
+ };
3327
+ }
3328
+ function inferSubagentAttachment(mainEvents, branchSession) {
3329
+ const startedAt = branchSession.startTimestamp;
3330
+ const taskUses = mainEvents.filter((event) => event.kind === 'tool_use' && /^(Task|Agent|Skill)$/i.test(event.toolName ?? ''));
3331
+ const candidates = startedAt
3332
+ ? taskUses.filter((event) => !event.timestamp || event.timestamp <= startedAt)
3333
+ : taskUses;
3334
+ const event = candidates.at(-1) ?? taskUses.at(-1);
3335
+ if (!event)
3336
+ return undefined;
3337
+ return {
3338
+ sourceTrace: event.sourceTrace,
3339
+ messageIndex: event.messageIndex,
3340
+ toolUseId: event.toolUseId,
3341
+ label: event.toolName,
3342
+ };
3343
+ }
3344
+ function minDefined(values) {
3345
+ const filtered = values.filter((value) => typeof value === 'number');
3346
+ return filtered.length > 0 ? Math.min(...filtered) : undefined;
3347
+ }
3348
+ function maxDefined(values) {
3349
+ const filtered = values.filter((value) => typeof value === 'number');
3350
+ return filtered.length > 0 ? Math.max(...filtered) : undefined;
3351
+ }
3352
+ function minString(values) {
3353
+ const filtered = values.filter((value) => Boolean(value));
3354
+ return filtered.length > 0 ? filtered.reduce((min, value) => value < min ? value : min, filtered[0]) : undefined;
3355
+ }
3356
+ function maxString(values) {
3357
+ const filtered = values.filter((value) => Boolean(value));
3358
+ return filtered.length > 0 ? filtered.reduce((max, value) => value > max ? value : max, filtered[0]) : undefined;
3359
+ }
3360
+ function uniqueEvidenceRefs(refs) {
3361
+ const byId = new Map();
3362
+ for (const ref of refs) {
3363
+ byId.set(ref.id, ref);
3364
+ }
3365
+ return Array.from(byId.values());
3366
+ }
3367
+ function inferUserGoal(userRefs) {
3368
+ const first = userRefs.find((ref) => ref.snippet && !ref.snippet.includes('tool_result'));
3369
+ return snippet(first?.snippet, 180);
3370
+ }
3371
+ function isSkillContextRecord(record, text) {
3372
+ const meta = record;
3373
+ if (meta.isMeta === true && typeof meta.sourceToolUseID === 'string')
3374
+ return true;
3375
+ return /^Base directory for this skill:\s+.+(?:\n| )#\s+[a-z0-9][\w.-]*/i.test(text);
3376
+ }
3377
+ function isRuntimeContextRecord(record, text) {
3378
+ const meta = record;
3379
+ if (isRuntimeProtocolPromptText(text))
3380
+ return true;
3381
+ if (/^Conversation info \(untrusted metadata\):\s*```json/i.test(text))
3382
+ return true;
3383
+ if (meta.entrypoint !== 'sdk-ts' || typeof meta.promptId !== 'string')
3384
+ return false;
3385
+ return /^进入.+流程。当前页面已经完成本地工作区恢复/.test(text)
3386
+ || /gui-workflow route/.test(text)
3387
+ || /当前页面已经完成本地工作区恢复/.test(text);
3388
+ }
3389
+ function timestampOf(record) {
3390
+ if (!record || typeof record !== 'object')
3391
+ return undefined;
3392
+ const value = record.timestamp;
3393
+ return typeof value === 'string' ? value : undefined;
3394
+ }
3395
+ function sourceKindForPath(path) {
3396
+ if (path.includes('/openclaw') || path.includes('/.openclaw/'))
3397
+ return 'openclaw';
3398
+ if (path.endsWith('.jsonl'))
3399
+ return 'claude';
3400
+ if (path.endsWith('.log'))
3401
+ return 'markdown_log';
3402
+ return 'unknown';
3403
+ }
3404
+ function inferEntrypointFromRecords(session) {
3405
+ for (const record of session.records) {
3406
+ if (!record || typeof record !== 'object')
3407
+ continue;
3408
+ const entrypoint = record.entrypoint;
3409
+ if (typeof entrypoint === 'string' && entrypoint.trim())
3410
+ return entrypoint;
3411
+ }
3412
+ if (session.entrypoint)
3413
+ return session.entrypoint;
3414
+ if (session.sourceKind === 'openclaw')
3415
+ return 'openclaw';
3416
+ if (session.sourceKind === 'markdown_log')
3417
+ return 'markdown_log';
3418
+ if (session.sourcePath.endsWith('.log'))
3419
+ return 'markdown_log';
3420
+ return undefined;
3421
+ }
3422
+ function snippet(value, max = 240) {
3423
+ const text = typeof value === 'string' ? value : String(value ?? '');
3424
+ const normalized = text.replace(/\s+/g, ' ').trim();
3425
+ return normalized ? normalized.slice(0, max) : undefined;
3426
+ }
3427
+ function fullText(value) {
3428
+ const text = typeof value === 'string' ? value : String(value ?? '');
3429
+ const normalized = text.trim();
3430
+ return normalized || undefined;
3431
+ }
3432
+ function unique(values) {
3433
+ return Array.from(new Set(values));
3434
+ }
3435
+ function hashParts(...parts) {
3436
+ return createHash('sha256').update(parts.join('\u0000')).digest('hex').slice(0, 16);
3437
+ }
3438
+ //# sourceMappingURL=experience.js.map