@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24

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 (327) hide show
  1. package/README.md +102 -194
  2. package/README.zh-CN.md +115 -210
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-bootstrap.js +466 -35
  5. package/bin/gotry-inner.js +255 -36
  6. package/bin/gotry-process-liveness.d.ts +27 -0
  7. package/bin/gotry-process-liveness.js +97 -0
  8. package/bin/gotry-runtime-resolution.d.ts +1 -1
  9. package/bin/gotry-runtime-resolution.js +2 -2
  10. package/client/client.js +174 -0
  11. package/cordis.gotry-patch.yml +70 -93
  12. package/dist/capabilities/agent-reach-deep.js +45 -122
  13. package/dist/capabilities/agent-reach.js +63 -148
  14. package/dist/capabilities/anything.js +89 -75
  15. package/dist/capabilities/artifacts.js +123 -104
  16. package/dist/capabilities/channel-health.js +65 -45
  17. package/dist/capabilities/channel-registry.js +108 -87
  18. package/dist/capabilities/doctor.js +357 -235
  19. package/dist/capabilities/effect.js +223 -309
  20. package/dist/capabilities/fact-log.js +28 -14
  21. package/dist/capabilities/flyai.js +245 -156
  22. package/dist/capabilities/ground-transfer.js +509 -0
  23. package/dist/capabilities/hbcli.js +144 -148
  24. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  25. package/dist/capabilities/incident-log.js +131 -61
  26. package/dist/capabilities/model-override.js +30 -10
  27. package/dist/capabilities/opensky.js +54 -43
  28. package/dist/capabilities/resilience.js +50 -32
  29. package/dist/capabilities/session/action-cache.js +50 -44
  30. package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
  31. package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
  32. package/dist/capabilities/session/adapters/dida-portal.js +206 -0
  33. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  34. package/dist/capabilities/session/adapters/rail-12306.js +169 -112
  35. package/dist/capabilities/session/benchmark.js +85 -149
  36. package/dist/capabilities/session/extension-bridge.js +320 -259
  37. package/dist/capabilities/session/extension-channel.js +57 -76
  38. package/dist/capabilities/session/extension-distribution.js +115 -119
  39. package/dist/capabilities/session/extract.js +31 -21
  40. package/dist/capabilities/session/golden-score.js +55 -33
  41. package/dist/capabilities/session/health-watch.js +146 -154
  42. package/dist/capabilities/session/read-guard.js +55 -47
  43. package/dist/capabilities/session/static-flight-golden.js +49 -36
  44. package/dist/capabilities/session/transport.js +54 -61
  45. package/dist/capabilities/session/wizard.js +63 -93
  46. package/dist/capabilities/session-consent.js +143 -47
  47. package/dist/capabilities/session-login.js +95 -126
  48. package/dist/capabilities/session-search.js +391 -253
  49. package/dist/capabilities/sponsor-plugin.js +341 -0
  50. package/dist/capabilities/visa-policy.js +87 -73
  51. package/dist/capabilities/weather.js +163 -235
  52. package/dist/scripts/action-cache-tests.js +24 -59
  53. package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
  54. package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
  55. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  56. package/dist/scripts/agent-reach-tests.js +26 -18
  57. package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
  58. package/dist/scripts/anything-tests.js +58 -62
  59. package/dist/scripts/artifact-client-contract-tests.js +69 -0
  60. package/dist/scripts/artifacts-capability-tests.js +235 -0
  61. package/dist/scripts/async-collect.js +26 -15
  62. package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
  63. package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
  64. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
  65. package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
  66. package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
  67. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
  68. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
  69. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
  70. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
  71. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
  72. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
  73. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
  74. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
  75. package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
  76. package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
  77. package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
  78. package/dist/scripts/booking-executor-tests.js +80 -0
  79. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  80. package/dist/scripts/booking-saga-tests.js +61 -123
  81. package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
  82. package/dist/scripts/bootstrap-tests.js +1383 -128
  83. package/dist/scripts/build-changelog.js +89 -130
  84. package/dist/scripts/build-metrics-report.js +135 -143
  85. package/dist/scripts/changelog-tests.js +35 -82
  86. package/dist/scripts/channel-probe-tests.js +44 -116
  87. package/dist/scripts/channel-probe.js +84 -167
  88. package/dist/scripts/channel-registry-tests.js +97 -185
  89. package/dist/scripts/companion-tests.js +25 -70
  90. package/dist/scripts/dida-cookie-probe.js +87 -0
  91. package/dist/scripts/dida-sw-probe.js +114 -0
  92. package/dist/scripts/diff-test.js +17 -12
  93. package/dist/scripts/doctor-tests.js +428 -120
  94. package/dist/scripts/dsh-artifact-e2e.js +150 -0
  95. package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
  96. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  97. package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
  98. package/dist/scripts/dsh-runtime-closure.js +75 -63
  99. package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
  100. package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
  101. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  102. package/dist/scripts/effect-tests.js +165 -552
  103. package/dist/scripts/engine-run.js +1 -3
  104. package/dist/scripts/engine-tests.js +16 -10
  105. package/dist/scripts/evaluation-cadence-tests.js +69 -242
  106. package/dist/scripts/evaluation-contract-tests.js +193 -499
  107. package/dist/scripts/extension-distribution-cli.js +19 -15
  108. package/dist/scripts/extension-distribution-tests.js +98 -211
  109. package/dist/scripts/extension-tests.js +366 -389
  110. package/dist/scripts/fact-gate-tests.js +725 -263
  111. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  112. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  113. package/dist/scripts/flight-malformed-tests.js +159 -0
  114. package/dist/scripts/flyai-tests.js +325 -68
  115. package/dist/scripts/g5-guard-tests.js +131 -0
  116. package/dist/scripts/g5-guard.js +189 -0
  117. package/dist/scripts/gen-lunar-anchors.js +106 -0
  118. package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
  119. package/dist/scripts/ground-transfer-tests.js +956 -0
  120. package/dist/scripts/hbcli-e2e-tests.js +71 -90
  121. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  122. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  123. package/dist/scripts/hbcli-tests.js +41 -115
  124. package/dist/scripts/health-watch-cli.js +42 -28
  125. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  126. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  127. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  128. package/dist/scripts/i18n-tests.js +16 -9
  129. package/dist/scripts/incident-tests.js +281 -87
  130. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  131. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  132. package/dist/scripts/issue-271-liveness-tests.js +547 -0
  133. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  134. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  135. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  136. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  137. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  138. package/dist/scripts/journey-tests.js +22 -27
  139. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  140. package/dist/scripts/kernel-manifest-gate.js +126 -0
  141. package/dist/scripts/kernel-manifest-tests.js +212 -0
  142. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  143. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  144. package/dist/scripts/kernel-manifest.js +369 -0
  145. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  146. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  147. package/dist/scripts/ledger-tests.js +325 -340
  148. package/dist/scripts/ledger-workflow-crash.js +10 -20
  149. package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
  150. package/dist/scripts/memory-capture-tests.js +242 -61
  151. package/dist/scripts/memory-decay-tests.js +18 -52
  152. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  153. package/dist/scripts/memory-lifecycle.js +196 -0
  154. package/dist/scripts/memory-metrics.js +24 -12
  155. package/dist/scripts/memory-value-report.js +474 -222
  156. package/dist/scripts/memory-value-tests.js +328 -0
  157. package/dist/scripts/metrics-report-tests.js +62 -199
  158. package/dist/scripts/model-override-e2e.js +66 -105
  159. package/dist/scripts/nightly-evidence-tests.js +64 -89
  160. package/dist/scripts/nightly-evidence.js +108 -111
  161. package/dist/scripts/nudge-digest.js +39 -28
  162. package/dist/scripts/onboarding-tests.js +63 -88
  163. package/dist/scripts/opensky-check.js +18 -13
  164. package/dist/scripts/opensky-tests.js +28 -28
  165. package/dist/scripts/persona-surface-guard-tests.js +37 -12
  166. package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
  167. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  168. package/dist/scripts/price-drift-tests.js +126 -227
  169. package/dist/scripts/price-drift-watch.js +209 -253
  170. package/dist/scripts/probe-poi-tests.js +52 -45
  171. package/dist/scripts/product-metrics.js +187 -273
  172. package/dist/scripts/publish-preverify.js +47 -41
  173. package/dist/scripts/realtime-pricing-tests.js +37 -76
  174. package/dist/scripts/replay-async.js +20 -18
  175. package/dist/scripts/replay-real.js +16 -18
  176. package/dist/scripts/replay.js +52 -82
  177. package/dist/scripts/session-attach-diagnose.js +16 -12
  178. package/dist/scripts/session-attach-poc.js +34 -37
  179. package/dist/scripts/session-benchmark.js +66 -147
  180. package/dist/scripts/session-dida-live-e2e.js +151 -0
  181. package/dist/scripts/session-extract-tests.js +14 -35
  182. package/dist/scripts/session-login.js +14 -13
  183. package/dist/scripts/session-tests.js +545 -583
  184. package/dist/scripts/sf-live-benchmark.js +92 -110
  185. package/dist/scripts/sf-live-cli-tests.js +2 -9
  186. package/dist/scripts/sf-soft-score-tests.js +19 -60
  187. package/dist/scripts/sf-summary-tests.js +149 -0
  188. package/dist/scripts/sf-summary.js +466 -84
  189. package/dist/scripts/skeleton-check.js +16 -14
  190. package/dist/scripts/skeleton-integration-test.js +11 -10
  191. package/dist/scripts/skills-contract-tests.js +51 -46
  192. package/dist/scripts/smoke-session-gate-tests.js +12 -20
  193. package/dist/scripts/smoke.js +541 -422
  194. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  195. package/dist/scripts/state-cli-tests.js +226 -101
  196. package/dist/scripts/state-cli.js +549 -212
  197. package/dist/scripts/static-golden-tests.js +46 -180
  198. package/dist/scripts/time-eval-tests.js +210 -192
  199. package/dist/scripts/travel-timeline-tests.js +29 -93
  200. package/dist/scripts/turn-handoff-collect-tests.js +46 -93
  201. package/dist/scripts/turn-handoff-collect.js +71 -99
  202. package/dist/scripts/turn-policy-tests.js +28 -29
  203. package/dist/scripts/typed-contract-canary.js +77 -162
  204. package/dist/scripts/tz-resolver-sanity.js +50 -0
  205. package/dist/scripts/tz-resolver-tests.js +584 -0
  206. package/dist/scripts/unified-tests.js +23 -19
  207. package/dist/scripts/visa-policy-tests.js +31 -44
  208. package/dist/scripts/weather-tests.js +121 -543
  209. package/dist/scripts/wish-channel-gate-tests.js +29 -86
  210. package/dist/scripts/write-gate-tests.js +522 -0
  211. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  212. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  213. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  214. package/dist/scripts/z3-race-tests.js +18 -12
  215. package/dist/src/artifact-gate.js +652 -221
  216. package/dist/src/backend/kernel.js +91 -0
  217. package/dist/src/backend/modules/booking-copilot.js +55 -0
  218. package/dist/src/backend/modules/booking-executor.js +124 -0
  219. package/dist/src/backend/modules/session-search.js +194 -0
  220. package/dist/src/benchmark-agent-conformance.js +296 -262
  221. package/dist/src/benchmark-environment-bridge.js +298 -413
  222. package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
  223. package/dist/src/benchmark-tool-isolation.js +94 -67
  224. package/dist/src/bookable-facts.js +299 -174
  225. package/dist/src/booking-saga.js +64 -120
  226. package/dist/src/booking-surface/availability-policy.js +443 -576
  227. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  228. package/dist/src/booking-surface/contracts.js +24 -80
  229. package/dist/src/booking-surface/dsh-planner.js +510 -281
  230. package/dist/src/booking-surface/dsh-plugin.js +17 -1
  231. package/dist/src/booking-surface/error-codes.js +82 -10
  232. package/dist/src/booking-surface/index.js +0 -3
  233. package/dist/src/booking-surface/profile.js +10 -54
  234. package/dist/src/booking-surface/recovery-chain.js +367 -0
  235. package/dist/src/booking-surface/runtime.js +765 -891
  236. package/dist/src/booking-surface/server.js +200 -251
  237. package/dist/src/booking-surface/startup.js +58 -54
  238. package/dist/src/booking-surface/validation.js +150 -218
  239. package/dist/src/bridge.js +20 -13
  240. package/dist/src/companions.js +48 -74
  241. package/dist/src/contracts.js +21 -43
  242. package/dist/src/dsh-llm.js +82 -120
  243. package/dist/src/engine.js +253 -239
  244. package/dist/src/evaluation-cadence.js +72 -209
  245. package/dist/src/evaluation-contracts.js +220 -843
  246. package/dist/src/flight-pack-adapter.js +54 -0
  247. package/dist/src/flight-pack-contract.js +2 -0
  248. package/dist/src/gotry-backend.js +70 -0
  249. package/dist/src/hotel-date-gate.js +118 -0
  250. package/dist/src/i18n.js +18 -7
  251. package/dist/src/index.js +1425 -1629
  252. package/dist/src/journey.js +132 -81
  253. package/dist/src/ledger-repair-apply.js +538 -0
  254. package/dist/src/ledger-repair-plan.js +463 -0
  255. package/dist/src/loop.js +257 -166
  256. package/dist/src/memory-capture.js +180 -32
  257. package/dist/src/memory-decay.js +30 -14
  258. package/dist/src/memory-lifecycle.js +1310 -0
  259. package/dist/src/memory-utility.js +27 -40
  260. package/dist/src/mock-llm.js +36 -61
  261. package/dist/src/model.js +81 -36
  262. package/dist/src/realtime-pricing.js +63 -72
  263. package/dist/src/slot-spec.js +84 -106
  264. package/dist/src/state-ledger.js +310 -471
  265. package/dist/src/subagent-job-id-guard.js +58 -0
  266. package/dist/src/time-anchor.js +231 -33
  267. package/dist/src/tool-packet.js +20 -8
  268. package/dist/src/travel-slots.js +58 -35
  269. package/dist/src/travel-timeline.js +44 -63
  270. package/dist/src/turn-deadline.js +121 -127
  271. package/dist/src/turn-policy.js +61 -89
  272. package/dist/src/tz-resolver.js +143 -0
  273. package/dist/src/unified.js +504 -381
  274. package/dist/src/wish-pool.js +39 -14
  275. package/dist/src/write-gate.js +495 -0
  276. package/dist/src/z3-shared.js +273 -7
  277. package/extension/README.md +4 -3
  278. package/extension/background.js +125 -14
  279. package/extension/content-bridge.js +23 -1
  280. package/extension/content-main.js +15 -4
  281. package/extension/manifest.json +16 -8
  282. package/package.json +251 -220
  283. package/ts/capabilities/agent-reach.ts +4 -4
  284. package/ts/capabilities/anything.ts +1 -1
  285. package/ts/capabilities/artifacts.ts +46 -9
  286. package/ts/capabilities/flyai.ts +156 -37
  287. package/ts/capabilities/hbcli.ts +1 -1
  288. package/ts/capabilities/incident-log.ts +88 -59
  289. package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
  290. package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
  291. package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
  292. package/ts/capabilities/session/extension-bridge.ts +226 -101
  293. package/ts/capabilities/session/extension-channel.ts +22 -9
  294. package/ts/capabilities/session/wizard.ts +2 -2
  295. package/ts/capabilities/session-consent.ts +98 -14
  296. package/ts/capabilities/session-login.ts +1 -0
  297. package/ts/capabilities/session-search.ts +315 -31
  298. package/ts/dsh-runtime/vendor/README.md +8 -4
  299. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
  300. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
  301. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
  302. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
  303. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
  304. package/ts/package.json +236 -16
  305. package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
  306. package/ts/scripts/state-cli.ts +418 -51
  307. package/ts/src/artifact-gate.ts +561 -15
  308. package/ts/src/benchmark-environment-bridge.ts +37 -20
  309. package/ts/src/bookable-facts.ts +183 -7
  310. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  311. package/ts/src/booking-surface/dsh-planner.ts +240 -32
  312. package/ts/src/booking-surface/dsh-plugin.js +17 -1
  313. package/ts/src/booking-surface/error-codes.ts +75 -0
  314. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  315. package/ts/src/booking-surface/server.ts +84 -51
  316. package/ts/src/booking-surface/validation.ts +2 -0
  317. package/ts/src/contracts.ts +12 -2
  318. package/ts/src/dsh-llm.ts +4 -8
  319. package/ts/src/index.ts +609 -90
  320. package/ts/src/loop.ts +116 -7
  321. package/ts/src/memory-capture.ts +227 -3
  322. package/ts/src/mock-llm.ts +4 -8
  323. package/ts/src/model.ts +88 -2
  324. package/ts/src/state-ledger.ts +47 -29
  325. package/ts/src/subagent-job-id-guard.ts +72 -0
  326. package/ts/src/time-anchor.ts +217 -13
  327. package/ts/src/unified.ts +316 -54
