@danceiny/gotry 0.0.1-rc.8 → 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 (506) hide show
  1. package/README.md +172 -217
  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 -49
  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 +968 -0
  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 +126 -0
  42. package/dist/capabilities/session/adapters/ctrip-flight.js +141 -0
  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 +54 -0
  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 +50 -0
  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 +107 -0
  56. package/dist/capabilities/session/static-flight-golden.js +150 -0
  57. package/dist/capabilities/session/transport.js +109 -0
  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 +686 -0
  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 +72 -0
  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 +68 -10
  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 +67 -0
  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 +480 -0
  191. package/dist/scripts/ledger-workflow-crash.js +32 -0
  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 +49 -0
  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 +29 -31
  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 +43 -42
  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 +31 -0
  227. package/dist/scripts/session-attach-poc.js +62 -0
  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 +60 -0
  231. package/dist/scripts/session-link-tests.js +478 -0
  232. package/dist/scripts/session-login.js +29 -0
  233. package/dist/scripts/session-tests.js +862 -0
  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 +703 -219
  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 +261 -0
  253. package/dist/scripts/state-cli.js +585 -0
  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 +59 -0
  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 +86 -0
  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 +2305 -731
  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 +358 -162
  325. package/dist/src/memory-capture.js +180 -32
  326. package/dist/src/memory-decay.js +47 -0
  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 +767 -0
  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 +59 -0
  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 +538 -360
  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 +296 -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 +2107 -224
  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 +278 -40
  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 +360 -89
  506. package/ts/src/wish-pool.ts +79 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * 12306 火车站点适配器(2026-09-03,继 ctrip-hotel 后会话面第三通道):
