@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.28

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 (533) hide show
  1. package/README.md +172 -219
  2. package/README.zh-CN.md +222 -0
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-booking-copilot.js +53 -0
  5. package/bin/gotry-bootstrap.js +1788 -0
  6. package/bin/gotry-inner.js +733 -83
  7. package/bin/gotry-process-liveness.d.ts +27 -0
  8. package/bin/gotry-process-liveness.js +97 -0
  9. package/bin/gotry-runtime-resolution.d.ts +27 -0
  10. package/bin/gotry-runtime-resolution.js +49 -0
  11. package/bin/gotry.js +1 -1
  12. package/client/client.js +203 -0
  13. package/cordis.gotry-patch.yml +80 -56
  14. package/data/airline-airports.json +39 -0
  15. package/data/stations-12306-verify.json +524 -0
  16. package/dist/capabilities/agent-reach-deep.js +45 -122
  17. package/dist/capabilities/agent-reach.js +101 -146
  18. package/dist/capabilities/anything.js +161 -94
  19. package/dist/capabilities/artifacts.js +370 -0
  20. package/dist/capabilities/channel-health.js +131 -0
  21. package/dist/capabilities/channel-registry.js +256 -0
  22. package/dist/capabilities/doctor.js +553 -0
  23. package/dist/capabilities/effect.js +394 -0
  24. package/dist/capabilities/external-event.js +149 -0
  25. package/dist/capabilities/fact-log.js +55 -0
  26. package/dist/capabilities/flyai-config.js +337 -0
  27. package/dist/capabilities/flyai.js +947 -110
  28. package/dist/capabilities/fx-contract.js +293 -0
  29. package/dist/capabilities/geo-atlas.js +246 -0
  30. package/dist/capabilities/ground-transfer.js +676 -0
  31. package/dist/capabilities/hbcli.js +260 -123
  32. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  33. package/dist/capabilities/incident-log.js +131 -61
  34. package/dist/capabilities/itinerary-artifact.js +190 -0
  35. package/dist/capabilities/itinerary-deck-artifact.js +194 -0
  36. package/dist/capabilities/itinerary-deck-export.js +339 -0
  37. package/dist/capabilities/lavish-local.js +924 -0
  38. package/dist/capabilities/model-override.js +38 -0
  39. package/dist/capabilities/opensky.js +54 -43
  40. package/dist/capabilities/provider-detail-sanitize.js +64 -0
  41. package/dist/capabilities/resilience.js +191 -0
  42. package/dist/capabilities/route-provider-conformance.js +341 -0
  43. package/dist/capabilities/session/action-cache.js +50 -44
  44. package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
  45. package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
  46. package/dist/capabilities/session/adapters/dida-portal.js +274 -0
  47. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  48. package/dist/capabilities/session/adapters/rail-12306.js +342 -0
  49. package/dist/capabilities/session/benchmark.js +189 -0
  50. package/dist/capabilities/session/bridge-ledger.js +224 -0
  51. package/dist/capabilities/session/extension-bridge.js +636 -0
  52. package/dist/capabilities/session/extension-channel.js +109 -0
  53. package/dist/capabilities/session/extension-distribution.js +230 -0
  54. package/dist/capabilities/session/extract.js +31 -21
  55. package/dist/capabilities/session/golden-score.js +114 -0
  56. package/dist/capabilities/session/health-watch.js +215 -0
  57. package/dist/capabilities/session/read-guard.js +75 -24
  58. package/dist/capabilities/session/static-flight-golden.js +150 -0
  59. package/dist/capabilities/session/transport.js +97 -83
  60. package/dist/capabilities/session/wizard.js +117 -0
  61. package/dist/capabilities/session-consent.js +178 -0
  62. package/dist/capabilities/session-login.js +170 -0
  63. package/dist/capabilities/session-search.js +635 -44
  64. package/dist/capabilities/spawn-bounded.js +203 -0
  65. package/dist/capabilities/sponsor-plugin.js +341 -0
  66. package/dist/capabilities/visa-policy.js +140 -0
  67. package/dist/capabilities/weather.js +243 -138
  68. package/dist/data/session-golden-20.json +25 -0
  69. package/dist/data/sf-golden-manifest.json +103 -0
  70. package/dist/data/sf-static-routes.json +91 -0
  71. package/dist/scripts/action-cache-tests.js +24 -59
  72. package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
  73. package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
  74. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  75. package/dist/scripts/agent-reach-error-tests.js +354 -0
  76. package/dist/scripts/agent-reach-tests.js +26 -18
  77. package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
  78. package/dist/scripts/anything-path-measure-tests.js +133 -0
  79. package/dist/scripts/anything-path-measure.js +150 -0
  80. package/dist/scripts/anything-tests.js +250 -60
  81. package/dist/scripts/artifact-client-contract-tests.js +220 -0
  82. package/dist/scripts/artifacts-capability-tests.js +727 -0
  83. package/dist/scripts/async-collect.js +50 -11
  84. package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
  85. package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
  86. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
  87. package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
  88. package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
  89. package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
  90. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
  91. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
  92. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
  93. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
  94. package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
  95. package/dist/scripts/booking-copilot-dsh-warmer-proof.js +126 -0
  96. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
  97. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
  98. package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
  99. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
  100. package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
  101. package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
  102. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
  103. package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
  104. package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
  105. package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
  106. package/dist/scripts/booking-executor-tests.js +80 -0
  107. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  108. package/dist/scripts/booking-saga-tests.js +125 -0
  109. package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
  110. package/dist/scripts/bootstrap-tests.js +1549 -0
  111. package/dist/scripts/bridge-events-tests.js +239 -0
  112. package/dist/scripts/bridge-ledger-tests.js +428 -0
  113. package/dist/scripts/build-changelog.js +238 -0
  114. package/dist/scripts/build-metrics-report.js +331 -0
  115. package/dist/scripts/changelog-tests.js +118 -0
  116. package/dist/scripts/channel-probe-tests.js +103 -0
  117. package/dist/scripts/channel-probe.js +169 -0
  118. package/dist/scripts/channel-registry-tests.js +208 -0
  119. package/dist/scripts/city-scenario-tier-tests.js +234 -0
  120. package/dist/scripts/companion-tests.js +25 -70
  121. package/dist/scripts/dida-cookie-probe.js +87 -0
  122. package/dist/scripts/dida-runner-stop-tests.js +311 -0
  123. package/dist/scripts/dida-sw-probe.js +114 -0
  124. package/dist/scripts/diff-test.js +17 -12
  125. package/dist/scripts/doctor-tests.js +600 -0
  126. package/dist/scripts/drill-multiuser-ledger-tests.js +1076 -0
  127. package/dist/scripts/drill-sdk-descendant-cleanup-tests.js +606 -0
  128. package/dist/scripts/drill-w2a-sensor-tests.js +788 -0
  129. package/dist/scripts/dsh-artifact-e2e.js +279 -0
  130. package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
  131. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  132. package/dist/scripts/dsh-llm-tests.js +437 -0
  133. package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
  134. package/dist/scripts/dsh-runtime-closure.js +162 -0
  135. package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
  136. package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
  137. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  138. package/dist/scripts/effect-tests.js +539 -0
  139. package/dist/scripts/engine-run.js +1 -3
  140. package/dist/scripts/engine-tests.js +16 -10
  141. package/dist/scripts/evaluation-cadence-tests.js +174 -0
  142. package/dist/scripts/evaluation-contract-tests.js +268 -0
  143. package/dist/scripts/extension-distribution-cli.js +39 -0
  144. package/dist/scripts/extension-distribution-tests.js +443 -0
  145. package/dist/scripts/extension-tests.js +988 -0
  146. package/dist/scripts/external-event-tests.js +350 -0
  147. package/dist/scripts/fact-gate-tests.js +869 -0
  148. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  149. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  150. package/dist/scripts/flight-malformed-tests.js +159 -0
  151. package/dist/scripts/flyai-setup-tests.js +228 -0
  152. package/dist/scripts/flyai-setup-tool-tests.js +54 -0
  153. package/dist/scripts/flyai-tests.js +692 -0
  154. package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
  155. package/dist/scripts/fx-contract-tests.js +201 -0
  156. package/dist/scripts/g5-guard-tests.js +150 -0
  157. package/dist/scripts/g5-guard.js +193 -0
  158. package/dist/scripts/gen-lunar-anchors.js +106 -0
  159. package/dist/scripts/geo-atlas-tests.js +283 -0
  160. package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
  161. package/dist/scripts/gotry-try-demo-tests.js +79 -0
  162. package/dist/scripts/gotry-try-demo.js +49 -0
  163. package/dist/scripts/ground-transfer-tests.js +956 -0
  164. package/dist/scripts/hbcli-e2e-tests.js +159 -0
  165. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  166. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  167. package/dist/scripts/hbcli-tests.js +121 -69
  168. package/dist/scripts/health-watch-cli.js +73 -0
  169. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  170. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  171. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  172. package/dist/scripts/i18n-tests.js +41 -0
  173. package/dist/scripts/incident-tests.js +281 -87
  174. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  175. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  176. package/dist/scripts/issue-271-liveness-tests.js +569 -0
  177. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  178. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  179. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  180. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  181. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  182. package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
  183. package/dist/scripts/itinerary-artifact-tests.js +368 -0
  184. package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
  185. package/dist/scripts/itinerary-deck-export-tests.js +552 -0
  186. package/dist/scripts/itinerary-deck-tests.js +435 -0
  187. package/dist/scripts/itinerary-html-tests.js +426 -0
  188. package/dist/scripts/journey-tests.js +22 -27
  189. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  190. package/dist/scripts/kernel-manifest-gate.js +126 -0
  191. package/dist/scripts/kernel-manifest-tests.js +212 -0
  192. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  193. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  194. package/dist/scripts/kernel-manifest.js +369 -0
  195. package/dist/scripts/lavish-local-tests.js +1226 -0
  196. package/dist/scripts/lavish-product-tools-tests.js +537 -0
  197. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  198. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  199. package/dist/scripts/ledger-tests.js +382 -246
  200. package/dist/scripts/ledger-workflow-crash.js +10 -20
  201. package/dist/scripts/m3-cohort-tests.js +714 -0
  202. package/dist/scripts/m3-cohort.js +310 -0
  203. package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
  204. package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
  205. package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
  206. package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
  207. package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
  208. package/dist/scripts/memory-capture-tests.js +242 -61
  209. package/dist/scripts/memory-decay-tests.js +18 -52
  210. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  211. package/dist/scripts/memory-lifecycle.js +196 -0
  212. package/dist/scripts/memory-metrics.js +24 -12
  213. package/dist/scripts/memory-value-report.js +631 -0
  214. package/dist/scripts/memory-value-tests.js +328 -0
  215. package/dist/scripts/metrics-report-tests.js +151 -0
  216. package/dist/scripts/model-override-e2e.js +137 -0
  217. package/dist/scripts/nightly-evidence-tests.js +203 -0
  218. package/dist/scripts/nightly-evidence.js +303 -0
  219. package/dist/scripts/nudge-digest.js +39 -28
  220. package/dist/scripts/observability-tests.js +381 -0
  221. package/dist/scripts/onboarding-tests.js +161 -0
  222. package/dist/scripts/opensky-check.js +18 -13
  223. package/dist/scripts/opensky-tests.js +28 -28
  224. package/dist/scripts/outcome-projection-tests.js +427 -0
  225. package/dist/scripts/persona-sim-tests.js +603 -0
  226. package/dist/scripts/persona-sim.js +1081 -0
  227. package/dist/scripts/persona-surface-guard-tests.js +49 -0
  228. package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
  229. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  230. package/dist/scripts/price-drift-tests.js +340 -0
  231. package/dist/scripts/price-drift-watch.js +447 -0
  232. package/dist/scripts/probe-poi-tests.js +53 -45
  233. package/dist/scripts/product-metrics.js +483 -0
  234. package/dist/scripts/publish-preverify.js +110 -0
  235. package/dist/scripts/realtime-pricing-tests.js +76 -0
  236. package/dist/scripts/recall-tests.js +513 -0
  237. package/dist/scripts/replay-async.js +22 -18
  238. package/dist/scripts/replay-real.js +18 -18
  239. package/dist/scripts/replay.js +54 -82
  240. package/dist/scripts/route-provider-conformance-tests.js +391 -0
  241. package/dist/scripts/session-attach-diagnose.js +20 -20
  242. package/dist/scripts/session-attach-poc.js +34 -37
  243. package/dist/scripts/session-benchmark.js +180 -0
  244. package/dist/scripts/session-dida-live-e2e.js +199 -0
  245. package/dist/scripts/session-extract-tests.js +14 -35
  246. package/dist/scripts/session-link-tests.js +478 -0
  247. package/dist/scripts/session-login.js +27 -46
  248. package/dist/scripts/session-tests.js +796 -96
  249. package/dist/scripts/session-zone-crash.js +49 -0
  250. package/dist/scripts/session-zone-ledger-tests.js +432 -0
  251. package/dist/scripts/session-zone-observation-tests.js +492 -0
  252. package/dist/scripts/session-zone-observe.js +135 -0
  253. package/dist/scripts/session-zone-wiring-tests.js +566 -0
  254. package/dist/scripts/session-zones-tests.js +374 -0
  255. package/dist/scripts/sf-live-benchmark.js +367 -0
  256. package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
  257. package/dist/scripts/sf-live-cli-tests.js +92 -0
  258. package/dist/scripts/sf-live-options.js +46 -0
  259. package/dist/scripts/sf-manifest-tests.js +30 -0
  260. package/dist/scripts/sf-manifest.js +33 -0
  261. package/dist/scripts/sf-soft-score-tests.js +67 -0
  262. package/dist/scripts/sf-summary-tests.js +324 -0
  263. package/dist/scripts/sf-summary.js +525 -0
  264. package/dist/scripts/share-tests.js +432 -0
  265. package/dist/scripts/skeleton-check.js +16 -14
  266. package/dist/scripts/skeleton-integration-test.js +28 -11
  267. package/dist/scripts/skills-contract-tests.js +51 -46
  268. package/dist/scripts/smoke-session-gate-tests.js +21 -0
  269. package/dist/scripts/smoke.js +690 -246
  270. package/dist/scripts/spawn-bounded-abort-tests.js +244 -0
  271. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  272. package/dist/scripts/state-cli-tests.js +226 -101
  273. package/dist/scripts/state-cli.js +565 -198
  274. package/dist/scripts/state-repair-cli.js +66 -0
  275. package/dist/scripts/state-repair-tests.js +140 -0
  276. package/dist/scripts/static-golden-tests.js +165 -0
  277. package/dist/scripts/time-eval-tests.js +212 -192
  278. package/dist/scripts/travel-timeline-tests.js +29 -93
  279. package/dist/scripts/turn-handoff-collect-tests.js +169 -0
  280. package/dist/scripts/turn-handoff-collect.js +161 -0
  281. package/dist/scripts/turn-policy-tests.js +55 -0
  282. package/dist/scripts/typed-contract-canary.js +176 -0
  283. package/dist/scripts/tz-resolver-sanity.js +50 -0
  284. package/dist/scripts/tz-resolver-tests.js +584 -0
  285. package/dist/scripts/unified-tests.js +60 -20
  286. package/dist/scripts/visa-policy-tests.js +62 -0
  287. package/dist/scripts/weather-tests.js +276 -41
  288. package/dist/scripts/wish-channel-gate-tests.js +54 -0
  289. package/dist/scripts/write-gate-tests.js +522 -0
  290. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  291. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  292. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  293. package/dist/scripts/z3-race-tests.js +46 -0
  294. package/dist/src/artifact-gate.js +829 -0
  295. package/dist/src/backend/kernel.js +91 -0
  296. package/dist/src/backend/modules/booking-copilot.js +92 -0
  297. package/dist/src/backend/modules/booking-executor.js +124 -0
  298. package/dist/src/backend/modules/session-search.js +551 -0
  299. package/dist/src/benchmark-agent-conformance.js +578 -0
  300. package/dist/src/benchmark-environment-bridge.js +515 -0
  301. package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
  302. package/dist/src/benchmark-tool-isolation.js +151 -0
  303. package/dist/src/bookable-facts.js +533 -0
  304. package/dist/src/booking-saga.js +97 -0
  305. package/dist/src/booking-surface/availability-policy.js +697 -0
  306. package/dist/src/booking-surface/booking-intent.js +43 -0
  307. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  308. package/dist/src/booking-surface/canonical-schema.js +116 -0
  309. package/dist/src/booking-surface/contracts.js +75 -0
  310. package/dist/src/booking-surface/dsh-planner.js +1854 -0
  311. package/dist/src/booking-surface/dsh-plugin.js +378 -0
  312. package/dist/src/booking-surface/dsh-worker.js +98 -0
  313. package/dist/src/booking-surface/error-codes.js +231 -0
  314. package/dist/src/booking-surface/index.js +9 -0
  315. package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
  316. package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
  317. package/dist/src/booking-surface/profile.js +24 -0
  318. package/dist/src/booking-surface/recovery-chain.js +367 -0
  319. package/dist/src/booking-surface/runtime.js +1968 -0
  320. package/dist/src/booking-surface/server.js +674 -0
  321. package/dist/src/booking-surface/startup.js +168 -0
  322. package/dist/src/booking-surface/validation.js +261 -0
  323. package/dist/src/bridge.js +20 -13
  324. package/dist/src/city-scenario-tier.js +378 -0
  325. package/dist/src/companions.js +48 -74
  326. package/dist/src/contracts.js +21 -43
  327. package/dist/src/dsh-llm.js +171 -115
  328. package/dist/src/engine.js +276 -193
  329. package/dist/src/evaluation-cadence.js +97 -0
  330. package/dist/src/evaluation-contracts.js +283 -0
  331. package/dist/src/flight-pack-adapter.js +54 -0
  332. package/dist/src/flight-pack-contract.js +2 -0
  333. package/dist/src/flyai-setup-tool.js +70 -0
  334. package/dist/src/gotry-backend.js +125 -0
  335. package/dist/src/hotel-date-gate.js +118 -0
  336. package/dist/src/i18n.js +88 -0
  337. package/dist/src/index.js +2385 -881
  338. package/dist/src/itinerary-deck.js +139 -0
  339. package/dist/src/itinerary-doc-shared.js +661 -0
  340. package/dist/src/itinerary-html.js +131 -0
  341. package/dist/src/journey.js +155 -108
  342. package/dist/src/lavish-tools.js +400 -0
  343. package/dist/src/ledger-repair-apply.js +538 -0
  344. package/dist/src/ledger-repair-plan.js +463 -0
  345. package/dist/src/loop.js +331 -161
  346. package/dist/src/m3-cohort.js +1577 -0
  347. package/dist/src/memory-capture.js +180 -32
  348. package/dist/src/memory-decay.js +30 -14
  349. package/dist/src/memory-lifecycle.js +1310 -0
  350. package/dist/src/memory-utility.js +27 -40
  351. package/dist/src/mock-llm.js +36 -61
  352. package/dist/src/model.js +81 -36
  353. package/dist/src/outcome-projection.js +746 -0
  354. package/dist/src/realtime-pricing.js +103 -0
  355. package/dist/src/recall/card.js +74 -0
  356. package/dist/src/recall/evaluator.js +140 -0
  357. package/dist/src/recall/integration.js +17 -0
  358. package/dist/src/recall/tick.js +166 -0
  359. package/dist/src/session-link/plan-it-action.js +48 -0
  360. package/dist/src/session-link/session-link.js +248 -0
  361. package/dist/src/session-zone-ledger.js +312 -0
  362. package/dist/src/session-zone-observation.js +354 -0
  363. package/dist/src/session-zone-wiring.js +658 -0
  364. package/dist/src/session-zones.js +510 -0
  365. package/dist/src/share/adapters.js +51 -0
  366. package/dist/src/share/share-consent.js +76 -0
  367. package/dist/src/share/share-deck.js +58 -0
  368. package/dist/src/share/share-token.js +146 -0
  369. package/dist/src/slot-spec.js +84 -106
  370. package/dist/src/state-ledger.js +696 -524
  371. package/dist/src/state-repair.js +299 -0
  372. package/dist/src/subagent-job-id-guard.js +58 -0
  373. package/dist/src/time-anchor.js +231 -33
  374. package/dist/src/tool-packet.js +20 -8
  375. package/dist/src/travel-slots.js +58 -35
  376. package/dist/src/travel-timeline.js +44 -63
  377. package/dist/src/turn-deadline.js +287 -0
  378. package/dist/src/turn-policy.js +118 -0
  379. package/dist/src/tz-resolver.js +143 -0
  380. package/dist/src/unified.js +600 -360
  381. package/dist/src/wish-pool.js +42 -12
  382. package/dist/src/write-gate.js +495 -0
  383. package/dist/src/z3-shared.js +284 -0
  384. package/extension/README.md +21 -0
  385. package/extension/README.zh-CN.md +21 -0
  386. package/extension/background.js +463 -0
  387. package/extension/content-bridge.js +270 -0
  388. package/extension/content-main.js +111 -0
  389. package/extension/manifest.json +60 -0
  390. package/package.json +407 -14
  391. package/schemas/booking.intent.schema.json +193 -0
  392. package/schemas/booking.surface.schema.json +2622 -0
  393. package/ts/capabilities/agent-reach-deep.ts +114 -0
  394. package/ts/capabilities/agent-reach.ts +198 -0
  395. package/ts/capabilities/anything.ts +282 -0
  396. package/ts/capabilities/artifacts.ts +428 -0
  397. package/ts/capabilities/fact-log.ts +54 -0
  398. package/ts/capabilities/flyai-config.ts +388 -0
  399. package/ts/capabilities/flyai.ts +1274 -0
  400. package/ts/capabilities/hbcli.ts +253 -71
  401. package/ts/capabilities/incident-log.ts +88 -59
  402. package/ts/capabilities/itinerary-artifact.ts +214 -0
  403. package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
  404. package/ts/capabilities/itinerary-deck-export.ts +378 -0
  405. package/ts/capabilities/opensky.ts +121 -0
  406. package/ts/capabilities/session/action-cache.ts +148 -0
  407. package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
  408. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  409. package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
  410. package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
  411. package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
  412. package/ts/capabilities/session/benchmark.ts +276 -0
  413. package/ts/capabilities/session/bridge-ledger.ts +333 -0
  414. package/ts/capabilities/session/extension-bridge.ts +794 -0
  415. package/ts/capabilities/session/extension-channel.ts +144 -0
  416. package/ts/capabilities/session/extension-distribution.ts +266 -0
  417. package/ts/capabilities/session/extract.ts +54 -0
  418. package/ts/capabilities/session/golden-score.ts +139 -0
  419. package/ts/capabilities/session/health-watch.ts +264 -0
  420. package/ts/capabilities/session/read-guard.ts +141 -0
  421. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  422. package/ts/capabilities/session/transport.ts +146 -0
  423. package/ts/capabilities/session/wizard.ts +140 -0
  424. package/ts/capabilities/session-consent.ts +211 -0
  425. package/ts/capabilities/session-login.ts +208 -0
  426. package/ts/capabilities/session-search.ts +893 -0
  427. package/ts/capabilities/weather.ts +330 -0
  428. package/ts/dsh-runtime/vendor/README.md +68 -0
  429. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  430. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
  431. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
  432. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
  433. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  434. package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
  435. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
  436. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  437. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
  438. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  439. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  440. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
  441. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  442. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
  443. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
  444. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
  445. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
  446. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  447. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  448. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
  449. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
  450. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  451. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  452. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  453. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  454. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
  455. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  456. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
  457. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  458. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  459. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
  460. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  461. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  462. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
  463. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
  464. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  465. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
  466. package/ts/package.json +293 -3
  467. package/ts/scripts/async-collect.ts +91 -0
  468. package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
  469. package/ts/scripts/skeleton-check.ts +1 -1
  470. package/ts/scripts/skeleton-integration-test.ts +23 -1
  471. package/ts/scripts/state-cli.ts +586 -0
  472. package/ts/scripts/turn-handoff-collect.ts +178 -0
  473. package/ts/src/artifact-gate.ts +991 -0
  474. package/ts/src/benchmark-agent-conformance.ts +619 -0
  475. package/ts/src/benchmark-environment-bridge.ts +538 -0
  476. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  477. package/ts/src/benchmark-tool-isolation.ts +166 -0
  478. package/ts/src/bookable-facts.ts +837 -0
  479. package/ts/src/booking-saga.ts +133 -0
  480. package/ts/src/booking-surface/availability-policy.ts +522 -0
  481. package/ts/src/booking-surface/booking-intent.ts +86 -0
  482. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  483. package/ts/src/booking-surface/canonical-schema.js +116 -0
  484. package/ts/src/booking-surface/contracts.ts +475 -0
  485. package/ts/src/booking-surface/dsh-planner.ts +1949 -0
  486. package/ts/src/booking-surface/dsh-plugin.js +378 -0
  487. package/ts/src/booking-surface/dsh-worker.ts +110 -0
  488. package/ts/src/booking-surface/error-codes.ts +241 -0
  489. package/ts/src/booking-surface/index.ts +9 -0
  490. package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
  491. package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
  492. package/ts/src/booking-surface/profile.ts +42 -0
  493. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  494. package/ts/src/booking-surface/runtime.ts +1793 -0
  495. package/ts/src/booking-surface/server.ts +733 -0
  496. package/ts/src/booking-surface/startup.ts +201 -0
  497. package/ts/src/booking-surface/validation.ts +217 -0
  498. package/ts/src/companions.ts +113 -0
  499. package/ts/src/contracts.ts +17 -3
  500. package/ts/src/dsh-llm.ts +152 -17
  501. package/ts/src/flyai-setup-tool.ts +77 -0
  502. package/ts/src/index.ts +2123 -200
  503. package/ts/src/itinerary-deck.ts +189 -0
  504. package/ts/src/itinerary-doc-shared.ts +741 -0
  505. package/ts/src/itinerary-html.ts +171 -0
  506. package/ts/src/loop.ts +234 -26
  507. package/ts/src/memory-capture.ts +283 -0
  508. package/ts/src/memory-decay.ts +55 -0
  509. package/ts/src/memory-utility.ts +70 -0
  510. package/ts/src/mock-llm.ts +4 -8
  511. package/ts/src/model.ts +88 -2
  512. package/ts/src/recall/card.ts +106 -0
  513. package/ts/src/recall/evaluator.ts +170 -0
  514. package/ts/src/recall/integration.ts +35 -0
  515. package/ts/src/recall/tick.ts +213 -0
  516. package/ts/src/session-link/plan-it-action.ts +78 -0
  517. package/ts/src/session-link/session-link.ts +275 -0
  518. package/ts/src/share/adapters.ts +93 -0
  519. package/ts/src/share/share-consent.ts +105 -0
  520. package/ts/src/share/share-deck.ts +85 -0
  521. package/ts/src/share/share-token.ts +166 -0
  522. package/ts/src/slot-spec.ts +172 -0
  523. package/ts/src/state-ledger.ts +1166 -0
  524. package/ts/src/subagent-job-id-guard.ts +72 -0
  525. package/ts/src/time-anchor.ts +338 -0
  526. package/ts/src/tool-packet.ts +32 -0
  527. package/ts/src/travel-slots.ts +219 -0
  528. package/ts/src/travel-timeline.ts +83 -0
  529. package/ts/src/turn-deadline.ts +366 -0
  530. package/ts/src/turn-policy.ts +154 -0
  531. package/ts/src/unified.ts +444 -105
  532. package/ts/src/wish-pool.ts +79 -0
  533. package/dist/scripts/session-attach-wait.js +0 -41
