@evomap/evolver-core 2.0.0-beta.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 (373) hide show
  1. package/dist/algo/antiDistill.d.ts +35 -0
  2. package/dist/algo/antiDistill.js +167 -0
  3. package/dist/algo/bans.d.ts +6 -0
  4. package/dist/algo/bans.js +33 -0
  5. package/dist/algo/candidateAssembly.d.ts +65 -0
  6. package/dist/algo/candidateAssembly.js +196 -0
  7. package/dist/algo/capabilityCandidates.d.ts +38 -0
  8. package/dist/algo/capabilityCandidates.js +131 -0
  9. package/dist/algo/confidence.d.ts +106 -0
  10. package/dist/algo/confidence.js +196 -0
  11. package/dist/algo/cycleEngine.d.ts +143 -0
  12. package/dist/algo/cycleEngine.js +417 -0
  13. package/dist/algo/cycleFailureClassifier.d.ts +52 -0
  14. package/dist/algo/cycleFailureClassifier.js +104 -0
  15. package/dist/algo/epigenetics.d.ts +14 -0
  16. package/dist/algo/epigenetics.js +41 -0
  17. package/dist/algo/evolutionEvent.d.ts +23 -0
  18. package/dist/algo/evolutionEvent.js +24 -0
  19. package/dist/algo/exploration.d.ts +34 -0
  20. package/dist/algo/exploration.js +55 -0
  21. package/dist/algo/geneHealth.d.ts +24 -0
  22. package/dist/algo/geneHealth.js +16 -0
  23. package/dist/algo/geneIntake.d.ts +37 -0
  24. package/dist/algo/geneIntake.js +100 -0
  25. package/dist/algo/genePromotion.d.ts +48 -0
  26. package/dist/algo/genePromotion.js +53 -0
  27. package/dist/algo/geneSelection.d.ts +128 -0
  28. package/dist/algo/geneSelection.js +222 -0
  29. package/dist/algo/index.d.ts +18 -0
  30. package/dist/algo/index.js +18 -0
  31. package/dist/algo/mutation.d.ts +12 -0
  32. package/dist/algo/mutation.js +22 -0
  33. package/dist/algo/orchestrator.d.ts +65 -0
  34. package/dist/algo/orchestrator.js +63 -0
  35. package/dist/algo/solidify.d.ts +43 -0
  36. package/dist/algo/solidify.js +63 -0
  37. package/dist/algo/strategyPresets.d.ts +32 -0
  38. package/dist/algo/strategyPresets.js +61 -0
  39. package/dist/assetstore/index.d.ts +8 -0
  40. package/dist/assetstore/index.js +8 -0
  41. package/dist/assetstore/learningHistory.d.ts +25 -0
  42. package/dist/assetstore/learningHistory.js +38 -0
  43. package/dist/assetstore/localJsonl.d.ts +34 -0
  44. package/dist/assetstore/localJsonl.js +182 -0
  45. package/dist/assetstore/pendingSignals.d.ts +22 -0
  46. package/dist/assetstore/pendingSignals.js +171 -0
  47. package/dist/assetstore/provenance.d.ts +39 -0
  48. package/dist/assetstore/provenance.js +109 -0
  49. package/dist/assetstore/provider.d.ts +48 -0
  50. package/dist/assetstore/provider.js +36 -0
  51. package/dist/assetstore/remoteStub.d.ts +32 -0
  52. package/dist/assetstore/remoteStub.js +36 -0
  53. package/dist/assetstore/reviewFilter.d.ts +20 -0
  54. package/dist/assetstore/reviewFilter.js +44 -0
  55. package/dist/assetstore/reviewLedger.d.ts +55 -0
  56. package/dist/assetstore/reviewLedger.js +121 -0
  57. package/dist/benchmark/antiGeneBenchmark.d.ts +79 -0
  58. package/dist/benchmark/antiGeneBenchmark.js +241 -0
  59. package/dist/benchmark/antiGeneRollout.d.ts +80 -0
  60. package/dist/benchmark/antiGeneRollout.js +256 -0
  61. package/dist/benchmark/benchmark.d.ts +32 -0
  62. package/dist/benchmark/benchmark.js +26 -0
  63. package/dist/benchmark/evolutionThesisSolver.d.ts +25 -0
  64. package/dist/benchmark/evolutionThesisSolver.js +41 -0
  65. package/dist/benchmark/index.d.ts +5 -0
  66. package/dist/benchmark/index.js +5 -0
  67. package/dist/benchmark/thesis.d.ts +85 -0
  68. package/dist/benchmark/thesis.js +150 -0
  69. package/dist/bootstrap/envFingerprint.d.ts +45 -0
  70. package/dist/bootstrap/envFingerprint.js +72 -0
  71. package/dist/bootstrap/index.d.ts +2 -0
  72. package/dist/bootstrap/index.js +2 -0
  73. package/dist/bootstrap/selfEvolve.d.ts +36 -0
  74. package/dist/bootstrap/selfEvolve.js +26 -0
  75. package/dist/cycle/cycleTimeline.d.ts +35 -0
  76. package/dist/cycle/cycleTimeline.js +74 -0
  77. package/dist/cycle/index.d.ts +2 -0
  78. package/dist/cycle/index.js +2 -0
  79. package/dist/cycle/stateMachine.d.ts +6 -0
  80. package/dist/cycle/stateMachine.js +25 -0
  81. package/dist/daemon/cycleContext.d.ts +12 -0
  82. package/dist/daemon/cycleContext.js +13 -0
  83. package/dist/daemon/drain.d.ts +11 -0
  84. package/dist/daemon/drain.js +16 -0
  85. package/dist/daemon/guards.d.ts +87 -0
  86. package/dist/daemon/guards.js +142 -0
  87. package/dist/daemon/idempotency.d.ts +11 -0
  88. package/dist/daemon/idempotency.js +36 -0
  89. package/dist/daemon/idleScheduler.d.ts +55 -0
  90. package/dist/daemon/idleScheduler.js +181 -0
  91. package/dist/daemon/index.d.ts +8 -0
  92. package/dist/daemon/index.js +8 -0
  93. package/dist/daemon/stepRunner.d.ts +12 -0
  94. package/dist/daemon/stepRunner.js +31 -0
  95. package/dist/daemon/unhandledRejectionWindow.d.ts +21 -0
  96. package/dist/daemon/unhandledRejectionWindow.js +52 -0
  97. package/dist/daemon/watchdog.d.ts +11 -0
  98. package/dist/daemon/watchdog.js +14 -0
  99. package/dist/events/eventSchema.d.ts +132 -0
  100. package/dist/events/eventSchema.js +30 -0
  101. package/dist/events/eventStore.d.ts +32 -0
  102. package/dist/events/eventStore.js +137 -0
  103. package/dist/events/ingest.d.ts +34 -0
  104. package/dist/events/ingest.js +61 -0
  105. package/dist/events/paths.d.ts +22 -0
  106. package/dist/events/paths.js +44 -0
  107. package/dist/events/projectors.d.ts +9 -0
  108. package/dist/events/projectors.js +11 -0
  109. package/dist/events/public.d.ts +14 -0
  110. package/dist/events/public.js +8 -0
  111. package/dist/events/replayer.d.ts +26 -0
  112. package/dist/events/replayer.js +67 -0
  113. package/dist/events/reports.d.ts +90 -0
  114. package/dist/events/reports.js +244 -0
  115. package/dist/events/retention.d.ts +70 -0
  116. package/dist/events/retention.js +177 -0
  117. package/dist/events/sink.d.ts +5 -0
  118. package/dist/events/sink.js +1 -0
  119. package/dist/exec/autoExec.d.ts +137 -0
  120. package/dist/exec/autoExec.js +221 -0
  121. package/dist/exec/autonomousCycle.d.ts +48 -0
  122. package/dist/exec/autonomousCycle.js +78 -0
  123. package/dist/exec/claudeBridge.d.ts +112 -0
  124. package/dist/exec/claudeBridge.js +219 -0
  125. package/dist/exec/index.d.ts +9 -0
  126. package/dist/exec/index.js +11 -0
  127. package/dist/exec/openPrRegistry.d.ts +60 -0
  128. package/dist/exec/openPrRegistry.js +130 -0
  129. package/dist/exec/policy/blastRadius.d.ts +31 -0
  130. package/dist/exec/policy/blastRadius.js +67 -0
  131. package/dist/exec/policy/constraints.d.ts +26 -0
  132. package/dist/exec/policy/constraints.js +62 -0
  133. package/dist/exec/policy/destructive.d.ts +45 -0
  134. package/dist/exec/policy/destructive.js +108 -0
  135. package/dist/exec/policy/failureMode.d.ts +33 -0
  136. package/dist/exec/policy/failureMode.js +49 -0
  137. package/dist/exec/policy/index.d.ts +24 -0
  138. package/dist/exec/policy/index.js +30 -0
  139. package/dist/exec/policy/protectedPaths.d.ts +17 -0
  140. package/dist/exec/policy/protectedPaths.js +65 -0
  141. package/dist/exec/policyCheck.d.ts +2 -0
  142. package/dist/exec/policyCheck.js +7 -0
  143. package/dist/exec/prompt.d.ts +34 -0
  144. package/dist/exec/prompt.js +108 -0
  145. package/dist/exec/proofOfWork.d.ts +9 -0
  146. package/dist/exec/proofOfWork.js +11 -0
  147. package/dist/exec/runnerRegistry.d.ts +123 -0
  148. package/dist/exec/runnerRegistry.js +237 -0
  149. package/dist/exec/selfPr.d.ts +83 -0
  150. package/dist/exec/selfPr.js +104 -0
  151. package/dist/exec/selfPrObfuscation.d.ts +21 -0
  152. package/dist/exec/selfPrObfuscation.js +85 -0
  153. package/dist/hooks/hooks.d.ts +117 -0
  154. package/dist/hooks/hooks.js +89 -0
  155. package/dist/hooks/index.d.ts +1 -0
  156. package/dist/hooks/index.js +1 -0
  157. package/dist/hub/assetCallLog.d.ts +43 -0
  158. package/dist/hub/assetCallLog.js +72 -0
  159. package/dist/hub/bindings.d.ts +28 -0
  160. package/dist/hub/bindings.js +90 -0
  161. package/dist/hub/capability.d.ts +278 -0
  162. package/dist/hub/capability.js +1 -0
  163. package/dist/hub/conversationDistiller.d.ts +88 -0
  164. package/dist/hub/conversationDistiller.js +264 -0
  165. package/dist/hub/fake.d.ts +45 -0
  166. package/dist/hub/fake.js +69 -0
  167. package/dist/hub/hubReview.d.ts +91 -0
  168. package/dist/hub/hubReview.js +106 -0
  169. package/dist/hub/index.d.ts +10 -0
  170. package/dist/hub/index.js +10 -0
  171. package/dist/hub/ingest.d.ts +15 -0
  172. package/dist/hub/ingest.js +15 -0
  173. package/dist/hub/questionGenerator.d.ts +59 -0
  174. package/dist/hub/questionGenerator.js +406 -0
  175. package/dist/hub/reuseDecision.d.ts +87 -0
  176. package/dist/hub/reuseDecision.js +127 -0
  177. package/dist/hub/sanitize.d.ts +52 -0
  178. package/dist/hub/sanitize.js +321 -0
  179. package/dist/index.d.ts +27 -0
  180. package/dist/index.js +27 -0
  181. package/dist/mailbox/catalog.d.ts +17 -0
  182. package/dist/mailbox/catalog.js +53 -0
  183. package/dist/mailbox/daemon.d.ts +48 -0
  184. package/dist/mailbox/daemon.js +61 -0
  185. package/dist/mailbox/dispatch.d.ts +46 -0
  186. package/dist/mailbox/dispatch.js +62 -0
  187. package/dist/mailbox/envelope.d.ts +43 -0
  188. package/dist/mailbox/envelope.js +52 -0
  189. package/dist/mailbox/index.d.ts +8 -0
  190. package/dist/mailbox/index.js +8 -0
  191. package/dist/mailbox/ipcServer.d.ts +46 -0
  192. package/dist/mailbox/ipcServer.js +168 -0
  193. package/dist/mailbox/requestReply.d.ts +40 -0
  194. package/dist/mailbox/requestReply.js +77 -0
  195. package/dist/mailbox/store.d.ts +74 -0
  196. package/dist/mailbox/store.js +308 -0
  197. package/dist/mailbox/wake.d.ts +45 -0
  198. package/dist/mailbox/wake.js +60 -0
  199. package/dist/material/boundary.d.ts +7 -0
  200. package/dist/material/boundary.js +14 -0
  201. package/dist/material/consumer.d.ts +21 -0
  202. package/dist/material/consumer.js +52 -0
  203. package/dist/material/emit.d.ts +55 -0
  204. package/dist/material/emit.js +52 -0
  205. package/dist/material/factory.d.ts +27 -0
  206. package/dist/material/factory.js +25 -0
  207. package/dist/material/index.d.ts +8 -0
  208. package/dist/material/index.js +8 -0
  209. package/dist/material/materialStore.d.ts +24 -0
  210. package/dist/material/materialStore.js +76 -0
  211. package/dist/material/sampling.d.ts +21 -0
  212. package/dist/material/sampling.js +39 -0
  213. package/dist/material/sources.d.ts +25 -0
  214. package/dist/material/sources.js +33 -0
  215. package/dist/material/watermark.d.ts +21 -0
  216. package/dist/material/watermark.js +76 -0
  217. package/dist/observers/cursorRewriteObserver.d.ts +46 -0
  218. package/dist/observers/cursorRewriteObserver.js +103 -0
  219. package/dist/observers/distillObserver.d.ts +31 -0
  220. package/dist/observers/distillObserver.js +93 -0
  221. package/dist/observers/index.d.ts +5 -0
  222. package/dist/observers/index.js +5 -0
  223. package/dist/observers/observerBus.d.ts +44 -0
  224. package/dist/observers/observerBus.js +73 -0
  225. package/dist/observers/reflectionObserver.d.ts +31 -0
  226. package/dist/observers/reflectionObserver.js +124 -0
  227. package/dist/observers/valueDigestObserver.d.ts +50 -0
  228. package/dist/observers/valueDigestObserver.js +70 -0
  229. package/dist/ops/cleanup.d.ts +23 -0
  230. package/dist/ops/cleanup.js +68 -0
  231. package/dist/ops/health.d.ts +37 -0
  232. package/dist/ops/health.js +96 -0
  233. package/dist/ops/index.d.ts +7 -0
  234. package/dist/ops/index.js +7 -0
  235. package/dist/ops/recall.d.ts +65 -0
  236. package/dist/ops/recall.js +152 -0
  237. package/dist/ops/reuseOutcomes.d.ts +68 -0
  238. package/dist/ops/reuseOutcomes.js +126 -0
  239. package/dist/ops/savingsCore.d.ts +47 -0
  240. package/dist/ops/savingsCore.js +112 -0
  241. package/dist/ops/selfUpdate.d.ts +106 -0
  242. package/dist/ops/selfUpdate.js +294 -0
  243. package/dist/ops/valueLedger.d.ts +205 -0
  244. package/dist/ops/valueLedger.js +238 -0
  245. package/dist/ops/valueOutreach.d.ts +70 -0
  246. package/dist/ops/valueOutreach.js +244 -0
  247. package/dist/personality/drift.d.ts +45 -0
  248. package/dist/personality/drift.js +106 -0
  249. package/dist/personality/events.d.ts +25 -0
  250. package/dist/personality/events.js +37 -0
  251. package/dist/personality/evolveOps.d.ts +20 -0
  252. package/dist/personality/evolveOps.js +93 -0
  253. package/dist/personality/index.d.ts +11 -0
  254. package/dist/personality/index.js +11 -0
  255. package/dist/personality/mutate.d.ts +21 -0
  256. package/dist/personality/mutate.js +39 -0
  257. package/dist/personality/pivot.d.ts +23 -0
  258. package/dist/personality/pivot.js +22 -0
  259. package/dist/personality/prompt.d.ts +6 -0
  260. package/dist/personality/prompt.js +62 -0
  261. package/dist/personality/riskGate.d.ts +42 -0
  262. package/dist/personality/riskGate.js +80 -0
  263. package/dist/personality/schema.d.ts +255 -0
  264. package/dist/personality/schema.js +119 -0
  265. package/dist/personality/select.d.ts +46 -0
  266. package/dist/personality/select.js +71 -0
  267. package/dist/personality/stats.d.ts +46 -0
  268. package/dist/personality/stats.js +82 -0
  269. package/dist/personality/store.d.ts +35 -0
  270. package/dist/personality/store.js +92 -0
  271. package/dist/schema/common.d.ts +28 -0
  272. package/dist/schema/common.js +17 -0
  273. package/dist/schema/index.d.ts +5 -0
  274. package/dist/schema/index.js +5 -0
  275. package/dist/schema/material.d.ts +201 -0
  276. package/dist/schema/material.js +50 -0
  277. package/dist/schema/problem.d.ts +160 -0
  278. package/dist/schema/problem.js +45 -0
  279. package/dist/schema/proofOfWork.d.ts +97 -0
  280. package/dist/schema/proofOfWork.js +9 -0
  281. package/dist/schema/signal.d.ts +41 -0
  282. package/dist/schema/signal.js +19 -0
  283. package/dist/shadow/index.d.ts +6 -0
  284. package/dist/shadow/index.js +6 -0
  285. package/dist/shadow/jsonlSink.d.ts +17 -0
  286. package/dist/shadow/jsonlSink.js +27 -0
  287. package/dist/shadow/redact.d.ts +11 -0
  288. package/dist/shadow/redact.js +18 -0
  289. package/dist/shadow/shadowHub.d.ts +12 -0
  290. package/dist/shadow/shadowHub.js +42 -0
  291. package/dist/shadow/shadowMailbox.d.ts +17 -0
  292. package/dist/shadow/shadowMailbox.js +25 -0
  293. package/dist/shadow/shadowStore.d.ts +18 -0
  294. package/dist/shadow/shadowStore.js +55 -0
  295. package/dist/shadow/sink.d.ts +37 -0
  296. package/dist/shadow/sink.js +18 -0
  297. package/dist/signals/cycleHistoryFromEvents.d.ts +11 -0
  298. package/dist/signals/cycleHistoryFromEvents.js +96 -0
  299. package/dist/signals/expand.d.ts +34 -0
  300. package/dist/signals/expand.js +125 -0
  301. package/dist/signals/extractor.d.ts +25 -0
  302. package/dist/signals/extractor.js +57 -0
  303. package/dist/signals/index.d.ts +6 -0
  304. package/dist/signals/index.js +6 -0
  305. package/dist/signals/metaSignals.d.ts +57 -0
  306. package/dist/signals/metaSignals.js +158 -0
  307. package/dist/signals/signalGate.d.ts +15 -0
  308. package/dist/signals/signalGate.js +40 -0
  309. package/dist/signals/traceSignals.d.ts +44 -0
  310. package/dist/signals/traceSignals.js +123 -0
  311. package/dist/signatures/classifier.d.ts +16 -0
  312. package/dist/signatures/classifier.js +29 -0
  313. package/dist/signatures/index.d.ts +2 -0
  314. package/dist/signatures/index.js +2 -0
  315. package/dist/signatures/signatures.d.ts +22 -0
  316. package/dist/signatures/signatures.js +42 -0
  317. package/dist/strategy/experiment.d.ts +48 -0
  318. package/dist/strategy/experiment.js +63 -0
  319. package/dist/strategy/index.d.ts +2 -0
  320. package/dist/strategy/index.js +2 -0
  321. package/dist/strategy/strategyPoint.d.ts +30 -0
  322. package/dist/strategy/strategyPoint.js +23 -0
  323. package/dist/trace/index.d.ts +2 -0
  324. package/dist/trace/index.js +2 -0
  325. package/dist/trace/trajectory.d.ts +82 -0
  326. package/dist/trace/trajectory.js +117 -0
  327. package/dist/trace/trajectoryExport.d.ts +237 -0
  328. package/dist/trace/trajectoryExport.js +1818 -0
  329. package/dist/trigger/budget.d.ts +16 -0
  330. package/dist/trigger/budget.js +20 -0
  331. package/dist/trigger/engine.d.ts +12 -0
  332. package/dist/trigger/engine.js +23 -0
  333. package/dist/trigger/index.d.ts +6 -0
  334. package/dist/trigger/index.js +6 -0
  335. package/dist/trigger/problemBuilder.d.ts +24 -0
  336. package/dist/trigger/problemBuilder.js +32 -0
  337. package/dist/trigger/trigger.d.ts +22 -0
  338. package/dist/trigger/trigger.js +27 -0
  339. package/dist/trigger/valueModel.d.ts +26 -0
  340. package/dist/trigger/valueModel.js +25 -0
  341. package/dist/trigger/wfq.d.ts +13 -0
  342. package/dist/trigger/wfq.js +25 -0
  343. package/dist/util/fileLock.d.ts +24 -0
  344. package/dist/util/fileLock.js +227 -0
  345. package/dist/util/index.d.ts +2 -0
  346. package/dist/util/index.js +2 -0
  347. package/dist/util/version.d.ts +12 -0
  348. package/dist/util/version.js +51 -0
  349. package/dist/verify/index.d.ts +4 -0
  350. package/dist/verify/index.js +4 -0
  351. package/dist/verify/rollback.d.ts +22 -0
  352. package/dist/verify/rollback.js +33 -0
  353. package/dist/verify/sandboxRunner.d.ts +39 -0
  354. package/dist/verify/sandboxRunner.js +116 -0
  355. package/dist/verify/sandboxedValidation.d.ts +35 -0
  356. package/dist/verify/sandboxedValidation.js +56 -0
  357. package/dist/verify/validation.d.ts +41 -0
  358. package/dist/verify/validation.js +85 -0
  359. package/dist/wire/canonicalize.golden.d.ts +23 -0
  360. package/dist/wire/canonicalize.golden.js +46 -0
  361. package/dist/wire/geneHints.d.ts +64 -0
  362. package/dist/wire/geneHints.js +100 -0
  363. package/dist/wire/index.d.ts +91 -0
  364. package/dist/wire/index.js +3 -0
  365. package/dist/wire/schemaGate.d.ts +11 -0
  366. package/dist/wire/schemaGate.js +49 -0
  367. package/dist/workflow/dsl.d.ts +54 -0
  368. package/dist/workflow/dsl.js +15 -0
  369. package/dist/workflow/engine.d.ts +30 -0
  370. package/dist/workflow/engine.js +94 -0
  371. package/dist/workflow/index.d.ts +2 -0
  372. package/dist/workflow/index.js +2 -0
  373. package/package.json +29 -0
