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

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 (507) 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 +1744 -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 +135 -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 +546 -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 +509 -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/resilience.js +191 -0
  41. package/dist/capabilities/session/action-cache.js +50 -44
  42. package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
  43. package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
  44. package/dist/capabilities/session/adapters/dida-portal.js +274 -0
  45. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  46. package/dist/capabilities/session/adapters/rail-12306.js +342 -0
  47. package/dist/capabilities/session/benchmark.js +189 -0
  48. package/dist/capabilities/session/bridge-ledger.js +224 -0
  49. package/dist/capabilities/session/extension-bridge.js +636 -0
  50. package/dist/capabilities/session/extension-channel.js +109 -0
  51. package/dist/capabilities/session/extension-distribution.js +230 -0
  52. package/dist/capabilities/session/extract.js +31 -21
  53. package/dist/capabilities/session/golden-score.js +114 -0
  54. package/dist/capabilities/session/health-watch.js +215 -0
  55. package/dist/capabilities/session/read-guard.js +75 -24
  56. package/dist/capabilities/session/static-flight-golden.js +150 -0
  57. package/dist/capabilities/session/transport.js +97 -83
  58. package/dist/capabilities/session/wizard.js +117 -0
  59. package/dist/capabilities/session-consent.js +178 -0
  60. package/dist/capabilities/session-login.js +170 -0
  61. package/dist/capabilities/session-search.js +635 -44
  62. package/dist/capabilities/spawn-bounded.js +203 -0
  63. package/dist/capabilities/sponsor-plugin.js +341 -0
  64. package/dist/capabilities/visa-policy.js +140 -0
  65. package/dist/capabilities/weather.js +243 -138
  66. package/dist/data/session-golden-20.json +25 -0
  67. package/dist/data/sf-golden-manifest.json +103 -0
  68. package/dist/data/sf-static-routes.json +91 -0
  69. package/dist/scripts/action-cache-tests.js +24 -59
  70. package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
  71. package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
  72. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  73. package/dist/scripts/agent-reach-error-tests.js +354 -0
  74. package/dist/scripts/agent-reach-tests.js +26 -18
  75. package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
  76. package/dist/scripts/anything-tests.js +116 -60
  77. package/dist/scripts/artifact-client-contract-tests.js +220 -0
  78. package/dist/scripts/artifacts-capability-tests.js +727 -0
  79. package/dist/scripts/async-collect.js +50 -11
  80. package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
  81. package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
  82. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
  83. package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
  84. package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
  85. package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
  86. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
  87. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
  88. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
  89. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
  90. package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
  91. package/dist/scripts/booking-copilot-dsh-warmer-proof.js +123 -0
  92. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
  93. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
  94. package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
  95. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
  96. package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
  97. package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
  98. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
  99. package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
  100. package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
  101. package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
  102. package/dist/scripts/booking-executor-tests.js +80 -0
  103. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  104. package/dist/scripts/booking-saga-tests.js +125 -0
  105. package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
  106. package/dist/scripts/bootstrap-tests.js +1479 -0
  107. package/dist/scripts/bridge-events-tests.js +239 -0
  108. package/dist/scripts/bridge-ledger-tests.js +428 -0
  109. package/dist/scripts/build-changelog.js +238 -0
  110. package/dist/scripts/build-metrics-report.js +331 -0
  111. package/dist/scripts/changelog-tests.js +118 -0
  112. package/dist/scripts/channel-probe-tests.js +103 -0
  113. package/dist/scripts/channel-probe.js +169 -0
  114. package/dist/scripts/channel-registry-tests.js +208 -0
  115. package/dist/scripts/companion-tests.js +25 -70
  116. package/dist/scripts/dida-cookie-probe.js +87 -0
  117. package/dist/scripts/dida-runner-stop-tests.js +311 -0
  118. package/dist/scripts/dida-sw-probe.js +114 -0
  119. package/dist/scripts/diff-test.js +17 -12
  120. package/dist/scripts/doctor-tests.js +564 -0
  121. package/dist/scripts/dsh-artifact-e2e.js +279 -0
  122. package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
  123. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  124. package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
  125. package/dist/scripts/dsh-runtime-closure.js +162 -0
  126. package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
  127. package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
  128. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  129. package/dist/scripts/effect-tests.js +539 -0
  130. package/dist/scripts/engine-run.js +1 -3
  131. package/dist/scripts/engine-tests.js +16 -10
  132. package/dist/scripts/evaluation-cadence-tests.js +174 -0
  133. package/dist/scripts/evaluation-contract-tests.js +268 -0
  134. package/dist/scripts/extension-distribution-cli.js +39 -0
  135. package/dist/scripts/extension-distribution-tests.js +443 -0
  136. package/dist/scripts/extension-tests.js +988 -0
  137. package/dist/scripts/external-event-tests.js +350 -0
  138. package/dist/scripts/fact-gate-tests.js +869 -0
  139. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  140. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  141. package/dist/scripts/flight-malformed-tests.js +159 -0
  142. package/dist/scripts/flyai-setup-tests.js +228 -0
  143. package/dist/scripts/flyai-setup-tool-tests.js +54 -0
  144. package/dist/scripts/flyai-tests.js +692 -0
  145. package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
  146. package/dist/scripts/fx-contract-tests.js +201 -0
  147. package/dist/scripts/g5-guard-tests.js +131 -0
  148. package/dist/scripts/g5-guard.js +189 -0
  149. package/dist/scripts/gen-lunar-anchors.js +106 -0
  150. package/dist/scripts/geo-atlas-tests.js +283 -0
  151. package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
  152. package/dist/scripts/gotry-try-demo-tests.js +79 -0
  153. package/dist/scripts/gotry-try-demo.js +49 -0
  154. package/dist/scripts/ground-transfer-tests.js +956 -0
  155. package/dist/scripts/hbcli-e2e-tests.js +159 -0
  156. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  157. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  158. package/dist/scripts/hbcli-tests.js +121 -69
  159. package/dist/scripts/health-watch-cli.js +73 -0
  160. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  161. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  162. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  163. package/dist/scripts/i18n-tests.js +41 -0
  164. package/dist/scripts/incident-tests.js +281 -87
  165. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  166. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  167. package/dist/scripts/issue-271-liveness-tests.js +569 -0
  168. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  169. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  170. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  171. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  172. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  173. package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
  174. package/dist/scripts/itinerary-artifact-tests.js +368 -0
  175. package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
  176. package/dist/scripts/itinerary-deck-export-tests.js +552 -0
  177. package/dist/scripts/itinerary-deck-tests.js +435 -0
  178. package/dist/scripts/itinerary-html-tests.js +426 -0
  179. package/dist/scripts/journey-tests.js +22 -27
  180. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  181. package/dist/scripts/kernel-manifest-gate.js +126 -0
  182. package/dist/scripts/kernel-manifest-tests.js +212 -0
  183. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  184. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  185. package/dist/scripts/kernel-manifest.js +369 -0
  186. package/dist/scripts/lavish-local-tests.js +1226 -0
  187. package/dist/scripts/lavish-product-tools-tests.js +537 -0
  188. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  189. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  190. package/dist/scripts/ledger-tests.js +382 -246
  191. package/dist/scripts/ledger-workflow-crash.js +10 -20
  192. package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
  193. package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
  194. package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
  195. package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
  196. package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
  197. package/dist/scripts/memory-capture-tests.js +242 -61
  198. package/dist/scripts/memory-decay-tests.js +18 -52
  199. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  200. package/dist/scripts/memory-lifecycle.js +196 -0
  201. package/dist/scripts/memory-metrics.js +24 -12
  202. package/dist/scripts/memory-value-report.js +631 -0
  203. package/dist/scripts/memory-value-tests.js +328 -0
  204. package/dist/scripts/metrics-report-tests.js +151 -0
  205. package/dist/scripts/model-override-e2e.js +137 -0
  206. package/dist/scripts/nightly-evidence-tests.js +161 -0
  207. package/dist/scripts/nightly-evidence.js +286 -0
  208. package/dist/scripts/nudge-digest.js +39 -28
  209. package/dist/scripts/observability-tests.js +381 -0
  210. package/dist/scripts/onboarding-tests.js +161 -0
  211. package/dist/scripts/opensky-check.js +18 -13
  212. package/dist/scripts/opensky-tests.js +28 -28
  213. package/dist/scripts/persona-surface-guard-tests.js +49 -0
  214. package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
  215. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  216. package/dist/scripts/price-drift-tests.js +340 -0
  217. package/dist/scripts/price-drift-watch.js +447 -0
  218. package/dist/scripts/probe-poi-tests.js +53 -45
  219. package/dist/scripts/product-metrics.js +483 -0
  220. package/dist/scripts/publish-preverify.js +110 -0
  221. package/dist/scripts/realtime-pricing-tests.js +76 -0
  222. package/dist/scripts/recall-tests.js +513 -0
  223. package/dist/scripts/replay-async.js +20 -18
  224. package/dist/scripts/replay-real.js +18 -18
  225. package/dist/scripts/replay.js +52 -82
  226. package/dist/scripts/session-attach-diagnose.js +20 -20
  227. package/dist/scripts/session-attach-poc.js +34 -37
  228. package/dist/scripts/session-benchmark.js +180 -0
  229. package/dist/scripts/session-dida-live-e2e.js +199 -0
  230. package/dist/scripts/session-extract-tests.js +14 -35
  231. package/dist/scripts/session-link-tests.js +478 -0
  232. package/dist/scripts/session-login.js +27 -46
  233. package/dist/scripts/session-tests.js +796 -96
  234. package/dist/scripts/session-zones-tests.js +358 -0
  235. package/dist/scripts/sf-live-benchmark.js +367 -0
  236. package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
  237. package/dist/scripts/sf-live-cli-tests.js +92 -0
  238. package/dist/scripts/sf-live-options.js +46 -0
  239. package/dist/scripts/sf-manifest-tests.js +30 -0
  240. package/dist/scripts/sf-manifest.js +33 -0
  241. package/dist/scripts/sf-soft-score-tests.js +67 -0
  242. package/dist/scripts/sf-summary-tests.js +324 -0
  243. package/dist/scripts/sf-summary.js +525 -0
  244. package/dist/scripts/share-tests.js +432 -0
  245. package/dist/scripts/skeleton-check.js +16 -14
  246. package/dist/scripts/skeleton-integration-test.js +28 -11
  247. package/dist/scripts/skills-contract-tests.js +51 -46
  248. package/dist/scripts/smoke-session-gate-tests.js +21 -0
  249. package/dist/scripts/smoke.js +690 -246
  250. package/dist/scripts/spawn-bounded-abort-tests.js +241 -0
  251. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  252. package/dist/scripts/state-cli-tests.js +226 -101
  253. package/dist/scripts/state-cli.js +550 -199
  254. package/dist/scripts/state-repair-cli.js +66 -0
  255. package/dist/scripts/state-repair-tests.js +140 -0
  256. package/dist/scripts/static-golden-tests.js +165 -0
  257. package/dist/scripts/time-eval-tests.js +210 -192
  258. package/dist/scripts/travel-timeline-tests.js +29 -93
  259. package/dist/scripts/turn-handoff-collect-tests.js +169 -0
  260. package/dist/scripts/turn-handoff-collect.js +161 -0
  261. package/dist/scripts/turn-policy-tests.js +55 -0
  262. package/dist/scripts/typed-contract-canary.js +176 -0
  263. package/dist/scripts/tz-resolver-sanity.js +50 -0
  264. package/dist/scripts/tz-resolver-tests.js +584 -0
  265. package/dist/scripts/unified-tests.js +23 -19
  266. package/dist/scripts/visa-policy-tests.js +62 -0
  267. package/dist/scripts/weather-tests.js +276 -41
  268. package/dist/scripts/wish-channel-gate-tests.js +54 -0
  269. package/dist/scripts/write-gate-tests.js +522 -0
  270. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  271. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  272. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  273. package/dist/scripts/z3-race-tests.js +41 -0
  274. package/dist/src/artifact-gate.js +829 -0
  275. package/dist/src/backend/kernel.js +91 -0
  276. package/dist/src/backend/modules/booking-copilot.js +92 -0
  277. package/dist/src/backend/modules/booking-executor.js +124 -0
  278. package/dist/src/backend/modules/session-search.js +551 -0
  279. package/dist/src/benchmark-agent-conformance.js +578 -0
  280. package/dist/src/benchmark-environment-bridge.js +515 -0
  281. package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
  282. package/dist/src/benchmark-tool-isolation.js +151 -0
  283. package/dist/src/bookable-facts.js +533 -0
  284. package/dist/src/booking-saga.js +97 -0
  285. package/dist/src/booking-surface/availability-policy.js +697 -0
  286. package/dist/src/booking-surface/booking-intent.js +43 -0
  287. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  288. package/dist/src/booking-surface/canonical-schema.js +116 -0
  289. package/dist/src/booking-surface/contracts.js +75 -0
  290. package/dist/src/booking-surface/dsh-planner.js +1854 -0
  291. package/dist/src/booking-surface/dsh-plugin.js +378 -0
  292. package/dist/src/booking-surface/dsh-worker.js +98 -0
  293. package/dist/src/booking-surface/error-codes.js +231 -0
  294. package/dist/src/booking-surface/index.js +9 -0
  295. package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
  296. package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
  297. package/dist/src/booking-surface/profile.js +24 -0
  298. package/dist/src/booking-surface/recovery-chain.js +367 -0
  299. package/dist/src/booking-surface/runtime.js +1968 -0
  300. package/dist/src/booking-surface/server.js +674 -0
  301. package/dist/src/booking-surface/startup.js +168 -0
  302. package/dist/src/booking-surface/validation.js +261 -0
  303. package/dist/src/bridge.js +20 -13
  304. package/dist/src/companions.js +48 -74
  305. package/dist/src/contracts.js +21 -43
  306. package/dist/src/dsh-llm.js +97 -111
  307. package/dist/src/engine.js +276 -193
  308. package/dist/src/evaluation-cadence.js +97 -0
  309. package/dist/src/evaluation-contracts.js +283 -0
  310. package/dist/src/flight-pack-adapter.js +54 -0
  311. package/dist/src/flight-pack-contract.js +2 -0
  312. package/dist/src/flyai-setup-tool.js +70 -0
  313. package/dist/src/gotry-backend.js +125 -0
  314. package/dist/src/hotel-date-gate.js +118 -0
  315. package/dist/src/i18n.js +88 -0
  316. package/dist/src/index.js +2227 -882
  317. package/dist/src/itinerary-deck.js +139 -0
  318. package/dist/src/itinerary-doc-shared.js +661 -0
  319. package/dist/src/itinerary-html.js +131 -0
  320. package/dist/src/journey.js +155 -108
  321. package/dist/src/lavish-tools.js +400 -0
  322. package/dist/src/ledger-repair-apply.js +538 -0
  323. package/dist/src/ledger-repair-plan.js +463 -0
  324. package/dist/src/loop.js +319 -160
  325. package/dist/src/memory-capture.js +180 -32
  326. package/dist/src/memory-decay.js +30 -14
  327. package/dist/src/memory-lifecycle.js +1310 -0
  328. package/dist/src/memory-utility.js +27 -40
  329. package/dist/src/mock-llm.js +36 -61
  330. package/dist/src/model.js +81 -36
  331. package/dist/src/realtime-pricing.js +103 -0
  332. package/dist/src/recall/card.js +74 -0
  333. package/dist/src/recall/evaluator.js +140 -0
  334. package/dist/src/recall/integration.js +17 -0
  335. package/dist/src/recall/tick.js +166 -0
  336. package/dist/src/session-link/plan-it-action.js +48 -0
  337. package/dist/src/session-link/session-link.js +248 -0
  338. package/dist/src/session-zones.js +507 -0
  339. package/dist/src/share/adapters.js +51 -0
  340. package/dist/src/share/share-consent.js +76 -0
  341. package/dist/src/share/share-deck.js +58 -0
  342. package/dist/src/share/share-token.js +146 -0
  343. package/dist/src/slot-spec.js +84 -106
  344. package/dist/src/state-ledger.js +435 -516
  345. package/dist/src/state-repair.js +299 -0
  346. package/dist/src/subagent-job-id-guard.js +58 -0
  347. package/dist/src/time-anchor.js +231 -33
  348. package/dist/src/tool-packet.js +20 -8
  349. package/dist/src/travel-slots.js +58 -35
  350. package/dist/src/travel-timeline.js +44 -63
  351. package/dist/src/turn-deadline.js +287 -0
  352. package/dist/src/turn-policy.js +118 -0
  353. package/dist/src/tz-resolver.js +143 -0
  354. package/dist/src/unified.js +535 -359
  355. package/dist/src/wish-pool.js +42 -12
  356. package/dist/src/write-gate.js +495 -0
  357. package/dist/src/z3-shared.js +284 -0
  358. package/extension/README.md +21 -0
  359. package/extension/README.zh-CN.md +21 -0
  360. package/extension/background.js +463 -0
  361. package/extension/content-bridge.js +270 -0
  362. package/extension/content-main.js +111 -0
  363. package/extension/manifest.json +60 -0
  364. package/package.json +407 -14
  365. package/schemas/booking.intent.schema.json +193 -0
  366. package/schemas/booking.surface.schema.json +2622 -0
  367. package/ts/capabilities/agent-reach-deep.ts +114 -0
  368. package/ts/capabilities/agent-reach.ts +198 -0
  369. package/ts/capabilities/anything.ts +255 -0
  370. package/ts/capabilities/artifacts.ts +428 -0
  371. package/ts/capabilities/fact-log.ts +54 -0
  372. package/ts/capabilities/flyai-config.ts +388 -0
  373. package/ts/capabilities/flyai.ts +1274 -0
  374. package/ts/capabilities/hbcli.ts +253 -71
  375. package/ts/capabilities/incident-log.ts +88 -59
  376. package/ts/capabilities/itinerary-artifact.ts +214 -0
  377. package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
  378. package/ts/capabilities/itinerary-deck-export.ts +378 -0
  379. package/ts/capabilities/opensky.ts +121 -0
  380. package/ts/capabilities/session/action-cache.ts +148 -0
  381. package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
  382. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  383. package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
  384. package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
  385. package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
  386. package/ts/capabilities/session/benchmark.ts +276 -0
  387. package/ts/capabilities/session/bridge-ledger.ts +333 -0
  388. package/ts/capabilities/session/extension-bridge.ts +794 -0
  389. package/ts/capabilities/session/extension-channel.ts +144 -0
  390. package/ts/capabilities/session/extension-distribution.ts +266 -0
  391. package/ts/capabilities/session/extract.ts +54 -0
  392. package/ts/capabilities/session/golden-score.ts +139 -0
  393. package/ts/capabilities/session/health-watch.ts +264 -0
  394. package/ts/capabilities/session/read-guard.ts +141 -0
  395. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  396. package/ts/capabilities/session/transport.ts +146 -0
  397. package/ts/capabilities/session/wizard.ts +140 -0
  398. package/ts/capabilities/session-consent.ts +211 -0
  399. package/ts/capabilities/session-login.ts +208 -0
  400. package/ts/capabilities/session-search.ts +893 -0
  401. package/ts/capabilities/weather.ts +330 -0
  402. package/ts/dsh-runtime/vendor/README.md +68 -0
  403. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  404. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
  405. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
  406. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
  407. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  408. package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
  409. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
  410. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  411. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
  412. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  413. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  414. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
  415. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  416. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
  417. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
  418. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
  419. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
  420. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  421. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  422. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
  423. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
  424. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  425. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  426. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  427. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  428. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
  429. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  430. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
  431. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  432. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  433. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
  434. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  435. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  436. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
  437. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
  438. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  439. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
  440. package/ts/package.json +293 -3
  441. package/ts/scripts/async-collect.ts +91 -0
  442. package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
  443. package/ts/scripts/skeleton-check.ts +1 -1
  444. package/ts/scripts/skeleton-integration-test.ts +23 -1
  445. package/ts/scripts/state-cli.ts +568 -0
  446. package/ts/scripts/turn-handoff-collect.ts +178 -0
  447. package/ts/src/artifact-gate.ts +991 -0
  448. package/ts/src/benchmark-agent-conformance.ts +619 -0
  449. package/ts/src/benchmark-environment-bridge.ts +538 -0
  450. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  451. package/ts/src/benchmark-tool-isolation.ts +166 -0
  452. package/ts/src/bookable-facts.ts +837 -0
  453. package/ts/src/booking-saga.ts +133 -0
  454. package/ts/src/booking-surface/availability-policy.ts +522 -0
  455. package/ts/src/booking-surface/booking-intent.ts +86 -0
  456. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  457. package/ts/src/booking-surface/canonical-schema.js +116 -0
  458. package/ts/src/booking-surface/contracts.ts +475 -0
  459. package/ts/src/booking-surface/dsh-planner.ts +1949 -0
  460. package/ts/src/booking-surface/dsh-plugin.js +378 -0
  461. package/ts/src/booking-surface/dsh-worker.ts +110 -0
  462. package/ts/src/booking-surface/error-codes.ts +241 -0
  463. package/ts/src/booking-surface/index.ts +9 -0
  464. package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
  465. package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
  466. package/ts/src/booking-surface/profile.ts +42 -0
  467. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  468. package/ts/src/booking-surface/runtime.ts +1793 -0
  469. package/ts/src/booking-surface/server.ts +733 -0
  470. package/ts/src/booking-surface/startup.ts +201 -0
  471. package/ts/src/booking-surface/validation.ts +217 -0
  472. package/ts/src/companions.ts +113 -0
  473. package/ts/src/contracts.ts +13 -3
  474. package/ts/src/dsh-llm.ts +44 -11
  475. package/ts/src/flyai-setup-tool.ts +77 -0
  476. package/ts/src/index.ts +1948 -198
  477. package/ts/src/itinerary-deck.ts +189 -0
  478. package/ts/src/itinerary-doc-shared.ts +741 -0
  479. package/ts/src/itinerary-html.ts +171 -0
  480. package/ts/src/loop.ts +223 -25
  481. package/ts/src/memory-capture.ts +283 -0
  482. package/ts/src/memory-decay.ts +55 -0
  483. package/ts/src/memory-utility.ts +70 -0
  484. package/ts/src/mock-llm.ts +4 -8
  485. package/ts/src/model.ts +88 -2
  486. package/ts/src/recall/card.ts +106 -0
  487. package/ts/src/recall/evaluator.ts +170 -0
  488. package/ts/src/recall/integration.ts +35 -0
  489. package/ts/src/recall/tick.ts +213 -0
  490. package/ts/src/session-link/plan-it-action.ts +78 -0
  491. package/ts/src/session-link/session-link.ts +275 -0
  492. package/ts/src/share/adapters.ts +93 -0
  493. package/ts/src/share/share-consent.ts +105 -0
  494. package/ts/src/share/share-deck.ts +85 -0
  495. package/ts/src/share/share-token.ts +166 -0
  496. package/ts/src/slot-spec.ts +172 -0
  497. package/ts/src/state-ledger.ts +912 -0
  498. package/ts/src/subagent-job-id-guard.ts +72 -0
  499. package/ts/src/time-anchor.ts +338 -0
  500. package/ts/src/tool-packet.ts +32 -0
  501. package/ts/src/travel-slots.ts +219 -0
  502. package/ts/src/travel-timeline.ts +83 -0
  503. package/ts/src/turn-deadline.ts +366 -0
  504. package/ts/src/turn-policy.ts +154 -0
  505. package/ts/src/unified.ts +354 -88
  506. package/ts/src/wish-pool.ts +79 -0
  507. package/dist/scripts/session-attach-wait.js +0 -41
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Issue #343 real-entry precedence proof.
3
+ *
4
+ * This stays offline: the dsh adapter's fetch calls are deterministic fixture
5
+ * responses and the mock adapter uses the same shared merge helper. The
6
+ * runTurn case exercises extractFacts -> extractSpec -> parse -> merge -> solve
7
+ * -> render without touching shared user state.
8
+ */
9
+ import assert from 'node:assert/strict';
10
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
11
+ import { join } from 'node:path';
12
+ import { tmpdir } from 'node:os';
13
+ import { createOpenAICompatLlm } from '../src/dsh-llm.js';
14
+ import { createMockLlm } from '../src/mock-llm.js';
15
+ import { runTurn } from '../src/loop.js';
16
+ import { parseFlightPackToSpec, solveUnified } from '../src/unified.js';
17
+ import { mergeProfileWorkWindow, WorkWindowPrecedenceError } from '../src/flight-pack-adapter.js';
18
+ import { FLIGHT_PACK_VERSION } from '../src/flight-pack-contract.js';
19
+ const schedule = {
20
+ startMin: 600,
21
+ endMin: 1140,
22
+ workdays: [0, 1, 2, 3, 4],
23
+ evidence: '用户原话:我的工作时间是洛杉矶当地 10:00 到 19:00',
24
+ };
25
+ const v2Pack = {
26
+ version: 2,
27
+ legs: [{
28
+ id: 'l1', hub: 'NRT', date: '2026-07-04',
29
+ iana_dep_zone: 'Asia/Tokyo', iana_arr_zone: 'Asia/Tokyo',
30
+ buffer_min: 0, origin_transfer_min: 0, dest_transfer_min: 0,
31
+ services: [{ id: 's1', dep: '06:30', arr: '09:00', price_cny: 100,
32
+ dep_local_date: '2026-07-04', arr_local_date: '2026-07-04' }],
33
+ }],
34
+ meta: { work_window: { home_zone: 'America/Los_Angeles', start: '10:00', end: '19:00', workdays: [0, 1, 2, 3, 4] } },
35
+ };
36
+ function initialState() {
37
+ return { calendar: { year: 2026, assertedWeekdays: {} }, profile: {}, gates: [], wishes: [] };
38
+ }
39
+ function assertPrecedence() {
40
+ const parsed = parseFlightPackToSpec(v2Pack);
41
+ assert.equal(parsed[FLIGHT_PACK_VERSION], 2);
42
+ assert.equal(JSON.stringify(parsed).includes('flightPackVersion'), false, 'internal version must not enter JSON');
43
+ const numericProfile = { ...schedule, homeTzOffsetMin: 999 };
44
+ const merged = mergeProfileWorkWindow(parsed, numericProfile);
45
+ assert.equal(merged.workWindow?.homeZone, 'America/Los_Angeles');
46
+ assert.equal(merged.workWindow?.startMin, 600);
47
+ assert.equal(merged.workWindow?.endMin, 1140);
48
+ const preserved = mergeProfileWorkWindow(parsed, undefined);
49
+ assert.deepEqual(preserved.workWindow, parsed.workWindow, 'v2 pack window survives omitted profile');
50
+ const vacation = mergeProfileWorkWindow(parsed, { vacation: true });
51
+ assert.equal(vacation.workWindow, undefined, 'explicit vacation clears v2 work window');
52
+ const withoutPackWindow = parseFlightPackToSpec({ ...v2Pack, meta: {} });
53
+ assert.throws(() => mergeProfileWorkWindow(withoutPackWindow, schedule), (e) => e instanceof WorkWindowPrecedenceError && e.code === 'v2_profile_requires_pack_home_zone');
54
+ const invalidZone = { ...parsed, workWindow: { ...parsed.workWindow, homeZone: 'Foo/Bar' } };
55
+ assert.throws(() => mergeProfileWorkWindow(invalidZone, schedule), (e) => e instanceof WorkWindowPrecedenceError && e.code === 'v2_profile_requires_pack_home_zone');
56
+ const v1 = parseFlightPackToSpec({
57
+ version: 1,
58
+ legs: [{ id: 'l1', date: '2026-07-04', hub: 'NRT', buffer_min: 0, origin_transfer_min: 0,
59
+ dest_transfer_min: 0, tz_offset_min: 0, services: [{ id: 's1', dep: '09:00', arr: '12:00', price_cny: 100 }] }],
60
+ meta: { work_window: { home_tz_offset_min: 480, start_min: 540, end_min: 1140, workdays: [0, 1, 2, 3, 4] } },
61
+ });
62
+ assert.equal(v1[FLIGHT_PACK_VERSION], 1);
63
+ const v1Merged = mergeProfileWorkWindow(v1, { ...schedule, homeTzOffsetMin: 240 });
64
+ assert.equal(v1Merged.workWindow?.homeTzOffsetMin, 240);
65
+ assert.equal(mergeProfileWorkWindow(v1, undefined).workWindow, undefined);
66
+ assert.equal(mergeProfileWorkWindow(v1, { vacation: true }).workWindow, undefined, 'explicit vacation clears v1 work window');
67
+ }
68
+ async function assertDshRunTurn(packPath, profileWindow, expectation) {
69
+ const previousFetch = globalThis.fetch;
70
+ const previousEnv = {
71
+ key: process.env['LLM_API_KEY'],
72
+ model: process.env['LLM_MODEL'],
73
+ base: process.env['LLM_BASE_URL'],
74
+ };
75
+ let calls = 0;
76
+ globalThis.fetch = (async () => {
77
+ calls++;
78
+ const content = calls === 1
79
+ ? JSON.stringify({
80
+ calendar: { year: 2026, assertedWeekdays: { '2026-07-04': 'sat' } },
81
+ profile: { workWindow: profileWindow, bookedResources: [{ kind: 'hotel', ref: 'fixture', window: '2026-07-04~2026-07-05' }] },
82
+ })
83
+ : calls === 2
84
+ ? JSON.stringify({ scenario: 'workation', segments: [{ id: 'l1', anchors: {} }] })
85
+ : '{}';
86
+ return new Response(JSON.stringify({
87
+ choices: [{ message: { content } }],
88
+ usage: { prompt_tokens: 1, completion_tokens: 1 },
89
+ }), { status: 200, headers: { 'content-type': 'application/json' } });
90
+ });
91
+ process.env['LLM_API_KEY'] = 'fixture-key';
92
+ process.env['LLM_MODEL'] = 'fixture-model';
93
+ process.env['LLM_BASE_URL'] = 'https://fixture.invalid/v1';
94
+ try {
95
+ const llm = createOpenAICompatLlm(packPath, () => new Date('2026-07-01T00:00:00Z'));
96
+ const result = await runTurn(initialState(), '请给我做机票和酒店的行程规划和推荐,工作时间,订了酒店', llm, [], solveUnified, new Date('2026-07-01T00:00:00Z'));
97
+ assert.equal(calls, 3);
98
+ assert.equal(result.state.spec?.workWindow?.homeZone, expectation.homeZone);
99
+ assert.equal(result.state.solve?.feasible, expectation.feasible);
100
+ const exclusions = result.state.solve?.work_window_exclusions ?? [];
101
+ if (expectation.hasExclusion) {
102
+ const exclusion = exclusions.find(e => e.option === 's1');
103
+ assert.ok(exclusion);
104
+ assert.match(exclusion.reason, /周五/);
105
+ assert.match(exclusion.reason, /14:30/);
106
+ assert.match(result.reply, /工作窗口/);
107
+ }
108
+ else {
109
+ assert.equal(exclusions.length, 0);
110
+ assert.doesNotMatch(result.reply, /工作窗口/);
111
+ }
112
+ }
113
+ finally {
114
+ globalThis.fetch = previousFetch;
115
+ if (previousEnv.key === undefined)
116
+ delete process.env['LLM_API_KEY'];
117
+ else
118
+ process.env['LLM_API_KEY'] = previousEnv.key;
119
+ if (previousEnv.model === undefined)
120
+ delete process.env['LLM_MODEL'];
121
+ else
122
+ process.env['LLM_MODEL'] = previousEnv.model;
123
+ if (previousEnv.base === undefined)
124
+ delete process.env['LLM_BASE_URL'];
125
+ else
126
+ process.env['LLM_BASE_URL'] = previousEnv.base;
127
+ }
128
+ }
129
+ async function assertMockEntry(packPath) {
130
+ const llm = createMockLlm(packPath);
131
+ const state = initialState();
132
+ state.profile = {
133
+ workWindow: { ...schedule, homeTzOffsetMin: 777 },
134
+ bookedResources: [{ kind: 'hotel', ref: 'fixture' }],
135
+ };
136
+ const spec = await llm.extractSpec([], state);
137
+ assert.equal(spec?.workWindow?.homeZone, 'America/Los_Angeles');
138
+ assert.equal(spec?.budgetCny, 9000);
139
+ }
140
+ const root = await mkdtemp(join(tmpdir(), 'gotry-343-real-entry-'));
141
+ try {
142
+ const packPath = join(root, 'flights_2026.json');
143
+ await writeFile(packPath, JSON.stringify(v2Pack), 'utf8');
144
+ assertPrecedence();
145
+ await assertDshRunTurn(packPath, { ...schedule, homeTzOffsetMin: 777 }, {
146
+ homeZone: 'America/Los_Angeles', feasible: false, hasExclusion: true,
147
+ });
148
+ await assertDshRunTurn(packPath, schedule, {
149
+ homeZone: 'America/Los_Angeles', feasible: false, hasExclusion: true,
150
+ });
151
+ await assertDshRunTurn(packPath, { vacation: true }, {
152
+ homeZone: undefined, feasible: true, hasExclusion: false,
153
+ });
154
+ await assertMockEntry(packPath);
155
+ assert.equal((await readFile(packPath, 'utf8')).includes('America/Los_Angeles'), true);
156
+ console.log('ISSUE 343 REAL-ENTRY E2E: precedence, runTurn, mock, v1/v2 typed contract OK');
157
+ }
158
+ finally {
159
+ await rm(root, { recursive: true, force: true });
160
+ }
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Issue #436 — 持久本地通道健康 → 真实路由建议(注册工具结果路径 E2E)。
3
+ *
4
+ * 真实执行面:在隔离临时 stateRoot 下 `apply(ctx, config, { effect })` 装载
5
+ * gotry-tools 插件,取注册定义 `gotry_flyai_search` / `gotry_session_search` 的
6
+ * execute 直接调用(与本仓库既有 registered-e2e 同构),断言工具结果里的 routing
7
+ * 字段。持久健康事件由**独立子进程**写的 fixture 探针 tick 产生——子进程走真实
8
+ * `evaluateProbeResults` + `recordChannelEvent`(channel-probe.ts 的映射与落盘),
9
+ * 因此父进程里没有任何该通道的会话态:排除只能来自持久面读回。
10
+ *
11
+ * 覆盖(镜像 issue #436 验收面):
12
+ * 1. 基线:无持久事件 → 非 hit 的 gotry_flyai_search 建议表含 session:ctrip-flight;
13
+ * 2. 跨进程生产者写 down → 父进程 readLatestChannelEvents 读回 latest=down(证据);
14
+ * 3. 产品结果:同一注册工具再调用 → 建议表排除 session:ctrip-flight(本进程会话态为空);
15
+ * 4. 非会话通道:hbcli-hotel 持久 down → 酒店建议表排除它,同意图其余通道不动;
16
+ * 5. 会话优先级:进程内会话失败 + 跨进程 'ok' 恢复事件 → 仍排除(持久恢复不解除会话失败);
17
+ * 6. 恢复:产品路径 hit(gotry_session_search 注入 hit)→ 会话清除,持久 latest=ok → 通道回到建议表;
18
+ * 7. 独立根:另一个 root 无事件 → 不受 root A 的 down 影响;
19
+ * 8. 过期:40 天前 down(读方既有 limitDays 保留期)→ 不压制通道;
20
+ * 9. 畸形行 / 未来时间戳各自独立不压制通道(两次独立写入,互不覆盖);
21
+ * 9c. 有效 down 之后跟未来/缺/坏时间戳行 → 坏行不得顶掉有效 down(评审反例回归);
22
+ * 9d. 保留期内的有效 ok 仍是恢复事件(latest-wins 不变);
23
+ * 10. 同根追加新鲜 down → 再次排除(读回+投影在同一根内可重复)。
24
+ *
25
+ * 断言全部落在**注册工具结果**上;注入的 effect 夹具按产品真实操作标签登记
26
+ * (FLYAI_SEARCH / SESSION_FLIGHT_SEARCH),并记录实际被请求的标签以防夹具错位。
27
+ * 无网络、无供应商/浏览器调用、无真实 stateRoot 访问:全部临时根。
28
+ *
29
+ * 运行(在 ts/ 下):
30
+ * PATH=/opt/homebrew/opt/node@24/bin:$PATH npx tsx scripts/issue436-persisted-routing-e2e.ts
31
+ */
32
+ import assert from 'node:assert/strict';
33
+ import { spawnSync } from 'node:child_process';
34
+ import { mkdirSync, mkdtempSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
35
+ import { createRequire } from 'node:module';
36
+ import { tmpdir } from 'node:os';
37
+ import { dirname, join } from 'node:path';
38
+ import { pathToFileURL } from 'node:url';
39
+ import { apply } from '../src/index.js';
40
+ import { makeMockInterpreter } from '../capabilities/effect.js';
41
+ import { channelState, noteChannelVerdict, readLatestChannelEvents, resetChannelHealth } from '../capabilities/channel-health.js';
42
+ const TS_ROOT = join(import.meta.dirname, '..');
43
+ const FUTURE_DATE = '2027-07-17';
44
+ const PRODUCER_TIMEOUT_MS = 60_000;
45
+ /** 产品真实操作标签 → 注入 observation(标签名与 capabilities/effect.ts 注册一致)。 */
46
+ const FIXTURES = {
47
+ FLYAI_SEARCH: {
48
+ ok: true, via: 'flyai', evidence: '[实时API:flyai@ts] 夹具回放:0 条', latencyMs: 1,
49
+ verdict: 'miss', kind: 'flight',
50
+ },
51
+ SESSION_FLIGHT_SEARCH: {
52
+ ok: true, via: 'session', evidence: '[会话:ctrip-flight@ts] 夹具回放:1 条', latencyMs: 1,
53
+ verdict: 'hit', kind: 'flight',
54
+ options: [{ flightNo: 'MU5101', airline: '东方航空', depDateTime: '2027-07-17T08:00:00', arrDateTime: '2027-07-17T10:30:00', price: 1280 }],
55
+ },
56
+ };
57
+ /** 记录实际被请求的 effect 标签:证明走的是产品注册调用点,而不是夹具自证。 */
58
+ const requestedEffects = [];
59
+ const recordingInterpreter = async (fx) => {
60
+ requestedEffects.push(fx.effect);
61
+ return await makeMockInterpreter(FIXTURES)(fx);
62
+ };
63
+ function loadTools(stateRoot) {
64
+ assert.ok(stateRoot.startsWith(tmpdir()), 'stateRoot 必须是隔离临时根(绝不触真实 stateRoot)');
65
+ const registered = [];
66
+ const ctx = {
67
+ tools: { register: (t) => { registered.push(t); } },
68
+ systemPrompt: { variable: () => { } },
69
+ on: () => () => { },
70
+ };
71
+ apply(ctx, { stateRoot, timeoutMs: 30_000, hbcliBin: 'hbcli-not-on-path', sessionAccess: 'ask' }, {
72
+ effect: recordingInterpreter,
73
+ });
74
+ return new Map(registered.map(t => [t.name, t]));
75
+ }
76
+ /** tsx CLI 文件(直接以 process.execPath 启动,不经 npx/shell 孙进程)。 */
77
+ function resolveTsxCli() {
78
+ const req = createRequire(join(TS_ROOT, 'package.json'));
79
+ const pkgPath = req.resolve('tsx/package.json');
80
+ const binField = JSON.parse(readFileSync(pkgPath, 'utf-8')).bin;
81
+ const bin = typeof binField === 'string' ? binField : binField?.tsx;
82
+ assert.ok(bin, 'tsx package.json 必须有 bin 入口');
83
+ return join(dirname(pkgPath), bin);
84
+ }
85
+ /**
86
+ * fixture 探针 tick(子进程执行):镜像 channel-probe.ts 的映射与落盘——
87
+ * readLatestChannelEvents → evaluateProbeResults → recordChannelEvent。
88
+ */
89
+ function writeProducer(producerPath) {
90
+ const health = pathToFileURL(join(TS_ROOT, 'capabilities', 'channel-health.ts')).href;
91
+ const probe = pathToFileURL(join(TS_ROOT, 'scripts', 'channel-probe.ts')).href;
92
+ writeFileSync(producerPath, [
93
+ `import { readLatestChannelEvents, recordChannelEvent } from ${JSON.stringify(health)}`,
94
+ `import { evaluateProbeResults } from ${JSON.stringify(probe)}`,
95
+ `const [stateRoot, channel, mode, reason] = process.argv.slice(2)`,
96
+ `const prior = await readLatestChannelEvents(stateRoot)`,
97
+ `const priorDown = new Set([...prior].filter(([, ev]) => ev.state === 'down' || ev.state === 'cooldown').map(([ch]) => ch))`,
98
+ `const result = mode === 'down' ? { ok: false, reason, ms: 1 } : { ok: true, ms: 1 }`,
99
+ `const events = evaluateProbeResults([{ probe: { channel, probeable: true, run: async () => result }, result }], priorDown)`,
100
+ `for (const ev of events) await recordChannelEvent(stateRoot, ev)`,
101
+ `console.log(JSON.stringify(events))`,
102
+ ].join('\n'), 'utf-8');
103
+ }
104
+ /** 子进程生产一批持久事件;返回子进程实际落盘的事件(证据)。带上限超时 + SIGKILL 兜底。 */
105
+ function runProbeChild(tsxCli, producerPath, stateRoot, channel, mode, reason = 'fixture') {
106
+ const res = spawnSync(process.execPath, [tsxCli, producerPath, stateRoot, channel, mode, reason], {
107
+ cwd: TS_ROOT, encoding: 'utf-8', timeout: PRODUCER_TIMEOUT_MS, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024,
108
+ });
109
+ assert.ok(!res.error, `探针子进程不得超时/启动失败:${res.error instanceof Error ? res.error.message : res.error}`);
110
+ assert.equal(res.status, 0, `探针子进程必须成功退出:status=${res.status} signal=${res.signal} stderr=${res.stderr}`);
111
+ return JSON.parse(res.stdout.trim());
112
+ }
113
+ const channels = (r) => (r.routing?.alternatives ?? []).map(a => a.channel ?? '');
114
+ async function searchFlight(tools) {
115
+ return await tools.get('gotry_flyai_search').execute({ kind: 'flight', from: '深圳', to: '普吉', date: FUTURE_DATE }, null);
116
+ }
117
+ async function searchHotel(tools) {
118
+ return await tools.get('gotry_flyai_search').execute({ kind: 'hotel', to: '大理' }, null);
119
+ }
120
+ async function main() {
121
+ const home = mkdtempSync(join(tmpdir(), 'gotry-436-persisted-routing-'));
122
+ const rootA = join(home, 'state-a');
123
+ const rootB = join(home, 'state-b');
124
+ const producerPath = join(home, 'fixture-probe-tick.mts');
125
+ const tsxCli = resolveTsxCli();
126
+ writeProducer(producerPath);
127
+ try {
128
+ const toolsA = loadTools(rootA);
129
+ const msgs = [];
130
+ // 1. 基线:无持久事件 → session:ctrip-flight 在建议表内(flyai 是发起通道,被排除)
131
+ const base = await searchFlight(toolsA);
132
+ assert.equal(base.verdict, 'miss', '夹具=飞猪非 hit');
133
+ assert.ok(channels(base).includes('session:ctrip-flight'), '基线:无持久 down 时 session 通道在建议表内');
134
+ assert.ok(!channels(base).includes('flyai'), '发起通道仍被排除');
135
+ assert.equal(channelState('session:ctrip-flight'), undefined, '基线:本进程无该通道会话态');
136
+ msgs.push(`1. 基线 OK (${channels(base).join(',')})`);
137
+ // 2. 跨进程生产:fixture 探针 tick 写 down
138
+ const produced = runProbeChild(tsxCli, producerPath, rootA, 'session:ctrip-flight', 'down', 'challenged');
139
+ assert.equal(produced.length, 1, '子进程写出 1 条 down 事件');
140
+ const ev0 = produced[0];
141
+ assert.equal(ev0.channel, 'session:ctrip-flight');
142
+ assert.equal(ev0.state, 'down');
143
+ assert.equal(ev0.reason, 'challenged');
144
+ const readBack = await readLatestChannelEvents(rootA);
145
+ assert.equal(readBack.get('session:ctrip-flight')?.state, 'down', '父进程读回 latest=down(跨进程生产者→读者)');
146
+ msgs.push(`2. 跨进程生产者 OK (state=${readBack.get('session:ctrip-flight')?.state})`);
147
+ // 3. 产品结果:注册工具的非 hit 结果排除持久 down 通道(本进程会话态仍为空)
148
+ const afterPersistedDown = await searchFlight(toolsA);
149
+ assert.equal(channelState('session:ctrip-flight'), undefined, '排除不可能来自会话态(仍是空)');
150
+ assert.ok(!channels(afterPersistedDown).includes('session:ctrip-flight'), '持久 down → 工具结果 routing 排除该通道');
151
+ assert.ok(channels(afterPersistedDown).includes('web-read'), '同意图其余通道不受影响');
152
+ msgs.push(`3. 注册工具结果排除持久 down OK (${channels(afterPersistedDown).join(',')})`);
153
+ // 4. 非会话通道同样生效(hotel 意图,hbcli-hotel 无会话态可言)
154
+ runProbeChild(tsxCli, producerPath, rootA, 'hbcli-hotel', 'down', 'auth-denied');
155
+ const hotel = await searchHotel(toolsA);
156
+ assert.equal(hotel.verdict, 'miss', '酒店夹具=非 hit');
157
+ assert.ok(!channels(hotel).includes('hbcli-hotel'), '持久 down 的非会话通道被排除');
158
+ assert.ok(channels(hotel).includes('session:ctrip-hotel'), '同意图其余通道保留');
159
+ msgs.push(`4. 非会话通道持久 down OK (${channels(hotel).join(',')})`);
160
+ // 5. 会话优先级:进程内会话失败 + 跨进程 'ok' 恢复事件 → 仍排除
161
+ noteChannelVerdict('session:ctrip-flight', 'challenged');
162
+ assert.equal(channelState('session:ctrip-flight')?.state, 'down', '会话态已 down(产品 noteChannel 入口)');
163
+ runProbeChild(tsxCli, producerPath, rootA, 'session:ctrip-flight', 'ok');
164
+ assert.equal((await readLatestChannelEvents(rootA)).get('session:ctrip-flight')?.state, 'ok', '持久 latest 已是恢复事件');
165
+ const sessionWins = await searchFlight(toolsA);
166
+ assert.ok(!channels(sessionWins).includes('session:ctrip-flight'), '持久 ok 不能解除本会话刚发生的失败');
167
+ assert.equal(channelState('session:ctrip-flight')?.state, 'down', '会话态未被持久面改写');
168
+ msgs.push(`5. 会话优先级 OK (${channels(sessionWins).join(',')})`);
169
+ // 6. 恢复:产品路径 hit 清会话态;持久 latest=ok → 通道回到建议表
170
+ const hit = await toolsA.get('gotry_session_search').execute({ kind: 'flight', from: '深圳', to: '普吉', date: FUTURE_DATE }, null);
171
+ assert.equal(hit.verdict, 'hit', '会话工具夹具=hit');
172
+ assert.equal(channelState('session:ctrip-flight'), undefined, 'hit 即清除会话态');
173
+ const recovered = await searchFlight(toolsA);
174
+ assert.ok(channels(recovered).includes('session:ctrip-flight'), '会话清除 + 持久 ok → 通道恢复进建议表');
175
+ msgs.push(`6. 恢复 OK (${channels(recovered).join(',')})`);
176
+ // 夹具错位防护:注册工具真实请求过的标签必须都在预期集合内(无 unknown-effect 退化)
177
+ assert.ok(requestedEffects.includes('FLYAI_SEARCH'), 'FLYAI_SEARCH 走产品注册调用点');
178
+ assert.ok(requestedEffects.includes('SESSION_FLIGHT_SEARCH'), 'SESSION_FLIGHT_SEARCH 走产品注册调用点');
179
+ for (const tag of requestedEffects)
180
+ assert.ok(tag in FIXTURES, `未登记夹具的效应标签=${tag}`);
181
+ msgs.push(`6b. 效应标签真实 OK (${[...new Set(requestedEffects)].join(',')})`);
182
+ // 7. 独立根:rootB 无任何事件 → 不受 rootA 影响
183
+ const toolsB = loadTools(rootB);
184
+ const rootBFresh = await searchFlight(toolsB);
185
+ assert.ok(channels(rootBFresh).includes('session:ctrip-flight'), '独立根无事件 → 不排除(不串根)');
186
+ msgs.push('7. 独立根隔离 OK');
187
+ // 8. 过期:40 天前 down 落在读方既有保留期外 → 不压制
188
+ const expiredAt = new Date(Date.now() - 40 * 86_400_000).toISOString();
189
+ mkdirSync(join(rootB, 'gotry-state'), { recursive: true });
190
+ const rawPath = join(rootB, 'gotry-state', 'channel-health.jsonl');
191
+ writeFileSync(rawPath, `${JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'stale', at: expiredAt })}\n`, 'utf-8');
192
+ const afterExpired = await searchFlight(toolsB);
193
+ assert.ok(channels(afterExpired).includes('session:ctrip-flight'), '过期(limitDays 外)down 不压制通道');
194
+ msgs.push('8. 过期事件不压制 OK');
195
+ // 9a. 未来时间戳(独立写入,文件内只有这一条)
196
+ const futureAt = new Date(Date.now() + 86_400_000).toISOString();
197
+ writeFileSync(rawPath, `${JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'clock-skew', at: futureAt })}\n`, 'utf-8');
198
+ const afterFuture = await searchFlight(toolsB);
199
+ assert.ok(channels(afterFuture).includes('session:ctrip-flight'), '未来时间戳 down 不压制通道');
200
+ msgs.push('9a. 未来时间戳不压制 OK');
201
+ // 9b. 畸形输入(坏 JSON 行 + 缺时间戳事件,各自独立文件状态)
202
+ writeFileSync(rawPath, `${JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'no-at' })}\nnot-json\n{"half":true}\n`, 'utf-8');
203
+ const afterNoAt = await searchFlight(toolsB);
204
+ assert.ok(channels(afterNoAt).includes('session:ctrip-flight'), '缺时间戳 down 不压制通道');
205
+ writeFileSync(rawPath, 'not-json\n{"half":true}\n', 'utf-8');
206
+ const afterGarbage = await searchFlight(toolsB);
207
+ assert.ok(channels(afterGarbage).includes('session:ctrip-flight'), '纯坏行文件不压制通道(读方容忍)');
208
+ msgs.push('9b. 畸形输入不压制 OK');
209
+ // 9c. 有效 down 之后跟不可用时间戳的行:坏行不得在 latest-wins 覆盖前顶掉有效 down(评审反例)
210
+ const validDownRow = JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'challenged', at: new Date(Date.now() - 60_000).toISOString() });
211
+ const unusableRows = [
212
+ ['未来 down', JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'clock-skew', at: futureAt })],
213
+ ['未来 ok', JSON.stringify({ channel: 'session:ctrip-flight', state: 'ok', at: futureAt })],
214
+ ['缺时间戳 ok', JSON.stringify({ channel: 'session:ctrip-flight', state: 'ok' })],
215
+ ['坏时间戳 down', JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'bad-at', at: 'not-a-date' })],
216
+ ['过期 down', JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'stale', at: expiredAt })],
217
+ ];
218
+ for (const [label, badRow] of unusableRows) {
219
+ writeFileSync(rawPath, `${validDownRow}\n${badRow}\n`, 'utf-8');
220
+ const r = await searchFlight(toolsB);
221
+ assert.ok(!channels(r).includes('session:ctrip-flight'), `有效 down 之后的${label}行不得顶掉它`);
222
+ assert.equal((await readLatestChannelEvents(rootB, { requireValidTimestamp: true })).get('session:ctrip-flight')?.state, 'down', `严格读取口径下 latest 仍是有效 down(${label})`);
223
+ }
224
+ msgs.push('9c. 有效 down 不被后续坏时间戳行顶掉 OK');
225
+ // 9d. 保留期内的有效 ok 仍是恢复事件(latest-wins 语义不变)
226
+ writeFileSync(rawPath, `${validDownRow}\n${JSON.stringify({ channel: 'session:ctrip-flight', state: 'ok', at: new Date(Date.now() - 30_000).toISOString() })}\n`, 'utf-8');
227
+ const afterValidRecovery = await searchFlight(toolsB);
228
+ assert.ok(channels(afterValidRecovery).includes('session:ctrip-flight'), '有效 ok 恢复事件仍能把通道放回建议表');
229
+ msgs.push('9d. 有效恢复事件恢复 OK');
230
+ // 10. 同根追加新鲜 down → 再次排除(读回+投影可重复)
231
+ writeFileSync(rawPath, `${JSON.stringify({ channel: 'session:ctrip-flight', state: 'down', reason: 'fresh', at: new Date().toISOString() })}\n`, 'utf-8');
232
+ const afterFresh = await searchFlight(toolsB);
233
+ assert.ok(!channels(afterFresh).includes('session:ctrip-flight'), '新鲜 down 在同一根内再次排除');
234
+ msgs.push('10. 同根重复生效 OK');
235
+ for (const m of msgs)
236
+ console.log(m);
237
+ console.log('issue436-persisted-routing-e2e: 全部通过');
238
+ }
239
+ finally {
240
+ resetChannelHealth();
241
+ rmSync(home, { recursive: true, force: true });
242
+ }
243
+ }
244
+ main().catch((err) => {
245
+ console.error('[issue436-e2e] fatal:', err instanceof Error ? err.stack ?? err.message : err);
246
+ process.exit(1);
247
+ });