@@ -1,57 +1,61 @@
1
+ /**
2
+ * issue #77 E2E:LLM_MODEL 对 dsh 会话面生效的端到端证据。
3
+ *
4
+ * 四场景(全部走真实路径:clean package bin → dsh headless →
5
+ * llm-deepseek → 本地 mock OpenAI 兼容中转,中转落盘每个请求体的 model 字段):
6
+ *
7
+ * 1. 设 LLM_MODEL,干净 DSH_HOME → 请求体 model = LLM_MODEL
8
+ * 2. 设 LLM_MODEL,DSH_HOME 预置用户层选择 → 请求体 model = LLM_MODEL
9
+ * (用户层 ~/.dsh 选择被 .env 显式意图压过——#77 发现场景的正面回归)
10
+ * 3. 不设 LLM_MODEL,干净 DSH_HOME → 请求体 model = deepseek-v4-flash
11
+ * (dsh 组合默认,默认路径面不变)
12
+ * 4. 不设 LLM_MODEL,DSH_HOME 预置用户层选择 → 请求体 model = 用户层模型
13
+ * (用户 web 选择不被误伤,默认路径面不变)
14
+ *
15
+ * 隔离纪律(巡检/测试状态纪律):DSH_HOME 指向临时目录(会话/设置零污染);
16
+ * mock 模型只回固定文本、从不调工具 → gotry-state 零写入(动机/愿望池不动);
17
+ * 环境变量直接 export(不走 .env 文件,repo 模式 .env 加载路径已由 #48 覆盖)。
18
+ *
19
+ * 形态:只接受 clean npm 安装形态——插件走 dist/ 纯 JS,不再通过移动源码树依赖
20
+ * 来伪装 package mode。调用方须提供当前 tarball 安装出的 bin。
21
+ *
22
+ * 运行(在仓根):GOTRY_MODEL_OVERRIDE_E2E_BIN=/clean/node_modules/.bin/gotry
23
+ * npx tsx ts/scripts/model-override-e2e.ts
24
+ */
1
25
  import { createServer } from 'node:http';