@@ -0,0 +1,741 @@
1
+ /** Offline contract for the opt-in benchmark environment bridge.
2
+ *
3
+ * Covers default-off, explicit opt-in, and fail-closed configuration paths.
4
+ * A local developer run exercises the source checkout. The packaged consumer
5
+ * path is built from the current root @deepseek-ai/dsh 0.2.0-rc.2 closure;
6
+ * version/source counterexamples use isolated synthetic fixtures.
7
+ */
8
+ import assert from 'node:assert/strict';
9
+ import { createServer } from 'node:http';
10
+ import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
11
+ import { createRequire } from 'node:module';
12
+ import { tmpdir } from 'node:os';
13
+ import { dirname, join } from 'node:path';
14
+ import { pathToFileURL } from 'node:url';
15
+ import { spawn } from 'node:child_process';
16
+ import { benchmarkRuntimeSupported, selectDshCwd, selectDshRuntime, supportsNodeVersion, } from '../../bin/gotry-runtime-resolution.js';
17
+ const ROOT = join(import.meta.dirname, '..', '..');
18
+ const BIN = join(ROOT, 'bin', 'gotry-inner.js');
19
+ const TOOL = 'gotry_benchmark_environment';
20
+ const MARKER = 'BENCHMARK_BRIDGE_LOOKUP_OK';
21
+ const TIMEOUT_MS = 30_000;
22
+ const LOOKUP_INPUT_SCHEMA = { type: 'object', properties: { city: { type: 'string', enum: ['Dubai', 'Singapore'] } }, required: ['city'], additionalProperties: false };
23
+ const TSX_LOADER = pathToFileURL(createRequire(import.meta.url).resolve('tsx')).href;
24
+ function runRuntimeProbe(options) {
25
+ const fixture = mkdtempSync(join(tmpdir(), 'gotry-runtime-probe-'));
26
+ try {
27
+ writeFileSync(join(fixture, 'package.json'), JSON.stringify({ name: 'runtime-probe', type: 'module' }));
28
+ const writeDsh = (root, version) => {
29
+ mkdirSync(join(root, 'lib'), { recursive: true });
30
+ writeFileSync(join(root, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh', version, type: 'module' }));
31
+ writeFileSync(join(root, 'lib', 'bin.js'), 'export {}\n');
32
+ };
33
+ if (options.rootVersion)
34
+ writeDsh(join(fixture, 'node_modules', '@deepseek-ai', 'dsh'), options.rootVersion);
35
+ if (options.vendorVersion)
36
+ writeDsh(join(fixture, 'ts', 'dsh-runtime', 'node_modules', '@deepseek-ai', 'dsh'), options.vendorVersion);
37
+ const runtime = selectDshRuntime({
38
+ repoRoot: fixture,
39
+ rootResolver: createRequire(join(fixture, 'package.json')),
40
+ benchmark: options.benchmark === true,
41
+ });
42
+ return runtime ? { source: runtime.source, version: runtime.version } : null;
43
+ }
44
+ finally {
45
+ rmSync(fixture, { recursive: true, force: true });
46
+ }
47
+ }
48
+ function assertRuntimeSelectionAndVersionGuards() {
49
+ const sourcePriority = runRuntimeProbe({ rootVersion: '0.2.0-rc.2', vendorVersion: '0.1.2-alpha.1' });
50
+ assert.deepEqual(sourcePriority, { source: 'root', version: '0.2.0-rc.2' }, 'source checkout uses the root dsh package even when legacy vendor is alpha.1');
51
+ const legacyFallback = runRuntimeProbe({ vendorVersion: '0.1.2-alpha.1' });
52
+ assert.deepEqual(legacyFallback, null, 'non-benchmark source checkout fail-closes instead of using the removed legacy vendored dsh fallback');
53
+ const wrongBenchmarkVersion = runRuntimeProbe({ rootVersion: '0.1.2-alpha.1', vendorVersion: '0.1.2-alpha.1', benchmark: true });
54
+ assert.deepEqual(wrongBenchmarkVersion, { source: 'root', version: '0.1.2-alpha.1' });
55
+ assert.equal(benchmarkRuntimeSupported(wrongBenchmarkVersion), false, 'benchmark mode rejects a non-target dsh runtime before spawn');
56
+ assert.equal(runRuntimeProbe({ vendorVersion: '0.1.2-alpha.1', benchmark: true }), null, 'benchmark mode never falls back to legacy vendored dsh');
57
+ assert.equal(supportsNodeVersion('22.14.0'), false, 'Node 22.14 is rejected before dsh resolution/spawn');
58
+ assert.equal(supportsNodeVersion('22.15.0'), true, 'Node 22.15 is the accepted minimum');
59
+ assert.equal(supportsNodeVersion('24.0.0'), true, 'newer Node majors remain accepted');
60
+ const invocationCwd = join(tmpdir(), 'gotry-runtime-invocation-cwd');
61
+ const sourceStateRoot = join(ROOT, 'ts/dsh-runtime');
62
+ assert.equal(selectDshCwd({ repoRoot: ROOT, invocationCwd, sourceCheckoutMode: true, benchmark: false }), sourceStateRoot, 'source checkout normal mode keeps dsh cwd at ts/dsh-runtime for gotry-state continuity');
63
+ assert.equal(selectDshCwd({ repoRoot: ROOT, invocationCwd, sourceCheckoutMode: true, benchmark: true }), invocationCwd, 'source checkout benchmark mode uses the isolated invocation cwd');
64
+ assert.equal(selectDshCwd({ repoRoot: ROOT, invocationCwd, sourceCheckoutMode: false, benchmark: false }), invocationCwd, 'installed package normal mode uses the user invocation cwd');
65
+ }
66
+ function writeResolutionProbe(path, resultPath, block) {
67
+ writeFileSync(path, `
68
+ const fs = require('node:fs')
69
+ const Module = require('node:module')
70
+ const resultPath = ${JSON.stringify(resultPath)}
71
+ const isOptional = value => typeof value === 'string' && (value.includes('dsh-calendar') || value.includes('dsh-map-tools'))
72
+ const record = (kind, target) => {
73
+ if (isOptional(target)) fs.appendFileSync(resultPath, JSON.stringify({ pid: process.pid, kind, target }) + '\\n')
74
+ }
75
+ const observe = request => {
76
+ if (typeof request !== 'string') return
77
+ record('resolve', request)
78
+ if (${JSON.stringify(block)} && isOptional(request)) {
79
+ const error = new Error('optional benchmark probe isolation')
80
+ error.code = 'MODULE_NOT_FOUND'
81
+ throw error
82
+ }
83
+ }
84
+ const originalResolve = Module._resolveFilename
85
+ Module._resolveFilename = function (request, parent, isMain, options) {
86
+ observe(request)
87
+ return originalResolve.call(this, request, parent, isMain, options)
88
+ }
89
+ const originalFindPath = Module._findPath
90
+ Module._findPath = function (request, paths, isMain) {
91
+ observe(request)
92
+ return originalFindPath.call(this, request, paths, isMain)
93
+ }
94
+ const originalExistsSync = fs.existsSync
95
+ fs.existsSync = function (target) {
96
+ record('existsSync', target)
97
+ if (${JSON.stringify(block)} && isOptional(target)) return false
98
+ return originalExistsSync.call(this, target)
99
+ }
100
+ const originalCreateRequire = Module.createRequire
101
+ Module.createRequire = function (...args) {
102
+ const required = originalCreateRequire.apply(this, args)
103
+ const originalRequiredResolve = required.resolve
104
+ required.resolve = function (request, ...args) {
105
+ record('resolve', request)
106
+ if (${JSON.stringify(block)} && isOptional(request)) {
107
+ const error = new Error('optional benchmark probe isolation')
108
+ error.code = 'MODULE_NOT_FOUND'
109
+ throw error
110
+ }
111
+ return originalRequiredResolve.call(required, request, ...args)
112
+ }
113
+ required.resolve.paths = originalRequiredResolve.paths
114
+ return required
115
+ }
116
+ Module.syncBuiltinESMExports()
117
+ `, { mode: 0o600, flag: 'wx' });
118
+ }
119
+ function sse(payload) {
120
+ return `data: ${JSON.stringify(payload)}\n\n`;
121
+ }
122
+ function finalText(text) {
123
+ // 0.2.0-rc.2 线型:Messages 流事件。
124
+ return sse({ type: 'message_start', message: { usage: { input_tokens: 10 } } })
125
+ + sse({ type: 'content_block_start', index: 0, content_block: { type: 'text', text } })
126
+ + sse({ type: 'content_block_stop', index: 0 })
127
+ + sse({ type: 'message_delta', delta: { stop_reason: 'end_turn' }, usage: { output_tokens: 4 } })
128
+ + sse({ type: 'message_stop' }) + 'data: [DONE]\n\n';
129
+ }
130
+ function toolCall(callId = 'bridge-call-1', city = 'Dubai') {
131
+ return sse({ type: 'message_start', message: { usage: { input_tokens: 10 } } })
132
+ + sse({ type: 'content_block_start', index: 0, content_block: { type: 'tool_use', id: callId, name: TOOL, input: {} } })
133
+ + sse({ type: 'content_block_delta', index: 0, delta: { type: 'input_json_delta', partial_json: JSON.stringify({ action: 'call', tool: 'lookup', arguments: { city } }) } })
134
+ + sse({ type: 'content_block_stop', index: 0 })
135
+ + sse({ type: 'message_delta', delta: { stop_reason: 'tool_use' }, usage: { output_tokens: 5 } })
136
+ + sse({ type: 'message_stop' }) + 'data: [DONE]\n\n';
137
+ }
138
+ function names(body) {
139
+ return (body.tools ?? []).map(t => { const f = t.function; return String(f?.name ?? t.name ?? ''); }).filter(Boolean);
140
+ }
141
+ function toolResultPresent(body) {
142
+ return (body.messages ?? []).some(m => (m.role === 'tool' || (Array.isArray(m.content) && m.content.some((b) => b?.type === 'tool_result'))) && JSON.stringify(m).includes(MARKER));
143
+ }
144
+ function anyToolResultPresent(body) {
145
+ // 新线型:工具结果以 user 消息携带 tool_result 块;旧线型是 role='tool'。
146
+ return (body.messages ?? []).some(m => m.role === 'tool' || (Array.isArray(m.content) && m.content.some((b) => b?.type === 'tool_result')));
147
+ }
148
+ // Round 12(#215):bridge config 显式携带无数据值 closed terminal body schema;
149
+ // 覆盖两个 e2e 终态形态 {"status":"succeeded"} 与 {payload:"…"}。
150
+ const BRIDGE_E2E_BODY_SCHEMA = { type: 'object', properties: { status: { type: 'string' }, payload: { type: 'string' } }, required: [], additionalProperties: false };
151
+ const TERMINAL_OUTLINE = 'object{?status:string,?payload:string}';
152
+ async function runCase(mode, executableOverride, extraEnv = {}) {
153
+ const requests = [];
154
+ let servedToolCalls = 0;
155
+ let spawnTarget = '';
156
+ const domainRecoveryMode = mode === 'domain-recovery' || mode === 'domain-recovery-failed';
157
+ const server = createServer((req, res) => {
158
+ const chunks = [];
159
+ req.on('data', c => chunks.push(Buffer.from(c)));
160
+ req.on('end', () => {
161
+ let body = {};
162
+ try {
163
+ body = JSON.parse(Buffer.concat(chunks).toString());
164
+ }
165
+ catch { /* diagnostic remains structural */ }
166
+ requests.push(body);
167
+ res.writeHead(200, { 'content-type': 'text/event-stream' });
168
+ if (mode === 'spawn-failed' && names(body).includes(TOOL) && !anyToolResultPresent(body) && spawnTarget)
169
+ rmSync(spawnTarget, { force: true });
170
+ if (domainRecoveryMode && servedToolCalls === 1 && names(body).includes(TOOL) && anyToolResultPresent(body) && !toolResultPresent(body)) {
171
+ servedToolCalls += 1;
172
+ res.end(toolCall('bridge-call-2', 'Singapore'));
173
+ }
174
+ else if (mode !== 'disabled' && mode !== 'invalid-path' && mode !== 'invalid-schema' && mode !== 'unsafe-config' && names(body).includes(TOOL) && !anyToolResultPresent(body)) {
175
+ servedToolCalls += 1;
176
+ res.end(toolCall());
177
+ }
178
+ else {
179
+ res.end(finalText('<benchmark_terminal>{"status":"succeeded"}</benchmark_terminal>'));
180
+ }
181
+ });
182
+ });
183
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
184
+ const port = server.address().port;
185
+ const cwd = mkdtempSync(join(tmpdir(), 'gotry-bridge-cwd-'));
186
+ const dsh = mkdtempSync(join(tmpdir(), 'gotry-bridge-dsh-'));
187
+ const probe = join(cwd, 'benchmark-resolution-probe.cjs');
188
+ const probeResult = join(cwd, 'benchmark-resolution-hits.json');
189
+ writeFileSync(probeResult, '', { mode: 0o600, flag: 'wx' });
190
+ writeResolutionProbe(probe, probeResult, mode === 'disabled');
191
+ const runner = join(cwd, 'synthetic-runner.js');
192
+ const runnerTrace = join(cwd, 'synthetic-runner-trace.jsonl');
193
+ spawnTarget = join(cwd, 'synthetic-spawn-target.js');
194
+ const configPath = join(cwd, mode === 'invalid-path' ? 'benchmark-env-config-\n.json' : 'benchmark-env-config.json');
195
+ const runnerBody = mode === 'domain-recovery-failed'
196
+ ? `if (args.city === 'Dubai') process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'miss', code: 'NOT_FOUND', recovery: 'revise_arguments' })); else { process.stderr.write('PRIVATE_RECOVERY_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'); process.exit(17) }`
197
+ : mode === 'domain-recovery'
198
+ ? `if (args.city === 'Dubai') process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'miss', code: 'NOT_FOUND', recovery: 'revise_arguments' })); else process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}', leaked: [] } }))`
199
+ : mode === 'timeout'
200
+ ? `setTimeout(() => {}, 60_000)`
201
+ : mode === 'runner-failed'
202
+ ? `process.stderr.write('PRIVATE_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'); process.exit(17)`
203
+ : mode === 'output-truncated'
204
+ ? `process.stdout.write('x'.repeat(20_000))`
205
+ : mode === 'unexpected-output'
206
+ ? `process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}', leaked: [], unexpected: 'must-not-reflect' } }))`
207
+ : `const forbidden = ['GOTRY_BENCHMARK_ENV_CONFIG', 'GOTRY_BENCHMARK_BRIDGE_PARENT_SECRET', 'LLM_API_KEY', 'LLM_BASE_URL', 'LLM_MODEL', 'DEEPSEEK_BASE_URL', 'GOTRY_LLM_MODEL', 'DATABASE_URL', 'SSH_AUTH_SOCK', 'AWS_PROFILE', 'HTTPS_PROXY']; const leaked = forbidden.filter(name => process.env[name] !== undefined); process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}', leaked } }))`;
208
+ writeFileSync(runner, `if (process.argv.length !== 5 || process.argv[2] !== 'call' || process.argv[3] !== 'lookup' || (!${JSON.stringify(domainRecoveryMode)} && JSON.parse(process.argv[4]).city !== 'Dubai') || (${JSON.stringify(domainRecoveryMode)} && !['Dubai', 'Singapore'].includes(JSON.parse(process.argv[4]).city))) process.exit(2); const fs = require('node:fs'); const args = JSON.parse(process.argv[4]); fs.appendFileSync(${JSON.stringify(runnerTrace)}, JSON.stringify({ city: args.city }) + '\\n'); ${runnerBody}`);
209
+ writeFileSync(spawnTarget, '#!/usr/bin/env node\nprocess.exit(0)\n', { mode: 0o700 });
210
+ writeFileSync(configPath, JSON.stringify({ schema_version: mode === 'invalid-schema' ? 'invalid' : 'gotry_benchmark_environment_bridge_v4', enabled: true, executable: mode === 'spawn-failed' ? spawnTarget : process.execPath, cwd, argv_prefix: mode === 'spawn-failed' ? ['placeholder'] : [runner], tools: [{ name: 'lookup', description: 'Lookup.', input_schema: LOOKUP_INPUT_SCHEMA, output_keys: ['marker', 'leaked'], domain_outcomes: [{ status: 'miss', code: 'NOT_FOUND', recovery: domainRecoveryMode ? 'revise_arguments' : 'none' }] }], timeout_ms: mode === 'timeout' ? 50 : 10_000, max_output_bytes: mode === 'output-truncated' ? 1_024 : 4_096, terminal_output: { tag: 'benchmark_terminal', max_bytes: 4_096, body_schema: BRIDGE_E2E_BODY_SCHEMA }, isolation: { mode: 'host-enforced', writes: 'forbidden', network: 'denied' } }));
211
+ if (mode === 'unsafe-config')
212
+ chmodSync(configPath, 0o666);
213
+ const env = {
214
+ ...process.env,
215
+ DSH_TOOLS_MODE: 'both',
216
+ DSH_HOME: dsh,
217
+ LLM_API_KEY: 'synthetic-bridge-key',
218
+ LLM_BASE_URL: `http://127.0.0.1:${port}/v1`,
219
+ LLM_MODEL: 'synthetic-bridge-model',
220
+ DEEPSEEK_API_KEY: 'synthetic-bridge-key',
221
+ DEEPSEEK_BASE_URL: `http://127.0.0.1:${port}/v1`,
222
+ ...(mode !== 'disabled' ? {
223
+ DATABASE_URL: 'postgres://sentinel',
224
+ SSH_AUTH_SOCK: '/tmp/sentinel.sock',
225
+ AWS_PROFILE: 'sentinel-profile',
226
+ } : {}),
227
+ GOTRY_BENCHMARK_ENV_CONFIG: mode === 'disabled' ? '' : configPath,
228
+ ...(mode !== 'disabled' ? { GOTRY_BENCHMARK_BRIDGE_PARENT_SECRET: 'do-not-leak' } : {}),
229
+ ...(mode === 'debug-redaction' ? { GOTRY_DEBUG: '1' } : {}),
230
+ ...extraEnv,
231
+ NODE_OPTIONS: [process.env.NODE_OPTIONS, ...(!executableOverride ? [`--import=${TSX_LOADER}`] : []), `--require=${probe}`].filter(Boolean).join(' '),
232
+ };
233
+ for (const key of ['GOTRY_LLM_MODEL', 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'http_proxy', 'https_proxy', 'all_proxy'])
234
+ delete env[key];
235
+ env.NO_PROXY = '127.0.0.1,localhost';
236
+ if (mode !== 'disabled')
237
+ env.HTTPS_PROXY = 'https://sentinel-proxy';
238
+ if (mode === 'disabled')
239
+ delete env.GOTRY_BENCHMARK_ENV_CONFIG;
240
+ const executable = executableOverride || process.execPath;
241
+ const invocation = mode === 'web-mode'
242
+ ? ['web', '--no-open']
243
+ : [mode === 'debug-redaction' ? 'PRIVATE_QUERY_SENTINEL_DO_NOT_REFLECT' : 'bridge smoke'];
244
+ const argv = executableOverride ? invocation : [BIN, ...invocation];
245
+ try {
246
+ const child = spawn(executable, argv, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
247
+ let stdout = '';
248
+ let stderr = '';
249
+ child.stdout.on('data', c => { stdout += c; });
250
+ child.stderr.on('data', c => { stderr += c; });
251
+ const exit = await new Promise(resolve => { const timer = setTimeout(() => { child.kill('SIGKILL'); resolve(null); }, TIMEOUT_MS); child.once('close', code => { clearTimeout(timer); resolve(code); }); });
252
+ const optionalResolutionHits = readFileSync(probeResult, 'utf8').split('\n').filter(Boolean).reduce((hits, line) => {
253
+ const event = JSON.parse(line);
254
+ if (event.target?.includes('dsh-calendar'))
255
+ hits.calendar += 1;
256
+ if (event.target?.includes('dsh-map-tools'))
257
+ hits.map += 1;
258
+ return hits;
259
+ }, { calendar: 0, map: 0 });
260
+ const runnerArguments = existsSync(runnerTrace)
261
+ ? readFileSync(runnerTrace, 'utf8').split('\n').filter(Boolean).map(line => JSON.parse(line))
262
+ : [];
263
+ return { exit, stdout, stderr, output: stdout + stderr, requests, optionalResolutionHits, servedToolCalls, runnerArguments };
264
+ }
265
+ finally {
266
+ await new Promise(resolve => server.close(() => resolve()));
267
+ rmSync(dsh, { recursive: true, force: true });
268
+ rmSync(cwd, { recursive: true, force: true });
269
+ }
270
+ }
271
+ async function assertRuntimeContract(executableOverride) {
272
+ const target = executableOverride ? 'packaged' : 'source';
273
+ const binSource = readFileSync(executableOverride ?? BIN, 'utf8');
274
+ assert.equal(binSource.includes('headless-keepalive'), false, `${target} bin must not inject a headless keepalive preload`);
275
+ assert.equal(binSource.includes('--require=${headlessKeepalivePath}'), false, `${target} bin must not mutate NODE_OPTIONS with a keepalive preload`);
276
+ assert.equal(binSource.includes('headless-startup-hold'), false, `${target} bin must not inject a timer-based startup hold`);
277
+ const disabled = await runCase('disabled', executableOverride);
278
+ assert.equal(disabled.exit, 0, `${target} default-off child exit=${disabled.exit}; output tail=${disabled.output.slice(-2_000)}`);
279
+ assert.ok(disabled.requests.length > 0 && !disabled.requests.some(r => names(r).includes(TOOL)), `${target} default-off must reach the relay without exposing benchmark tool; exit=${disabled.exit}; requests=${disabled.requests.length}; output=${disabled.output.slice(-2_000)}`);
280
+ assert.ok(disabled.optionalResolutionHits.calendar > 0 || disabled.optionalResolutionHits.map > 0, `${target} default-off must retain optional plugin resolution as a counter-proof`);
281
+ const ordinaryPlannerRequests = disabled.requests.filter(request => names(request).some(name => name.startsWith('gotry_')));
282
+ assert.ok(ordinaryPlannerRequests.length > 0, `${target} default-off must surface at least one ordinary planner request exposing a gotry_* tool (auxiliary title requests are excluded); requests=${disabled.requests.length}; tool surfaces=${JSON.stringify(disabled.requests.map(names))}; output=${disabled.output.slice(-2_000)}`);
283
+ const ORDINARY_IDENTITY_SENTENCE = '你是 GoTry——从出发到下一次出发的 AI 旅行伙伴';
284
+ const CANONICAL_RAW_VARIABLES = ['{{current_date}}', '{{time_anchor_card}}', '{{motivation_brief}}', '{{channel_routing_card}}'];
285
+ const EXPANDED_DATE_PATTERN = /今天是\s*\d{4}-\d{2}-\d{2}/;
286
+ for (const request of ordinaryPlannerRequests) {
287
+ const prompt = JSON.stringify(request);
288
+ assert.ok(prompt.includes(ORDINARY_IDENTITY_SENTENCE), `${target} ordinary planner request must carry the canonical Chinese GoTry identity sentence; prompt head=${prompt.slice(0, 600)}`);
289
+ for (const variable of CANONICAL_RAW_VARIABLES) {
290
+ assert.equal(prompt.includes(variable), false, `${target} ordinary planner request must not retain raw ${variable}; prompt head=${prompt.slice(0, 600)}`);
291
+ }
292
+ assert.match(prompt, EXPANDED_DATE_PATTERN, `${target} ordinary planner request must show the expanded "今天是 YYYY-MM-DD" shape from the canonical {{current_date}} variable; prompt head=${prompt.slice(0, 600)}`);
293
+ }
294
+ const enabled = await runCase('enabled', executableOverride);
295
+ assert.equal(enabled.exit, 0, `${target} opt-in child exit=${enabled.exit}; requests=${enabled.requests.length}; tool surfaces=${JSON.stringify(enabled.requests.map(names))}; output tail=${enabled.output.slice(-10000)}`);
296
+ assert.ok(enabled.requests.some(r => names(r).includes(TOOL)), `${target} opt-in planner request must expose benchmark tool; schemas=${JSON.stringify(enabled.requests.map(names))}; output=${enabled.output.slice(-4_000)}`);
297
+ const enabledToolNames = [...new Set(enabled.requests.flatMap(names))].sort();
298
+ assert.deepEqual(enabledToolNames, [TOOL], `${target} enabled runtime must expose exactly the benchmark tool; observed tool names=${JSON.stringify(enabledToolNames)}`);
299
+ const bridgeTool = enabled.requests.find(request => names(request).includes(TOOL))?.tools?.find(tool => names({ tools: [tool] }).includes(TOOL));
300
+ // 0.2.0-rc.2 线型:schema 是平铺 tool.input_schema(旧线型 tool.function.parameters)。
301
+ const toolAny = bridgeTool;
302
+ const flatSchema = toolAny?.function?.parameters ?? toolAny?.input_schema;
303
+ assert.equal(flatSchema?.type, 'object', `${target} bridge exposes an object-root wire schema`);
304
+ assert.equal(flatSchema?.oneOf, undefined, `${target} bridge wire has no top-level oneOf`);
305
+ assert.deepEqual(flatSchema?.required, ['action']);
306
+ assert.equal(flatSchema?.additionalProperties, false);
307
+ assert.deepEqual(flatSchema?.properties?.action?.enum, ['tools', 'call', 'errors']);
308
+ assert.deepEqual(flatSchema?.properties?.tool?.enum, ['lookup']);
309
+ assert.deepEqual(flatSchema?.properties?.arguments, { type: 'object', additionalProperties: true, description: 'action=call 时传给工具的参数对象' });
310
+ assert.equal(enabledToolNames.some(name => name.startsWith('calendar_') || name.startsWith('map_')), false, `${target} benchmark projection must not expose calendar/map tools`);
311
+ assert.deepEqual(enabled.optionalResolutionHits, { calendar: 0, map: 0 }, `${target} benchmark mode must not resolve optional calendar/map plugins`);
312
+ assert.ok(enabled.requests.some(toolResultPresent), `${target} marker must enter model history as tool result`);
313
+ const leakedReport = enabled.requests.map(r => JSON.stringify(r).match(/\\?"leaked\\?":\[(.*?)\]/)?.[1]).filter(Boolean).join('|');
314
+ assert.ok(enabled.requests.some(r => /\\?"leaked\\?":\[\]/.test(JSON.stringify(r))), `${target} tool result must report no forbidden environment names; observed names=${leakedReport || '(none)'}`);
315
+ assert.equal(enabled.requests.some(r => JSON.stringify(r).includes('do-not-leak')), false, `${target} tool result must not expose the parent secret value`);
316
+ const enabledPrompt = enabled.requests.map(r => JSON.stringify(r)).join('\n');
317
+ for (const variable of ['{{current_date}}', '{{time_anchor_card}}', '{{motivation_brief}}']) {
318
+ assert.equal(enabledPrompt.includes(variable), false, `${target} benchmark persona must not retain ${variable}`);
319
+ }
320
+ assert.equal(enabledPrompt.includes('gotry_feasibility_check'), false, `${target} benchmark persona must not retain ordinary GoTry tool instructions`);
321
+ // Persona invariant: every request that exposes the benchmark tool (a
322
+ // planner request) must carry each stable sentence exactly once. The
323
+ // separate session-title request does NOT expose the benchmark tool and is
324
+ // intentionally not required to carry the persona — dsh-system-prompt
325
+ // 0.2.0-rc.2 emits a distinct system prompt for it (auto-title). This
326
+ // is the precise observed public protocol invariant under target closure,
327
+ // not a relaxed `some` over the full request stream.
328
+ const SENTENCE_A = 'You are GoTry, a task-agnostic travel planning assistant.';
329
+ const SENTENCE_B = 'Use only the current conversation and tools available in this benchmark session.';
330
+ const plannerRequests = enabled.requests.filter(request => names(request).includes(TOOL));
331
+ assert.ok(plannerRequests.length > 0, `${target} must emit at least one planner request exposing the benchmark tool; requests=${enabled.requests.length}; schemas=${JSON.stringify(enabled.requests.map(names))}`);
332
+ for (const request of plannerRequests) {
333
+ // 0.2.0-rc.2 线型:persona 在请求体顶层 system 字段;请求还携带协议级会话
334
+ // replay(内含 system/message 事件),整包计数会把 replay 里的事件误计为重复。
335
+ const systemText = typeof request.system === 'string' ? request.system : JSON.stringify(request.system ?? '');
336
+ const aCount = (systemText.match(new RegExp(SENTENCE_A.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) ?? []).length;
337
+ const bCount = (systemText.match(new RegExp(SENTENCE_B.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) ?? []).length;
338
+ assert.equal(aCount, 1, `${target} planner request must carry stable sentence A exactly once (observed=${aCount}); system head=${systemText.slice(0, 400)}`);
339
+ assert.equal(bCount, 1, `${target} planner request must carry stable sentence B exactly once (observed=${bCount}); system head=${systemText.slice(0, 400)}`);
340
+ }
341
+ assert.match(enabled.output, /benchmark_terminal/);
342
+ const recovered = await runCase('domain-recovery', executableOverride);
343
+ assert.equal(recovered.exit, 0, `${target} model-driven domain miss recovery exits successfully; output=${recovered.output.slice(-2_000)}`);
344
+ assert.equal(recovered.servedToolCalls, 2, `${target} model emits exactly two tool calls around one declared miss`);
345
+ assert.deepEqual(recovered.runnerArguments, [{ city: 'Dubai' }, { city: 'Singapore' }], `${target} model revises the declared city before the second adapter invocation`);
346
+ assert.ok(recovered.requests.some(request => (request.messages ?? []).some(message => {
347
+ // 新线型:工具结果是携带 tool_result 块的 user 消息(role='tool' 旧线型兼容保留)。
348
+ const isToolResult = message.role === 'tool' || (Array.isArray(message.content) && message.content.some((block) => block?.type === 'tool_result'));
349
+ if (!isToolResult)
350
+ return false;
351
+ const serialized = JSON.stringify(message);
352
+ return /status\\?":\\?"miss/.test(serialized) && /recovery\\?":\\?"revise_arguments/.test(serialized);
353
+ })), `${target} declared typed miss reaches model history`);
354
+ assert.match(recovered.stdout, /<benchmark_terminal>/, `${target} corrected second call reaches tagged terminal output`);
355
+ const failedRecovery = await runCase('domain-recovery-failed', executableOverride);
356
+ assert.equal(failedRecovery.exit, 1, `${target} infrastructure failure after a declared miss cannot be masked by the earlier domain outcome`);
357
+ assert.equal(failedRecovery.servedToolCalls, 2, `${target} failed recovery still exercises exactly two model-owned tool calls`);
358
+ assert.deepEqual(failedRecovery.runnerArguments, [{ city: 'Dubai' }, { city: 'Singapore' }], `${target} failed recovery reaches the revised second adapter invocation`);
359
+ assert.equal(failedRecovery.stdout, '', `${target} failed recovery releases no terminal stdout`);
360
+ assert.match(failedRecovery.stderr, /benchmark terminal output unavailable \(child_bridge_runner_failed\)/, `${target} failed recovery preserves the second runner failure classification`);
361
+ assert.equal(failedRecovery.output.includes('PRIVATE_RECOVERY_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'), false, `${target} failed recovery never reflects private runner stderr`);
362
+ const debugRedaction = await runCase('debug-redaction', executableOverride);
363
+ assert.equal(debugRedaction.exit, 0, `${target} benchmark debug mode preserves successful execution`);
364
+ assert.equal(debugRedaction.output.includes('PRIVATE_QUERY_SENTINEL_DO_NOT_REFLECT'), false, `${target} benchmark debug output never reflects the private task`);
365
+ const unexpected = await runCase('unexpected-output', executableOverride);
366
+ assert.equal(unexpected.exit, 1, `${target} unexpected output must fail the successful-call conformance gate; exit=${unexpected.exit}; output tail=${unexpected.output.slice(-1000)}`);
367
+ assert.equal(unexpected.stdout, '', `${target} rejected runner output keeps stdout empty`);
368
+ assert.ok(unexpected.requests.some(r => JSON.stringify(r).includes('forbidden_output')), `${target} positive output allowlist must reject unexpected output`);
369
+ assert.equal(unexpected.requests.some(r => JSON.stringify(r).includes('must-not-reflect')), false, `${target} positive output rejection must not reflect unexpected key/value`);
370
+ assert.equal(unexpected.output.includes('<benchmark_terminal>'), false, `${target} failed bridge result cannot release a terminal body`);
371
+ const invalidPath = await runCase('invalid-path', executableOverride);
372
+ assert.equal(invalidPath.exit, 1, `${target} invalid config basename child exit=${invalidPath.exit}`);
373
+ assert.equal(invalidPath.requests.length, 0, `${target} invalid config must fail before relay/network activity`);
374
+ assert.match(invalidPath.output, /benchmark environment configuration unavailable/);
375
+ assert.equal(invalidPath.output.includes('benchmark-env-config-'), false, `${target} invalid config error must not expose path or filename`);
376
+ const unsafeConfig = await runCase('unsafe-config', executableOverride);
377
+ assert.equal(unsafeConfig.exit, 1, `${target} group/world-writable config child exit=${unsafeConfig.exit}; requests=${unsafeConfig.requests.length}; output=${unsafeConfig.output.slice(-2_000)}`);
378
+ assert.equal(unsafeConfig.requests.length, 0, `${target} unsafe config must fail before relay/network activity`);
379
+ assert.match(unsafeConfig.output, /benchmark environment configuration unavailable/);
380
+ const invalidSchema = await runCase('invalid-schema', executableOverride);
381
+ assert.equal(invalidSchema.exit, 1, `${target} invalid config schema child exit=${invalidSchema.exit}`);
382
+ assert.equal(invalidSchema.requests.length, 0, `${target} invalid schema must fail before relay/network activity`);
383
+ assert.match(invalidSchema.output, /benchmark environment configuration unavailable/);
384
+ const webMode = await runCase('web-mode', executableOverride);
385
+ assert.equal(webMode.exit, 1, `${target} benchmark opt-in rejects web mode`);
386
+ assert.equal(webMode.stdout, '', `${target} web-mode rejection keeps stdout empty`);
387
+ assert.equal(webMode.requests.length, 0, `${target} web rejection occurs before any model request`);
388
+ assert.match(webMode.output, /benchmark environment requires headless mode/);
389
+ const truncated = await runCase('output-truncated', executableOverride);
390
+ assert.equal(truncated.exit, 1, `${target} output truncation fails the successful-call conformance gate`);
391
+ assert.ok(truncated.requests.some(r => JSON.stringify(r).includes('output_truncated')), `${target} real runner output over the configured cap is rejected`);
392
+ assert.equal(truncated.stdout, '', `${target} output truncation keeps stdout empty`);
393
+ assert.match(truncated.stderr, /benchmark terminal output unavailable \(child_bridge_output_truncated\)/, `${target} output truncation emits a stable bridge reason code`);
394
+ const timedOut = await runCase('timeout', executableOverride);
395
+ assert.equal(timedOut.exit, 1, `${target} timeout fails the successful-call conformance gate`);
396
+ assert.ok(timedOut.requests.some(r => JSON.stringify(r).includes('timed_out')), `${target} real runner deadline is enforced; requests=${timedOut.requests.length}; output=${timedOut.output.slice(-2_000)}`);
397
+ assert.equal(timedOut.stdout, '', `${target} timeout keeps stdout empty`);
398
+ assert.match(timedOut.stderr, /benchmark terminal output unavailable \(child_bridge_timed_out\)/, `${target} timeout emits a stable child bridge reason code`);
399
+ const runnerFailed = await runCase('runner-failed', executableOverride);
400
+ assert.equal(runnerFailed.exit, 1, `${target} runner failure fails the successful-call conformance gate`);
401
+ assert.ok(runnerFailed.requests.some(r => JSON.stringify(r).includes('runner_failed')), `${target} nonzero runner exit is surfaced structurally`);
402
+ assert.equal(runnerFailed.stdout, '', `${target} runner failure keeps stdout empty`);
403
+ assert.match(runnerFailed.stderr, /benchmark terminal output unavailable \(child_bridge_runner_failed\)/, `${target} runner failure emits a stable child bridge reason code`);
404
+ assert.equal(runnerFailed.output.includes('PRIVATE_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'), false, `${target} runner stderr is never reflected`);
405
+ const spawnFailed = await runCase('spawn-failed', executableOverride);
406
+ assert.equal(spawnFailed.exit, 1, `${target} runner spawn failure fails the successful-call conformance gate`);
407
+ assert.ok(spawnFailed.requests.some(r => JSON.stringify(r).includes('spawn_failed')), `${target} runner spawn failure is surfaced structurally`);
408
+ assert.equal(spawnFailed.stdout, '', `${target} runner spawn failure keeps stdout empty`);
409
+ assert.match(spawnFailed.stderr, /benchmark terminal output unavailable \(child_bridge_spawn_failed\)/, `${target} runner spawn failure emits a stable child bridge reason code`);
410
+ }
411
+ async function assertSourceRuntimeContractWhenAvailable() {
412
+ const vendoredDsh = join(ROOT, 'ts', 'dsh-runtime', 'node_modules', '@deepseek-ai', 'dsh', 'lib', 'bin.js');
413
+ let sourceRuntimeAvailable = existsSync(vendoredDsh);
414
+ if (!sourceRuntimeAvailable) {
415
+ try {
416
+ createRequire(BIN).resolve('@deepseek-ai/dsh/lib/bin.js');
417
+ sourceRuntimeAvailable = true;
418
+ }
419
+ catch {
420
+ sourceRuntimeAvailable = false;
421
+ }
422
+ }
423
+ if (!sourceRuntimeAvailable) {
424
+ const binSource = readFileSync(BIN, 'utf8');
425
+ assert.equal(binSource.includes('headless-keepalive'), false, 'source bin must not inject a headless keepalive preload');
426
+ assert.equal(binSource.includes('--require=${headlessKeepalivePath}'), false, 'source bin must not mutate NODE_OPTIONS with a keepalive preload');
427
+ assert.equal(binSource.includes('headless-startup-hold'), false, 'source bin must not inject a timer-based startup hold');
428
+ return false;
429
+ }
430
+ await assertRuntimeContract();
431
+ return true;
432
+ }
433
+ function installedPackageRoot(executable) {
434
+ const consumerRoot = dirname(dirname(dirname(executable)));
435
+ const packageMain = createRequire(join(consumerRoot, 'package.json')).resolve('@danceiny/gotry');
436
+ return dirname(dirname(dirname(packageMain)));
437
+ }
438
+ async function assertPackagedPatchProjection(executable) {
439
+ const sourcePackageRoot = installedPackageRoot(executable);
440
+ const packageScope = dirname(sourcePackageRoot);
441
+ const probeParent = mkdtempSync(join(packageScope, 'round4-projection-'));
442
+ const probePackageRoot = join(probeParent, 'gotry');
443
+ const probeExecutable = join(probePackageRoot, 'bin', 'gotry-inner.js');
444
+ const patchPath = join(probePackageRoot, 'cordis.gotry-patch.yml');
445
+ const inlinePoisonModule = join(probePackageRoot, 'future-inline-plugin.cjs');
446
+ const reorderedPoisonModule = join(probePackageRoot, 'future-reordered-plugin.cjs');
447
+ const inlinePoisonProof = join(probeParent, 'future-inline-loaded.txt');
448
+ const reorderedPoisonProof = join(probeParent, 'future-reordered-loaded.txt');
449
+ cpSync(sourcePackageRoot, probePackageRoot, { recursive: true });
450
+ chmodSync(probeExecutable, 0o755);
451
+ const basePatch = readFileSync(patchPath, 'utf8');
452
+ const stableError = /benchmark environment configuration unavailable/;
453
+ const poisonEnv = {
454
+ GOTRY_FUTURE_INLINE_PROOF: inlinePoisonProof,
455
+ GOTRY_FUTURE_REORDERED_PROOF: reorderedPoisonProof,
456
+ };
457
+ const runRejectedPatch = async (label, patch, forbiddenValues = []) => {
458
+ rmSync(inlinePoisonProof, { force: true });
459
+ rmSync(reorderedPoisonProof, { force: true });
460
+ writeFileSync(patchPath, patch);
461
+ const result = await runCase('enabled', probeExecutable, poisonEnv);
462
+ assert.equal(result.exit, 1, `${label} must reject the benchmark startup`);
463
+ assert.equal(result.requests.length, 0, `${label} must fail before relay activity`);
464
+ assert.deepEqual(result.optionalResolutionHits, { calendar: 0, map: 0 }, `${label} must fail before optional plugin resolution`);
465
+ assert.match(result.output, stableError, `${label} emits a stable generic error`);
466
+ assert.equal(result.output.includes(probePackageRoot), false, `${label} must not reflect the package path`);
467
+ assert.equal(existsSync(inlinePoisonProof) || existsSync(reorderedPoisonProof), false, `${label} must not execute a poison plugin`);
468
+ for (const value of forbiddenValues)
469
+ assert.equal(result.output.includes(value), false, `${label} must not reflect rejected input`);
470
+ };
471
+ try {
472
+ writeFileSync(inlinePoisonModule, `const fs = require('node:fs'); fs.appendFileSync(process.env.GOTRY_FUTURE_INLINE_PROOF, 'loaded\\n'); exports.name = 'round4-future-inline'; exports.apply = () => {}`);
473
+ writeFileSync(reorderedPoisonModule, `const fs = require('node:fs'); fs.appendFileSync(process.env.GOTRY_FUTURE_REORDERED_PROOF, 'loaded\\n'); exports.name = 'round4-future-reordered'; exports.apply = () => {}`);
474
+ const futureEntries = [
475
+ ` - { id: dsh-future-inline, name: '${inlinePoisonModule}' }`,
476
+ ` - name: '${reorderedPoisonModule}'\n id: dsh-future-reordered`,
477
+ ].join('\n');
478
+ const futurePatch = basePatch.replace(" - id: dsh-map-tools", `${futureEntries}\n - id: dsh-map-tools`);
479
+ assert.notEqual(futurePatch, basePatch, 'future-plugin fixture must enter the insert sequence');
480
+ writeFileSync(patchPath, futurePatch);
481
+ const ordinary = await runCase('disabled', probeExecutable, poisonEnv);
482
+ assert.ok(existsSync(inlinePoisonProof), `default-off must execute the inline future-plugin top level; exit=${ordinary.exit}; output=${ordinary.output.slice(-2_000)}`);
483
+ assert.ok(existsSync(reorderedPoisonProof), `default-off must execute the reordered future-plugin top level; exit=${ordinary.exit}; output=${ordinary.output.slice(-2_000)}`);
484
+ rmSync(inlinePoisonProof, { force: true });
485
+ rmSync(reorderedPoisonProof, { force: true });
486
+ const benchmark = await runCase('enabled', probeExecutable, poisonEnv);
487
+ assert.equal(benchmark.exit, 0, `benchmark future-plugin projection exits 0; output=${benchmark.output.slice(-2_000)}`);
488
+ assert.ok(benchmark.requests.some(request => names(request).includes(TOOL)), 'benchmark future-plugin projection reaches the bridge relay');
489
+ assert.equal(existsSync(inlinePoisonProof) || existsSync(reorderedPoisonProof), false, 'benchmark projection must not execute inline or reordered future plugins');
490
+ assert.deepEqual(benchmark.optionalResolutionHits, { calendar: 0, map: 0 }, 'benchmark future-plugin projection does not resolve optional host plugins');
491
+ await runRejectedPatch('missing gotry-tools', basePatch.replace(' - id: gotry-tools', ' - id: gotry-tools-missing'));
492
+ await runRejectedPatch('duplicate gotry-tools', basePatch.replace(' - id: dsh-map-tools', " - id: gotry-tools\n name: 'duplicate/gotry-tools'\n - id: dsh-map-tools"));
493
+ await runRejectedPatch('second insert block', `${basePatch}\n- insert:\n - id: dsh-second-insert\n name: '${inlinePoisonModule}'\n`, [inlinePoisonModule]);
494
+ await runRejectedPatch('flow second insert block', `${basePatch}\n- insert: [{ id: dsh-flow-second-insert, name: '${inlinePoisonModule}' }]\n`, [inlinePoisonModule]);
495
+ await runRejectedPatch('spoofed gotry-tools name', basePatch.replace("name: 'placeholder/ts/src/index.ts'", `name: '${inlinePoisonModule}'`), [inlinePoisonModule]);
496
+ await runRejectedPatch('spoofed gotry-tools name with decoy anchor', `${basePatch.replace("name: 'placeholder/ts/src/index.ts'", `name: '${inlinePoisonModule}'`)}\n- id: benchmark-name-decoy\n name: 'placeholder/ts/src/index.ts'\n`, [inlinePoisonModule]);
497
+ await runRejectedPatch('spoofed gotry-tools name with nested decoy anchor', basePatch
498
+ .replace("name: 'placeholder/ts/src/index.ts'", `name: '${inlinePoisonModule}'`)
499
+ .replace(" stateRoot: '.'", " name: 'placeholder/ts/src/index.ts'\n stateRoot: '.'"), [inlinePoisonModule]);
500
+ await runRejectedPatch('missing benchmark config anchor', basePatch.replace(/^\s*hbcliBin:.*\n/m, ''));
501
+ await runRejectedPatch('missing benchmark config anchor with decoy', `${basePatch.replace(/^\s*hbcliBin:.*\n/m, '')}\n- id: benchmark-anchor-decoy\n hbcliBin: 'hbcli'\n`);
502
+ await runRejectedPatch('missing benchmark config anchor with nested decoy', basePatch
503
+ .replace(/^\s*hbcliBin:.*\n/m, '')
504
+ .replace(" stateRoot: '.'", " nestedAnchorDecoy:\n hbcliBin: 'hbcli'\n stateRoot: '.'"));
505
+ await runRejectedPatch('duplicate benchmark config anchor', basePatch.replace(" hbcliBin: 'hbcli'", " hbcliBin: 'hbcli'\n hbcliBin: 'hbcli'"));
506
+ await runRejectedPatch('pre-existing benchmark config path', basePatch.replace(" hbcliBin: 'hbcli'", " hbcliBin: 'hbcli'\n benchmarkEnvironmentConfigPath: '/not/used'"), ['/not/used']);
507
+ await runRejectedPatch('pre-existing service injection', basePatch.replace(" config:", " inject: [missing-service]\n config:"));
508
+ await runRejectedPatch('quoted service injection', basePatch.replace(" config:", " 'inject': [missing-service]\n config:"));
509
+ await runRejectedPatch('missing system-prompt anchor', basePatch.replace(/^- id: system-prompt[\s\S]*$/m, ''));
510
+ await runRejectedPatch('duplicate system-prompt anchor', `${basePatch}\n- id: system-prompt\n config:\n personaPrefix: >-\n duplicate\n`);
511
+ await runRejectedPatch('quoted system-prompt duplicate', `${basePatch}\n- id: 'system-prompt'\n config:\n personaPrefix: >-\n quoted duplicate\n`);
512
+ const systemPromptMutationSentinel = 'ROUND7_SYSTEM_PROMPT_MUTATION_SENTINEL_DO_NOT_REFLECT';
513
+ await runRejectedPatch('quoted mapping-key system-prompt duplicate', `${basePatch}\n- "id": system-prompt\n config:\n personaPrefix: >-\n ${systemPromptMutationSentinel}\n`, [systemPromptMutationSentinel]);
514
+ await runRejectedPatch('flow quoted-key system-prompt duplicate', `${basePatch}\n- { "id": system-prompt, config: { personaPrefix: ${systemPromptMutationSentinel} } }\n`, [systemPromptMutationSentinel]);
515
+ await runRejectedPatch('reordered system-prompt duplicate', `${basePatch}\n- name: reordered-system-prompt\n id: system-prompt\n config:\n personaPrefix: >-\n reordered duplicate\n`);
516
+ await runRejectedPatch('flow system-prompt duplicate', `${basePatch}\n- { id: system-prompt, config: { personaPrefix: flow duplicate } }\n`);
517
+ await runRejectedPatch('noncanonical insert id root item', `${basePatch}\n- id: insert\n config:\n personaPrefix: >-\n ${systemPromptMutationSentinel}\n`, [systemPromptMutationSentinel]);
518
+ await runRejectedPatch('malformed system-prompt persona', basePatch.replace(/^ personaPrefix: >-$/m, ' personaPrefix: plain'));
519
+ }
520
+ finally {
521
+ rmSync(probeParent, { recursive: true, force: true });
522
+ }
523
+ }
524
+ const LARGE_TERMINAL_PAYLOAD = 'x'.repeat(80 * 1024);
525
+ function taggedTerminal(valid) {
526
+ return `<benchmark_terminal>${valid ? '{"status":"succeeded"}' : '{"status":'}</benchmark_terminal>`;
527
+ }
528
+ function conformanceResponse(mode, request, plannerCount) {
529
+ const hasToolResult = anyToolResultPresent(request);
530
+ if (mode === 'a' && plannerCount === 1 && !hasToolResult)
531
+ return finalText('assistant prose without a call');
532
+ const call = mode === 'a' && plannerCount === 2
533
+ || ['b', 'd', 'f', 'large', 'schema'].includes(mode) && plannerCount === 1;
534
+ if (call && !hasToolResult)
535
+ return toolCall();
536
+ if (mode === 'f' && plannerCount === 3)
537
+ return toolCall('bridge-call-retry');
538
+ if (mode === 'large' && hasToolResult) {
539
+ return finalText(`<benchmark_terminal>${JSON.stringify({ payload: LARGE_TERMINAL_PAYLOAD })}</benchmark_terminal>`);
540
+ }
541
+ if (mode === 'schema') {
542
+ // Round 11 真实失败形态:结构合法信封内多出 root 键(extra budget)——
543
+ // 信封解析通过,exact body schema 必须拒绝,且纠正后仍不 autofix。
544
+ return finalText('<benchmark_terminal>{"status":"succeeded","budget":{"total_cost":1}}</benchmark_terminal>');
545
+ }
546
+ const valid = mode === 'a' ? hasToolResult : mode === 'b' ? plannerCount >= 3 : mode === 'e' ? true : false;
547
+ return finalText(mode === 'c' || mode === 'd' ? 'bad benchmark body' : taggedTerminal(valid));
548
+ }
549
+ async function runConformanceCase(mode, executableOverride) {
550
+ const requests = [];
551
+ let plannerCount = 0;
552
+ let recoveredAttempts = 0;
553
+ let servedToolCalls = 0;
554
+ const server = createServer((req, res) => {
555
+ const chunks = [];
556
+ req.on('data', chunk => chunks.push(Buffer.from(chunk)));
557
+ req.on('end', () => {
558
+ let body = {};
559
+ try {
560
+ body = JSON.parse(Buffer.concat(chunks).toString());
561
+ }
562
+ catch { /* structural failure */ }
563
+ requests.push(body);
564
+ const requestHasToolResult = anyToolResultPresent(body);
565
+ const plannerRequest = names(body).includes(TOOL);
566
+ if (mode === 'exhausted' || mode === 'unknown' || (mode === 'post-failure' && requestHasToolResult)) {
567
+ res.writeHead(mode === 'exhausted' ? 429 : mode === 'unknown' ? 418 : 500, { 'content-type': 'application/json' });
568
+ res.end(JSON.stringify({ error: { message: 'PRIVATE_SENTINEL_DO_NOT_REFLECT', type: 'server_error' } }));
569
+ return;
570
+ }
571
+ if (mode === 'recovered' && plannerRequest && !requestHasToolResult && recoveredAttempts++ === 0) {
572
+ res.writeHead(503, { 'content-type': 'application/json' });
573
+ res.end(JSON.stringify({ error: { message: 'PRIVATE_SENTINEL_DO_NOT_REFLECT', type: 'server_error' } }));
574
+ return;
575
+ }
576
+ if (plannerRequest)
577
+ plannerCount += 1;
578
+ res.writeHead(200, { 'content-type': 'text/event-stream' });
579
+ const response = plannerRequest
580
+ ? conformanceResponse(mode === 'recovered' ? 'b' : mode === 'post-failure' ? 'f' : mode, body, plannerCount)
581
+ : finalText('auxiliary request');
582
+ if (response.includes(`"name":"${TOOL}"`))
583
+ servedToolCalls += 1;
584
+ res.end(response);
585
+ });
586
+ });
587
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
588
+ const port = server.address().port;
589
+ const cwd = mkdtempSync(join(tmpdir(), 'gotry-conformance-cwd-'));
590
+ const dsh = mkdtempSync(join(tmpdir(), 'gotry-conformance-dsh-'));
591
+ const runner = join(cwd, 'synthetic-runner.js');
592
+ const runnerCount = join(cwd, 'runner-count.txt');
593
+ const configPath = join(cwd, 'benchmark-env-config.json');
594
+ writeFileSync(runner, `const fs = require('node:fs'); const path = ${JSON.stringify(runnerCount)}; const count = fs.existsSync(path) ? Number(fs.readFileSync(path, 'utf8')) : 0; fs.writeFileSync(path, String(count + 1)); process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}' } }))`);
595
+ writeFileSync(configPath, JSON.stringify({
596
+ schema_version: 'gotry_benchmark_environment_bridge_v4', enabled: true,
597
+ executable: process.execPath, cwd, argv_prefix: [runner],
598
+ tools: [{ name: 'lookup', description: 'Lookup.', input_schema: LOOKUP_INPUT_SCHEMA, output_keys: ['marker'], domain_outcomes: [{ status: 'miss', code: 'NOT_FOUND', recovery: 'none' }] }], timeout_ms: 2_000, max_output_bytes: 4_096,
599
+ terminal_output: { tag: 'benchmark_terminal', max_bytes: mode === 'large' ? 128 * 1024 : 4_096, body_schema: BRIDGE_E2E_BODY_SCHEMA },
600
+ isolation: { mode: 'host-enforced', writes: 'forbidden', network: 'denied' },
601
+ }));
602
+ const env = {
603
+ ...process.env,
604
+ DSH_TOOLS_MODE: 'both', DSH_HOME: dsh,
605
+ LLM_API_KEY: 'synthetic-conformance-key', LLM_BASE_URL: `http://127.0.0.1:${port}/v1`,
606
+ LLM_MODEL: 'synthetic-conformance-model', GOTRY_BENCHMARK_ENV_CONFIG: configPath,
607
+ DEEPSEEK_API_KEY: 'synthetic-conformance-key', DEEPSEEK_BASE_URL: `http://127.0.0.1:${port}/v1`,
608
+ NODE_OPTIONS: [
609
+ process.env.NODE_OPTIONS,
610
+ ...(!executableOverride ? [`--import=${TSX_LOADER}`] : []),
611
+ ].filter(Boolean).join(' '),
612
+ };
613
+ for (const key of ['GOTRY_LLM_MODEL', 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'http_proxy', 'https_proxy', 'all_proxy'])
614
+ delete env[key];
615
+ env.NO_PROXY = '127.0.0.1,localhost';
616
+ let stdout = '';
617
+ let stderr = '';
618
+ try {
619
+ const executable = executableOverride || process.execPath;
620
+ const argv = executableOverride ? ['conformance smoke'] : [BIN, 'conformance smoke'];
621
+ const child = spawn(executable, argv, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
622
+ child.stdout.on('data', chunk => { stdout += chunk.toString(); });
623
+ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
624
+ const exit = await new Promise(resolve => {
625
+ const timer = setTimeout(() => { child.kill('SIGKILL'); resolve(null); }, TIMEOUT_MS);
626
+ child.once('close', code => { clearTimeout(timer); resolve(code); });
627
+ });
628
+ const runnerInvocations = existsSync(runnerCount) ? Number(readFileSync(runnerCount, 'utf8')) : 0;
629
+ return { exit, stdout, stderr, requests, servedToolCalls, runnerInvocations };
630
+ }
631
+ finally {
632
+ await new Promise(resolve => server.close(() => resolve()));
633
+ rmSync(dsh, { recursive: true, force: true });
634
+ rmSync(cwd, { recursive: true, force: true });
635
+ }
636
+ }
637
+ async function assertTerminalDiagnostics(executableOverride) {
638
+ const terminalReasons = (stderr) => [...stderr.matchAll(/benchmark terminal output unavailable \(([^)]+)\)/g)].map(match => match[1]);
639
+ const exhausted = await runConformanceCase('exhausted', executableOverride);
640
+ assert.notEqual(exhausted.exit, 0, 'exhausted transient model failure exits non-zero');
641
+ assert.match(exhausted.stderr, /child_model_capacity/, 'exhausted transient model failure emits coarse capacity enum');
642
+ assert.ok(exhausted.requests.length > 1, 'exhausted case actually exercises retry attempts');
643
+ assert.equal(exhausted.stdout, '', 'exhausted transient model failure releases no terminal stdout');
644
+ assert.equal(exhausted.stderr.includes('PRIVATE_SENTINEL_DO_NOT_REFLECT'), false, 'exhausted error body is never reflected');
645
+ assert.deepEqual(terminalReasons(exhausted.stderr), ['child_model_capacity'], 'exhausted emits exactly one terminal reason');
646
+ const recovered = await runConformanceCase('recovered', executableOverride);
647
+ assert.equal(recovered.exit, 0, 'transient model failure followed by valid terminal recovers');
648
+ assert.match(recovered.stdout, /<benchmark_terminal>/, 'recovered run releases terminal stdout');
649
+ assert.equal(recovered.stderr.includes('child_model_'), false, 'recovered run emits no failure enum');
650
+ assert.deepEqual(terminalReasons(recovered.stderr), [], 'recovered emits no terminal reason');
651
+ const postFailure = await runConformanceCase('post-failure', executableOverride);
652
+ assert.notEqual(postFailure.exit, 0, 'model failure after successful bridge exits non-zero');
653
+ assert.equal(postFailure.runnerInvocations, 1, 'post-bridge failure follows exactly one successful bridge invocation');
654
+ assert.match(postFailure.stderr, /child_model_server/, 'post-bridge model failure emits server enum');
655
+ assert.equal(postFailure.stdout, '', 'post-bridge model failure releases no terminal stdout');
656
+ assert.deepEqual(terminalReasons(postFailure.stderr), ['child_model_server'], 'post-bridge emits exactly one terminal reason');
657
+ const unknown = await runConformanceCase('unknown', executableOverride);
658
+ assert.notEqual(unknown.exit, 0, 'unknown model failure exits non-zero');
659
+ assert.match(unknown.stderr, /child_runtime_error/, 'unknown model failure collapses to generic runtime enum');
660
+ assert.equal(unknown.stdout, '', 'unknown model failure releases no terminal stdout');
661
+ assert.equal(unknown.stderr.includes('PRIVATE_SENTINEL_DO_NOT_REFLECT'), false, 'unknown error body is never reflected');
662
+ assert.deepEqual(terminalReasons(unknown.stderr), ['child_runtime_error'], 'unknown emits exactly one terminal reason');
663
+ const precedence = await runConformanceCase('f', executableOverride);
664
+ assert.match(precedence.stderr, /child_conformance_failure/, 'conformance-specific failure remains higher precedence than final generic error');
665
+ assert.equal(precedence.stderr.includes('child_runtime_error'), false, 'generic terminal classification does not double-write');
666
+ assert.deepEqual(terminalReasons(precedence.stderr), ['child_conformance_failure'], 'precedence emits exactly one terminal reason');
667
+ }
668
+ async function assertOutputConformance(executableOverride) {
669
+ const a = await runConformanceCase('a', executableOverride);
670
+ assert.equal(a.exit, 0, 'A prose/no-call correction then one bridge call and valid terminal exits 0');
671
+ assert.equal(a.servedToolCalls, 1, 'A exposes exactly one bridge call');
672
+ assert.equal(a.runnerInvocations, 1, 'A executes the bridge subprocess exactly once');
673
+ assert.ok(a.stdout.includes('<benchmark_terminal>'), 'A forwards only tagged terminal output');
674
+ // Round 12(#215):system prompt 投影 exact body schema outline(结构合同单一来源)。
675
+ assert.ok(a.requests.some(request => JSON.stringify(request).includes(`matching exactly ${TERMINAL_OUTLINE}`)), 'A system prompt projects the exact terminal schema outline');
676
+ const b = await runConformanceCase('b', executableOverride);
677
+ assert.equal(b.exit, 0, 'B malformed terminal correction then valid terminal exits 0');
678
+ assert.equal(b.servedToolCalls, 1, `B exposes exactly one bridge call; request shapes=${JSON.stringify(b.requests.map(request => ({ tools: names(request), roles: (request.messages ?? []).map(message => message.role) })))}`);
679
+ assert.equal(b.runnerInvocations, 1, 'B format-only correction does not rerun the bridge subprocess');
680
+ assert.ok(b.requests.some(request => JSON.stringify(request).includes('BENCHMARK_CONFORMANCE_TERMINAL') && JSON.stringify(request).includes(`matching exactly ${TERMINAL_OUTLINE}`)), 'B terminal correction projects the same schema outline as the system prompt');
681
+ const schema = await runConformanceCase('schema', executableOverride);
682
+ assert.notEqual(schema.exit, 0, 'schema-invalid terminal body (extra root key) is rejected after the single correction');
683
+ assert.match(schema.stderr, /benchmark terminal output unavailable \(child_conformance_failure\)/, 'schema-invalid terminal emits the stable conformance reason code');
684
+ assert.equal(schema.runnerInvocations, 1, 'schema-invalid terminal still executed the bridge exactly once');
685
+ assert.equal(schema.stdout.includes('<benchmark_terminal>'), false, 'schema-invalid terminal body is never released to stdout');
686
+ for (const mode of ['c', 'd']) {
687
+ const result = await runConformanceCase(mode, executableOverride);
688
+ assert.notEqual(result.exit, 0, `${mode.toUpperCase()} repeated invalid output is non-zero`);
689
+ assert.match(result.stderr, /benchmark terminal output unavailable \(child_conformance_failure\)/, `${mode.toUpperCase()} emits a stable conformance reason code`);
690
+ assert.equal(result.stdout.includes('bad benchmark body'), false, `${mode.toUpperCase()} does not forward invalid body to stdout`);
691
+ assert.equal(result.stderr.includes('bad benchmark body'), false, `${mode.toUpperCase()} stable diagnostics do not reflect invalid body`);
692
+ assert.equal(result.runnerInvocations, mode === 'c' ? 0 : 1, `${mode.toUpperCase()} subprocess count matches the accepted call history`);
693
+ }
694
+ const e = await runConformanceCase('e', executableOverride);
695
+ assert.notEqual(e.exit, 0, 'E valid tagged terminal without bridge call is rejected');
696
+ assert.match(e.stderr, /benchmark terminal output unavailable \(child_conformance_failure\)/, 'E emits a stable conformance reason code');
697
+ assert.equal(e.stdout.includes('<benchmark_terminal>'), false, 'E does not forward terminal without call');
698
+ assert.equal(e.runnerInvocations, 0, 'E never executes the bridge subprocess');
699
+ const f = await runConformanceCase('f', executableOverride);
700
+ assert.notEqual(f.exit, 0, 'F format correction that tries another bridge call is rejected');
701
+ assert.match(f.stderr, /benchmark terminal output unavailable \(child_conformance_failure\)/, 'F emits a stable conformance reason code');
702
+ assert.equal(f.servedToolCalls, 2, 'F model attempts a second native call');
703
+ assert.equal(f.runnerInvocations, 1, 'F conformance guard blocks the second subprocess dispatch');
704
+ assert.equal(f.stdout.includes('<benchmark_terminal>'), false, 'F does not release a terminal body after retry redispatch');
705
+ const large = await runConformanceCase('large', executableOverride);
706
+ assert.equal(large.exit, 0, 'large valid terminal flushes before successful process close');
707
+ assert.equal(large.runnerInvocations, 1, 'large terminal still executes the bridge once');
708
+ assert.ok(Buffer.byteLength(large.stdout, 'utf8') > 64 * 1024, 'large terminal exceeds the ordinary pipe buffer');
709
+ assert.match(large.stdout, /<\/benchmark_terminal>\s*$/);
710
+ }
711
+ const packaged = process.env.GOTRY_BRIDGE_E2E_BIN;
712
+ assertRuntimeSelectionAndVersionGuards();
713
+ const sourceRuntimeChecked = await assertSourceRuntimeContractWhenAvailable();
714
+ if (sourceRuntimeChecked)
715
+ await assertOutputConformance();
716
+ if (sourceRuntimeChecked)
717
+ await assertTerminalDiagnostics();
718
+ if (packaged) {
719
+ await assertRuntimeContract(packaged);
720
+ await assertOutputConformance(packaged);
721
+ await assertTerminalDiagnostics(packaged);
722
+ await assertPackagedPatchProjection(packaged);
723
+ const packageRoot = installedPackageRoot(packaged);
724
+ const packagedBridge = await import(pathToFileURL(join(packageRoot, 'dist', 'src', 'benchmark-environment-bridge.js')).href);
725
+ const missingServiceRoot = mkdtempSync(join(tmpdir(), 'gotry-bridge-missing-service-'));
726
+ try {
727
+ const configPath = join(missingServiceRoot, 'bridge.json');
728
+ writeFileSync(configPath, JSON.stringify({
729
+ schema_version: 'gotry_benchmark_environment_bridge_v4', enabled: true,
730
+ executable: process.execPath, cwd: missingServiceRoot, argv_prefix: ['-e', 'process.exit(0)'],
731
+ tools: [{ name: 'lookup', description: 'Lookup.', input_schema: LOOKUP_INPUT_SCHEMA, output_keys: ['marker'], domain_outcomes: [{ status: 'miss', code: 'NOT_FOUND', recovery: 'none' }] }], timeout_ms: 100, max_output_bytes: 4_096,
732
+ terminal_output: { tag: 'benchmark_terminal', max_bytes: 4_096, body_schema: BRIDGE_E2E_BODY_SCHEMA },
733
+ isolation: { mode: 'host-enforced', writes: 'forbidden', network: 'denied' },
734
+ }));
735
+ assert.throws(() => packagedBridge.registerBenchmarkEnvironmentBridge(configPath, () => { }, undefined), /benchmark environment bridge subprocess unavailable/, 'packaged explicit opt-in fails hard when no active subprocess provider exists');
736
+ }
737
+ finally {
738
+ rmSync(missingServiceRoot, { recursive: true, force: true });
739
+ }
740
+ }
741
+ console.log(`benchmark environment bridge E2E: OK (${packaged ? (sourceRuntimeChecked ? 'source + packaged' : 'source-static + packaged') : (sourceRuntimeChecked ? 'source' : 'source-static')})`);