@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,674 @@
1
+ /**
2
+ * BFF-only HTTP/SSE seam for the embedded Booking Copilot.
3
+ *
4
+ * The browser never calls this server directly. A same-origin HotelByte BFF
5
+ * authenticates the actor, mints contextRef and sends a strict planner turn
6
+ * with a deployment-level Bearer key. No portal token field exists here.
7
+ */
8
+ import { timingSafeEqual } from 'node:crypto';
9
+ import { createServer } from 'node:http';
10
+ import { BOOKING_COPILOT_ACCEPTED_TURN_KINDS, BOOKING_COPILOT_INGRESS_MODES, BOOKING_READ_ACTION_KINDS, BOOKING_SURFACE_ALLOWED_ACTIONS, BOOKING_SURFACE_FEATURES, BOOKING_SURFACE_FEATURES_HEADER, BOOKING_SURFACE_SCHEMA_SHA256, BOOKING_SURFACE_SCHEMA_SHA256_HEADER, BOOKING_SURFACE_SCHEMA_VERSION, BOOKING_SURFACE_VERSION_HEADER, bookingSurfaceActionsForFeatures, } from './contracts.js';
11
+ import { bookingDigest, } from './runtime.js';
12
+ import { validateBookingSurface } from './validation.js';
13
+ import { extractBookingDispatchReason, normalizeBookingErrorCode, safeBookingErrorMessage } from './error-codes.js';
14
+ /**
15
+ * 单请求处理闭包(startBookingCopilotServer 的 createServer 回调原样摘出):
16
+ * 探活(/healthz /status)+ 唯一 turn 路由 + 鉴权 + schema 头校验。
17
+ * gotry-backend 的 booking-copilot 模块复用同一闭包,保证两条挂载路径行为逐字一致。
18
+ */
19
+ export function bookingCopilotTrafficHandler(deps) {
20
+ const { apiKey, composition, maxBodyBytes, artifactId, ingressMode, runningIdentity } = deps;
21
+ return async (req, res) => {
22
+ const isProbe = req.method === 'GET' && (req.url === '/healthz' || req.url === '/status');
23
+ if (!isProbe && (req.method !== 'POST' || req.url !== '/a2a/booking-copilot/turn')) {
24
+ sendJson(res, 404, { error: { code: 'not_found' } });
25
+ return;
26
+ }
27
+ const auth = String(req.headers.authorization ?? '');
28
+ if (!safeSecretEqual(auth, `Bearer ${apiKey}`)) {
29
+ sendJson(res, 401, { error: { code: 'unauthorized' } });
30
+ return;
31
+ }
32
+ if (lifecycleFor(composition).shuttingDown) {
33
+ sendJson(res, 503, { error: { code: 'booking_copilot_shutting_down' } });
34
+ return;
35
+ }
36
+ if (isProbe) {
37
+ res.setHeader(BOOKING_SURFACE_VERSION_HEADER, BOOKING_SURFACE_SCHEMA_VERSION);
38
+ res.setHeader(BOOKING_SURFACE_SCHEMA_SHA256_HEADER, BOOKING_SURFACE_SCHEMA_SHA256);
39
+ res.setHeader(BOOKING_SURFACE_FEATURES_HEADER, BOOKING_SURFACE_FEATURES.join(','));
40
+ if (artifactId)
41
+ res.setHeader('X-GoTry-Artifact-ID', artifactId);
42
+ res.setHeader('X-GoTry-Node-Version', runningIdentity.nodeVersion);
43
+ res.setHeader('X-GoTry-Node-Modules-ABI', runningIdentity.nodeModulesAbi);
44
+ res.setHeader('X-GoTry-Release-Tuple', runningIdentity.releaseTuple);
45
+ if (runningIdentity.glibcVersion)
46
+ res.setHeader('X-GoTry-Glibc-Version', runningIdentity.glibcVersion);
47
+ const acceptedTurnKinds = ingressMode === 'bff-ingress-binding'
48
+ ? [...BOOKING_COPILOT_ACCEPTED_TURN_KINDS, 'user.turn.ingress']
49
+ : [...BOOKING_COPILOT_ACCEPTED_TURN_KINDS];
50
+ const healthBody = {
51
+ schemaVersion: BOOKING_SURFACE_SCHEMA_VERSION,
52
+ schemaSha256: BOOKING_SURFACE_SCHEMA_SHA256,
53
+ status: 'ready',
54
+ ingressMode,
55
+ acceptedTurnKinds,
56
+ features: [...BOOKING_SURFACE_FEATURES],
57
+ };
58
+ res.setHeader('X-GoTry-Ingress-Mode', ingressMode);
59
+ res.setHeader('X-GoTry-Accepted-Turn-Kinds', acceptedTurnKinds.join(','));
60
+ sendJson(res, 200, healthBody);
61
+ return;
62
+ }
63
+ const schemaVersion = String(req.headers[BOOKING_SURFACE_VERSION_HEADER] ?? '');
64
+ const schemaHash = String(req.headers[BOOKING_SURFACE_SCHEMA_SHA256_HEADER] ?? '');
65
+ // `booking.surface` is the v1 compatibility line. Compatible additive
66
+ // releases keep this version and may have different schema bytes; the
67
+ // canonical payload validator below remains authoritative. Only a breaking
68
+ // contract line changes this version and is rejected at the version gate.
69
+ if (schemaVersion !== BOOKING_SURFACE_SCHEMA_VERSION) {
70
+ sendJson(res, 409, {
71
+ error: {
72
+ code: 'booking_surface_schema_mismatch',
73
+ expectedVersion: BOOKING_SURFACE_SCHEMA_VERSION,
74
+ },
75
+ });
76
+ return;
77
+ }
78
+ if (schemaHash && !/^[0-9a-f]{64}$/i.test(schemaHash)) {
79
+ sendJson(res, 400, { error: { code: 'booking_surface_schema_hash_invalid' } });
80
+ return;
81
+ }
82
+ const lifecycle = lifecycleFor(composition);
83
+ lifecycle.acceptedRequests += 1;
84
+ try {
85
+ await handleBookingCopilotRequest(req, res, composition, maxBodyBytes, negotiatedBookingSurfaceFeatures(req));
86
+ }
87
+ finally {
88
+ finishAcceptedRequest(lifecycle);
89
+ }
90
+ };
91
+ }
92
+ const plannerLifecycles = new WeakMap();
93
+ const decisionFlightsByComposition = new WeakMap();
94
+ function lifecycleFor(composition) {
95
+ let lifecycle = plannerLifecycles.get(composition);
96
+ if (!lifecycle) {
97
+ lifecycle = {
98
+ active: new Map(),
99
+ closing: new Set(),
100
+ cleanupFailures: new Map(),
101
+ acceptedRequests: 0,
102
+ acceptedRequestDrainWaiters: new Set(),
103
+ shuttingDown: false,
104
+ };
105
+ plannerLifecycles.set(composition, lifecycle);
106
+ }
107
+ return lifecycle;
108
+ }
109
+ function finishAcceptedRequest(lifecycle) {
110
+ lifecycle.acceptedRequests -= 1;
111
+ if (lifecycle.acceptedRequests !== 0)
112
+ return;
113
+ for (const resolve of lifecycle.acceptedRequestDrainWaiters)
114
+ resolve();
115
+ lifecycle.acceptedRequestDrainWaiters.clear();
116
+ }
117
+ function drainAcceptedRequests(composition) {
118
+ const lifecycle = lifecycleFor(composition);
119
+ if (lifecycle.acceptedRequests === 0)
120
+ return Promise.resolve();
121
+ return new Promise((resolve) => lifecycle.acceptedRequestDrainWaiters.add(resolve));
122
+ }
123
+ function sessionsFor(composition) {
124
+ return lifecycleFor(composition).active;
125
+ }
126
+ function schedulePlannerSessionClose(composition, session) {
127
+ const lifecycle = lifecycleFor(composition);
128
+ const cleanup = Promise.resolve().then(() => session.close?.()).then(() => { lifecycle.cleanupFailures.delete(session); }, (error) => {
129
+ lifecycle.cleanupFailures.set(session, error);
130
+ // Cleanup must not replace an already-durable booking outcome. Keep the
131
+ // failure machine-readable and free of task/prompt/PII values. Session
132
+ // close promises are allowed to be sticky, so shutdown reports the
133
+ // original failure rather than retrying and laundering it into success.
134
+ console.error(JSON.stringify({ code: 'PLANNER_SESSION_CLOSE_FAILED' }));
135
+ });
136
+ lifecycle.closing.add(cleanup);
137
+ void cleanup.then(() => lifecycle.closing.delete(cleanup));
138
+ }
139
+ /** Detach a session from the request path and close it in the composition lifecycle. */
140
+ function releasePlannerSession(composition, taskId) {
141
+ const sessions = sessionsFor(composition);
142
+ const session = sessions.get(taskId);
143
+ if (!session)
144
+ return;
145
+ sessions.delete(taskId);
146
+ schedulePlannerSessionClose(composition, session);
147
+ }
148
+ async function drainPlannerSessions(composition) {
149
+ const lifecycle = lifecycleFor(composition);
150
+ for (const [taskId, session] of lifecycle.active) {
151
+ lifecycle.active.delete(taskId);
152
+ schedulePlannerSessionClose(composition, session);
153
+ }
154
+ while (lifecycle.closing.size > 0) {
155
+ await Promise.all([...lifecycle.closing]);
156
+ }
157
+ if (lifecycle.cleanupFailures.size > 0) {
158
+ throw new AggregateError([...lifecycle.cleanupFailures.values()], 'planner session cleanup failed during shutdown');
159
+ }
160
+ }
161
+ async function drainDecisionFlights(composition) {
162
+ const flights = decisionFlightsFor(composition);
163
+ while (flights.size > 0) {
164
+ await Promise.allSettled([...flights.values()]);
165
+ }
166
+ }
167
+ /**
168
+ * Stop admitting turns and drain every planner resource owned by a composition.
169
+ * This is shared by the standalone listener and the unified gotry-backend
170
+ * module, whose listener is owned by the backend kernel.
171
+ */
172
+ export function shutdownBookingCopilotTraffic(composition) {
173
+ const lifecycle = lifecycleFor(composition);
174
+ if (lifecycle.shutdownPromise)
175
+ return lifecycle.shutdownPromise;
176
+ lifecycle.shuttingDown = true;
177
+ lifecycle.shutdownPromise = (async () => {
178
+ await drainAcceptedRequests(composition);
179
+ await drainDecisionFlights(composition);
180
+ await drainPlannerSessions(composition);
181
+ })();
182
+ return lifecycle.shutdownPromise;
183
+ }
184
+ function decisionFlightsFor(composition) {
185
+ let flights = decisionFlightsByComposition.get(composition);
186
+ if (!flights) {
187
+ flights = new Map();
188
+ decisionFlightsByComposition.set(composition, flights);
189
+ }
190
+ return flights;
191
+ }
192
+ async function runDecisionSingleFlight(composition, key, work) {
193
+ const flights = decisionFlightsFor(composition);
194
+ const prior = flights.get(key);
195
+ if (prior)
196
+ return prior;
197
+ const current = work();
198
+ flights.set(key, current);
199
+ try {
200
+ return await current;
201
+ }
202
+ finally {
203
+ if (flights.get(key) === current)
204
+ flights.delete(key);
205
+ }
206
+ }
207
+ export function runtimeIdentity() {
208
+ const report = process.report?.getReport?.();
209
+ const header = report?.header;
210
+ const glibcVersion = header?.glibcVersionRuntime ?? '';
211
+ return {
212
+ nodeVersion: process.version,
213
+ nodeModulesAbi: process.versions.modules ?? '',
214
+ releaseTuple: `${process.platform}-${process.arch}-${glibcVersion ? 'glibc' : 'unknown'}`,
215
+ glibcVersion,
216
+ };
217
+ }
218
+ function safeSecretEqual(actual, expected) {
219
+ const actualBytes = Buffer.from(actual);
220
+ const expectedBytes = Buffer.from(expected);
221
+ return actualBytes.length === expectedBytes.length && timingSafeEqual(actualBytes, expectedBytes);
222
+ }
223
+ function sendJson(res, status, body) {
224
+ res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' });
225
+ res.end(JSON.stringify(body));
226
+ }
227
+ function readBody(req, maxBodyBytes) {
228
+ return new Promise((resolve, reject) => {
229
+ const chunks = [];
230
+ let size = 0;
231
+ let exceeded = false;
232
+ req.on('data', (chunk) => {
233
+ size += chunk.length;
234
+ if (size > maxBodyBytes) {
235
+ exceeded = true;
236
+ return;
237
+ }
238
+ chunks.push(chunk);
239
+ });
240
+ req.on('end', () => exceeded ? reject(new Error('payload_too_large')) : resolve(Buffer.concat(chunks).toString('utf8')));
241
+ req.on('error', reject);
242
+ });
243
+ }
244
+ function write(res, event) {
245
+ res.write(`event: ${event.kind}\ndata: ${JSON.stringify(event)}\n\n`);
246
+ res.flushHeaders();
247
+ res.socket?.setNoDelay(true);
248
+ res.socket?.uncork();
249
+ }
250
+ function flush(res) {
251
+ res.flushHeaders();
252
+ const maybeFlush = res;
253
+ maybeFlush.flush?.();
254
+ }
255
+ function sameCapabilities(a, b) {
256
+ return a.length === b.length && [...a].sort().every((kind, index) => kind === [...b].sort()[index]);
257
+ }
258
+ function exactKeys(value, allowed) {
259
+ return Object.keys(value).length === allowed.length && Object.keys(value).every((key) => allowed.includes(key));
260
+ }
261
+ const SURFACE_ACTION_MATRIX = BOOKING_SURFACE_ALLOWED_ACTIONS;
262
+ /**
263
+ * The authenticated BFF sends the fresh fleet-wide feature intersection in
264
+ * the same header that GoTry advertises on probes. Missing, malformed, and
265
+ * unknown values fail closed to the empty optional-feature set.
266
+ */
267
+ function negotiatedBookingSurfaceFeatures(req) {
268
+ const raw = req.headers[BOOKING_SURFACE_FEATURES_HEADER];
269
+ if (typeof raw !== 'string' || raw.trim() === '')
270
+ return [];
271
+ const values = new Set(raw.split(',').map((value) => value.trim()).filter(Boolean));
272
+ return BOOKING_SURFACE_FEATURES.filter((feature) => values.has(feature));
273
+ }
274
+ function validSurfaceActions(surface, allowed) {
275
+ if (typeof surface !== 'string' || !Object.prototype.hasOwnProperty.call(SURFACE_ACTION_MATRIX, surface))
276
+ return false;
277
+ if (!Array.isArray(allowed) || allowed.length < 1 || new Set(allowed).size !== allowed.length)
278
+ return false;
279
+ const matrix = SURFACE_ACTION_MATRIX[surface];
280
+ return allowed.every((action) => typeof action === 'string'
281
+ && matrix.includes(action)
282
+ && BOOKING_READ_ACTION_KINDS.includes(action));
283
+ }
284
+ function validObservableOrders(value) {
285
+ if (!Array.isArray(value) || value.length > 100)
286
+ return false;
287
+ const orderRefs = new Set();
288
+ return value.every((item) => {
289
+ if (!item || typeof item !== 'object' || Array.isArray(item))
290
+ return false;
291
+ const candidate = item;
292
+ if (!exactKeys(candidate, ['orderRef', 'factRefs']) || typeof candidate.orderRef !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(candidate.orderRef) || orderRefs.has(candidate.orderRef))
293
+ return false;
294
+ if (!Array.isArray(candidate.factRefs) || candidate.factRefs.length > 64 || new Set(candidate.factRefs).size !== candidate.factRefs.length || candidate.factRefs.some((ref) => typeof ref !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(ref)))
295
+ return false;
296
+ orderRefs.add(candidate.orderRef);
297
+ return true;
298
+ });
299
+ }
300
+ function validBoundWorkspaceAuthority(workspace) {
301
+ return workspace.capabilities.surface === workspace.surface
302
+ && validSurfaceActions(workspace.surface, workspace.capabilities.allowedActions)
303
+ && (workspace.observableOrders === undefined || validObservableOrders(workspace.observableOrders));
304
+ }
305
+ /**
306
+ * Old compatible BFF releases may still send the base action matrix. Fresh
307
+ * tasks are narrowed to the negotiated optional features instead of failing
308
+ * the whole turn. Existing task allowlists remain stable across a rolling
309
+ * deploy, while unsupported trusted projections are still removed so no new
310
+ * optional action can gain authority.
311
+ */
312
+ function narrowWorkspaceOptionalAuthority(workspace, features, existingTaskActions) {
313
+ const negotiated = new Set(bookingSurfaceActionsForFeatures(workspace.surface, features));
314
+ if (existingTaskActions?.some((action) => !workspace.capabilities.allowedActions.includes(action)))
315
+ return null;
316
+ const allowedActions = existingTaskActions
317
+ ? [...existingTaskActions]
318
+ : workspace.capabilities.allowedActions.filter((action) => negotiated.has(action));
319
+ if (allowedActions.length === 0)
320
+ return null;
321
+ const { observableOrders, ...base } = workspace;
322
+ return {
323
+ ...base,
324
+ capabilities: { surface: workspace.surface, allowedActions },
325
+ ...(features.includes('trusted-order-observation-v1') && observableOrders !== undefined
326
+ ? { observableOrders: observableOrders.map((order) => ({ orderRef: order.orderRef, factRefs: [...order.factRefs] })) }
327
+ : {}),
328
+ };
329
+ }
330
+ function validIngressBinding(binding, ingress) {
331
+ if (!binding || typeof binding !== 'object' || Array.isArray(binding))
332
+ return false;
333
+ const candidate = binding;
334
+ if (!exactKeys(candidate, ['taskId', 'turnId', 'contextRef', 'surface', 'allowedActions'])
335
+ && !exactKeys(candidate, ['taskId', 'turnId', 'contextRef', 'surface', 'allowedActions', 'observableOrders']))
336
+ return false;
337
+ if (typeof candidate.taskId !== 'string' || typeof candidate.turnId !== 'string' || typeof candidate.contextRef !== 'string')
338
+ return false;
339
+ if (typeof candidate.surface !== 'string' || candidate.surface !== ingress.surfaceHint || !Object.prototype.hasOwnProperty.call(SURFACE_ACTION_MATRIX, candidate.surface))
340
+ return false;
341
+ return validSurfaceActions(candidate.surface, candidate.allowedActions)
342
+ && (candidate.observableOrders === undefined || validObservableOrders(candidate.observableOrders));
343
+ }
344
+ function validIngressApprovalAuthority(ingress, binding) {
345
+ if (!ingress.request.approval)
346
+ return true;
347
+ return ingress.request.approval.taskId === binding.taskId
348
+ && ingress.request.approval.contextRef === binding.contextRef;
349
+ }
350
+ function fallbackErrorEvent(task, code) {
351
+ return {
352
+ schemaVersion: BOOKING_SURFACE_SCHEMA_VERSION,
353
+ eventId: `error-${task.taskId}-${task.lastSequence + 1}`,
354
+ taskId: task.taskId,
355
+ contextRef: task.contextRef,
356
+ sequence: task.lastSequence + 1,
357
+ emittedAt: new Date().toISOString(),
358
+ kind: 'error',
359
+ error: { code: normalizeBookingErrorCode(code), message: safeBookingErrorMessage(normalizeBookingErrorCode(code)), retryable: false },
360
+ };
361
+ }
362
+ function writeTypedError(res, composition, task, error, requestKey, includeSubmitted = false) {
363
+ const code = normalizeBookingErrorCode(error);
364
+ // Provider/adapter exceptions can contain response bodies, URLs or request
365
+ // context. Preserve only the closed code and coarse thrown-value class.
366
+ console.error('[booking-copilot] planner boundary error:', JSON.stringify({
367
+ code,
368
+ thrownType: error instanceof Error ? 'error' : typeof error,
369
+ }));
370
+ try {
371
+ const decision = { kind: 'error', error: { code, message: safeBookingErrorMessage(code), retryable: false } };
372
+ if (requestKey) {
373
+ const events = composition.runtime.applyDecisionBatch(task.taskId, requestKey, [decision], includeSubmitted, { suppressProgressStatuses: true });
374
+ for (const event of events)
375
+ write(res, event);
376
+ return;
377
+ }
378
+ write(res, composition.runtime.emitEvent(task.taskId, decision));
379
+ }
380
+ catch {
381
+ // The stream is already committed. Emit a schema-valid, non-durable
382
+ // typed envelope instead of silently returning an empty HTTP 200 stream.
383
+ write(res, fallbackErrorEvent(task, code));
384
+ }
385
+ }
386
+ /** Handles one already-authenticated request inside the sole HTTP listener. */
387
+ async function handleBookingCopilotRequest(req, res, composition, maxBodyBytes, negotiatedFeatures) {
388
+ let turn;
389
+ let browserRequestKey;
390
+ let requestBinding;
391
+ let authorityFailureStatus = 403;
392
+ try {
393
+ const parsed = JSON.parse(await readBody(req, maxBodyBytes));
394
+ const valid = validateBookingSurface(parsed);
395
+ if (!valid.ok) {
396
+ sendJson(res, 400, { error: { code: 'invalid_booking_surface_turn', details: valid.errors } });
397
+ return;
398
+ }
399
+ if (parsed.kind === 'user.turn.ingress') {
400
+ const ingress = parsed;
401
+ browserRequestKey = ingress.requestKey;
402
+ if (composition.ingressMode !== 'bff-ingress-binding' || !composition.ingressBinding || !composition.principal) {
403
+ sendJson(res, 503, { error: { code: 'trusted_ingress_binding_required', mode: composition.ingressMode ?? 'bff-bound-turn-only', acceptedTurnKinds: [...BOOKING_COPILOT_ACCEPTED_TURN_KINDS] } });
404
+ return;
405
+ }
406
+ const binding = await composition.ingressBinding.bind(ingress, composition.principal);
407
+ if (!validIngressBinding(binding, ingress)) {
408
+ sendJson(res, 502, { error: { code: 'invalid_ingress_binding' } });
409
+ return;
410
+ }
411
+ if (!validIngressApprovalAuthority(ingress, binding)) {
412
+ sendJson(res, 400, { error: { code: 'invalid_ingress_approval_authority' } });
413
+ return;
414
+ }
415
+ const proposedBoundWorkspace = {
416
+ ...ingress.workspace,
417
+ contextRef: binding.contextRef,
418
+ surface: binding.surface,
419
+ capabilities: { surface: binding.surface, allowedActions: [...binding.allowedActions] },
420
+ ...(binding.observableOrders !== undefined ? { observableOrders: binding.observableOrders.map((order) => ({ orderRef: order.orderRef, factRefs: [...order.factRefs] })) } : {}),
421
+ };
422
+ const boundRequest = ingress.request.approval
423
+ ? { text: ingress.request.text, approval: { ...ingress.request.approval } }
424
+ : { text: ingress.request.text };
425
+ turn = { schemaVersion: BOOKING_SURFACE_SCHEMA_VERSION, kind: 'user.turn', taskId: binding.taskId, turnId: binding.turnId, workspace: proposedBoundWorkspace, request: boundRequest };
426
+ requestBinding = { requestKey: ingress.requestKey, principal: composition.principal, ...(ingress.taskHandle ? { taskHandle: ingress.taskHandle } : {}) };
427
+ authorityFailureStatus = 502;
428
+ const boundValid = validateBookingSurface(turn);
429
+ if (!boundValid.ok) {
430
+ sendJson(res, 502, { error: { code: 'invalid_ingress_binding', details: boundValid.errors } });
431
+ return;
432
+ }
433
+ }
434
+ else {
435
+ const boundTurn = parsed;
436
+ if (!validBoundWorkspaceAuthority(boundTurn.workspace)) {
437
+ sendJson(res, 403, { error: { code: 'invalid_bound_turn_authority' } });
438
+ return;
439
+ }
440
+ turn = boundTurn;
441
+ }
442
+ }
443
+ catch (error) {
444
+ const code = normalizeBookingErrorCode(error);
445
+ sendJson(res, code === 'PAYLOAD_TOO_LARGE' ? 413 : 400, { error: { code } });
446
+ return;
447
+ }
448
+ let task;
449
+ let fresh = false;
450
+ let replayKey;
451
+ let replayEvents = null;
452
+ let activeDecisionKey;
453
+ try {
454
+ const existingAtAdmission = turn.taskId ? composition.runtime.resumeTask(turn.taskId) : null;
455
+ const narrowedWorkspace = narrowWorkspaceOptionalAuthority(turn.workspace, negotiatedFeatures, existingAtAdmission?.allowedActions);
456
+ if (!narrowedWorkspace) {
457
+ const code = authorityFailureStatus === 502 ? 'invalid_ingress_binding' : 'invalid_bound_turn_authority';
458
+ sendJson(res, authorityFailureStatus, { error: { code } });
459
+ return;
460
+ }
461
+ turn = { ...turn, workspace: narrowedWorkspace };
462
+ fresh = turn.kind === 'action.receipt.continuation' ? false : existingAtAdmission === null;
463
+ if (turn.kind === 'action.receipt.continuation') {
464
+ replayKey = `receipt:${turn.receipt.actionId}:${composition.runtime.receiptDigest(turn.receipt)}`;
465
+ task = composition.runtime.continueWithReceipt(turn);
466
+ if (task.awaitingApproval) {
467
+ replayKey = composition.runtime.approvalPresentationRequestKey(turn.taskId);
468
+ }
469
+ replayEvents = composition.runtime.readDecisionBatch(turn.taskId, replayKey);
470
+ if (!replayEvents && task.phase === 'terminal')
471
+ replayEvents = composition.runtime.terminalDecisionBatch(turn.taskId, replayKey);
472
+ }
473
+ else {
474
+ const existing = existingAtAdmission;
475
+ if (requestBinding && turn.kind === 'user.turn')
476
+ composition.runtime.assertRequestBinding(browserRequestKey, turn, requestBinding);
477
+ const requestReplayKey = existing && browserRequestKey ? `ingress:${existing.taskId}:${browserRequestKey}` : undefined;
478
+ const directReplayKey = existing && turn.kind === 'user.turn' && turn.turnId ? `turn:${existing.taskId}:${turn.turnId}` : undefined;
479
+ const stableReplayKey = requestReplayKey ?? directReplayKey;
480
+ const stableReplay = stableReplayKey ? composition.runtime.readDecisionBatch(existing.taskId, stableReplayKey) : null;
481
+ if (stableReplay) {
482
+ if (existing && turn.kind === 'user.turn' && directReplayKey)
483
+ composition.runtime.assertTurnBinding(existing.taskId, turn);
484
+ task = existing;
485
+ replayKey = stableReplayKey;
486
+ replayEvents = stableReplay;
487
+ }
488
+ else if (existing?.phase === 'terminal' || existing?.phase === 'error') {
489
+ throw new Error('task_terminal');
490
+ }
491
+ else if (existing?.phase === 'waiting_receipt') {
492
+ if (turn.kind === 'user.turn' && turn.request.approval)
493
+ composition.runtime.assertApprovalNotConsumed(existing.taskId, turn.request.approval);
494
+ const workspace = turn.workspace;
495
+ if (workspace.contextRef !== existing.contextRef || workspace.surface !== existing.surface || workspace.revision !== existing.revision || workspace.capabilities.surface !== existing.surface || !sameCapabilities(workspace.capabilities.allowedActions, existing.allowedActions))
496
+ throw new Error('task_conflict:workspace_mismatch');
497
+ // A waiting task can only be replayed with the caller's stable turn
498
+ // identity. Without it, an identical sentence is not distinguishable
499
+ // from a new user turn and must not receive an old operation batch.
500
+ if (!turn.turnId || existing.lastTurnId !== turn.turnId)
501
+ throw new Error('receipt_required');
502
+ replayKey = browserRequestKey ? `ingress:${existing.taskId}:${browserRequestKey}` : `turn:${existing.taskId}:${existing.lastTurnId ?? existing.userTurnCount}`;
503
+ replayEvents = composition.runtime.readDecisionBatch(existing.taskId, replayKey);
504
+ if (!replayEvents)
505
+ throw new Error('receipt_required');
506
+ task = existing;
507
+ }
508
+ else {
509
+ if (existing && turn.kind === 'user.turn' && turn.request.approval)
510
+ composition.runtime.assertApprovalNotConsumed(existing.taskId, turn.request.approval);
511
+ task = composition.runtime.startTask(turn, requestBinding);
512
+ if (turn.kind === 'user.turn' && turn.taskId) {
513
+ replayKey = browserRequestKey ? `ingress:${task.taskId}:${browserRequestKey}` : `turn:${task.taskId}:${turn.turnId ?? task.userTurnCount}`;
514
+ replayEvents = composition.runtime.readDecisionBatch(task.taskId, replayKey);
515
+ }
516
+ }
517
+ }
518
+ }
519
+ catch (error) {
520
+ const code = normalizeBookingErrorCode(error);
521
+ console.error(JSON.stringify({ code, reason: extractBookingDispatchReason(error) }));
522
+ sendJson(res, 409, { error: { code } });
523
+ return;
524
+ }
525
+ res.writeHead(200, { 'content-type': 'text/event-stream; charset=utf-8', 'cache-control': 'no-cache, no-transform', connection: 'keep-alive', 'x-content-type-options': 'nosniff', [BOOKING_SURFACE_VERSION_HEADER]: BOOKING_SURFACE_SCHEMA_VERSION, [BOOKING_SURFACE_SCHEMA_SHA256_HEADER]: BOOKING_SURFACE_SCHEMA_SHA256 });
526
+ res.flushHeaders();
527
+ try {
528
+ if (replayEvents) {
529
+ const progressEvents = composition.runtime.readProgressBatch(task.taskId, replayKey ?? `turn:${task.taskId}:${task.userTurnCount}`) ?? [];
530
+ for (const event of progressEvents)
531
+ write(res, event);
532
+ if (progressEvents.length) {
533
+ flush(res);
534
+ await new Promise((resolve) => setImmediate(resolve));
535
+ }
536
+ if (task.phase === 'terminal' || task.phase === 'error')
537
+ releasePlannerSession(composition, task.taskId);
538
+ for (const event of replayEvents)
539
+ write(res, event);
540
+ }
541
+ else {
542
+ const requestKey = replayKey ?? `turn:${task.taskId}:${task.userTurnCount}`;
543
+ activeDecisionKey = requestKey;
544
+ // A concurrent identical request may arrive after the first request has
545
+ // committed progress but before its planner flight finishes. Re-serve
546
+ // the durable progress batch so it receives the same prefix instead of
547
+ // exposing a terminal-only tail.
548
+ const newlyPersistedProgress = composition.runtime.ensureProgressBatch(task.taskId, requestKey, fresh);
549
+ const progressEvents = newlyPersistedProgress.length > 0
550
+ ? newlyPersistedProgress
551
+ : (composition.runtime.readProgressBatch(task.taskId, requestKey) ?? []);
552
+ const approval = turn.kind === 'user.turn' ? turn.request.approval : undefined;
553
+ const flightKey = JSON.stringify([task.taskId, approval ? `approval:${bookingDigest(approval)}` : requestKey]);
554
+ // Register or join the decision flight before yielding after the progress
555
+ // flush. A duplicate that observed the durable progress must never miss
556
+ // an about-to-finish flight and pay for a second model invocation.
557
+ const decisionOutcome = runDecisionSingleFlight(composition, flightKey, async () => {
558
+ // Cover the complementary late-join case: the prior flight may have
559
+ // committed its exact batch and left the in-memory map between request
560
+ // admission and this work starting.
561
+ const durableDecision = composition.runtime.readDecisionBatch(task.taskId, requestKey);
562
+ if (durableDecision)
563
+ return durableDecision;
564
+ if (turn.kind === 'action.receipt.continuation' && task.awaitingApproval) {
565
+ return composition.runtime.applyDecisionBatch(task.taskId, requestKey, [composition.runtime.approvalQuestion(task.taskId)], false);
566
+ }
567
+ const sessions = sessionsFor(composition);
568
+ const session = sessions.get(task.taskId) ?? composition.plannerFactory(task);
569
+ sessions.set(task.taskId, session);
570
+ const decisions = await session.next({ turn, task: composition.runtime.resumeTask(task.taskId) ?? task });
571
+ const plannerDecisions = decisions.length ? decisions : [{ kind: 'error', error: { code: 'PLANNER_NO_DECISION', message: 'Planner returned no typed decision.', retryable: false } }];
572
+ const events = composition.runtime.applyDecisionBatch(task.taskId, requestKey, plannerDecisions, fresh, { suppressProgressStatuses: true });
573
+ const updated = composition.runtime.resumeTask(task.taskId);
574
+ if (updated?.phase === 'terminal' || updated?.phase === 'error')
575
+ releasePlannerSession(composition, task.taskId);
576
+ return events;
577
+ }).then((events) => ({ ok: true, events }), (error) => ({ ok: false, error }));
578
+ for (const event of progressEvents)
579
+ write(res, event);
580
+ if (progressEvents.length) {
581
+ flush(res);
582
+ await new Promise((resolve) => setImmediate(resolve));
583
+ }
584
+ const outcome = await decisionOutcome;
585
+ if (!outcome.ok)
586
+ throw outcome.error;
587
+ for (const event of outcome.events)
588
+ write(res, event);
589
+ }
590
+ }
591
+ catch (error) {
592
+ writeTypedError(res, composition, task, error, activeDecisionKey, fresh);
593
+ const updated = composition.runtime.resumeTask(task.taskId);
594
+ if (updated?.phase === 'terminal' || updated?.phase === 'error')
595
+ releasePlannerSession(composition, task.taskId);
596
+ }
597
+ finally {
598
+ res.end();
599
+ }
600
+ }
601
+ export function startBookingCopilotServer(options) {
602
+ if (!options.apiKey)
603
+ return Promise.reject(new Error('booking_copilot_api_key_required'));
604
+ if (options.artifactId !== undefined && !/^[0-9a-f]{40}$/.test(options.artifactId)) {
605
+ return Promise.reject(new Error('booking_copilot_artifact_id_invalid'));
606
+ }
607
+ if (!options.runtime || !options.plannerFactory) {
608
+ return Promise.reject(new Error('booking_copilot_runtime_and_planner_required'));
609
+ }
610
+ const hasIngressBinding = Boolean(options.ingressBinding);
611
+ const hasPrincipal = Boolean(options.principal);
612
+ if (hasIngressBinding !== hasPrincipal) {
613
+ return Promise.reject(new Error('booking_copilot_ingress_binding_pair_required'));
614
+ }
615
+ if (hasIngressBinding && (typeof options.ingressBinding?.bind !== 'function'
616
+ || typeof options.principal?.subject !== 'string' || options.principal.subject.length === 0 || options.principal.subject.length > 256
617
+ || typeof options.principal?.scope !== 'string' || options.principal.scope.length === 0 || options.principal.scope.length > 256)) {
618
+ return Promise.reject(new Error('booking_copilot_ingress_binding_invalid'));
619
+ }
620
+ const ingressMode = options.ingressMode ?? (hasIngressBinding ? 'bff-ingress-binding' : 'bff-bound-turn-only');
621
+ if (!BOOKING_COPILOT_INGRESS_MODES.includes(ingressMode)) {
622
+ return Promise.reject(new Error('booking_copilot_ingress_mode_invalid'));
623
+ }
624
+ if (ingressMode === 'bff-ingress-binding' && !hasIngressBinding) {
625
+ return Promise.reject(new Error('booking_copilot_ingress_binding_required'));
626
+ }
627
+ if (ingressMode === 'bff-bound-turn-only' && hasIngressBinding) {
628
+ return Promise.reject(new Error('booking_copilot_ingress_mode_conflict'));
629
+ }
630
+ const composition = {
631
+ runtime: options.runtime,
632
+ plannerFactory: options.plannerFactory,
633
+ ...(hasIngressBinding ? { ingressBinding: options.ingressBinding, principal: options.principal } : {}),
634
+ ingressMode,
635
+ };
636
+ const maxBodyBytes = options.maxBodyBytes ?? 1_000_000;
637
+ const runningIdentity = runtimeIdentity();
638
+ const server = createServer((req, res) => {
639
+ void bookingCopilotTrafficHandler({
640
+ apiKey: options.apiKey,
641
+ composition,
642
+ maxBodyBytes,
643
+ ...(options.artifactId !== undefined ? { artifactId: options.artifactId } : {}),
644
+ ingressMode,
645
+ runningIdentity,
646
+ })(req, res);
647
+ });
648
+ let closePromise;
649
+ return new Promise((resolve, reject) => {
650
+ server.once('error', reject);
651
+ server.listen(options.port ?? 0, options.host ?? '127.0.0.1', () => {
652
+ server.off('error', reject);
653
+ const address = server.address();
654
+ const port = typeof address === 'object' && address ? address.port : (options.port ?? 0);
655
+ resolve({
656
+ server,
657
+ port,
658
+ close: () => {
659
+ if (closePromise)
660
+ return closePromise;
661
+ const trafficShutdown = shutdownBookingCopilotTraffic(composition);
662
+ closePromise = (async () => {
663
+ const listenerShutdown = new Promise((done, fail) => server.close((error) => error ? fail(error) : done()));
664
+ const results = await Promise.allSettled([listenerShutdown, trafficShutdown]);
665
+ const failures = results.flatMap((result) => result.status === 'rejected' ? [result.reason] : []);
666
+ if (failures.length > 0)
667
+ throw new AggregateError(failures, 'booking_copilot_server_close_failed');
668
+ })();
669
+ return closePromise;
670
+ },
671
+ });
672
+ });
673
+ });
674
+ }