2
26
  import { existsSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs';
3
27
  import { tmpdir } from 'node:os';
4
28
  import { join } from 'node:path';
5
29
  import { spawn } from 'node:child_process';
6
30
  const BOOT_TIMEOUT_MS = 120_000;
31
+ /** mock OpenAI 兼容中转:落盘请求体 model,回最小合法 SSE 流 */
7
32
  async function startMockRelay() {
8
33
  const bodies = [];
9
- const server = createServer((req, res)=>{
34
+ const server = createServer((req, res) => {
10
35
  if (req.method !== 'POST' || !req.url?.endsWith('/chat/completions')) {
11
36
  res.writeHead(404).end('not found');
12
37
  return;
13
38
  }
14
39
  const chunks = [];
15
- req.on('data', (c)=>chunks.push(c));
16
- req.on('end', ()=>{
40
+ req.on('data', (c) => chunks.push(c));
41
+ req.on('end', () => {
17
42
  try {
18
43
  bodies.push(JSON.parse(Buffer.concat(chunks).toString('utf-8')));
19
- } catch {
44
+ }
45
+ catch {
20
46
  bodies.push({});
21
47
  }
22
- const chunk = (delta, finish)=>`data: ${JSON.stringify({
23
- id: 'e2e',
24
- object: 'chat.completion.chunk',
25
- created: 1,
26
- model: 'mock',
27
- choices: [
28
- {
29
- index: 0,
30
- delta,
31
- finish_reason: finish
32
- }
33
- ]
34
- })}\n\n`;
35
- res.writeHead(200, {
36
- 'content-type': 'text/event-stream',
37
- 'cache-control': 'no-cache'
38
- });
39
- res.write(chunk({
40
- role: 'assistant',
41
- content: 'ok'
42
- }, null));
48
+ const chunk = (delta, finish) => `data: ${JSON.stringify({ id: 'e2e', object: 'chat.completion.chunk', created: 1, model: 'mock', choices: [{ index: 0, delta, finish_reason: finish }] })}\n\n`;
49
+ res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' });
50
+ res.write(chunk({ role: 'assistant', content: 'ok' }, null));
43
51
  res.write(chunk({}, 'stop'));
44
52
  res.write('data: [DONE]\n\n');
45
53
  res.end();
46
54
  });
47
55
  });
48
- await new Promise((resolve)=>server.listen(0, '127.0.0.1', resolve));
56
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
49
57
  const port = server.address().port;
50
- return {
51
- port,
52
- bodies,
53
- close: ()=>new Promise((resolve)=>server.close(()=>resolve()))
54
- };
58
+ return { port, bodies, close: () => new Promise((resolve) => server.close(() => resolve())) };
55
59
  }
56
60
  async function runScenario(s, packageBin) {
57
61
  const relay = await startMockRelay();
@@ -59,70 +63,45 @@ async function runScenario(s, packageBin) {
59
63
  const childCwd = mkdtempSync(join(tmpdir(), 'gotry-77-cwd-'));
60
64
  try {
61
65
  if (s.userLayerModel) {
66
+ // 预置用户层设置(等价于用户在 dsh web UI 选过模型并持久化)
62
67
  writeFileSync(join(dshHome, 'settings.yaml'), `agent-default-model:\n provider: deepseek-official\n model: ${s.userLayerModel}\n`);
63
68
  }
64
69
  const env = {
65
70
  ...process.env,
66
71
  LLM_API_KEY: 'e2e-mock-key',
67
72
  LLM_BASE_URL: `http://127.0.0.1:${relay.port}/v1`,
68
- DSH_HOME: dshHome
73
+ DSH_HOME: dshHome,
69
74
  };
70
- delete env['GOTRY_LLM_MODEL'];
71
- if (s.llmModel) env['LLM_MODEL'] = s.llmModel;
72
- else delete env['LLM_MODEL'];
73
- const child = spawn(packageBin, [
74
- 'reply with the word ok'
75
- ], {
75
+ delete env['GOTRY_LLM_MODEL']; // 防宿主环境串扰;只认本场景显式设定
76
+ if (s.llmModel)
77
+ env['LLM_MODEL'] = s.llmModel;
78
+ else
79
+ delete env['LLM_MODEL'];
80
+ const child = spawn(packageBin, ['reply with the word ok'], {
76
81
  env,
77
82
  cwd: childCwd,
78
- stdio: [
79
- 'ignore',
80
- 'pipe',
81
- 'pipe'
82
- ]
83
+ stdio: ['ignore', 'pipe', 'pipe'],
83
84
  });
84
85
  let logTail = '';
85
- child.stdout.on('data', (d)=>{
86
- logTail = (logTail + d).slice(-2000);
87
- });
88
- child.stderr.on('data', (d)=>{
89
- logTail = (logTail + d).slice(-2000);
90
- });
91
- const exit = await new Promise((resolve)=>{
92
- const timer = setTimeout(()=>{
93
- child.kill('SIGKILL');
94
- resolve(null);
95
- }, BOOT_TIMEOUT_MS);
96
- child.on('exit', (code)=>{
97
- clearTimeout(timer);
98
- resolve(code);
99
- });
86
+ child.stdout.on('data', (d) => { logTail = (logTail + d).slice(-2000); });
87
+ child.stderr.on('data', (d) => { logTail = (logTail + d).slice(-2000); });
88
+ const exit = await new Promise((resolve) => {
89
+ const timer = setTimeout(() => { child.kill('SIGKILL'); resolve(null); }, BOOT_TIMEOUT_MS);
90
+ child.on('exit', (code) => { clearTimeout(timer); resolve(code); });
100
91
  });
101
92
  if (relay.bodies.length === 0) {
102
- return {
103
- ok: false,
104
- detail: `中转未收到任何请求(exit=${exit});尾部日志: ${logTail.slice(-400)}`
105
- };
93
+ return { ok: false, detail: `中转未收到任何请求(exit=${exit});尾部日志: ${logTail.slice(-400)}` };
106
94
  }
107
- const models = relay.bodies.map((b)=>String(b.model));
108
- const bad = models.filter((m)=>m !== s.expectModel);
109
- return bad.length === 0 ? {
110
- ok: true,
111
- detail: `${models.length} 个请求体 model 全部 = ${s.expectModel}(exit=${exit})`
112
- } : {
113
- ok: false,
114
- detail: `请求体 model 不符:期望 ${s.expectModel},实际 ${JSON.stringify(models)}`
115
- };
116
- } finally{
95
+ const models = relay.bodies.map((b) => String(b.model));
96
+ const bad = models.filter((m) => m !== s.expectModel);
97
+ return bad.length === 0
98
+ ? { ok: true, detail: `${models.length} 个请求体 model 全部 = ${s.expectModel}(exit=${exit})` }
99
+ : { ok: false, detail: `请求体 model 不符:期望 ${s.expectModel},实际 ${JSON.stringify(models)}` };
100
+ }
101
+ finally {
117
102
  await relay.close();
118
- rmSync(dshHome, {
119
- recursive: true,
120
- force: true
121
- });
122
- rmSync(childCwd, {
123
- recursive: true,
124
- force: true
125
- });
103
+ rmSync(dshHome, { recursive: true, force: true });
104
+ rmSync(childCwd, { recursive: true, force: true });
126
105
  }
127
106
  }
128
107
  async function main() {
@@ -135,34 +114,19 @@ async function main() {
135
114
  }
136
115
  async function runAll(packageBin) {
137
116
  const scenarios = [
138
- {
139
- name: ' 设 LLM_MODEL(干净 DSH_HOME)',
140
- llmModel: 'gotry-e2e-model',
141
- expectModel: 'gotry-e2e-model'
142
- },
143
- {
144
- name: '② 设 LLM_MODEL + 用户层已选模型',
145
- llmModel: 'gotry-e2e-model',
146
- userLayerModel: 'user-picked-model',
147
- expectModel: 'gotry-e2e-model'
148
- },
149
- {
150
- name: '③ 不设 LLM_MODEL(干净 DSH_HOME)',
151
- expectModel: 'deepseek-v4-flash'
152
- },
153
- {
154
- name: '④ 不设 LLM_MODEL + 用户层已选模型',
155
- userLayerModel: 'user-picked-model',
156
- expectModel: 'user-picked-model'
157
- }
117
+ { name: '① 设 LLM_MODEL(干净 DSH_HOME)', llmModel: 'gotry-e2e-model', expectModel: 'gotry-e2e-model' },
118
+ { name: ' 设 LLM_MODEL + 用户层已选模型', llmModel: 'gotry-e2e-model', userLayerModel: 'user-picked-model', expectModel: 'gotry-e2e-model' },
119
+ { name: '③ 不设 LLM_MODEL(干净 DSH_HOME)', expectModel: 'deepseek-v4-flash' },
120
+ { name: '④ 不设 LLM_MODEL + 用户层已选模型', userLayerModel: 'user-picked-model', expectModel: 'user-picked-model' },
158
121
  ];
159
122
  let fail = 0;
160
- for (const s of scenarios){
123
+ for (const s of scenarios) {
161
124
  process.stdout.write(`${s.name} … `);
162
125
  const t0 = Date.now();
163
126
  const r = await runScenario(s, packageBin);
164
127
  console.log(`${r.ok ? 'PASS' : 'FAIL'}(${((Date.now() - t0) / 1000).toFixed(1)}s)${r.detail}`);
165
- if (!r.ok) fail = 1;
128
+ if (!r.ok)
129
+ fail = 1;
166
130
  }
167
131
  if (fail) {
168
132
  console.error('\nMODEL OVERRIDE E2E FAIL');
@@ -171,6 +135,3 @@ async function runAll(packageBin) {
171
135
  console.log('\nMODEL OVERRIDE E2E OK');
172
136
  }
173
137
  await main();
174
-
175
-
176
- //# sourceURL=ts/scripts/model-override-e2e.ts
@@ -1,14 +1,29 @@
1
+ /**
2
+ * M3 nightly 证据生产器合同测试(run-all §35,issue #22 验收⑥「nightly real-LLM 与成本质量证据可复跑」)。
3
+ * 全确定性、零网络、零 API 花费:
4
+ * - 封存价表仅可审计换算(peak 保守上界),未知模型 fail-closed,usage 缺失 fail-closed;
5
+ * - run_key 确定性 + 记录必须过消费方 parseNightlyRun(生产器写的就是评分器读的);
6
+ * - 无凭证 = waiting_external_evidence,不写任何文件(issue #22 停机纪律);
7
+ * - --dry-run mock 全链演练可跑通且绝不落盘。
8
+ * 真跑(花钱)不在 CI:heartbeat/founder 手动 `npx tsx scripts/nightly-evidence.ts`。
9
+ *
10
+ * 价表 schema 演进(issue #49):v1 (legacy DeepSeek only) / v2 (provider-aware;DeepSeek+MiniMax);
11
+ * 本测试在 v2 形态下覆盖:DeepSeek alias、裸模型名、MiniMax 跨 provider 命中、跨 provider 守门、
12
+ * MiniMax flat_no_offpeak 纪律同价守门、未声明 schema_version 拒绝、unknown model fail-closed、
13
+ * usage 缺失 fail-closed、run_key 确定性 + 消费方 parseNightlyRun、无凭证 waiting、dry-run 零落盘。
14
+ */
1
15
  import assert from 'node:assert/strict';
2
16
  import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
17
  import { join } from 'node:path';
4
18
  import { tmpdir } from 'node:os';
5
- import { buildNightlyRunRecord, loadPriceTable, loadPromptSet, priceRunCost, runNightlyEvidence } from './nightly-evidence.js';
19
+ import { buildNightlyRunRecord, loadPriceTable, loadPromptSet, priceRunCost, runNightlyEvidence, } from './nightly-evidence.js';
6
20
  import { parseNightlyRun } from './product-metrics.js';
7
21
  let passed = 0;
8
22
  function ok(label) {
9
23
  passed += 1;
10
24
  console.log(` ok ${label}`);
11
25
  }
26
+ // ---- 1. 封存文件可加载,schema v2 + provider-aware alias/裸模型双查找(以仓库内真正随发的数据文件为夹具) ----
12
27
  const priceTable = loadPriceTable('data/llm-price-table.json');
13
28
  const promptSet = loadPromptSet('data/m3-nightly-prompts.json');
14
29
  assert.equal(priceTable.schema_version, 'gotry_llm_price_table_v2');
@@ -20,100 +35,76 @@ assert.ok(priceTable.prices['minimax:MiniMax-M3'], 'qualified 键 minimax:MiniMa
20
35
  assert.ok(priceTable.aliases['deepseek-chat'], 'deepseek-chat 必须在 aliases 表');
21
36
  assert.equal(priceTable.aliases['deepseek-chat'], 'deepseek-v4-flash');
22
37
  ok('v2 schema 载入 + 裸键 + qualified 键 + alias 双查找');
23
- for (const [name, entry] of Object.entries(priceTable.prices)){
38
+ // ---- 2. 保守上界不变量:每个价目 hit<=miss 且 offpeak<=peak(只高不低) ----
39
+ for (const [name, entry] of Object.entries(priceTable.prices)) {
24
40
  assert.ok(entry.input_cache_hit_usd_per_1m_peak <= entry.input_cache_miss_usd_per_1m_peak, `${name} hit<=miss`);
25
41
  assert.ok(entry.output_usd_per_1m_offpeak <= entry.output_usd_per_1m_peak, `${name} offpeak<=peak`);
26
42
  }
27
43
  ok('价目保守上界不变量(hit<=miss, offpeak<=peak)');
44
+ // ---- 3. 换算数学:deepseek-chat -> v4-flash peak(miss 1M + 1M out = 1.76) ----
28
45
  const baseUsage = {
29
46
  calls: 3,
30
47
  inputTokens: 1_000_000,
31
48
  outputTokens: 1_000_000,
32
49
  inputCacheHitTokens: 0,
33
50
  inputCacheMissTokens: 1_000_000,
34
- responsesMissingUsage: 0
51
+ responsesMissingUsage: 0,
35
52
  };
36
53
  const cost = priceRunCost('deepseek-chat', baseUsage, priceTable);
37
- assert.equal(cost, 1.76);
54
+ assert.equal(cost, 1.76); // 0.44 + 1.32
38
55
  ok('peak miss 换算 0.44+1.32=1.76(经 alias)');
39
- const hitUsage = {
40
- ...baseUsage,
41
- inputCacheHitTokens: 1_000_000,
42
- inputCacheMissTokens: 0
43
- };
44
- assert.equal(priceRunCost('deepseek-chat', hitUsage, priceTable), 1.334);
56
+ const hitUsage = { ...baseUsage, inputCacheHitTokens: 1_000_000, inputCacheMissTokens: 0 };
57
+ assert.equal(priceRunCost('deepseek-chat', hitUsage, priceTable), 1.334); // 0.014+1.32
45
58
  ok('peak hit 换算 0.014+1.32=1.334');
59
+ // ---- 3b. MiniMax-M3 peak 换算(issue #49 验收):miss 1M + 1M out = 3.00(0.60+2.40,ADR-11 保守上界) ----
46
60
  const m3cost = priceRunCost('MiniMax-M3', baseUsage, priceTable);
47
- assert.equal(m3cost, 3.00);
61
+ assert.equal(m3cost, 3.00); // 0.60 + 2.40
48
62
  ok('MiniMax-M3 peak miss 换算 0.60+2.40=3.00');
49
63
  const m3hit = priceRunCost('MiniMax-M3', hitUsage, priceTable);
50
- assert.equal(m3hit, 2.52);
64
+ assert.equal(m3hit, 2.52); // 0.12+2.40
51
65
  ok('MiniMax-M3 peak hit 换算 0.12+2.40=2.52');
66
+ // ---- 3c. MiniMax flat_no_offpeak 纪律:peak 与 offpeak 列同价 —— 给定用量下两种策略产出同一价格 ----
52
67
  const m3prov = priceTable.prices['minimax:MiniMax-M3'];
53
68
  assert.equal(m3prov.input_cache_miss_usd_per_1m_peak, m3prov.input_cache_miss_usd_per_1m_offpeak, 'MiniMax-M3 flat 纪律:peak==offpeak miss');
54
69
  assert.equal(m3prov.input_cache_hit_usd_per_1m_peak, m3prov.input_cache_hit_usd_per_1m_offpeak, 'MiniMax-M3 flat 纪律:peak==offpeak hit');
55
70
  assert.equal(m3prov.output_usd_per_1m_peak, m3prov.output_usd_per_1m_offpeak, 'MiniMax-M3 flat 纪律:peak==offpeak output');
56
71
  ok('MiniMax flat_no_offpeak 纪律同价守门');
57
- assert.throws(()=>priceRunCost('unknown-model-xyz', baseUsage, priceTable), /no entry for model/);
72
+ // ---- 3d. issue #49 验收:人为改一个不存在的模型名 fail-closed(waiting/error,不猜价) ----
73
+ assert.throws(() => priceRunCost('unknown-model-xyz', baseUsage, priceTable), /no entry for model/);
58
74
  ok('未知模型名 → fail-closed');
59
- const dangling = {
60
- ...priceTable,
61
- aliases: {
62
- 'deepseek-chat': 'not-a-model'
63
- }
64
- };
65
- assert.throws(()=>priceRunCost('deepseek-chat', baseUsage, dangling), /has no entry/);
66
- assert.throws(()=>priceRunCost('deepseek-chat', {
67
- ...baseUsage,
68
- responsesMissingUsage: 1
69
- }, priceTable), /cost unprovable/);
75
+ // ---- 4. fail-closed:未知模型 / alias 悬空 / usage 缺失 / 非法 schema_version 都不许出现价格 ----
76
+ const dangling = { ...priceTable, aliases: { 'deepseek-chat': 'not-a-model' } };
77
+ assert.throws(() => priceRunCost('deepseek-chat', baseUsage, dangling), /has no entry/);
78
+ assert.throws(() => priceRunCost('deepseek-chat', { ...baseUsage, responsesMissingUsage: 1 }, priceTable), /cost unprovable/);
79
+ // 非法 schema_version 守门(写入 tmp 后用 loadPriceTable 加载应抛错)
70
80
  const tmpSchemaDir = mkdtempSync(join(tmpdir(), 'nightly-bad-schema-'));
71
81
  const badSchemaPath = join(tmpSchemaDir, 'bad.json');
72
82
  try {
73
- writeFileSync(badSchemaPath, JSON.stringify({
74
- schema_version: 'gotry_llm_price_table_v99',
75
- prices: {}
76
- }));
77
- assert.throws(()=>loadPriceTable(badSchemaPath), /schema_version must be/);
83
+ writeFileSync(badSchemaPath, JSON.stringify({ schema_version: 'gotry_llm_price_table_v99', prices: {} }));
84
+ assert.throws(() => loadPriceTable(badSchemaPath), /schema_version must be/);
78
85
  ok('非法 schema_version 拒绝');
79
- } finally{
80
- rmSync(tmpSchemaDir, {
81
- recursive: true,
82
- force: true
83
- });
84
86
  }
87
+ finally {
88
+ rmSync(tmpSchemaDir, { recursive: true, force: true });
89
+ }
90
+ // 非法 provider price_strategy 守门(在 v2 表写入非法策略应抛错)
85
91
  const tmpStrategyDir = mkdtempSync(join(tmpdir(), 'nightly-bad-strategy-'));
86
92
  const badStrategyPath = join(tmpStrategyDir, 'bad.json');
87
93
  try {
88
94
  writeFileSync(badStrategyPath, JSON.stringify({
89
95
  schema_version: 'gotry_llm_price_table_v2',
90
96
  providers: {
91
- bad: {
92
- family: 'x',
93
- price_strategy: 'mystery_strategy',
94
- aliases: {},
95
- models: {
96
- 'm': {
97
- input_cache_miss_usd_per_1m_peak: 0.1,
98
- input_cache_hit_usd_per_1m_peak: 0.01,
99
- output_usd_per_1m_peak: 0.5,
100
- input_cache_miss_usd_per_1m_offpeak: 0.1,
101
- input_cache_hit_usd_per_1m_offpeak: 0.01,
102
- output_usd_per_1m_offpeak: 0.5
103
- }
104
- }
105
- }
106
- }
97
+ bad: { family: 'x', price_strategy: 'mystery_strategy', aliases: {}, models: { 'm': { input_cache_miss_usd_per_1m_peak: 0.1, input_cache_hit_usd_per_1m_peak: 0.01, output_usd_per_1m_peak: 0.5, input_cache_miss_usd_per_1m_offpeak: 0.1, input_cache_hit_usd_per_1m_offpeak: 0.01, output_usd_per_1m_offpeak: 0.5 } } },
98
+ },
107
99
  }));
108
- assert.throws(()=>loadPriceTable(badStrategyPath), /price_strategy must be/);
100
+ assert.throws(() => loadPriceTable(badStrategyPath), /price_strategy must be/);
109
101
  ok('v2 非法 price_strategy 拒绝');
110
- } finally{
111
- rmSync(tmpStrategyDir, {
112
- recursive: true,
113
- force: true
114
- });
102
+ }
103
+ finally {
104
+ rmSync(tmpStrategyDir, { recursive: true, force: true });
115
105
  }
116
106
  ok('未知模型/悬空alias/usage缺失 fail-closed');
107
+ // ---- 5. run_key 确定性 + 消费方合同 ----
117
108
  const executeAt = '2026-08-29T12:00:00.000Z';
118
109
  const recordInput = {
119
110
  schema_version: 'gotry_m3_nightly_run_v1',
@@ -121,21 +112,17 @@ const recordInput = {
121
112
  real_llm: true,
122
113
  prompt_set_sha256: 'a'.repeat(64),
123
114
  output_sha256: 'b'.repeat(64),
124
- cost_usd: 0.25
115
+ cost_usd: 0.25,
125
116
  };
126
117
  const rec1 = buildNightlyRunRecord(recordInput);
127
- const rec2 = buildNightlyRunRecord({
128
- ...recordInput
129
- });
118
+ const rec2 = buildNightlyRunRecord({ ...recordInput });
130
119
  assert.equal(rec1.run_key, rec2.run_key);
131
120
  assert.match(rec1.run_key, /^hmac-sha256:[0-9a-f]{64}$/);
132
121
  parseNightlyRun(JSON.parse(JSON.stringify(rec1)), 0);
133
- const rec3 = buildNightlyRunRecord({
134
- ...recordInput,
135
- cost_usd: 0.26
136
- });
122
+ const rec3 = buildNightlyRunRecord({ ...recordInput, cost_usd: 0.26 });
137
123
  assert.notEqual(rec3.run_key, rec1.run_key);
138
124
  ok('run_key 确定性 + 已过消费方 parseNightlyRun');
125
+ // ---- 6. 无凭证 = waiting_external_evidence,零写入(evidence root 自身也不得被创建) ----
139
126
  const prevKey = process.env['LLM_API_KEY'];
140
127
  const prevDeepseekKey = process.env['DEEPSEEK_API_KEY'];
141
128
  const waitParent = mkdtempSync(join(tmpdir(), 'nightly-wait-'));
@@ -143,44 +130,32 @@ const waitRoot = join(waitParent, 'evidence', 'm3');
143
130
  try {
144
131
  delete process.env['LLM_API_KEY'];
145
132
  delete process.env['DEEPSEEK_API_KEY'];
146
- const waiting = await runNightlyEvidence({
147
- evidenceRoot: waitRoot,
148
- dryRun: false,
149
- clock: ()=>new Date(executeAt)
150
- });
133
+ const waiting = await runNightlyEvidence({ evidenceRoot: waitRoot, dryRun: false, clock: () => new Date(executeAt) });
151
134
  assert.equal(waiting.state, 'waiting_external_evidence');
152
135
  assert.equal(waiting.record, null);
153
136
  assert.ok(waiting.reason?.includes('backoff'));
154
137
  assert.equal(existsSync(waitRoot), false, 'waiting 不得创建任何证据目录');
155
- } finally{
156
- if (prevKey !== undefined) process.env['LLM_API_KEY'] = prevKey;
157
- if (prevDeepseekKey !== undefined) process.env['DEEPSEEK_API_KEY'] = prevDeepseekKey;
158
- rmSync(waitParent, {
159
- recursive: true,
160
- force: true
161
- });
138
+ }
139
+ finally {
140
+ if (prevKey !== undefined)
141
+ process.env['LLM_API_KEY'] = prevKey;
142
+ if (prevDeepseekKey !== undefined)
143
+ process.env['DEEPSEEK_API_KEY'] = prevDeepseekKey;
144
+ rmSync(waitParent, { recursive: true, force: true });
162
145
  }
163
146
  ok('无凭证 → waiting_external_evidence 且零写入');
147
+ // ---- 7. dry-run:mock 全链演练,记录为 real_llm=false 且绝不落盘 ----
164
148
  const dryParent = mkdtempSync(join(tmpdir(), 'nightly-dry-'));
165
149
  const dryRoot = join(dryParent, 'evidence', 'm3');
166
150
  try {
167
- const dry = await runNightlyEvidence({
168
- evidenceRoot: dryRoot,
169
- dryRun: true,
170
- clock: ()=>new Date(executeAt)
171
- });
151
+ const dry = await runNightlyEvidence({ evidenceRoot: dryRoot, dryRun: true, clock: () => new Date(executeAt) });
172
152
  assert.equal(dry.state, 'dry_run');
173
153
  assert.equal(dry.record?.real_llm, false);
174
154
  parseNightlyRun(JSON.parse(JSON.stringify(dry.record)), 0);
175
155
  assert.equal(existsSync(dryRoot), false, 'dry-run 不得写任何证据文件');
176
- } finally{
177
- rmSync(dryParent, {
178
- recursive: true,
179
- force: true
180
- });
156
+ }
157
+ finally {
158
+ rmSync(dryParent, { recursive: true, force: true });
181
159
  }
182
160
  ok('dry-run mock 全链演练 + real_llm=false + 零落盘');
183
161
  console.log(`nightly-evidence tests: ${passed} 组断言全绿(offline,真跑不在 CI)`);
184
-
185
-
186
- //# sourceURL=ts/scripts/nightly-evidence-tests.ts