3
+ * - entry:https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=<城市名,电报码>&ts=<...>&date=YYYY-MM-DD&flag=N,N,Y
4
+ * (电报码表官方站表全量校准 2026-09-03,129 城;曾纠出南宁 NIZ→NNZ 错码)
5
+ * - networkHints:kyfw leftTicket/query(负载均衡变体 queryG/queryZ/queryA 等全部命中)
6
+ * - 解析:leftTicket/query 返回 data.result[] 管道行(12306 经典 | 拼接格式)。
7
+ * **第一方校准(2026-09-03)**:字段索引与站名映射(data.map[电报码])逐一
8
+ * 取自官方前端 queryLeftTicket_end_js.js 的 cN(result,map) 转换函数——旧版
9
+ * 公开常识索引与官方现行映射不符(站名/座位桶全错),真会话前即纠;行级
10
+ * 签名校验(车次码/时刻形态)继续兜住未来漂移:失配整行跳过,fail-visible
11
+ *
12
+ * 与机/酒通道的两点刻意差异:
13
+ * - 无登录闸:12306 余票查询是公开面(登录只关系下单),无账号数据过手;
14
+ * 扩展照样只读被动嗅探(零写行为),证据链标注「公开查询面」
15
+ * - 列表接口不含票价:如实只呈现 车次/时刻/历时/余票,票价以 12306 落地页为准
16
+ */
17
+
18
+ /** 城市电报码表(**官方口径校准 2026-09-03**:全部条目逐一核对自
19
+ * kyfw.12306.cn/otn/resources/js/framework/station_name.js 官方全量站表
20
+ * ——曾借此抓出南宁 NIZ→NNZ 错码;代表站规则=城市同名站,fs/ts 用城市码
21
+ * 服务端扩到市内全部站。词表外 unresolved 逐字保留,调用方可显式传电报码
22
+ * 覆盖——kyfw 查询页 URL 里的 fs=城市,XXX 三位码;核对快照见
23
+ * data/stations-12306-verify.json) */
24
+ export const STATION_TELECODES: Record<string, string> = {
25
+ "三亚": 'SEQ',
26
+ "上海": 'SHH',
27
+ "上饶": 'SRG',
28
+ "东莞": 'RTQ',
29
+ "中卫": 'ZWJ',
30
+ "中山": 'ZSQ',
31
+ "丽江": 'LHM',
32
+ "乌鲁木齐": 'WAR',
33
+ "乐山": 'IVW',
34
+ "九江": 'JJG',
35
+ "伊宁": 'YMR',
36
+ "佛山": 'FSQ',
37
+ "保山": 'BAM',
38
+ "六盘水": 'UMW',
39
+ "兰州": 'LZJ',
40
+ "包头": 'BTC',
41
+ "北京": 'BJP',
42
+ "北海": 'BHZ',
43
+ "十堰": 'SNN',
44
+ "南京": 'NJH',
45
+ "南充": 'NCW',
46
+ "南宁": 'NNZ',
47
+ "南昌": 'NCG',
48
+ "南通": 'NUH',
49
+ "南阳": 'NFF',
50
+ "厦门": 'XMS',
51
+ "台州": 'TEU',
52
+ "合肥": 'HFH',
53
+ "吉林": 'JLL',
54
+ "吐鲁番": 'TFR',
55
+ "呼和浩特": 'HHC',
56
+ "咸阳": 'XYY',
57
+ "哈密": 'HMR',
58
+ "哈尔滨": 'HBB',
59
+ "喀什": 'KSR',
60
+ "嘉兴": 'JXH',
61
+ "嘉峪关": 'JGJ',
62
+ "大同": 'DTV',
63
+ "大庆": 'DZX',
64
+ "大理": 'DKM',
65
+ "大连": 'DLT',
66
+ "天水": 'TSJ',
67
+ "天津": 'TJP',
68
+ "太原": 'TYV',
69
+ "宁波": 'NGH',
70
+ "安顺": 'ASW',
71
+ "宜宾": 'YKE',
72
+ "宜昌": 'YCN',
73
+ "宝鸡": 'BJY',
74
+ "岳阳": 'YYQ',
75
+ "常州": 'CZH',
76
+ "常德": 'VGQ',
77
+ "广州": 'GZQ',
78
+ "库尔勒": 'KLR',
79
+ "延安": 'YWY',
80
+ "开封": 'KFF',
81
+ "张掖": 'ZYJ',
82
+ "徐州": 'XCH',
83
+ "德阳": 'DYW',
84
+ "惠州": 'HCQ',
85
+ "成都": 'CDW',
86
+ "扬州": 'YLH',
87
+ "承德": 'CDP',
88
+ "抚顺": 'FET',
89
+ "拉萨": 'LSO',
90
+ "揭阳": 'JYA',
91
+ "新乡": 'XXF',
92
+ "无锡": 'WGH',
93
+ "日喀则": 'RKO',
94
+ "昆明": 'KMM',
95
+ "普洱": 'PEM',
96
+ "景德镇": 'JCG',
97
+ "曲靖": 'QJM',
98
+ "杭州": 'HZH',
99
+ "林芝": 'LZO',
100
+ "柳州": 'LZZ',
101
+ "株洲": 'ZZQ',
102
+ "格尔木": 'GRO',
103
+ "桂林": 'GLZ',
104
+ "梧州": 'WZZ',
105
+ "武汉": 'WHN',
106
+ "汉中": 'HOY',
107
+ "汕头": 'OTQ',
108
+ "沈阳": 'SYT',
109
+ "泉州": 'QYS',
110
+ "泸州": 'LUE',
111
+ "洛阳": 'LYF',
112
+ "济南": 'JNK',
113
+ "深圳": 'SZQ',
114
+ "温州": 'RZH',
115
+ "湛江": 'ZJZ',
116
+ "漳州": 'ZUS',
117
+ "潮州": 'CKQ',
118
+ "牡丹江": 'MDB',
119
+ "珠海": 'ZHQ',
120
+ "盐城": 'AFH',
121
+ "石家庄": 'SJP',
122
+ "福州": 'FZS',
123
+ "秦皇岛": 'QTP',
124
+ "绍兴": 'SOH',
125
+ "绵阳": 'MYW',
126
+ "芜湖": 'WHH',
127
+ "苏州": 'SZH',
128
+ "茂名": 'MDQ',
129
+ "莆田": 'PTS',
130
+ "衡阳": 'HYQ',
131
+ "襄阳": 'XFN',
132
+ "西宁": 'XNO',
133
+ "西安": 'XAY',
134
+ "西昌": 'ECW',
135
+ "贵阳": 'GIW',
136
+ "赣州": 'GZG',
137
+ "达州": 'RXW',
138
+ "遵义": 'ZYE',
139
+ "郑州": 'ZZF',
140
+ "重庆": 'CUW',
141
+ "金华": 'JBH',
142
+ "银川": 'YIJ',
143
+ "镇江": 'ZJH',
144
+ "长春": 'CCT',
145
+ "长沙": 'CSQ',
146
+ "阜阳": 'FYH',
147
+ "青岛": 'QDK',
148
+ "鞍山": 'AST',
149
+ "韶关": 'SNQ',
150
+ "香格里拉": 'EUM',
151
+ "香港": 'XJA',
152
+ "齐齐哈尔": 'QHX',
153
+ "龙岩": 'LYS',
154
+ }
155
+
156
+ export interface TrainEntryQuery {
157
+ from: string
158
+ to: string
159
+ /** YYYY-MM-DD */
160
+ date: string
161
+ /** 显式城市电报码(覆盖码表;kyfw 查询页 URL fs=城市,XXX 的三位码) */
162
+ fromStationTelecode?: string
163
+ toStationTelecode?: string
164
+ }
165
+
166
+ export interface AdapterEntry {
167
+ ok: boolean
168
+ url?: string
169
+ unresolved?: string[]
170
+ }
171
+
172
+ export interface TrainQueryTelecodes {
173
+ fromStationTelecode: string
174
+ toStationTelecode: string
175
+ date: string
176
+ }
177
+
178
+ export interface TrainResponseBinding extends TrainQueryTelecodes {
179
+ /** Exact URL observed on the response whose body was parsed. */
180
+ url: string
181
+ }
182
+
183
+ export type TrainResponseUrlValidation =
184
+ | { ok: true; binding: TrainResponseBinding }
185
+ | { ok: false; reason: string }
186
+
187
+ export function resolveTrainQueryTelecodes(q: TrainEntryQuery): { ok: true; telecodes: TrainQueryTelecodes } | { ok: false; unresolved: string[] } {
188
+ const unresolved: string[] = []
189
+ const fromTc = (q.fromStationTelecode ?? '').trim().toUpperCase() || STATION_TELECODES[q.from.trim()]
190
+ const toTc = (q.toStationTelecode ?? '').trim().toUpperCase() || STATION_TELECODES[q.to.trim()]
191
+ if (!fromTc) unresolved.push(q.from)
192
+ if (!toTc) unresolved.push(q.to)
193
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(q.date)) unresolved.push(q.date)
194
+ if (unresolved.length > 0 || !fromTc || !toTc) return { ok: false, unresolved }
195
+ return { ok: true, telecodes: { fromStationTelecode: fromTc, toStationTelecode: toTc, date: q.date } }
196
+ }
197
+
198
+ /** Bind a parsed body to the exact 12306 query request that this invocation built. */
199
+ export function validateTrainQueryResponseUrl(observedUrl: string, expected: TrainQueryTelecodes): TrainResponseUrlValidation {
200
+ let parsed: URL
201
+ try {
202
+ parsed = new URL(observedUrl)
203
+ } catch {
204
+ return { ok: false, reason: 'response URL malformed' }
205
+ }
206
+ if (parsed.protocol !== 'https:' || parsed.hostname.toLowerCase() !== TRAIN_SITE_HOST || parsed.port) {
207
+ return { ok: false, reason: 'response URL host/protocol is not the 12306 HTTPS host' }
208
+ }
209
+ if (!/^\/otn\/leftTicket\/query[A-Za-z0-9_-]*$/i.test(parsed.pathname)
210
+ || !TRAIN_NETWORK_HINTS.some((hint) => hint.test(parsed.pathname))) {
211
+ return { ok: false, reason: 'response URL is not an allowed leftTicket query endpoint' }
212
+ }
213
+ const exactParam = (name: string): string | undefined => {
214
+ const values = parsed.searchParams.getAll(name)
215
+ return values.length === 1 ? values[0] : undefined
216
+ }
217
+ if (exactParam('leftTicketDTO.train_date') !== expected.date
218
+ || exactParam('leftTicketDTO.from_station') !== expected.fromStationTelecode
219
+ || exactParam('leftTicketDTO.to_station') !== expected.toStationTelecode) {
220
+ return { ok: false, reason: 'response URL query route/date does not match the invocation' }
221
+ }
222
+ return { ok: true, binding: { url: observedUrl, ...expected } }
223
+ }
224
+
225
+ export function buildTrainEntryUrl(q: TrainEntryQuery): AdapterEntry {
226
+ const resolved = resolveTrainQueryTelecodes(q)
227
+ if (!resolved.ok) return { ok: false, unresolved: resolved.unresolved }
228
+ const { fromStationTelecode: fromTc, toStationTelecode: toTc } = resolved.telecodes
229
+ const fs = encodeURIComponent(`${q.from.trim()},${fromTc}`)
230
+ const ts = encodeURIComponent(`${q.to.trim()},${toTc}`)
231
+ return { ok: true, url: `https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=${fs}&ts=${ts}&date=${q.date}&flag=N,N,Y` }
232
+ }
233
+
234
+ /** networkHints:余票查询 XHR(queryG/Z/A/U 等负载均衡变体全命中) */
235
+ export const TRAIN_NETWORK_HINTS = [/leftTicket\/query/i]
236
+
237
+ /** 12306 页域(content_scripts 注入面与 background 白名单的对账源) */
238
+ export const TRAIN_SITE_HOST = 'kyfw.12306.cn'
239
+
240
+ export interface SessionTrainOption {
241
+ /** 车次(G1375 / K79 等) */
242
+ trainCode: string
243
+ fromStation: string
244
+ toStation: string
245
+ /** 出发/到达时刻(HH:MM;跨日由到达时刻本身表达,官方 DTO 无隔日字段) */
246
+ depTime: string
247
+ arrTime: string
248
+ /** 历时(分钟) */
249
+ durationMin: number
250
+ /** 12306 train-origin date(YYYY-MM-DD);validated and preserved, not passenger query date */
251
+ startTrainDate?: string
252
+ /** Y=可预订 / N=不可 / 其他上游原话 */
253
+ canWebBuy: string
254
+ /** 余票分桶(第一方校准索引,见 SEAT_BUCKETS;值原样:数字 / 有 / 无 / --) */
255
+ seats: Record<string, string>
256
+ /** 查询落地页(由人选车完成预订;gotry 不碰) */
257
+ jumpUrl?: string
258
+ }
259
+
260
+ /** 座位桶索引表(**第一方校准 2026-09-03**:逐字段取自 12306 官方前端
261
+ * queryLeftTicket_end_js.js 的 cN(result,map) 转换函数——旧版公开常识索引
262
+ * (19-28 段)与官方现行映射不符,首查前即纠);label=页面席别口径,未知席别
263
+ * 保留上游字段 id 原样 */
264
+ const SEAT_BUCKETS: Array<{ index: number; field: string; label: string }> = [
265
+ { index: 20, field: 'gg_num', label: '其他(通勤)' },
266
+ { index: 21, field: 'gr_num', label: '高级软卧' },
267
+ { index: 22, field: 'qt_num', label: '其他' },
268
+ { index: 23, field: 'rw_num', label: '软卧(一等卧)' },
269
+ { index: 24, field: 'rz_num', label: '软座' },
270
+ { index: 25, field: 'tz_num', label: '特等座' },
271
+ { index: 26, field: 'wz_num', label: '无座' },
272
+ { index: 27, field: 'yb_num', label: 'yp_b(上游席别)' },
273
+ { index: 28, field: 'yw_num', label: '硬卧(二等卧)' },
274
+ { index: 29, field: 'yz_num', label: '硬座' },
275
+ { index: 30, field: 'ze_num', label: '二等座' },
276
+ { index: 31, field: 'zy_num', label: '一等座' },
277
+ { index: 32, field: 'swz_num', label: '商务座' },
278
+ { index: 33, field: 'srrb_num', label: '动卧' },
279
+ ]
280
+
281
+ const TRAIN_CODE_RE = /^[GDCZTKYLSF]\d{1,5}[A-Z]?$/i
282
+ const HHMM_RE = /^\d{2}:\d{2}$/
283
+
284
+ export const TRAIN_QUERY_PARSE_SCHEMA = 'gotry_session_train_parse.v1' as const
285
+
286
+ export type TrainQueryParseOutcome =
287
+ | { kind: 'recognized-nonempty'; trains: SessionTrainOption[] }
288
+ | { kind: 'recognized-empty'; trains: [] }
289
+ | { kind: 'malformed'; reason: string }
290
+
291
+ function isRecord(value: unknown): value is Record<string, unknown> {
292
+ return !!value && typeof value === 'object' && !Array.isArray(value)
293
+ }
294
+
295
+ /** 历时 "07:52" → 分钟 */
296
+ function lishiToMin(s: string | undefined): number {
297
+ const m = /^(\d{1,3}):(\d{2})$/.exec((s ?? '').trim())
298
+ if (!m) return 0
299
+ if (Number(m[2]) > 59) return 0
300
+ return Number(m[1]) * 60 + Number(m[2])
301
+ }
302
+
303
+ function normalizeStartTrainDate(value: string): string | undefined {
304
+ if (!/^\d{8}$/.test(value)) return undefined
305
+ const normalized = `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6, 8)}`
306
+ const parsed = new Date(`${normalized}T00:00:00.000Z`)
307
+ if (!Number.isFinite(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== normalized) return undefined
308
+ return normalized
309
+ }
310
+
311
+ /** 行级签名:车次码形态 + 时刻形态不符即整行跳过(索引漂移 fail-visible,不造数) */
312
+ function rowToOption(row: string, stationMap: Record<string, unknown> | undefined, entryUrl: string): SessionTrainOption | null {
313
+ const f = row.split('|')
314
+ if (f.length < 34) return null
315
+ const trainCode = (f[3] ?? '').trim()
316
+ const depTime = (f[8] ?? '').trim()
317
+ const arrTime = (f[9] ?? '').trim()
318
+ const durationMin = lishiToMin(f[10])
319
+ const startTrainDateRaw = (f[13] ?? '').trim()
320
+ const startTrainDate = normalizeStartTrainDate(startTrainDateRaw)
321
+ const validHm = (value: string): boolean => HHMM_RE.test(value)
322
+ && Number(value.slice(0, 2)) <= 23 && Number(value.slice(3, 5)) <= 59
323
+ if (!TRAIN_CODE_RE.test(trainCode) || !validHm(depTime) || !validHm(arrTime)
324
+ || durationMin <= 0 || !startTrainDate || !(f[6] ?? '').trim() || !(f[7] ?? '').trim()) return null
325
+ // 官方 cN 口径:站名 = data.map[电报码](map 缺失/缺键时退电报码原样,不猜名)
326
+ const stationName = (code: string): string | null => {
327
+ if (!stationMap || !Object.prototype.hasOwnProperty.call(stationMap, code)) return code
328
+ const mapped = stationMap[code]
329
+ return typeof mapped === 'string' && mapped.trim() ? mapped.trim() : null
330
+ }
331
+ const fromStation = stationName((f[6] ?? '').trim())
332
+ const toStation = stationName((f[7] ?? '').trim())
333
+ if (!fromStation || !toStation) return null
334
+ const seats: Record<string, string> = {}
335
+ for (const b of SEAT_BUCKETS) {
336
+ const v = (f[b.index] ?? '').trim()
337
+ if (v && v !== '--') seats[b.label] = v
338
+ }
339
+ return {
340
+ trainCode,
341
+ fromStation,
342
+ toStation,
343
+ depTime,
344
+ arrTime,
345
+ durationMin,
346
+ startTrainDate,
347
+ canWebBuy: (f[11] ?? '').trim(),
348
+ seats,
349
+ jumpUrl: entryUrl,
350
+ }
351
+ }
352
+
353
+ /** 解析 leftTicket/query 响应的 typed outcome。
354
+ * recognized-empty 只代表明确存在的 data.result=[];
355
+ * recognized-nonempty 要求批次每一行都通过签名校验,任一畸形行即整个批次
356
+ * malformed,不暴露部分候选。transport/runtime failure 不在此纯解析函数伪造。
357
+ */
358
+ export function parseLeftTicketQueryResult(body: string, entryUrl: string, opts: { maxItems?: number } = {}): TrainQueryParseOutcome {
359
+ let raw: unknown
360
+ try {
361
+ raw = JSON.parse(body) as unknown
362
+ } catch {
363
+ return { kind: 'malformed', reason: 'response body is not valid JSON' }
364
+ }
365
+ if (!isRecord(raw) || !isRecord(raw.data)) return { kind: 'malformed', reason: 'response root/data is not an object' }
366
+ const rows = raw.data.result
367
+ if (!Array.isArray(rows)) return { kind: 'malformed', reason: 'data.result is not an array' }
368
+ if (raw.data.map !== undefined && !isRecord(raw.data.map)) {
369
+ return { kind: 'malformed', reason: 'data.map is not an object' }
370
+ }
371
+ if (rows.length === 0) return { kind: 'recognized-empty', trains: [] }
372
+ const map = raw.data.map as Record<string, unknown> | undefined
373
+ const out: SessionTrainOption[] = []
374
+ for (const r of rows) {
375
+ if (typeof r !== 'string') return { kind: 'malformed', reason: 'data.result contains a non-string row' }
376
+ const opt = rowToOption(r, map, entryUrl)
377
+ if (!opt) return { kind: 'malformed', reason: 'data.result contains a malformed or unrecognized row' }
378
+ if (out.length < (opts.maxItems ?? 30)) out.push(opt)
379
+ }
380
+ return { kind: 'recognized-nonempty', trains: out }
381
+ }
382
+
383
+ /** 兼容旧调用方:typed outcome 之外仍保留旧的数组投影,错误与 genuine empty 均为 []。 */
384
+ export function parseLeftTicketQuery(body: string, entryUrl: string, opts: { maxItems?: number } = {}): SessionTrainOption[] {
385
+ const outcome = parseLeftTicketQueryResult(body, entryUrl, opts)
386
+ return outcome.kind === 'recognized-nonempty' ? outcome.trains : []
387
+ }
388
+
389
+ /** Positive availability is deliberately a small closed set, not canWebBuy alone. */
390
+ export function hasRecognizedAvailableSeat(option: SessionTrainOption): boolean {
391
+ return Object.values(option.seats).some(value => value === '有' || /^[1-9]\d*$/.test(value))
392
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Session 双源字段合同与 fixture 评分器。
3
+ *
4
+ * 只处理已经脱敏、结构化的证据;不打开浏览器、不读登录态,也不执行任何写操作。
5
+ */
6
+
7
+ export type SessionEvidenceVerdict = 'hit' | 'miss' | 'error' | 'challenged' | 'cooldown' | 'needs-login' | 'needs-attach' | 'needs-extension'
8
+
9
+ export const SESSION_BENCHMARK_SCHEMA_VERSION = 'session-double-source.v1' as const
10
+ export const SESSION_FIELD_ACCURACY_THRESHOLD = 0.9
11
+ export const REQUIRED_COMPARABLE_FIELDS = [
12
+ 'query_id',
13
+ 'route_segments',
14
+ 'journey_type',
15
+ 'route_segments[].departure_at',
16
+ 'route_segments[].arrival_at',
17
+ 'route_segments[].transport_number',
18
+ 'currency',
19
+ 'price',
20
+ 'source',
21
+ 'fetched_at',
22
+ 'verdict',
23
+ ] as const
24
+
25
+ export interface SessionComparableSegment {
26
+ from: string
27
+ to: string
28
+ departure_at: string
29
+ arrival_at: string
30
+ transport_number: string
31
+ }
32
+
33
+ export interface SessionComparableRecord {
34
+ query_id: string
35
+ route_segments: SessionComparableSegment[]
36
+ journey_type: 'direct' | 'transfer'
37
+ currency: string
38
+ price: number
39
+ /** 具体通道标识,如 flyai / ctrip-flight;official/session 角色由双源输入位置表达。 */
40
+ source: string
41
+ fetched_at: string
42
+ verdict: SessionEvidenceVerdict
43
+ latency_ms: number
44
+ read_guard_blocked: number
45
+ }
46
+
47
+ export interface SessionFixtureScore {
48
+ pass: boolean
49
+ threshold: number
50
+ correct: number
51
+ total: number
52
+ accuracy: number
53
+ missing: string[]
54
+ incorrect: string[]
55
+ fixture_errors: string[]
56
+ }
57
+
58
+ interface ComparableRecordInput {
59
+ query_id?: unknown
60
+ route_segments?: Array<Partial<SessionComparableSegment>>
61
+ journey_type?: unknown
62
+ currency?: unknown
63
+ price?: unknown
64
+ source?: unknown
65
+ fetched_at?: unknown
66
+ verdict?: unknown
67
+ latency_ms?: unknown
68
+ read_guard_blocked?: unknown
69
+ }
70
+
71
+ interface FieldComparison {
72
+ path: string
73
+ expected: unknown
74
+ actual: unknown
75
+ }
76
+
77
+ function isMissing(value: unknown): boolean {
78
+ return value === undefined || value === null || (typeof value === 'string' && value.trim() === '')
79
+ }
80
+
81
+ function sameValue(left: unknown, right: unknown): boolean {
82
+ if (typeof left !== typeof right) return false
83
+ return typeof left === 'number' && typeof right === 'number'
84
+ ? Object.is(left, right)
85
+ : left === right
86
+ }
87
+
88
+ function sameComparison(row: FieldComparison): boolean {
89
+ if (/\.(?:departure_at|arrival_at)$/.test(row.path)
90
+ && typeof row.expected === 'string'
91
+ && typeof row.actual === 'string') {
92
+ const expectedAt = Date.parse(row.expected)
93
+ const actualAt = Date.parse(row.actual)
94
+ if (Number.isFinite(expectedAt) && Number.isFinite(actualAt)) return expectedAt === actualAt
95
+ }
96
+ return sameValue(row.expected, row.actual)
97
+ }
98
+
99
+ function fixtureComparisons(expected: SessionComparableRecord, actual: ComparableRecordInput): FieldComparison[] {
100
+ const rows: FieldComparison[] = [
101
+ { path: 'query_id', expected: expected.query_id, actual: actual.query_id },
102
+ { path: 'route_segments.length', expected: expected.route_segments.length, actual: actual.route_segments?.length },
103
+ { path: 'journey_type', expected: expected.journey_type, actual: actual.journey_type },
104
+ { path: 'currency', expected: expected.currency, actual: actual.currency },
105
+ { path: 'price', expected: expected.price, actual: actual.price },
106
+ { path: 'source', expected: expected.source, actual: actual.source },
107
+ { path: 'fetched_at', expected: expected.fetched_at, actual: actual.fetched_at },
108
+ { path: 'verdict', expected: expected.verdict, actual: actual.verdict },
109
+ ]
110
+ for (let index = 0; index < expected.route_segments.length; index += 1) {
111
+ const exp = expected.route_segments[index]!
112
+ const act = actual.route_segments?.[index]
113
+ rows.push(
114
+ { path: `route_segments[${index}].from`, expected: exp.from, actual: act?.from },
115
+ { path: `route_segments[${index}].to`, expected: exp.to, actual: act?.to },
116
+ { path: `route_segments[${index}].departure_at`, expected: exp.departure_at, actual: act?.departure_at },
117
+ { path: `route_segments[${index}].arrival_at`, expected: exp.arrival_at, actual: act?.arrival_at },
118
+ { path: `route_segments[${index}].transport_number`, expected: exp.transport_number, actual: act?.transport_number },
119
+ )
120
+ }
121
+ return rows
122
+ }
123
+
124
+ export function scoreSessionFixture(
125
+ expected: SessionComparableRecord,
126
+ actual: ComparableRecordInput,
127
+ threshold = SESSION_FIELD_ACCURACY_THRESHOLD,
128
+ ): SessionFixtureScore {
129
+ const rows = fixtureComparisons(expected, actual)
130
+ const fixtureErrors = [
131
+ ...rows.filter((row) => isMissing(row.expected)).map((row) => row.path),
132
+ ...requiredMissing(expected),
133
+ ]
134
+ const missing: string[] = []
135
+ const incorrect: string[] = []
136
+ let correct = 0
137
+ for (const row of rows) {
138
+ if (isMissing(row.actual)) {
139
+ missing.push(row.path)
140
+ } else if (!sameValue(row.expected, row.actual)) {
141
+ incorrect.push(row.path)
142
+ } else {
143
+ correct += 1
144
+ }
145
+ }
146
+ const total = rows.length
147
+ const accuracy = total > 0 ? correct / total : 0
148
+ return {
149
+ pass: fixtureErrors.length === 0 && accuracy >= threshold,
150
+ threshold,
151
+ correct,
152
+ total,
153
+ accuracy,
154
+ missing,
155
+ incorrect,
156
+ fixture_errors: [...new Set(fixtureErrors)],
157
+ }
158
+ }
159
+
160
+ export type DoubleSourceState =
161
+ | 'comparable'
162
+ | 'divergent'
163
+ | 'waiting_attach'
164
+ | 'waiting_login'
165
+ | 'waiting_extension'
166
+ | 'challenge_stop'
167
+ | 'guard_violation'
168
+ | 'source_unavailable'
169
+ | 'invalid_contract'
170
+
171
+ export interface DoubleSourceEvaluation {
172
+ state: DoubleSourceState
173
+ retry_allowed: boolean
174
+ quota_disposition: 'evidence_ready' | 'no_spend_waiting_user' | 'no_spend_stop'
175
+ mismatches: string[]
176
+ missing: string[]
177
+ price_delta?: number
178
+ }
179
+
180
+ function isIsoTimestamp(value: string): boolean {
181
+ return value.trim() !== '' && Number.isFinite(Date.parse(value))
182
+ }
183
+
184
+ function requiredMissing(record: SessionComparableRecord): string[] {
185
+ const missing: string[] = []
186
+ if (!record.query_id.trim()) missing.push('query_id')
187
+ if (record.route_segments.length === 0) missing.push('route_segments')
188
+ if (!record.currency.trim()) missing.push('currency')
189
+ if (!(record.price > 0)) missing.push('price')
190
+ if (!record.source.trim()) missing.push('source')
191
+ if (!isIsoTimestamp(record.fetched_at)) missing.push('fetched_at')
192
+ if (record.journey_type === 'direct' && record.route_segments.length !== 1) missing.push('journey_type/route_segments')
193
+ if (record.journey_type === 'transfer' && record.route_segments.length < 2) missing.push('journey_type/route_segments')
194
+ for (let index = 0; index < record.route_segments.length; index += 1) {
195
+ const segment = record.route_segments[index]!
196
+ if (!segment.from.trim()) missing.push(`route_segments[${index}].from`)
197
+ if (!segment.to.trim()) missing.push(`route_segments[${index}].to`)
198
+ if (!isIsoTimestamp(segment.departure_at)) missing.push(`route_segments[${index}].departure_at`)
199
+ if (!isIsoTimestamp(segment.arrival_at)) missing.push(`route_segments[${index}].arrival_at`)
200
+ if (!segment.transport_number.trim()) missing.push(`route_segments[${index}].transport_number`)
201
+ }
202
+ return missing
203
+ }
204
+
205
+ function baseEvaluation(
206
+ state: DoubleSourceState,
207
+ quotaDisposition: DoubleSourceEvaluation['quota_disposition'],
208
+ extra: Partial<Pick<DoubleSourceEvaluation, 'mismatches' | 'missing' | 'price_delta'>> = {},
209
+ ): DoubleSourceEvaluation {
210
+ return {
211
+ state,
212
+ retry_allowed: false,
213
+ quota_disposition: quotaDisposition,
214
+ mismatches: extra.mismatches ?? [],
215
+ missing: extra.missing ?? [],
216
+ ...(extra.price_delta === undefined ? {} : { price_delta: extra.price_delta }),
217
+ }
218
+ }
219
+
220
+ function alignmentComparisons(official: SessionComparableRecord, session: SessionComparableRecord): FieldComparison[] {
221
+ const rows: FieldComparison[] = [
222
+ { path: 'query_id', expected: official.query_id, actual: session.query_id },
223
+ { path: 'route_segments.length', expected: official.route_segments.length, actual: session.route_segments.length },
224
+ { path: 'journey_type', expected: official.journey_type, actual: session.journey_type },
225
+ { path: 'currency', expected: official.currency, actual: session.currency },
226
+ ]
227
+ const comparableSegments = Math.min(official.route_segments.length, session.route_segments.length)
228
+ for (let index = 0; index < comparableSegments; index += 1) {
229
+ const exp = official.route_segments[index]!
230
+ const act = session.route_segments[index]!
231
+ rows.push(
232
+ { path: `route_segments[${index}].from`, expected: exp.from, actual: act.from },
233
+ { path: `route_segments[${index}].to`, expected: exp.to, actual: act.to },
234
+ { path: `route_segments[${index}].departure_at`, expected: exp.departure_at, actual: act.departure_at },
235
+ { path: `route_segments[${index}].arrival_at`, expected: exp.arrival_at, actual: act.arrival_at },
236
+ { path: `route_segments[${index}].transport_number`, expected: exp.transport_number, actual: act.transport_number },
237
+ )
238
+ }
239
+ return rows
240
+ }
241
+
242
+ export function evaluateDoubleSource(input: {
243
+ official?: SessionComparableRecord
244
+ session?: SessionComparableRecord
245
+ }): DoubleSourceEvaluation {
246
+ const official = input.official
247
+ const session = input.session
248
+ if (session?.verdict === 'challenged' || official?.verdict === 'challenged') {
249
+ return baseEvaluation('challenge_stop', 'no_spend_stop')
250
+ }
251
+ if ((session?.read_guard_blocked ?? 0) !== 0 || (official?.read_guard_blocked ?? 0) !== 0) {
252
+ return baseEvaluation('guard_violation', 'no_spend_stop')
253
+ }
254
+ if (!session) return baseEvaluation('source_unavailable', 'no_spend_stop')
255
+ if (session.verdict === 'needs-attach') return baseEvaluation('waiting_attach', 'no_spend_waiting_user')
256
+ // 扩展车道(2026-08-29 PRIMARY 传输)未就绪:与 waiting-* 同族 no-spend,等待用户一次性安装
257
+ if (session.verdict === 'needs-extension') return baseEvaluation('waiting_extension', 'no_spend_waiting_user')
258
+ if (session.verdict === 'needs-login') return baseEvaluation('waiting_login', 'no_spend_waiting_user')
259
+ if (!official || official.verdict !== 'hit' || session.verdict !== 'hit') {
260
+ return baseEvaluation('source_unavailable', 'no_spend_stop')
261
+ }
262
+ const missing = [
263
+ ...requiredMissing(official).map((path) => `official.${path}`),
264
+ ...requiredMissing(session).map((path) => `session.${path}`),
265
+ ]
266
+ if (missing.length > 0) {
267
+ return baseEvaluation('invalid_contract', 'no_spend_stop', { missing })
268
+ }
269
+ const mismatches = alignmentComparisons(official, session)
270
+ .filter((row) => !sameComparison(row))
271
+ .map((row) => row.path)
272
+ return baseEvaluation(mismatches.length === 0 ? 'comparable' : 'divergent', 'evidence_ready', {
273
+ mismatches,
274
+ price_delta: session.price - official.price,
275
+ })
276
+ }