@@ -0,0 +1,123 @@
1
+ // Signal extraction from the LLM proxy's per-turn trace records (the `llm-trace-*.jsonl` files written by
2
+ // the proxy's JsonlTraceSink). Trace records are TURN METADATA — models, tier decision, status, latency,
3
+ // usage, stop_reason — never conversation text, so the signals here are the economic/reliability ones the
4
+ // session transcripts can't see: upstream failures, credential breakage, router fallbacks, truncation
5
+ // pressure, sustained latency. This closes the loop opened by the proxy's trace-capture seam: the proxy
6
+ // captures turn metadata, and `evolver ingest` mines it through this extractor.
7
+ //
8
+ // Three-leg mapping: every rule below is a zero-cost structured judgment over counters, so every emitted
9
+ // signal is `strong` with `needsAnalysis: false`. The `agent` and `weak` legs do not apply to metadata —
10
+ // there is no free text for an agent marker or difficulty wording to live in.
11
+ import { ulid as makeUlid } from 'ulid';
12
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : null);
13
+ const str = (v) => (typeof v === 'string' && v.length > 0 ? v : null);
14
+ const strong = (kind, text) => ({ id: makeUlid(), strength: 'strong', kind, text: text.slice(0, 2000), needsAnalysis: false });
15
+ /**
16
+ * Extract economic/reliability signals from a batch of llm-trace records. Returns the SAME signal shape as
17
+ * `extractSignals` (one batch-aggregated signal per fired rule, not one per record — a day file holds
18
+ * thousands of turns and per-record emission would flood every downstream consumer with duplicates).
19
+ * Records whose `event` is not `'llm_turn'` are ignored, so callers can pass a raw parsed JSONL batch.
20
+ */
21
+ export function extractTraceSignals(records, cfg = {}) {
22
+ const min5xx = cfg.min5xx ?? 2;
23
+ const minRateLimited = cfg.minRateLimited ?? 3;
24
+ const minAuthFailures = cfg.minAuthFailures ?? 2;
25
+ const minTruncations = cfg.minTruncations ?? 3;
26
+ const minPolicyFallbacks = cfg.minPolicyFallbacks ?? 3;
27
+ const slowTtfbMs = cfg.slowTtfbMs ?? 30_000;
28
+ const minSlowTurns = cfg.minSlowTurns ?? 5;
29
+ const minSlowShare = cfg.minSlowShare ?? 0.2;
30
+ const handlerErrors = new Map(); // normalized error text → count
31
+ const fallbacks = new Map(); // fallback reason → count
32
+ const fiveXxStatuses = new Map(); // 5xx status → count
33
+ let authFailures = 0;
34
+ let rateLimited = 0;
35
+ let truncations = 0;
36
+ let eligibleTurns = 0; // successful turns with a measured TTFB — the latency denominator
37
+ let slowTurns = 0;
38
+ for (const r of records) {
39
+ if (r.event !== 'llm_turn')
40
+ continue;
41
+ const status = num(r.status);
42
+ const error = str(r.error);
43
+ const fallback = str(r.fallback);
44
+ const stopReason = str(r.stop_reason);
45
+ const ttfb = num(r.ttfb_ms);
46
+ // Handler/transport errors (upstream unreachable, drain failure, …). Auth statuses are carved out into
47
+ // their own rule below so a 401's "x-api-key required" error text is not double-counted.
48
+ if (error && status !== 401 && status !== 403) {
49
+ const key = error.replace(/\s+/g, ' ').trim().slice(0, 200);
50
+ handlerErrors.set(key, (handlerErrors.get(key) ?? 0) + 1);
51
+ }
52
+ if (status === 401 || status === 403)
53
+ authFailures++;
54
+ if (status !== null && status >= 500)
55
+ fiveXxStatuses.set(status, (fiveXxStatuses.get(status) ?? 0) + 1);
56
+ if (status === 429)
57
+ rateLimited++;
58
+ if (fallback)
59
+ fallbacks.set(fallback, (fallbacks.get(fallback) ?? 0) + 1);
60
+ if (stopReason === 'max_tokens')
61
+ truncations++;
62
+ if (status === 200 && ttfb !== null) {
63
+ eligibleTurns++;
64
+ if (ttfb >= slowTtfbMs)
65
+ slowTurns++;
66
+ }
67
+ }
68
+ const out = [];
69
+ // RULE: handler error — the proxy could not complete the turn at all (e.g. ECONNREFUSED, drain timeout).
70
+ // Threshold 1: a hard transport/handler failure is never routine, mirroring how a single tool error is a
71
+ // strong signal in the session extractor. Grouped by error text so a repeated failure stays one signal.
72
+ for (const [text, count] of handlerErrors) {
73
+ out.push(strong('trace_handler_error', `LLM proxy handler error ×${count}: ${text}`));
74
+ }
75
+ // RULE: upstream 5xx — server-side failures that survived the route's own retry. Threshold 2: a single
76
+ // 5xx is normal API weather (overload blips happen and the handler already retries rewritten requests);
77
+ // two or more in one ingested batch is a reliability pattern worth surfacing.
78
+ const total5xx = [...fiveXxStatuses.values()].reduce((s, n) => s + n, 0);
79
+ if (total5xx >= min5xx) {
80
+ const breakdown = [...fiveXxStatuses.keys()].sort((a, b) => a - b).join('/');
81
+ out.push(strong('trace_upstream_5xx', `upstream 5xx ×${total5xx} (status ${breakdown}) — upstream reliability degraded`));
82
+ }
83
+ // RULE: rate limiting — 429s are the purest economic signal in the trace (quota/budget pressure).
84
+ // Threshold 3: occasional 429s are expected under bursty load; sustained ones mean the tier/quota
85
+ // configuration is mismatched with real usage.
86
+ if (rateLimited >= minRateLimited) {
87
+ out.push(strong('trace_rate_limited', `upstream 429 rate-limited ×${rateLimited} — quota/tier pressure`));
88
+ }
89
+ // RULE: auth failures — 401/403 from the credential path. Threshold 2: a single 401 can be one client's
90
+ // typo; repeated ones mean the proxy or its clients are misconfigured (broken key, missing env cred).
91
+ if (authFailures >= minAuthFailures) {
92
+ out.push(strong('trace_auth_failure', `auth failures (401/403) ×${authFailures} — credential configuration broken`));
93
+ }
94
+ // RULE: router fallbacks, split by what the reason MEANS:
95
+ // - classifier_error / rewrite_error: the router's own code threw. Threshold 1 — a throwing classifier
96
+ // is a bug, never routine operation.
97
+ // - downgrade_blocked: the safety guard refused an intra-family downgrade. Threshold 3 — single blocks
98
+ // are the guard working as designed; a sustained pattern means the tier config conflicts with what
99
+ // clients actually request and routing silently does nothing.
100
+ // - anything else (future reasons): threshold 2 — unknown but repeated is worth a look.
101
+ for (const [reason, count] of fallbacks) {
102
+ const min = reason === 'classifier_error' || reason === 'rewrite_error' ? 1
103
+ : reason === 'downgrade_blocked' ? minPolicyFallbacks
104
+ : 2;
105
+ if (count >= min) {
106
+ out.push(strong('trace_router_fallback', `router fallback ${reason} ×${count} — routing degraded to passthrough`));
107
+ }
108
+ }
109
+ // RULE: truncation pressure — turns that stopped at max_tokens. Threshold 3: a single truncation is often
110
+ // deliberate (caller set a tight cap); repeated ones mean responses are systematically being cut off,
111
+ // which silently corrupts agent behavior and wastes the tokens already paid for.
112
+ if (truncations >= minTruncations) {
113
+ out.push(strong('trace_truncation', `stop_reason=max_tokens ×${truncations} — output truncation pressure`));
114
+ }
115
+ // RULE: sustained high TTFB. TTFB (time to upstream response headers), NOT total latency_ms, because total
116
+ // latency scales with legitimate output length — long generations are normal, slow first bytes are queue/
117
+ // network/server pressure. Two-part gate so single spikes never fire: an absolute count (>= minSlowTurns)
118
+ // AND a meaningful share of successful turns (>= minSlowShare) must be slow.
119
+ if (slowTurns >= minSlowTurns && eligibleTurns > 0 && slowTurns / eligibleTurns >= minSlowShare) {
120
+ out.push(strong('trace_slow_ttfb', `sustained high TTFB: ${slowTurns}/${eligibleTurns} successful turns ≥ ${slowTtfbMs}ms`));
121
+ }
122
+ return out;
123
+ }
@@ -0,0 +1,16 @@
1
+ import type { ProblemSignatureInput } from './signatures.js';
2
+ /** 把工程事件归类成领域问题 (event_sig → problem_sig). 可插拔: 规则/LLM/agent, 实验选 (硬化). */
3
+ export interface ProblemClassifier {
4
+ readonly name: string;
5
+ classify(input: ClassifyInput): ProblemSignatureInput | null;
6
+ }
7
+ export interface ClassifyInput {
8
+ text: string;
9
+ errorClass?: string;
10
+ affectedSurface?: string;
11
+ }
12
+ /** MVP 规则分类器 (LLM/agent 分类器是 M4). */
13
+ export declare class RuleProblemClassifier implements ProblemClassifier {
14
+ readonly name = "rules";
15
+ classify(input: ClassifyInput): ProblemSignatureInput | null;
16
+ }
@@ -0,0 +1,29 @@
1
+ const KEYWORD_TAGS = [
2
+ [/(auth|401|403|token|credential|认证|鉴权)/i, 'auth'],
3
+ [/\bhttp|api|request|fetch|网络|network/i, 'network'],
4
+ [/\bdb|sql|database|prisma/i, 'db'],
5
+ [/\btest|测试|vitest|jest/i, 'test'],
6
+ ];
7
+ const FAILURE_MODES = [
8
+ [/\btimeout|timed out|ETIMEDOUT|超时/i, 'timeout'],
9
+ [/\bECONNRESET|reset|断开|disconnect/i, 'connection_reset'],
10
+ [/\b401|403|unauthorized|forbidden/i, 'unauthorized'],
11
+ [/\bENOENT|not found|404|缺失/i, 'not_found'],
12
+ ];
13
+ /** MVP 规则分类器 (LLM/agent 分类器是 M4). */
14
+ export class RuleProblemClassifier {
15
+ name = 'rules';
16
+ classify(input) {
17
+ const hay = `${input.text} ${input.errorClass ?? ''}`;
18
+ const domainTags = KEYWORD_TAGS.filter(([re]) => re.test(hay)).map(([, t]) => t);
19
+ const fm = FAILURE_MODES.find(([re]) => re.test(hay));
20
+ if (domainTags.length === 0 && !fm)
21
+ return null;
22
+ return {
23
+ problemKind: domainTags[0] ?? 'general',
24
+ domainTags,
25
+ affectedSurface: input.affectedSurface ?? domainTags[0] ?? 'unknown',
26
+ failureMode: fm?.[1] ?? 'unknown',
27
+ };
28
+ }
29
+ }
@@ -0,0 +1,2 @@
1
+ export * from './signatures.js';
2
+ export * from './classifier.js';
@@ -0,0 +1,2 @@
1
+ export * from './signatures.js';
2
+ export * from './classifier.js';
@@ -0,0 +1,22 @@
1
+ export declare const SIGNATURE_V = 1;
2
+ /** 文本归一化: 小写+NFKC+去标点+折叠空白 → 同义不同措辞归并. */
3
+ export declare function normalizeText(s: string): string;
4
+ /** 栈帧归一化: 去绝对路径/行列号, 留 fn@basename. */
5
+ export declare function normalizeStackFrame(frame: string): string;
6
+ export declare function exitCodeClass(code?: number): string;
7
+ /** 工程层指纹 (军杰 §4.2): 纯工程特征, 不含领域语义. */
8
+ export interface EventSignatureInput {
9
+ errorClass: string;
10
+ stack?: readonly string[];
11
+ syscall?: string;
12
+ exitCode?: number;
13
+ }
14
+ export declare function eventSignature(input: EventSignatureInput): string;
15
+ /** 领域层指纹 (军杰 §4.3): 不含 gene_id/capsule_id. */
16
+ export interface ProblemSignatureInput {
17
+ problemKind: string;
18
+ domainTags?: readonly string[];
19
+ affectedSurface?: string;
20
+ failureMode?: string;
21
+ }
22
+ export declare function problemSignature(input: ProblemSignatureInput): string;
@@ -0,0 +1,42 @@
1
+ import { createHash } from 'node:crypto';
2
+ export const SIGNATURE_V = 1;
3
+ function sha1(...parts) {
4
+ return createHash('sha1').update(parts.join('\x1f')).digest('hex');
5
+ }
6
+ /** 文本归一化: 小写+NFKC+去标点+折叠空白 → 同义不同措辞归并. */
7
+ export function normalizeText(s) {
8
+ return s.toLowerCase().normalize('NFKC').replace(/[^\p{L}\p{N}]+/gu, ' ').trim().replace(/\s+/g, ' ');
9
+ }
10
+ /** 栈帧归一化: 去绝对路径/行列号, 留 fn@basename. */
11
+ export function normalizeStackFrame(frame) {
12
+ const f = frame.trim().replace(/^at\s+/, '');
13
+ const pathMatch = f.match(/\(?([^()\s]+?):(\d+)(?::(\d+))?\)?$/);
14
+ const fnMatch = f.match(/^([^(]+?)\s*\(/);
15
+ const fileWithPath = pathMatch?.[1] ?? f;
16
+ const base = fileWithPath.split(/[/\\]/).pop() ?? fileWithPath;
17
+ const fn = fnMatch?.[1]?.trim() ?? '';
18
+ return fn ? `${fn}@${base}` : base;
19
+ }
20
+ export function exitCodeClass(code) {
21
+ if (code === undefined)
22
+ return 'none';
23
+ if (code === 0)
24
+ return 'ok';
25
+ if (code === 1)
26
+ return 'general';
27
+ if (code === 2)
28
+ return 'misuse';
29
+ if (code === 126 || code === 127)
30
+ return 'notfound';
31
+ if (code >= 128)
32
+ return 'signal';
33
+ return 'other';
34
+ }
35
+ export function eventSignature(input) {
36
+ const top3 = (input.stack ?? []).slice(0, 3).map(normalizeStackFrame).join('|');
37
+ return sha1('ev', normalizeText(input.errorClass), top3, input.syscall ?? '', exitCodeClass(input.exitCode));
38
+ }
39
+ export function problemSignature(input) {
40
+ const tags = [...(input.domainTags ?? [])].map(normalizeText).sort().join(',');
41
+ return sha1('pb', normalizeText(input.problemKind), tags, normalizeText(input.affectedSurface ?? ''), normalizeText(input.failureMode ?? ''));
42
+ }
@@ -0,0 +1,48 @@
1
+ import type { Strategy, StrategyContext, StrategyPoint } from './strategyPoint.js';
2
+ /** 策略多指标适应度(算法草案§7). 权重可调, 初值 0.5/0.3/0.2(待实验校准). */
3
+ export interface FitnessWeights {
4
+ resolved: number;
5
+ reuse: number;
6
+ cost: number;
7
+ }
8
+ export declare const DEFAULT_WEIGHTS: FitnessWeights;
9
+ export interface FitnessSample {
10
+ resolvedByEvidence: boolean;
11
+ reused: boolean;
12
+ cost: number;
13
+ }
14
+ export interface FitnessScore {
15
+ n: number;
16
+ resolvedRate: number;
17
+ reuseRate: number;
18
+ avgCost: number;
19
+ score: number;
20
+ }
21
+ /** fitness = w1·resolved_by_evidence率 + w2·capsule复用率 − w3·归一成本. */
22
+ export declare function computeFitness(samples: readonly FitnessSample[], w?: FitnessWeights): FitnessScore;
23
+ export interface ShadowResult<O> {
24
+ active: O;
25
+ activeName: string;
26
+ shadows: Array<{
27
+ name: string;
28
+ output?: O;
29
+ error?: string;
30
+ }>;
31
+ }
32
+ /**
33
+ * Shadow 评估: active 权威返回; 其余实现旁路跑同输入, 记录产出/异常供离线对比, 不影响主流程.
34
+ * 实验台打底(算法草案§7): 所有候选先 shadow, 关键的才 A/B.
35
+ */
36
+ export declare function runShadow<I, O>(sp: StrategyPoint<I, O>, input: I, ctx: StrategyContext): Promise<ShadowResult<O>>;
37
+ /** 确定性 A/B 分桶: 同 key 永远同变体(可复现, 不依赖随机). */
38
+ export declare function assignVariant(key: string, variants: readonly string[]): string;
39
+ /** A/B 路由: 按 ctx.cycleId 确定性选变体并跑. */
40
+ export declare function runAB<I, O>(sp: StrategyPoint<I, O>, variants: readonly string[], input: I, ctx: StrategyContext): Promise<{
41
+ variant: string;
42
+ output: O;
43
+ }>;
44
+ /**
45
+ * 离线回放: 用历史输入集跑某策略, 由打分器产出 fitness 样本, 算总分.
46
+ * 用途: 新策略上线前先离线对比 active(算法草案§7 offline replay).
47
+ */
48
+ export declare function offlineReplay<I, O>(impl: Strategy<I, O>, inputs: readonly I[], score: (input: I, output: O) => FitnessSample, ctxOf: (i: number) => StrategyContext, w?: FitnessWeights): Promise<FitnessScore>;
@@ -0,0 +1,63 @@
1
+ import { createHash } from 'node:crypto';
2
+ export const DEFAULT_WEIGHTS = { resolved: 0.5, reuse: 0.3, cost: 0.2 };
3
+ /** fitness = w1·resolved_by_evidence率 + w2·capsule复用率 − w3·归一成本. */
4
+ export function computeFitness(samples, w = DEFAULT_WEIGHTS) {
5
+ const n = samples.length;
6
+ if (n === 0)
7
+ return { n: 0, resolvedRate: 0, reuseRate: 0, avgCost: 0, score: 0 };
8
+ const resolvedRate = samples.filter((s) => s.resolvedByEvidence).length / n;
9
+ const reuseRate = samples.filter((s) => s.reused).length / n;
10
+ const avgCost = samples.reduce((a, s) => a + s.cost, 0) / n;
11
+ const score = w.resolved * resolvedRate + w.reuse * reuseRate - w.cost * avgCost;
12
+ return { n, resolvedRate, reuseRate, avgCost, score };
13
+ }
14
+ /**
15
+ * Shadow 评估: active 权威返回; 其余实现旁路跑同输入, 记录产出/异常供离线对比, 不影响主流程.
16
+ * 实验台打底(算法草案§7): 所有候选先 shadow, 关键的才 A/B.
17
+ */
18
+ export async function runShadow(sp, input, ctx) {
19
+ const active = sp.active();
20
+ const activeOut = await active.run(input, ctx);
21
+ const shadows = [];
22
+ for (const impl of sp.list()) {
23
+ if (impl.name === active.name)
24
+ continue;
25
+ try {
26
+ shadows.push({ name: impl.name, output: await impl.run(input, ctx) });
27
+ }
28
+ catch (e) {
29
+ shadows.push({ name: impl.name, error: e instanceof Error ? e.message : String(e) });
30
+ }
31
+ }
32
+ return { active: activeOut, activeName: active.name, shadows };
33
+ }
34
+ /** 确定性 A/B 分桶: 同 key 永远同变体(可复现, 不依赖随机). */
35
+ export function assignVariant(key, variants) {
36
+ if (variants.length === 0)
37
+ throw new Error('A/B 变体不能为空');
38
+ const h = createHash('sha256').update(key).digest();
39
+ const bucket = h.readUInt32BE(0) % variants.length;
40
+ return variants[bucket];
41
+ }
42
+ /** A/B 路由: 按 ctx.cycleId 确定性选变体并跑. */
43
+ export async function runAB(sp, variants, input, ctx) {
44
+ const variant = assignVariant(ctx.cycleId, variants);
45
+ const impl = sp.get(variant);
46
+ if (!impl)
47
+ throw new Error(`A/B 变体无实现: ${variant}`);
48
+ return { variant, output: await impl.run(input, ctx) };
49
+ }
50
+ /**
51
+ * 离线回放: 用历史输入集跑某策略, 由打分器产出 fitness 样本, 算总分.
52
+ * 用途: 新策略上线前先离线对比 active(算法草案§7 offline replay).
53
+ */
54
+ export async function offlineReplay(impl, inputs, score, ctxOf, w = DEFAULT_WEIGHTS) {
55
+ const samples = [];
56
+ let i = 0;
57
+ for (const input of inputs) {
58
+ const out = await impl.run(input, ctxOf(i));
59
+ samples.push(score(input, out));
60
+ i += 1;
61
+ }
62
+ return computeFitness(samples, w);
63
+ }
@@ -0,0 +1,2 @@
1
+ export * from './strategyPoint.js';
2
+ export * from './experiment.js';
@@ -0,0 +1,2 @@
1
+ export * from './strategyPoint.js';
2
+ export * from './experiment.js';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * StrategyPoint 框架(M4A-1a). 算法设计内核: 固定 cycle 骨架, 处处可插拔策略点.
3
+ * 每个 StrategyPoint = 一处可换算法(分类/触发/选 gene/变异/...); 多实现注册, 一个 active, 其余可 shadow/A-B.
4
+ * 经验主义: 不先验判优劣, 靠实验台(experiment.ts)按 fitness 选优.
5
+ */
6
+ export interface StrategyContext {
7
+ now: number;
8
+ cycleId: string;
9
+ /** Injected randomness for stochastic strategies (e.g. exploration drift). Defaults to Math.random; inject a seeded rng for deterministic tests. */
10
+ rng?: () => number;
11
+ }
12
+ export interface Strategy<I, O> {
13
+ readonly name: string;
14
+ readonly version: string;
15
+ run(input: I, ctx: StrategyContext): Promise<O> | O;
16
+ }
17
+ export declare class StrategyPoint<I, O> {
18
+ readonly id: string;
19
+ private readonly impls;
20
+ private activeName;
21
+ constructor(id: string, primary: Strategy<I, O>);
22
+ register(s: Strategy<I, O>): this;
23
+ setActive(name: string): this;
24
+ active(): Strategy<I, O>;
25
+ get(name: string): Strategy<I, O> | undefined;
26
+ list(): Strategy<I, O>[];
27
+ names(): string[];
28
+ /** 跑 active, 透传 ctx. */
29
+ run(input: I, ctx: StrategyContext): Promise<O> | O;
30
+ }
@@ -0,0 +1,23 @@
1
+ export class StrategyPoint {
2
+ id;
3
+ impls = new Map();
4
+ activeName;
5
+ constructor(id, primary) {
6
+ this.id = id;
7
+ this.impls.set(primary.name, primary);
8
+ this.activeName = primary.name;
9
+ }
10
+ register(s) { this.impls.set(s.name, s); return this; }
11
+ setActive(name) {
12
+ if (!this.impls.has(name))
13
+ throw new Error(`StrategyPoint(${this.id}) 无实现: ${name}`);
14
+ this.activeName = name;
15
+ return this;
16
+ }
17
+ active() { return this.impls.get(this.activeName); }
18
+ get(name) { return this.impls.get(name); }
19
+ list() { return [...this.impls.values()]; }
20
+ names() { return [...this.impls.keys()]; }
21
+ /** 跑 active, 透传 ctx. */
22
+ run(input, ctx) { return this.active().run(input, ctx); }
23
+ }
@@ -0,0 +1,2 @@
1
+ export * from './trajectory.js';
2
+ export * from './trajectoryExport.js';
@@ -0,0 +1,2 @@
1
+ export * from './trajectory.js';
2
+ export * from './trajectoryExport.js';
@@ -0,0 +1,82 @@
1
+ export interface NativeTraceUsage {
2
+ input_tokens?: number;
3
+ output_tokens?: number;
4
+ cache_creation_input_tokens?: number;
5
+ cache_read_input_tokens?: number;
6
+ }
7
+ export interface NativeTraceRecord {
8
+ event?: unknown;
9
+ ts?: unknown;
10
+ id?: unknown;
11
+ request_id?: unknown;
12
+ route?: unknown;
13
+ provider?: unknown;
14
+ wire_api?: unknown;
15
+ client?: unknown;
16
+ session_id?: unknown;
17
+ response_id?: unknown;
18
+ previous_response_id?: unknown;
19
+ original_model?: unknown;
20
+ chosen_model?: unknown;
21
+ status?: unknown;
22
+ stream?: unknown;
23
+ ttfb_ms?: unknown;
24
+ latency_ms?: unknown;
25
+ usage?: unknown;
26
+ stop_reason?: unknown;
27
+ error?: unknown;
28
+ requestBody?: unknown;
29
+ responseBody?: unknown;
30
+ request_body?: unknown;
31
+ response_body?: unknown;
32
+ redaction?: unknown;
33
+ body_truncated?: unknown;
34
+ reasoning?: unknown;
35
+ tool_calls?: unknown;
36
+ diff?: unknown;
37
+ validation?: unknown;
38
+ }
39
+ export type TraceCoverageStatus = 'covered' | 'missing';
40
+ export interface TraceCoverageItem {
41
+ field: string;
42
+ status: TraceCoverageStatus;
43
+ source?: string;
44
+ }
45
+ export interface TraceTurnDraft {
46
+ trace_id: string | null;
47
+ ts: string | null;
48
+ provider: string | null;
49
+ wire_api: string | null;
50
+ route: string | null;
51
+ client: string | null;
52
+ session_id: string | null;
53
+ request_id: string | null;
54
+ response_id: string | null;
55
+ previous_response_id: string | null;
56
+ original_model: string | null;
57
+ chosen_model: string | null;
58
+ status: number | null;
59
+ stream: boolean | null;
60
+ ttfb_ms: number | null;
61
+ latency_ms: number | null;
62
+ usage?: NativeTraceUsage;
63
+ stop_reason?: string | null;
64
+ error?: string;
65
+ request_body?: unknown;
66
+ response_body?: unknown;
67
+ redaction?: string;
68
+ body_truncated?: boolean;
69
+ reasoning?: unknown;
70
+ tool_calls?: unknown;
71
+ diff?: unknown;
72
+ validation?: unknown;
73
+ }
74
+ export interface TraceTrajectoryDraft {
75
+ schema: 'evolver_trace_trajectory_draft.v1';
76
+ session_id: string | null;
77
+ turns: TraceTurnDraft[];
78
+ coverage: TraceCoverageItem[];
79
+ }
80
+ export declare function traceRecordToTurnDraft(record: NativeTraceRecord): TraceTurnDraft | null;
81
+ export declare function buildTraceTrajectoryDraft(records: readonly NativeTraceRecord[]): TraceTrajectoryDraft;
82
+ export declare function coverageForTurns(turns: readonly TraceTurnDraft[]): TraceCoverageItem[];
@@ -0,0 +1,117 @@
1
+ const REQUIRED_COVERAGE = [
2
+ { field: 'native_request_body', source: 'request_body' },
3
+ { field: 'native_response_body', source: 'response_body' },
4
+ { field: 'provider', source: 'provider' },
5
+ { field: 'wire_api', source: 'wire_api' },
6
+ { field: 'session_id', source: 'session_id' },
7
+ { field: 'usage', source: 'usage' },
8
+ { field: 'status', source: 'status' },
9
+ { field: 'reasoning_summary', source: 'reasoning' },
10
+ { field: 'tool_calls', source: 'tool_calls' },
11
+ { field: 'diff_or_before_after', source: 'diff' },
12
+ { field: 'validation_or_test_commands', source: 'validation' },
13
+ ];
14
+ function stringOrNull(value) {
15
+ return typeof value === 'string' && value.length > 0 ? value : null;
16
+ }
17
+ function numberOrNull(value) {
18
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
19
+ }
20
+ function booleanOrNull(value) {
21
+ return typeof value === 'boolean' ? value : null;
22
+ }
23
+ function isRecord(value) {
24
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
25
+ }
26
+ function parseSerializedBody(value) {
27
+ if (typeof value !== 'string')
28
+ return value;
29
+ try {
30
+ return JSON.parse(value);
31
+ }
32
+ catch {
33
+ return value;
34
+ }
35
+ }
36
+ function usageOrUndefined(value) {
37
+ if (!isRecord(value))
38
+ return undefined;
39
+ const out = {};
40
+ for (const key of ['input_tokens', 'output_tokens', 'cache_creation_input_tokens', 'cache_read_input_tokens']) {
41
+ const raw = value[key];
42
+ if (typeof raw === 'number' && Number.isFinite(raw))
43
+ out[key] = raw;
44
+ }
45
+ return Object.keys(out).length > 0 ? out : undefined;
46
+ }
47
+ function present(value) {
48
+ if (value === undefined || value === null)
49
+ return false;
50
+ if (typeof value === 'string')
51
+ return value.length > 0;
52
+ if (Array.isArray(value))
53
+ return value.length > 0;
54
+ if (typeof value === 'object')
55
+ return Object.keys(value).length > 0;
56
+ return true;
57
+ }
58
+ export function traceRecordToTurnDraft(record) {
59
+ if (record.event !== 'llm_turn')
60
+ return null;
61
+ const usage = usageOrUndefined(record.usage);
62
+ const requestBody = parseSerializedBody(record.requestBody !== undefined ? record.requestBody : record.request_body);
63
+ const responseBody = parseSerializedBody(record.responseBody !== undefined ? record.responseBody : record.response_body);
64
+ const reasoning = parseSerializedBody(record.reasoning);
65
+ const toolCalls = parseSerializedBody(record.tool_calls);
66
+ const diff = parseSerializedBody(record.diff);
67
+ const validation = parseSerializedBody(record.validation);
68
+ return {
69
+ trace_id: stringOrNull(record.id),
70
+ ts: stringOrNull(record.ts),
71
+ provider: stringOrNull(record.provider),
72
+ wire_api: stringOrNull(record.wire_api),
73
+ route: stringOrNull(record.route),
74
+ client: stringOrNull(record.client),
75
+ session_id: stringOrNull(record.session_id),
76
+ request_id: stringOrNull(record.request_id),
77
+ response_id: stringOrNull(record.response_id),
78
+ previous_response_id: stringOrNull(record.previous_response_id),
79
+ original_model: stringOrNull(record.original_model),
80
+ chosen_model: stringOrNull(record.chosen_model),
81
+ status: numberOrNull(record.status),
82
+ stream: booleanOrNull(record.stream),
83
+ ttfb_ms: numberOrNull(record.ttfb_ms),
84
+ latency_ms: numberOrNull(record.latency_ms),
85
+ ...(usage ? { usage } : {}),
86
+ ...(record.stop_reason !== undefined ? { stop_reason: stringOrNull(record.stop_reason) } : {}),
87
+ ...(typeof record.error === 'string' ? { error: record.error } : {}),
88
+ ...(requestBody !== undefined ? { request_body: requestBody } : {}),
89
+ ...(responseBody !== undefined ? { response_body: responseBody } : {}),
90
+ ...(typeof record.redaction === 'string' ? { redaction: record.redaction } : {}),
91
+ ...(typeof record.body_truncated === 'boolean' ? { body_truncated: record.body_truncated } : {}),
92
+ ...(reasoning !== undefined ? { reasoning } : {}),
93
+ ...(toolCalls !== undefined ? { tool_calls: toolCalls } : {}),
94
+ ...(diff !== undefined ? { diff } : {}),
95
+ ...(validation !== undefined ? { validation } : {}),
96
+ };
97
+ }
98
+ export function buildTraceTrajectoryDraft(records) {
99
+ const turns = records.map(traceRecordToTurnDraft).filter((turn) => turn !== null);
100
+ const sessionIds = [...new Set(turns.map((turn) => turn.session_id).filter((value) => typeof value === 'string' && value.length > 0))];
101
+ return {
102
+ schema: 'evolver_trace_trajectory_draft.v1',
103
+ session_id: sessionIds.length === 1 ? sessionIds[0] : null,
104
+ turns,
105
+ coverage: coverageForTurns(turns),
106
+ };
107
+ }
108
+ export function coverageForTurns(turns) {
109
+ return REQUIRED_COVERAGE.map(({ field, source }) => {
110
+ const hasField = turns.some((turn) => present(turn[source]));
111
+ return {
112
+ field,
113
+ status: hasField ? 'covered' : 'missing',
114
+ ...(hasField ? { source: 'llm_trace' } : {}),
115
+ };
116
+ });
117
+ }