@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,264 @@
1
+ /**
2
+ * 扩展健康检查 + 自动重放(issue #21 onboarding UX,P3.6 gotry-session-onboarding-goal,
3
+ * RFC docs/rfc/user-session-data-rfc.md §3.3)。
4
+ *
5
+ * 动机:首次 `sessionFlightSearch` 遇 `needs-extension` 时,旧版要求用户装完扩展后**自己重跑命令**;
6
+ * 本模块把「用户手工重跑」改成「后台有界轮询 + 扩展一就位自动重放同一 query_id」——
7
+ * 用户装完扩展零后续动作,gotry 自己感知到 ready 后立刻跑同一条 query。
8
+ *
9
+ * 形态:
10
+ * - `startExtensionHealthWatch({ timeoutMs, intervalMs, probe })` 启后台轮询。
11
+ * probe 是异步检测函数(默认 GET http://127.0.0.1:{port}/status,扩展 ≤30s 心跳一次,
12
+ * 1.5 倍容差 EXTENSION_CONNECTED_WINDOW_MS=45s;§38 已经验证扩展心跳节奏)。
13
+ * - 命中 ready → 调用方自动重放(query_id / args 不变;同一证据文件覆盖)。
14
+ * - 超时(timeoutMs 默认 120_000)→ 返回 'timeout',调用方降级回 `needs-extension` 错误面。
15
+ *
16
+ * 离线合同(run-all §40 onboarding-tests 5/5):
17
+ * - 三时序分支:probe 0ms ready / 中途 ready / 120s+1ms 超时;
18
+ * - 同 query_id 重放语义(参数透传);
19
+ * - 取消语义(timeoutMs=0 立即 timeout;cancel() 立即返回 'cancelled');
20
+ * - 重放上限(防止 watch 反复 ready/抖动导致无限重放;maxRetries 默认 1)。
21
+ *
22
+ * 纪律:
23
+ * - 不引 GUI 依赖;只 spawn `node:http` 探活(已经在扩展桥验证过,§38 24 断言覆盖);
24
+ * - 不持锁、不轮询文件、不读 dsh-runtime 共享状态;
25
+ * - 不污染扩展桥 single-instance;probe 走独立短连接。
26
+ */
27
+
28
+ import { request } from 'node:http'
29
+ import {
30
+ BRIDGE_PORTS,
31
+ EXTENSION_CONNECTED_WINDOW_MS,
32
+ type SessionBridgeOptions,
33
+ } from './extension-bridge.ts'
34
+
35
+ export type HealthWatchOutcome =
36
+ | { ready: true; attempts: number; waitedMs: number }
37
+ | { ready: false; reason: 'timeout' | 'cancelled'; attempts: number; waitedMs: number }
38
+
39
+ export interface HealthWatchOptions extends SessionBridgeOptions {
40
+ /** 最长等多久(默认 120s);0 = 立即 timeout(便于测试) */
41
+ timeoutMs?: number
42
+ /** 探活间隔(默认 5s);必须 < EXTENSION_CONNECTED_WINDOW_MS,否则抖动期漏心跳 */
43
+ intervalMs?: number
44
+ /** 探活函数:返回 true 即视为扩展就绪(测试可注入 fake probe)。
45
+ * 默认走双因子:① 心跳 `/status` extensionConnected === true
46
+ * + ② 业务探针 cookie-names 真回包(timeout 5s);二者同时通过才算真 ready。
47
+ * 仅心跳通过但 cookie-names 失败 = 扩展 SW 跑着但 chrome.cookies 权限缺 → 不算 ready。 */
48
+ probe?: () => Promise<boolean>
49
+ /** 最大重放次数(默认 1);防止 ready 抖动反复触发 query 重放 */
50
+ maxRetries?: number
51
+ /** 取消信号(可选)——给调用方提供立刻终止 watch 的逃生口 */
52
+ signal?: AbortSignal
53
+ /** 时间源(测试可注入 fake clock) */
54
+ now?: () => number
55
+ /**
56
+ * 是否启用业务探针(cookie-names)。wizard 默认 true(用户刚装扩展,首次需要双因子验证);
57
+ * 上线 session 检索可传 false(快速心跳足矣,业务探针会另发一次请求)。
58
+ */
59
+ businessProbe?: boolean
60
+ }
61
+
62
+ export interface HealthWatchController {
63
+ /** Promise:resolve 时扩展已 ready,可执行重放 */
64
+ waitReady(): Promise<HealthWatchOutcome>
65
+ /** 立刻取消;waitReady 立即 resolve {ready:false, reason:'cancelled'} */
66
+ cancel(reason?: string): void
67
+ /** 当前 ready 次数(供诊断/审计) */
68
+ readonly readyCount: number
69
+ }
70
+
71
+ interface InternalState {
72
+ cancelled: boolean
73
+ readyCount: number
74
+ resolveOutcome: ((o: HealthWatchOutcome) => void) | null
75
+ }
76
+
77
+ /**
78
+ * 双因子 probe(默认):心跳 + 业务探针(cookie-names)。
79
+ * 心跳通过只能证 SW 跑着;业务探针通过证 chrome.cookies 权限齐——二者同过才算
80
+ * 扩展真正 ready。wizard 必须双因子(用户刚装的扩展可能权限不全),上线 session
81
+ * 检索可传 `businessProbe:false` 跳业务探针(避免每次检索额外打一次 cookie-names)。
82
+ *
83
+ * 不直接 import extension-channel.ts(避免环)—用 dynamic import 懒拿。
84
+ */
85
+ export async function combinedProbe(opts: HealthWatchOptions, businessProbe: boolean): Promise<boolean> {
86
+ if (!(await defaultProbe(opts))) return false
87
+ if (!businessProbe) return true
88
+ try {
89
+ // 动态 import 避免 health-watch ↔ extension-channel 双向环
90
+ const channel = await import('./extension-channel.ts')
91
+ const bridge = await import('./extension-bridge.ts')
92
+ // 双因子探针需要常驻桥(wizard 期间):keepBridge=true 透传到 bridge 单例
93
+ bridge.getOrCreateSessionBridge({ keepBridge: opts.keepBridge === true })
94
+ // 心跳已 ready(刚 defaultProbe 通过)= 扩展一定在线;cookie-names 超时给 8s,
95
+ // 比心跳间隔 5s 长——保证 tick 间不会撞死 cookie job
96
+ const cookie = await channel.extensionCookieNames({
97
+ site: 'ctrip-flight',
98
+ domain: 'ctrip.com',
99
+ ticketNames: ['cticket', 'uid'],
100
+ timeoutMs: 8_000,
101
+ })
102
+ // cookie-names.ok = true 表示扩展 SW 真能调 chrome.cookies.getAll(权限齐全);
103
+ // cookie-names.ok = true + names = [] 也算 ok(用户没登携程但扩展权限齐)。
104
+ // cookie-names.ok = false 分两类:bridge-unavailable/extension-not-connected(心跳 false 时) / timeout(扩展没回包)——
105
+ // 后者更可能是 SW 卡了,不应当 ready。但 wizard 必须严格:让用户明确知道权限缺或登录缺。
106
+ return cookie.ok
107
+ } catch {
108
+ return false
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 默认 probe:GET http://127.0.0.1:{port}/status,看 extensionConnected: true
114
+ * (扩展心跳 ≤30s 一刷,窗口 EXTENSION_CONNECTED_WINDOW_MS=45s 容差)。
115
+ * 不在端口池内挑一个试——端口是已启动桥的事实,直接拿 opts.ports[0] 或 8791。
116
+ * 测试可注入 fake probe 跳过网络。
117
+ */
118
+ export async function defaultProbe(opts: HealthWatchOptions): Promise<boolean> {
119
+ const ports = opts.ports ?? [...BRIDGE_PORTS]
120
+ for (const port of ports) {
121
+ const ok = await new Promise<boolean>((resolve) => {
122
+ const req = request(
123
+ { host: '127.0.0.1', port, path: '/status', method: 'GET', timeout: 2_000 },
124
+ (res) => {
125
+ if (!res.statusCode || res.statusCode >= 400) { resolve(false); return }
126
+ let buf = ''
127
+ res.setEncoding('utf8')
128
+ res.on('data', (c: string) => { buf += c })
129
+ res.on('end', () => {
130
+ try {
131
+ const payload = JSON.parse(buf) as { extensionConnected?: boolean }
132
+ resolve(payload.extensionConnected === true)
133
+ } catch { resolve(false) }
134
+ })
135
+ res.on('error', () => resolve(false))
136
+ },
137
+ )
138
+ req.on('error', () => resolve(false))
139
+ req.on('timeout', () => { req.destroy(); resolve(false) })
140
+ req.end()
141
+ })
142
+ if (ok) return true
143
+ }
144
+ return false
145
+ }
146
+
147
+ export function startExtensionHealthWatch(opts: HealthWatchOptions = {}): HealthWatchController {
148
+ const timeoutMs = opts.timeoutMs ?? 120_000
149
+ const intervalMs = opts.intervalMs ?? 5_000
150
+ const maxRetries = opts.maxRetries ?? 1
151
+ const businessProbe = opts.businessProbe ?? true
152
+ // 关键:wizard 期间需要常驻桥(keepBridge=true 透传到 getOrCreateSessionBridge),
153
+ // 否则启桥后 wizard main 一退桥就退,扩展心跳发不进来。
154
+ // 用 dynamic import 避免与 extension-bridge 顶层环(channel 也引 bridge,但 health-watch 引 channel/bridge 双向会死循环)
155
+ const probe = opts.probe ?? (async () => {
156
+ // 先确保桥存在 + keepBridge
157
+ const bridge = await import('./extension-bridge.ts')
158
+ const created = await bridge.getOrCreateSessionBridge({ keepBridge: opts.keepBridge === true, ports: opts.ports })
159
+ // The default path must keep scanning the canonical pool: another GoTry
160
+ // process may already own the extension-connected bridge. An explicit
161
+ // port override is an isolation boundary, so probe only the bridge that
162
+ // this process actually bound (including an ephemeral `0` request).
163
+ const probeOpts = created.ok && opts.ports
164
+ ? { ...opts, ports: [created.bridge.port] }
165
+ : opts
166
+ return combinedProbe(probeOpts, businessProbe)
167
+ })
168
+ const now = opts.now ?? Date.now
169
+ const state: InternalState = { cancelled: false, readyCount: 0, resolveOutcome: null }
170
+
171
+ const finish = (outcome: HealthWatchOutcome): void => {
172
+ if (state.resolveOutcome) {
173
+ const resolve = state.resolveOutcome
174
+ state.resolveOutcome = null
175
+ resolve(outcome)
176
+ }
177
+ }
178
+
179
+ // 取消信号 -> 立刻 timeout-ish 终态(不进 ready 路径)
180
+ const onAbort = (): void => {
181
+ state.cancelled = true
182
+ finish({ ready: false, reason: 'cancelled', attempts: state.readyCount, waitedMs: 0 })
183
+ }
184
+ opts.signal?.addEventListener('abort', onAbort, { once: true })
185
+
186
+ const waitReady = (): Promise<HealthWatchOutcome> => new Promise<HealthWatchOutcome>((resolve) => {
187
+ if (state.cancelled) { resolve({ ready: false, reason: 'cancelled', attempts: 0, waitedMs: 0 }); return }
188
+ if (timeoutMs === 0) { resolve({ ready: false, reason: 'timeout', attempts: 0, waitedMs: 0 }); return }
189
+ state.resolveOutcome = resolve
190
+ const deadline = now() + timeoutMs
191
+ let attempts = 0
192
+ let waitedMs = 0
193
+
194
+ const tick = async (): Promise<void> => {
195
+ if (state.cancelled || state.resolveOutcome === null) return
196
+ attempts += 1
197
+ try {
198
+ const ready = await probe()
199
+ if (ready && !state.cancelled) {
200
+ state.readyCount += 1
201
+ waitedMs = timeoutMs - Math.max(0, deadline - now())
202
+ // readyCount > maxRetries → 视为抖动,继续等(避免一次性重放洪流)
203
+ if (state.readyCount <= maxRetries) {
204
+ finish({ ready: true, attempts, waitedMs: waitedMs > 0 ? waitedMs : 0 })
205
+ return
206
+ }
207
+ }
208
+ } catch { /* probe 失败继续轮询,不挡主流程 */ }
209
+ waitedMs = timeoutMs - Math.max(0, deadline - now())
210
+ if (now() >= deadline) {
211
+ finish({ ready: false, reason: 'timeout', attempts, waitedMs })
212
+ return
213
+ }
214
+ // 间隔 = intervalMs;若剩余时间 < intervalMs 则最后一次等剩余时间
215
+ const remaining = deadline - now()
216
+ const delay = Math.min(intervalMs, Math.max(0, remaining))
217
+ if (delay <= 0) {
218
+ finish({ ready: false, reason: 'timeout', attempts, waitedMs })
219
+ return
220
+ }
221
+ setTimeout(tick, delay)
222
+ }
223
+ void tick()
224
+ })
225
+
226
+ return {
227
+ waitReady,
228
+ cancel: () => onAbort(),
229
+ get readyCount() { return state.readyCount },
230
+ }
231
+ }
232
+
233
+ /**
234
+ * 自动重放 wrapper:首次调用走 fetch(返回 needs-extension) → 启 watch →
235
+ * 扩展 ready → 同 query 重放。语义:用户侧**只看到一条 query 的最终结果**;
236
+ * 中间产物(query_id 标记 + 尝试次数)落 evidence 文件,便于离线追溯。
237
+ *
238
+ * 这是 §3.3「用户零手工重跑」的对外契约:调用方把 fetch 包成 withAutoRetryOnExtension,
239
+ * 用户装完扩展→自然出结果;用户不装→等 120s 后返回 needs-extension,而不是「再跑一次试试」。
240
+ */
241
+ export async function withAutoRetryOnExtension<T>(args: {
242
+ fetch: () => Promise<T>
243
+ isExtensionNeeded: (result: T) => boolean
244
+ /** watch 配置(测试可注入 fake probe) */
245
+ watch?: HealthWatchOptions
246
+ /** 重放 fetcher(默认复用 fetch) */
247
+ retry?: () => Promise<T>
248
+ }): Promise<{ result: T; retried: boolean; attempts: number; outcome: HealthWatchOutcome }> {
249
+ const first = await args.fetch()
250
+ if (!args.isExtensionNeeded(first)) {
251
+ return { result: first, retried: false, attempts: 1, outcome: { ready: true, attempts: 0, waitedMs: 0 } }
252
+ }
253
+ const watch = startExtensionHealthWatch(args.watch ?? {})
254
+ const outcome = await watch.waitReady()
255
+ if (!outcome.ready) {
256
+ return { result: first, retried: false, attempts: 1, outcome }
257
+ }
258
+ const retriedResult = await (args.retry ?? args.fetch)()
259
+ return { result: retriedResult, retried: true, attempts: 2, outcome }
260
+ }
261
+
262
+ /** 留作运行时只读检查,防止扩展心跳节律假设漂移(§38 防漂移同款语义) */
263
+ export const HEALTH_WATCH_INTERVAL_FLOOR_MS = 1_000
264
+ export const HEALTH_WATCH_INTERVAL_CEIL_MS = EXTENSION_CONNECTED_WINDOW_MS - 1_000
@@ -0,0 +1,141 @@
1
+ /**
2
+ * ReadGuard:会话检索面的只读物理隔离(RFC docs/rfc/user-session-data-rfc.md §3.3)。
3
+ *
4
+ * 写不是「被禁止的行为」,是「不存在的原语」——WriteGate(L0-L4)的检索态前置:
5
+ * ① 网络层:route 拦截——命中硬支付模式(任意方法)或写方法+写URL模式 → abort + 审计落盘;
6
+ * ② DOM 层:提交类按钮文本黑名单,从快照/断言文本中剔除(模型看不到可点的提交件);
7
+ * ③ fail-closed:guard 未装上,session transport 不允许导航(openSession 装不上即整会话失败)。
8
+ *
9
+ * 审计:append-only JSONL(session-incidents.jsonl,对齐 incident-log 惯例),隔离 stateRoot。
10
+ * 注意:搜索接口多为 POST(如携程 batchSearch),因此写判定必须「方法 × URL 模式」双因子,
11
+ * 不能一刀切禁 POST——batchSearch 不含写词,放行。
12
+ */
13
+
14
+ import { mkdirSync, appendFileSync } from 'node:fs'
15
+ import { dirname } from 'node:path'
16
+
17
+ /** 硬支付模式:任意方法(含 GET)都视为写——进支付/收银面本身就是风险 */
18
+ const HARD_PAY_RE = /\/(pay|payment|cashier)(\/|\?|$)/i
19
+ /** 驼峰复合写词(OTA 常见 /submitOrder、/createOrder):段式正则漏接,单列 */
20
+ const WRITE_COMPOUND_RE = /(submitorder|createorder|placeorder|cancelorder|refundorder|orderconfirm|orderpay|confirmpay)/i
21
+ /** 写词 URL 模式:仅在写方法(POST/PUT/DELETE/PATCH)下触发 */
22
+ const WRITE_URL_RE = /\/(order|orders|submit|trade|booking|book|purchase|buy|create|cancel|refund)(\/|\?|$)/i
23
+ const WRITE_METHODS = new Set(['POST', 'PUT', 'DELETE', 'PATCH'])
24
+ /** DOM 提交件文本黑名单(§3.3-②):命中即从可交互面剔除 */
25
+ export const SUBMIT_TEXT_RE = /下单|去支付|支付|预订|订票|购买|提交订单|确认订单/
26
+
27
+ /** 网络层写判定(纯函数,测试锚点) */
28
+ export function classifyRequest(method: string, url: string): 'allow' | 'block' {
29
+ if (HARD_PAY_RE.test(url) || WRITE_COMPOUND_RE.test(url)) return 'block'
30
+ if (WRITE_METHODS.has(method.toUpperCase()) && WRITE_URL_RE.test(url)) return 'block'
31
+ return 'allow'
32
+ }
33
+
34
+ /** DOM 层提交件过滤(纯函数,测试锚点):true=剔除 */
35
+ export function isSubmitText(text: string): boolean {
36
+ return SUBMIT_TEXT_RE.test(text ?? '')
37
+ }
38
+
39
+ export interface GuardAuditEntry {
40
+ ts: string
41
+ kind: 'blocked-write-request'
42
+ method: string
43
+ url: string
44
+ }
45
+
46
+ export interface ReadGuardHandle {
47
+ /** 被拦截的写请求数(测试断言锚点) */
48
+ blockedCount(): number
49
+ /** 经手的请求总数 */
50
+ requestCount(): number
51
+ }
52
+
53
+ export interface RoutableContext {
54
+ route(glob: string, handler: (route: { request(): { method(): string; url(): string }; abort(code?: string): Promise<void>; continue(): Promise<void> }) => Promise<void>): Promise<void>
55
+ }
56
+
57
+ /**
58
+ * 装上网络层守卫(fail-closed 的「closed」在调用方:openSession 装不上 guard 即失败,不给无 guard 的会话)。
59
+ * 审计落 auditPath(JSONL append);auditPath 缺省不落盘(仅内存计数)。
60
+ */
61
+ export async function attachReadGuard(ctx: RoutableContext, auditPath?: string): Promise<ReadGuardHandle> {
62
+ let blocked = 0
63
+ let total = 0
64
+ await ctx.route('**/*', async (route) => {
65
+ const req = route.request()
66
+ total += 1
67
+ const verdict = classifyRequest(req.method(), req.url())
68
+ if (verdict === 'block') {
69
+ blocked += 1
70
+ if (auditPath) {
71
+ const entry: GuardAuditEntry = { ts: new Date().toISOString(), kind: 'blocked-write-request', method: req.method(), url: req.url().slice(0, 400) }
72
+ try {
73
+ mkdirSync(dirname(auditPath), { recursive: true })
74
+ appendFileSync(auditPath, JSON.stringify(entry) + '\n')
75
+ } catch { /* 审计失败不阻塞拦截本身 */ }
76
+ }
77
+ await route.abort('blockedbyclient')
78
+ return
79
+ }
80
+ await route.continue()
81
+ })
82
+ return {
83
+ blockedCount: () => blocked,
84
+ requestCount: () => total,
85
+ }
86
+ }
87
+
88
+ /* ============ puppeteer-core 适配(2026-08-28 传输层定案:Chrome 144+ 调试服务与 playwright 不兼容) ============ */
89
+
90
+ interface PpRequest {
91
+ method(): string
92
+ url(): string
93
+ continue(): Promise<void>
94
+ abort(code?: string): Promise<void>
95
+ }
96
+ interface PpPage {
97
+ setRequestInterception(on: boolean): Promise<void>
98
+ on(event: 'request', handler: (req: PpRequest) => void): void
99
+ }
100
+ interface PpBrowser {
101
+ pages(): Promise<PpPage[]>
102
+ on(event: string, handler: (target: { type(): string; page(): Promise<PpPage> }) => void): unknown
103
+ }
104
+
105
+ /**
106
+ * puppeteer 版守卫:对既有页面与新建页面全部开启请求拦截,写请求 abort + 审计。
107
+ * 与 playwright 版共用 classifyRequest 判定与审计条目(单一事实源)。
108
+ */
109
+ export async function attachReadGuardPuppeteer(browser: PpBrowser, auditPath?: string): Promise<ReadGuardHandle> {
110
+ let blocked = 0
111
+ let total = 0
112
+ const wire = (page: PpPage): void => {
113
+ void (async () => {
114
+ try {
115
+ await page.setRequestInterception(true)
116
+ page.on('request', async (req) => {
117
+ total += 1
118
+ const verdict = classifyRequest(req.method(), req.url())
119
+ if (verdict === 'block') {
120
+ blocked += 1
121
+ if (auditPath) {
122
+ const entry: GuardAuditEntry = { ts: new Date().toISOString(), kind: 'blocked-write-request', method: req.method(), url: req.url().slice(0, 400) }
123
+ try {
124
+ mkdirSync(dirname(auditPath), { recursive: true })
125
+ appendFileSync(auditPath, JSON.stringify(entry) + '\n')
126
+ } catch { /* 审计失败不阻塞拦截本身 */ }
127
+ }
128
+ await req.abort('blockedbyclient').catch(() => { /* 目标已销毁等竞态 */ })
129
+ return
130
+ }
131
+ await req.continue().catch(() => { /* 竞态容忍 */ })
132
+ })
133
+ } catch { /* 已关闭的 target 容忍 */ }
134
+ })()
135
+ }
136
+ for (const p of await browser.pages()) wire(p)
137
+ browser.on('targetcreated', (t) => {
138
+ if (t.type() === 'page') void t.page().then(wire).catch(() => { /* ignore */ })
139
+ })
140
+ return { blockedCount: () => blocked, requestCount: () => total }
141
+ }
@@ -0,0 +1,209 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+
4
+ import type { SessionComparableRecord, SessionComparableSegment } from './benchmark.ts'
5
+
6
+ export type GoldenSource = 'manual' | 'flyai' | 'static'
7
+
8
+ export interface StaticFlightRouteLeg {
9
+ from_iata: string
10
+ to_iata: string
11
+ carrier_codes: string[]
12
+ }
13
+
14
+ export interface StaticFlightRoute {
15
+ query_id: string
16
+ from_city: string
17
+ to_city: string
18
+ route_kind: 'direct' | 'transfer'
19
+ legs: StaticFlightRouteLeg[]
20
+ }
21
+
22
+ export interface StaticFlightSnapshot {
23
+ schema_version: 'sf-static-routes.v1'
24
+ source: {
25
+ name: string
26
+ url: string
27
+ revision: string
28
+ license: string
29
+ retrieved_at: string
30
+ limitations: string[]
31
+ }
32
+ routes: StaticFlightRoute[]
33
+ }
34
+
35
+ export interface StaticGoldenResolution {
36
+ requested_source: 'static'
37
+ effective_source: 'static-openflights+manual-band' | 'manual-golden'
38
+ record: SessionComparableRecord
39
+ fallback_reason?: string
40
+ estimated_fields: string[]
41
+ provenance: {
42
+ route_source: string
43
+ route_revision: string
44
+ route_license: string
45
+ band_source: 'sf-golden-manifest.json'
46
+ }
47
+ }
48
+
49
+ export function parseGoldenSource(args: string[]): GoldenSource {
50
+ const raw = args.find((arg) => arg.startsWith('--golden='))?.slice('--golden='.length) ?? 'manual'
51
+ if (raw === 'manual' || raw === 'flyai' || raw === 'static') return raw
52
+ throw new Error(`不支持的 golden vendor: ${raw}`)
53
+ }
54
+
55
+ export function loadStaticFlightSnapshot(
56
+ path = join(import.meta.dirname, '..', '..', 'data', 'sf-static-routes.json'),
57
+ ): StaticFlightSnapshot {
58
+ const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown
59
+ if (!isRecord(parsed) || parsed.schema_version !== 'sf-static-routes.v1') {
60
+ throw new Error(`invalid static snapshot schema_version: ${String(isRecord(parsed) ? parsed.schema_version : undefined)}`)
61
+ }
62
+ const source = parsed.source
63
+ if (!isRecord(source)
64
+ || !['name', 'url', 'revision', 'license', 'retrieved_at'].every((key) => typeof source[key] === 'string')
65
+ || !Array.isArray(source.limitations)
66
+ || !source.limitations.every((item) => typeof item === 'string')) {
67
+ throw new Error('invalid static snapshot source')
68
+ }
69
+ if (!Array.isArray(parsed.routes) || !parsed.routes.every(isStaticFlightRoute)) {
70
+ throw new Error('invalid static snapshot routes')
71
+ }
72
+ return parsed as unknown as StaticFlightSnapshot
73
+ }
74
+
75
+ function isRecord(value: unknown): value is Record<string, unknown> {
76
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
77
+ }
78
+
79
+ function isStaticFlightRoute(value: unknown): boolean {
80
+ if (!isRecord(value)
81
+ || typeof value.query_id !== 'string'
82
+ || typeof value.from_city !== 'string'
83
+ || typeof value.to_city !== 'string'
84
+ || (value.route_kind !== 'direct' && value.route_kind !== 'transfer')
85
+ || !Array.isArray(value.legs)
86
+ || value.legs.length === 0) return false
87
+ return value.legs.every((leg) => isRecord(leg)
88
+ && typeof leg.from_iata === 'string'
89
+ && typeof leg.to_iata === 'string'
90
+ && Array.isArray(leg.carrier_codes)
91
+ && leg.carrier_codes.every((carrier) => typeof carrier === 'string'))
92
+ }
93
+
94
+ function provenance(snapshot?: StaticFlightSnapshot): StaticGoldenResolution['provenance'] {
95
+ return {
96
+ route_source: snapshot?.source.url ?? '',
97
+ route_revision: snapshot?.source.revision ?? '',
98
+ route_license: snapshot?.source.license ?? '',
99
+ band_source: 'sf-golden-manifest.json',
100
+ }
101
+ }
102
+
103
+ function formatWithOffset(timestampMs: number, offset: string): string {
104
+ const sign = offset.startsWith('-') ? -1 : 1
105
+ const [hours, minutes] = offset.slice(1).split(':').map(Number)
106
+ const offsetMinutes = sign * ((hours ?? 0) * 60 + (minutes ?? 0))
107
+ const local = new Date(timestampMs + offsetMinutes * 60_000).toISOString().slice(0, 19)
108
+ return `${local}${offset}`
109
+ }
110
+
111
+ function buildSegments(
112
+ query: { from: string; to: string },
113
+ route: StaticFlightRoute,
114
+ manualSegment: SessionComparableSegment,
115
+ ): SessionComparableSegment[] | null {
116
+ if (route.route_kind === 'direct' && route.legs.length === 1) {
117
+ return [{
118
+ from: query.from,
119
+ to: query.to,
120
+ departure_at: manualSegment.departure_at,
121
+ arrival_at: manualSegment.arrival_at,
122
+ transport_number: `${route.legs[0]!.carrier_codes[0]}-STATIC`,
123
+ }]
124
+ }
125
+ if (route.route_kind !== 'transfer' || route.legs.length !== 2) return null
126
+ const departureMs = Date.parse(manualSegment.departure_at)
127
+ const arrivalMs = Date.parse(manualSegment.arrival_at)
128
+ if (!Number.isFinite(departureMs) || !Number.isFinite(arrivalMs) || arrivalMs <= departureMs) return null
129
+ const offset = manualSegment.departure_at.match(/([+-]\d{2}:\d{2})$/)?.[1] ?? '+08:00'
130
+ const totalMinutes = Math.floor((arrivalMs - departureMs) / 60_000)
131
+ if (totalMinutes < 90) return null
132
+ const layoverMinutes = Math.min(60, Math.floor(totalMinutes / 3))
133
+ const travelMinutes = totalMinutes - layoverMinutes
134
+ const firstTravelMinutes = Math.floor(travelMinutes / 2)
135
+ const secondDepartureMs = arrivalMs - (travelMinutes - firstTravelMinutes) * 60_000
136
+ const middle = route.legs[0]!.to_iata
137
+ return [
138
+ {
139
+ from: query.from,
140
+ to: middle,
141
+ departure_at: manualSegment.departure_at,
142
+ arrival_at: formatWithOffset(departureMs + firstTravelMinutes * 60_000, offset),
143
+ transport_number: `${route.legs[0]!.carrier_codes[0]}-STATIC`,
144
+ },
145
+ {
146
+ from: middle,
147
+ to: query.to,
148
+ departure_at: formatWithOffset(secondDepartureMs, offset),
149
+ arrival_at: manualSegment.arrival_at,
150
+ transport_number: `${route.legs[1]!.carrier_codes[0]}-STATIC`,
151
+ },
152
+ ]
153
+ }
154
+
155
+ export function resolveStaticGolden(input: {
156
+ query: { id: string; from: string; to: string; date: string }
157
+ manualRecord: SessionComparableRecord
158
+ snapshot?: StaticFlightSnapshot
159
+ snapshotError?: string
160
+ warn?: (message: string) => void
161
+ }): StaticGoldenResolution {
162
+ const route = input.snapshot?.routes.find((candidate) => candidate.query_id === input.query.id)
163
+ let fallbackReason: string | undefined
164
+ if (input.snapshotError) {
165
+ fallbackReason = `static snapshot unavailable: ${input.snapshotError}`
166
+ } else if (!route) {
167
+ fallbackReason = `static route missing for ${input.query.id}`
168
+ } else if (route.from_city !== input.query.from || route.to_city !== input.query.to) {
169
+ fallbackReason = `static route mismatch for ${input.query.id}`
170
+ } else if (route.legs.some((leg) => leg.carrier_codes.length === 0)) {
171
+ fallbackReason = `static carrier coverage missing for ${input.query.id}`
172
+ }
173
+
174
+ const manualSegment = input.manualRecord.route_segments[0]
175
+ const segments = !fallbackReason && route && manualSegment
176
+ ? buildSegments(input.query, route, manualSegment)
177
+ : null
178
+ if (!fallbackReason && !segments) fallbackReason = `static route cannot form comparable record for ${input.query.id}`
179
+
180
+ if (fallbackReason) {
181
+ input.warn?.(`[sf-live-benchmark] static vendor failed for ${input.query.id}: ${fallbackReason}; fallback=manual-golden`)
182
+ return {
183
+ requested_source: 'static',
184
+ effective_source: 'manual-golden',
185
+ record: input.manualRecord,
186
+ fallback_reason: fallbackReason,
187
+ estimated_fields: [],
188
+ provenance: provenance(input.snapshot),
189
+ }
190
+ }
191
+
192
+ return {
193
+ requested_source: 'static',
194
+ effective_source: 'static-openflights+manual-band',
195
+ record: {
196
+ ...input.manualRecord,
197
+ route_segments: segments!,
198
+ journey_type: route!.route_kind,
199
+ source: 'static-openflights+manual-band',
200
+ },
201
+ estimated_fields: [
202
+ 'route_segments[].departure_at',
203
+ 'route_segments[].arrival_at',
204
+ 'route_segments[].transport_number',
205
+ 'price',
206
+ ],
207
+ provenance: provenance(input.snapshot),
208
+ }
209
+ }