@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24

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 (327) hide show
  1. package/README.md +102 -194
  2. package/README.zh-CN.md +115 -210
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-bootstrap.js +466 -35
  5. package/bin/gotry-inner.js +255 -36
  6. package/bin/gotry-process-liveness.d.ts +27 -0
  7. package/bin/gotry-process-liveness.js +97 -0
  8. package/bin/gotry-runtime-resolution.d.ts +1 -1
  9. package/bin/gotry-runtime-resolution.js +2 -2
  10. package/client/client.js +174 -0
  11. package/cordis.gotry-patch.yml +70 -93
  12. package/dist/capabilities/agent-reach-deep.js +45 -122
  13. package/dist/capabilities/agent-reach.js +63 -148
  14. package/dist/capabilities/anything.js +89 -75
  15. package/dist/capabilities/artifacts.js +123 -104
  16. package/dist/capabilities/channel-health.js +65 -45
  17. package/dist/capabilities/channel-registry.js +108 -87
  18. package/dist/capabilities/doctor.js +357 -235
  19. package/dist/capabilities/effect.js +223 -309
  20. package/dist/capabilities/fact-log.js +28 -14
  21. package/dist/capabilities/flyai.js +245 -156
  22. package/dist/capabilities/ground-transfer.js +509 -0
  23. package/dist/capabilities/hbcli.js +144 -148
  24. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  25. package/dist/capabilities/incident-log.js +131 -61
  26. package/dist/capabilities/model-override.js +30 -10
  27. package/dist/capabilities/opensky.js +54 -43
  28. package/dist/capabilities/resilience.js +50 -32
  29. package/dist/capabilities/session/action-cache.js +50 -44
  30. package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
  31. package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
  32. package/dist/capabilities/session/adapters/dida-portal.js +206 -0
  33. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  34. package/dist/capabilities/session/adapters/rail-12306.js +169 -112
  35. package/dist/capabilities/session/benchmark.js +85 -149
  36. package/dist/capabilities/session/extension-bridge.js +320 -259
  37. package/dist/capabilities/session/extension-channel.js +57 -76
  38. package/dist/capabilities/session/extension-distribution.js +115 -119
  39. package/dist/capabilities/session/extract.js +31 -21
  40. package/dist/capabilities/session/golden-score.js +55 -33
  41. package/dist/capabilities/session/health-watch.js +146 -154
  42. package/dist/capabilities/session/read-guard.js +55 -47
  43. package/dist/capabilities/session/static-flight-golden.js +49 -36
  44. package/dist/capabilities/session/transport.js +54 -61
  45. package/dist/capabilities/session/wizard.js +63 -93
  46. package/dist/capabilities/session-consent.js +143 -47
  47. package/dist/capabilities/session-login.js +95 -126
  48. package/dist/capabilities/session-search.js +391 -253
  49. package/dist/capabilities/sponsor-plugin.js +341 -0
  50. package/dist/capabilities/visa-policy.js +87 -73
  51. package/dist/capabilities/weather.js +163 -235
  52. package/dist/scripts/action-cache-tests.js +24 -59
  53. package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
  54. package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
  55. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  56. package/dist/scripts/agent-reach-tests.js +26 -18
  57. package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
  58. package/dist/scripts/anything-tests.js +58 -62
  59. package/dist/scripts/artifact-client-contract-tests.js +69 -0
  60. package/dist/scripts/artifacts-capability-tests.js +235 -0
  61. package/dist/scripts/async-collect.js +26 -15
  62. package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
  63. package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
  64. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
  65. package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
  66. package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
  67. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
  68. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
  69. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
  70. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
  71. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
  72. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
  73. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
  74. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
  75. package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
  76. package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
  77. package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
  78. package/dist/scripts/booking-executor-tests.js +80 -0
  79. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  80. package/dist/scripts/booking-saga-tests.js +61 -123
  81. package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
  82. package/dist/scripts/bootstrap-tests.js +1383 -128
  83. package/dist/scripts/build-changelog.js +89 -130
  84. package/dist/scripts/build-metrics-report.js +135 -143
  85. package/dist/scripts/changelog-tests.js +35 -82
  86. package/dist/scripts/channel-probe-tests.js +44 -116
  87. package/dist/scripts/channel-probe.js +84 -167
  88. package/dist/scripts/channel-registry-tests.js +97 -185
  89. package/dist/scripts/companion-tests.js +25 -70
  90. package/dist/scripts/dida-cookie-probe.js +87 -0
  91. package/dist/scripts/dida-sw-probe.js +114 -0
  92. package/dist/scripts/diff-test.js +17 -12
  93. package/dist/scripts/doctor-tests.js +428 -120
  94. package/dist/scripts/dsh-artifact-e2e.js +150 -0
  95. package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
  96. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  97. package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
  98. package/dist/scripts/dsh-runtime-closure.js +75 -63
  99. package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
  100. package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
  101. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  102. package/dist/scripts/effect-tests.js +165 -552
  103. package/dist/scripts/engine-run.js +1 -3
  104. package/dist/scripts/engine-tests.js +16 -10
  105. package/dist/scripts/evaluation-cadence-tests.js +69 -242
  106. package/dist/scripts/evaluation-contract-tests.js +193 -499
  107. package/dist/scripts/extension-distribution-cli.js +19 -15
  108. package/dist/scripts/extension-distribution-tests.js +98 -211
  109. package/dist/scripts/extension-tests.js +366 -389
  110. package/dist/scripts/fact-gate-tests.js +725 -263
  111. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  112. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  113. package/dist/scripts/flight-malformed-tests.js +159 -0
  114. package/dist/scripts/flyai-tests.js +325 -68
  115. package/dist/scripts/g5-guard-tests.js +131 -0
  116. package/dist/scripts/g5-guard.js +189 -0
  117. package/dist/scripts/gen-lunar-anchors.js +106 -0
  118. package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
  119. package/dist/scripts/ground-transfer-tests.js +956 -0
  120. package/dist/scripts/hbcli-e2e-tests.js +71 -90
  121. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  122. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  123. package/dist/scripts/hbcli-tests.js +41 -115
  124. package/dist/scripts/health-watch-cli.js +42 -28
  125. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  126. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  127. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  128. package/dist/scripts/i18n-tests.js +16 -9
  129. package/dist/scripts/incident-tests.js +281 -87
  130. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  131. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  132. package/dist/scripts/issue-271-liveness-tests.js +547 -0
  133. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  134. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  135. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  136. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  137. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  138. package/dist/scripts/journey-tests.js +22 -27
  139. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  140. package/dist/scripts/kernel-manifest-gate.js +126 -0
  141. package/dist/scripts/kernel-manifest-tests.js +212 -0
  142. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  143. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  144. package/dist/scripts/kernel-manifest.js +369 -0
  145. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  146. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  147. package/dist/scripts/ledger-tests.js +325 -340
  148. package/dist/scripts/ledger-workflow-crash.js +10 -20
  149. package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
  150. package/dist/scripts/memory-capture-tests.js +242 -61
  151. package/dist/scripts/memory-decay-tests.js +18 -52
  152. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  153. package/dist/scripts/memory-lifecycle.js +196 -0
  154. package/dist/scripts/memory-metrics.js +24 -12
  155. package/dist/scripts/memory-value-report.js +474 -222
  156. package/dist/scripts/memory-value-tests.js +328 -0
  157. package/dist/scripts/metrics-report-tests.js +62 -199
  158. package/dist/scripts/model-override-e2e.js +66 -105
  159. package/dist/scripts/nightly-evidence-tests.js +64 -89
  160. package/dist/scripts/nightly-evidence.js +108 -111
  161. package/dist/scripts/nudge-digest.js +39 -28
  162. package/dist/scripts/onboarding-tests.js +63 -88
  163. package/dist/scripts/opensky-check.js +18 -13
  164. package/dist/scripts/opensky-tests.js +28 -28
  165. package/dist/scripts/persona-surface-guard-tests.js +37 -12
  166. package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
  167. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  168. package/dist/scripts/price-drift-tests.js +126 -227
  169. package/dist/scripts/price-drift-watch.js +209 -253
  170. package/dist/scripts/probe-poi-tests.js +52 -45
  171. package/dist/scripts/product-metrics.js +187 -273
  172. package/dist/scripts/publish-preverify.js +47 -41
  173. package/dist/scripts/realtime-pricing-tests.js +37 -76
  174. package/dist/scripts/replay-async.js +20 -18
  175. package/dist/scripts/replay-real.js +16 -18
  176. package/dist/scripts/replay.js +52 -82
  177. package/dist/scripts/session-attach-diagnose.js +16 -12
  178. package/dist/scripts/session-attach-poc.js +34 -37
  179. package/dist/scripts/session-benchmark.js +66 -147
  180. package/dist/scripts/session-dida-live-e2e.js +151 -0
  181. package/dist/scripts/session-extract-tests.js +14 -35
  182. package/dist/scripts/session-login.js +14 -13
  183. package/dist/scripts/session-tests.js +545 -583
  184. package/dist/scripts/sf-live-benchmark.js +92 -110
  185. package/dist/scripts/sf-live-cli-tests.js +2 -9
  186. package/dist/scripts/sf-soft-score-tests.js +19 -60
  187. package/dist/scripts/sf-summary-tests.js +149 -0
  188. package/dist/scripts/sf-summary.js +466 -84
  189. package/dist/scripts/skeleton-check.js +16 -14
  190. package/dist/scripts/skeleton-integration-test.js +11 -10
  191. package/dist/scripts/skills-contract-tests.js +51 -46
  192. package/dist/scripts/smoke-session-gate-tests.js +12 -20
  193. package/dist/scripts/smoke.js +541 -422
  194. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  195. package/dist/scripts/state-cli-tests.js +226 -101
  196. package/dist/scripts/state-cli.js +549 -212
  197. package/dist/scripts/static-golden-tests.js +46 -180
  198. package/dist/scripts/time-eval-tests.js +210 -192
  199. package/dist/scripts/travel-timeline-tests.js +29 -93
  200. package/dist/scripts/turn-handoff-collect-tests.js +46 -93
  201. package/dist/scripts/turn-handoff-collect.js +71 -99
  202. package/dist/scripts/turn-policy-tests.js +28 -29
  203. package/dist/scripts/typed-contract-canary.js +77 -162
  204. package/dist/scripts/tz-resolver-sanity.js +50 -0
  205. package/dist/scripts/tz-resolver-tests.js +584 -0
  206. package/dist/scripts/unified-tests.js +23 -19
  207. package/dist/scripts/visa-policy-tests.js +31 -44
  208. package/dist/scripts/weather-tests.js +121 -543
  209. package/dist/scripts/wish-channel-gate-tests.js +29 -86
  210. package/dist/scripts/write-gate-tests.js +522 -0
  211. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  212. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  213. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  214. package/dist/scripts/z3-race-tests.js +18 -12
  215. package/dist/src/artifact-gate.js +652 -221
  216. package/dist/src/backend/kernel.js +91 -0
  217. package/dist/src/backend/modules/booking-copilot.js +55 -0
  218. package/dist/src/backend/modules/booking-executor.js +124 -0
  219. package/dist/src/backend/modules/session-search.js +194 -0
  220. package/dist/src/benchmark-agent-conformance.js +296 -262
  221. package/dist/src/benchmark-environment-bridge.js +298 -413
  222. package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
  223. package/dist/src/benchmark-tool-isolation.js +94 -67
  224. package/dist/src/bookable-facts.js +299 -174
  225. package/dist/src/booking-saga.js +64 -120
  226. package/dist/src/booking-surface/availability-policy.js +443 -576
  227. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  228. package/dist/src/booking-surface/contracts.js +24 -80
  229. package/dist/src/booking-surface/dsh-planner.js +510 -281
  230. package/dist/src/booking-surface/dsh-plugin.js +17 -1
  231. package/dist/src/booking-surface/error-codes.js +82 -10
  232. package/dist/src/booking-surface/index.js +0 -3
  233. package/dist/src/booking-surface/profile.js +10 -54
  234. package/dist/src/booking-surface/recovery-chain.js +367 -0
  235. package/dist/src/booking-surface/runtime.js +765 -891
  236. package/dist/src/booking-surface/server.js +200 -251
  237. package/dist/src/booking-surface/startup.js +58 -54
  238. package/dist/src/booking-surface/validation.js +150 -218
  239. package/dist/src/bridge.js +20 -13
  240. package/dist/src/companions.js +48 -74
  241. package/dist/src/contracts.js +21 -43
  242. package/dist/src/dsh-llm.js +82 -120
  243. package/dist/src/engine.js +253 -239
  244. package/dist/src/evaluation-cadence.js +72 -209
  245. package/dist/src/evaluation-contracts.js +220 -843
  246. package/dist/src/flight-pack-adapter.js +54 -0
  247. package/dist/src/flight-pack-contract.js +2 -0
  248. package/dist/src/gotry-backend.js +70 -0
  249. package/dist/src/hotel-date-gate.js +118 -0
  250. package/dist/src/i18n.js +18 -7
  251. package/dist/src/index.js +1425 -1629
  252. package/dist/src/journey.js +132 -81
  253. package/dist/src/ledger-repair-apply.js +538 -0
  254. package/dist/src/ledger-repair-plan.js +463 -0
  255. package/dist/src/loop.js +257 -166
  256. package/dist/src/memory-capture.js +180 -32
  257. package/dist/src/memory-decay.js +30 -14
  258. package/dist/src/memory-lifecycle.js +1310 -0
  259. package/dist/src/memory-utility.js +27 -40
  260. package/dist/src/mock-llm.js +36 -61
  261. package/dist/src/model.js +81 -36
  262. package/dist/src/realtime-pricing.js +63 -72
  263. package/dist/src/slot-spec.js +84 -106
  264. package/dist/src/state-ledger.js +310 -471
  265. package/dist/src/subagent-job-id-guard.js +58 -0
  266. package/dist/src/time-anchor.js +231 -33
  267. package/dist/src/tool-packet.js +20 -8
  268. package/dist/src/travel-slots.js +58 -35
  269. package/dist/src/travel-timeline.js +44 -63
  270. package/dist/src/turn-deadline.js +121 -127
  271. package/dist/src/turn-policy.js +61 -89
  272. package/dist/src/tz-resolver.js +143 -0
  273. package/dist/src/unified.js +504 -381
  274. package/dist/src/wish-pool.js +39 -14
  275. package/dist/src/write-gate.js +495 -0
  276. package/dist/src/z3-shared.js +273 -7
  277. package/extension/README.md +4 -3
  278. package/extension/background.js +125 -14
  279. package/extension/content-bridge.js +23 -1
  280. package/extension/content-main.js +15 -4
  281. package/extension/manifest.json +16 -8
  282. package/package.json +251 -220
  283. package/ts/capabilities/agent-reach.ts +4 -4
  284. package/ts/capabilities/anything.ts +1 -1
  285. package/ts/capabilities/artifacts.ts +46 -9
  286. package/ts/capabilities/flyai.ts +156 -37
  287. package/ts/capabilities/hbcli.ts +1 -1
  288. package/ts/capabilities/incident-log.ts +88 -59
  289. package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
  290. package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
  291. package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
  292. package/ts/capabilities/session/extension-bridge.ts +226 -101
  293. package/ts/capabilities/session/extension-channel.ts +22 -9
  294. package/ts/capabilities/session/wizard.ts +2 -2
  295. package/ts/capabilities/session-consent.ts +98 -14
  296. package/ts/capabilities/session-login.ts +1 -0
  297. package/ts/capabilities/session-search.ts +315 -31
  298. package/ts/dsh-runtime/vendor/README.md +8 -4
  299. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
  300. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
  301. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
  302. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
  303. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
  304. package/ts/package.json +236 -16
  305. package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
  306. package/ts/scripts/state-cli.ts +418 -51
  307. package/ts/src/artifact-gate.ts +561 -15
  308. package/ts/src/benchmark-environment-bridge.ts +37 -20
  309. package/ts/src/bookable-facts.ts +183 -7
  310. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  311. package/ts/src/booking-surface/dsh-planner.ts +240 -32
  312. package/ts/src/booking-surface/dsh-plugin.js +17 -1
  313. package/ts/src/booking-surface/error-codes.ts +75 -0
  314. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  315. package/ts/src/booking-surface/server.ts +84 -51
  316. package/ts/src/booking-surface/validation.ts +2 -0
  317. package/ts/src/contracts.ts +12 -2
  318. package/ts/src/dsh-llm.ts +4 -8
  319. package/ts/src/index.ts +609 -90
  320. package/ts/src/loop.ts +116 -7
  321. package/ts/src/memory-capture.ts +227 -3
  322. package/ts/src/mock-llm.ts +4 -8
  323. package/ts/src/model.ts +88 -2
  324. package/ts/src/state-ledger.ts +47 -29
  325. package/ts/src/subagent-job-id-guard.ts +72 -0
  326. package/ts/src/time-anchor.ts +217 -13
  327. package/ts/src/unified.ts +316 -54
@@ -1,9 +1,74 @@
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
+ */
1
8
  import { timingSafeEqual } from 'node:crypto';
2
9
  import { createServer } from 'node:http';
3
- import { BOOKING_COPILOT_ACCEPTED_TURN_KINDS, BOOKING_COPILOT_INGRESS_MODES, BOOKING_READ_ACTION_KINDS, BOOKING_SURFACE_ALLOWED_ACTIONS, BOOKING_SURFACE_SCHEMA_SHA256, BOOKING_SURFACE_SCHEMA_SHA256_HEADER, BOOKING_SURFACE_SCHEMA_VERSION, BOOKING_SURFACE_VERSION_HEADER } from './contracts.js';
4
- import { bookingDigest, BookingCopilotTaskRuntime } from './runtime.js';
10
+ import { BOOKING_COPILOT_ACCEPTED_TURN_KINDS, BOOKING_COPILOT_INGRESS_MODES, BOOKING_READ_ACTION_KINDS, BOOKING_SURFACE_ALLOWED_ACTIONS, BOOKING_SURFACE_SCHEMA_SHA256, BOOKING_SURFACE_SCHEMA_SHA256_HEADER, BOOKING_SURFACE_SCHEMA_VERSION, BOOKING_SURFACE_VERSION_HEADER, } from './contracts.js';
11
+ import { bookingDigest, } from './runtime.js';
5
12
  import { validateBookingSurface } from './validation.js';
6
- import { normalizeBookingErrorCode, safeBookingErrorMessage } from './error-codes.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 (isProbe) {
33
+ res.setHeader(BOOKING_SURFACE_VERSION_HEADER, BOOKING_SURFACE_SCHEMA_VERSION);
34
+ res.setHeader(BOOKING_SURFACE_SCHEMA_SHA256_HEADER, BOOKING_SURFACE_SCHEMA_SHA256);
35
+ if (artifactId)
36
+ res.setHeader('X-GoTry-Artifact-ID', artifactId);
37
+ res.setHeader('X-GoTry-Node-Version', runningIdentity.nodeVersion);
38
+ res.setHeader('X-GoTry-Node-Modules-ABI', runningIdentity.nodeModulesAbi);
39
+ res.setHeader('X-GoTry-Release-Tuple', runningIdentity.releaseTuple);
40
+ if (runningIdentity.glibcVersion)
41
+ res.setHeader('X-GoTry-Glibc-Version', runningIdentity.glibcVersion);
42
+ const acceptedTurnKinds = ingressMode === 'bff-ingress-binding'
43
+ ? [...BOOKING_COPILOT_ACCEPTED_TURN_KINDS, 'user.turn.ingress']
44
+ : [...BOOKING_COPILOT_ACCEPTED_TURN_KINDS];
45
+ const healthBody = {
46
+ schemaVersion: BOOKING_SURFACE_SCHEMA_VERSION,
47
+ schemaSha256: BOOKING_SURFACE_SCHEMA_SHA256,
48
+ status: 'ready',
49
+ ingressMode,
50
+ acceptedTurnKinds,
51
+ };
52
+ res.setHeader('X-GoTry-Ingress-Mode', ingressMode);
53
+ res.setHeader('X-GoTry-Accepted-Turn-Kinds', acceptedTurnKinds.join(','));
54
+ sendJson(res, 200, healthBody);
55
+ return;
56
+ }
57
+ const schemaVersion = String(req.headers[BOOKING_SURFACE_VERSION_HEADER] ?? '');
58
+ const schemaHash = String(req.headers[BOOKING_SURFACE_SCHEMA_SHA256_HEADER] ?? '');
59
+ if (schemaVersion !== BOOKING_SURFACE_SCHEMA_VERSION || schemaHash !== BOOKING_SURFACE_SCHEMA_SHA256) {
60
+ sendJson(res, 409, {
61
+ error: {
62
+ code: 'booking_surface_schema_mismatch',
63
+ expectedVersion: BOOKING_SURFACE_SCHEMA_VERSION,
64
+ expectedSchemaSha256: BOOKING_SURFACE_SCHEMA_SHA256,
65
+ },
66
+ });
67
+ return;
68
+ }
69
+ await handleBookingCopilotRequest(req, res, composition, maxBodyBytes);
70
+ };
71
+ }
7
72
  const sessionsByComposition = new WeakMap();
8
73
  const decisionFlightsByComposition = new WeakMap();
9
74
  function sessionsFor(composition) {
@@ -25,16 +90,19 @@ function decisionFlightsFor(composition) {
25
90
  async function runDecisionSingleFlight(composition, key, work) {
26
91
  const flights = decisionFlightsFor(composition);
27
92
  const prior = flights.get(key);
28
- if (prior) return prior;
93
+ if (prior)
94
+ return prior;
29
95
  const current = work();
30
96
  flights.set(key, current);
31
97
  try {
32
98
  return await current;
33
- } finally{
34
- if (flights.get(key) === current) flights.delete(key);
99
+ }
100
+ finally {
101
+ if (flights.get(key) === current)
102
+ flights.delete(key);
35
103
  }
36
104
  }
37
- function runtimeIdentity() {
105
+ export function runtimeIdentity() {
38
106
  const report = process.report?.getReport?.();
39
107
  const header = report?.header;
40
108
  const glibcVersion = header?.glibcVersionRuntime ?? '';
@@ -42,7 +110,7 @@ function runtimeIdentity() {
42
110
  nodeVersion: process.version,
43
111
  nodeModulesAbi: process.versions.modules ?? '',
44
112
  releaseTuple: `${process.platform}-${process.arch}-${glibcVersion ? 'glibc' : 'unknown'}`,
45
- glibcVersion
113
+ glibcVersion,
46
114
  };
47
115
  }
48
116
  function safeSecretEqual(actual, expected) {
@@ -51,17 +119,15 @@ function safeSecretEqual(actual, expected) {
51
119
  return actualBytes.length === expectedBytes.length && timingSafeEqual(actualBytes, expectedBytes);
52
120
  }
53
121
  function sendJson(res, status, body) {
54
- res.writeHead(status, {
55
- 'content-type': 'application/json; charset=utf-8'
56
- });
122
+ res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' });
57
123
  res.end(JSON.stringify(body));
58
124
  }
59
125
  function readBody(req, maxBodyBytes) {
60
- return new Promise((resolve, reject)=>{
126
+ return new Promise((resolve, reject) => {
61
127
  const chunks = [];
62
128
  let size = 0;
63
129
  let exceeded = false;
64
- req.on('data', (chunk)=>{
130
+ req.on('data', (chunk) => {
65
131
  size += chunk.length;
66
132
  if (size > maxBodyBytes) {
67
133
  exceeded = true;
@@ -69,7 +135,7 @@ function readBody(req, maxBodyBytes) {
69
135
  }
70
136
  chunks.push(chunk);
71
137
  });
72
- req.on('end', ()=>exceeded ? reject(new Error('payload_too_large')) : resolve(Buffer.concat(chunks).toString('utf8')));
138
+ req.on('end', () => exceeded ? reject(new Error('payload_too_large')) : resolve(Buffer.concat(chunks).toString('utf8')));
73
139
  req.on('error', reject);
74
140
  });
75
141
  }
@@ -77,42 +143,43 @@ function write(res, event) {
77
143
  res.write(`event: ${event.kind}\ndata: ${JSON.stringify(event)}\n\n`);
78
144
  }
79
145
  function sameCapabilities(a, b) {
80
- return a.length === b.length && [
81
- ...a
82
- ].sort().every((kind, index)=>kind === [
83
- ...b
84
- ].sort()[index]);
146
+ return a.length === b.length && [...a].sort().every((kind, index) => kind === [...b].sort()[index]);
85
147
  }
86
148
  function exactKeys(value, allowed) {
87
- return Object.keys(value).length === allowed.length && Object.keys(value).every((key)=>allowed.includes(key));
149
+ return Object.keys(value).length === allowed.length && Object.keys(value).every((key) => allowed.includes(key));
88
150
  }
89
151
  const SURFACE_ACTION_MATRIX = BOOKING_SURFACE_ALLOWED_ACTIONS;
90
152
  function validSurfaceActions(surface, allowed) {
91
- if (typeof surface !== 'string' || !Object.prototype.hasOwnProperty.call(SURFACE_ACTION_MATRIX, surface)) return false;
92
- if (!Array.isArray(allowed) || allowed.length < 1 || new Set(allowed).size !== allowed.length) return false;
153
+ if (typeof surface !== 'string' || !Object.prototype.hasOwnProperty.call(SURFACE_ACTION_MATRIX, surface))
154
+ return false;
155
+ if (!Array.isArray(allowed) || allowed.length < 1 || new Set(allowed).size !== allowed.length)
156
+ return false;
93
157
  const matrix = SURFACE_ACTION_MATRIX[surface];
94
- return allowed.every((action)=>typeof action === 'string' && matrix.includes(action) && BOOKING_READ_ACTION_KINDS.includes(action));
158
+ return allowed.every((action) => typeof action === 'string'
159
+ && matrix.includes(action)
160
+ && BOOKING_READ_ACTION_KINDS.includes(action));
95
161
  }
96
162
  function validBoundWorkspaceAuthority(workspace) {
97
- return workspace.capabilities.surface === workspace.surface && validSurfaceActions(workspace.surface, workspace.capabilities.allowedActions);
163
+ return workspace.capabilities.surface === workspace.surface
164
+ && validSurfaceActions(workspace.surface, workspace.capabilities.allowedActions);
98
165
  }
99
166
  function validIngressBinding(binding, ingress) {
100
- if (!binding || typeof binding !== 'object' || Array.isArray(binding)) return false;
167
+ if (!binding || typeof binding !== 'object' || Array.isArray(binding))
168
+ return false;
101
169
  const candidate = binding;
102
- if (!exactKeys(candidate, [
103
- 'taskId',
104
- 'turnId',
105
- 'contextRef',
106
- 'surface',
107
- 'allowedActions'
108
- ])) return false;
109
- if (typeof candidate.taskId !== 'string' || typeof candidate.turnId !== 'string' || typeof candidate.contextRef !== 'string') return false;
110
- if (typeof candidate.surface !== 'string' || candidate.surface !== ingress.surfaceHint || !Object.prototype.hasOwnProperty.call(SURFACE_ACTION_MATRIX, candidate.surface)) return false;
170
+ if (!exactKeys(candidate, ['taskId', 'turnId', 'contextRef', 'surface', 'allowedActions']))
171
+ return false;
172
+ if (typeof candidate.taskId !== 'string' || typeof candidate.turnId !== 'string' || typeof candidate.contextRef !== 'string')
173
+ return false;
174
+ if (typeof candidate.surface !== 'string' || candidate.surface !== ingress.surfaceHint || !Object.prototype.hasOwnProperty.call(SURFACE_ACTION_MATRIX, candidate.surface))
175
+ return false;
111
176
  return validSurfaceActions(candidate.surface, candidate.allowedActions);
112
177
  }
113
178
  function validIngressApprovalAuthority(ingress, binding) {
114
- if (!ingress.request.approval) return true;
115
- return ingress.request.approval.taskId === binding.taskId && ingress.request.approval.contextRef === binding.contextRef;
179
+ if (!ingress.request.approval)
180
+ return true;
181
+ return ingress.request.approval.taskId === binding.taskId
182
+ && ingress.request.approval.contextRef === binding.contextRef;
116
183
  }
117
184
  function fallbackErrorEvent(task, code) {
118
185
  return {
@@ -123,37 +190,31 @@ function fallbackErrorEvent(task, code) {
123
190
  sequence: task.lastSequence + 1,
124
191
  emittedAt: new Date().toISOString(),
125
192
  kind: 'error',
126
- error: {
127
- code: normalizeBookingErrorCode(code),
128
- message: safeBookingErrorMessage(normalizeBookingErrorCode(code)),
129
- retryable: false
130
- }
193
+ error: { code: normalizeBookingErrorCode(code), message: safeBookingErrorMessage(normalizeBookingErrorCode(code)), retryable: false },
131
194
  };
132
195
  }
133
196
  function writeTypedError(res, composition, task, error, requestKey, includeSubmitted = false) {
134
197
  const code = normalizeBookingErrorCode(error);
198
+ // Operator-side diagnosability only: the raw boundary error stays on the
199
+ // server stderr/journal; clients keep receiving the safe typed vocabulary.
135
200
  console.error(`[booking-copilot] planner boundary error (${code}):`, error instanceof Error ? error.stack || error.message : error);
136
201
  try {
137
- const decision = {
138
- kind: 'error',
139
- error: {
140
- code,
141
- message: safeBookingErrorMessage(code),
142
- retryable: false
143
- }
144
- };
202
+ const decision = { kind: 'error', error: { code, message: safeBookingErrorMessage(code), retryable: false } };
145
203
  if (requestKey) {
146
- const events = composition.runtime.applyDecisionBatch(task.taskId, requestKey, [
147
- decision
148
- ], includeSubmitted);
149
- for (const event of events)write(res, event);
204
+ const events = composition.runtime.applyDecisionBatch(task.taskId, requestKey, [decision], includeSubmitted);
205
+ for (const event of events)
206
+ write(res, event);
150
207
  return;
151
208
  }
152
209
  write(res, composition.runtime.emitEvent(task.taskId, decision));
153
- } catch {
210
+ }
211
+ catch {
212
+ // The stream is already committed. Emit a schema-valid, non-durable
213
+ // typed envelope instead of silently returning an empty HTTP 200 stream.
154
214
  write(res, fallbackErrorEvent(task, code));
155
215
  }
156
216
  }
217
+ /** Handles one already-authenticated request inside the sole HTTP listener. */
157
218
  async function handleBookingCopilotRequest(req, res, composition, maxBodyBytes) {
158
219
  let turn;
159
220
  let browserRequestKey;
@@ -162,108 +223,53 @@ async function handleBookingCopilotRequest(req, res, composition, maxBodyBytes)
162
223
  const parsed = JSON.parse(await readBody(req, maxBodyBytes));
163
224
  const valid = validateBookingSurface(parsed);
164
225
  if (!valid.ok) {
165
- sendJson(res, 400, {
166
- error: {
167
- code: 'invalid_booking_surface_turn',
168
- details: valid.errors
169
- }
170
- });
226
+ sendJson(res, 400, { error: { code: 'invalid_booking_surface_turn', details: valid.errors } });
171
227
  return;
172
228
  }
173
229
  if (parsed.kind === 'user.turn.ingress') {
174
230
  const ingress = parsed;
175
231
  browserRequestKey = ingress.requestKey;
176
232
  if (composition.ingressMode !== 'bff-ingress-binding' || !composition.ingressBinding || !composition.principal) {
177
- sendJson(res, 503, {
178
- error: {
179
- code: 'trusted_ingress_binding_required',
180
- mode: composition.ingressMode ?? 'bff-bound-turn-only',
181
- acceptedTurnKinds: [
182
- ...BOOKING_COPILOT_ACCEPTED_TURN_KINDS
183
- ]
184
- }
185
- });
233
+ sendJson(res, 503, { error: { code: 'trusted_ingress_binding_required', mode: composition.ingressMode ?? 'bff-bound-turn-only', acceptedTurnKinds: [...BOOKING_COPILOT_ACCEPTED_TURN_KINDS] } });
186
234
  return;
187
235
  }
188
236
  const binding = await composition.ingressBinding.bind(ingress, composition.principal);
189
237
  if (!validIngressBinding(binding, ingress)) {
190
- sendJson(res, 502, {
191
- error: {
192
- code: 'invalid_ingress_binding'
193
- }
194
- });
238
+ sendJson(res, 502, { error: { code: 'invalid_ingress_binding' } });
195
239
  return;
196
240
  }
197
241
  if (!validIngressApprovalAuthority(ingress, binding)) {
198
- sendJson(res, 400, {
199
- error: {
200
- code: 'invalid_ingress_approval_authority'
201
- }
202
- });
242
+ sendJson(res, 400, { error: { code: 'invalid_ingress_approval_authority' } });
203
243
  return;
204
244
  }
205
245
  const boundWorkspace = {
206
246
  ...ingress.workspace,
207
247
  contextRef: binding.contextRef,
208
248
  surface: binding.surface,
209
- capabilities: {
210
- surface: binding.surface,
211
- allowedActions: [
212
- ...binding.allowedActions
213
- ]
214
- }
215
- };
216
- const boundRequest = ingress.request.approval ? {
217
- text: ingress.request.text,
218
- approval: {
219
- ...ingress.request.approval
220
- }
221
- } : {
222
- text: ingress.request.text
223
- };
224
- turn = {
225
- schemaVersion: BOOKING_SURFACE_SCHEMA_VERSION,
226
- kind: 'user.turn',
227
- taskId: binding.taskId,
228
- turnId: binding.turnId,
229
- workspace: boundWorkspace,
230
- request: boundRequest
231
- };
232
- requestBinding = {
233
- requestKey: ingress.requestKey,
234
- principal: composition.principal,
235
- ...ingress.taskHandle ? {
236
- taskHandle: ingress.taskHandle
237
- } : {}
249
+ capabilities: { surface: binding.surface, allowedActions: [...binding.allowedActions] },
238
250
  };
251
+ const boundRequest = ingress.request.approval
252
+ ? { text: ingress.request.text, approval: { ...ingress.request.approval } }
253
+ : { text: ingress.request.text };
254
+ turn = { schemaVersion: BOOKING_SURFACE_SCHEMA_VERSION, kind: 'user.turn', taskId: binding.taskId, turnId: binding.turnId, workspace: boundWorkspace, request: boundRequest };
255
+ requestBinding = { requestKey: ingress.requestKey, principal: composition.principal, ...(ingress.taskHandle ? { taskHandle: ingress.taskHandle } : {}) };
239
256
  const boundValid = validateBookingSurface(turn);
240
257
  if (!boundValid.ok) {
241
- sendJson(res, 502, {
242
- error: {
243
- code: 'invalid_ingress_binding',
244
- details: boundValid.errors
245
- }
246
- });
258
+ sendJson(res, 502, { error: { code: 'invalid_ingress_binding', details: boundValid.errors } });
247
259
  return;
248
260
  }
249
- } else {
261
+ }
262
+ else {
250
263
  turn = parsed;
251
264
  if (!validBoundWorkspaceAuthority(turn.workspace)) {
252
- sendJson(res, 403, {
253
- error: {
254
- code: 'invalid_bound_turn_authority'
255
- }
256
- });
265
+ sendJson(res, 403, { error: { code: 'invalid_bound_turn_authority' } });
257
266
  return;
258
267
  }
259
268
  }
260
- } catch (error) {
269
+ }
270
+ catch (error) {
261
271
  const code = normalizeBookingErrorCode(error);
262
- sendJson(res, code === 'PAYLOAD_TOO_LARGE' ? 413 : 400, {
263
- error: {
264
- code
265
- }
266
- });
272
+ sendJson(res, code === 'PAYLOAD_TOO_LARGE' ? 413 : 400, { error: { code } });
267
273
  return;
268
274
  }
269
275
  let task;
@@ -280,32 +286,47 @@ async function handleBookingCopilotRequest(req, res, composition, maxBodyBytes)
280
286
  replayKey = composition.runtime.approvalPresentationRequestKey(turn.taskId);
281
287
  }
282
288
  replayEvents = composition.runtime.readDecisionBatch(turn.taskId, replayKey);
283
- if (!replayEvents && task.phase === 'terminal') replayEvents = composition.runtime.terminalDecisionBatch(turn.taskId, replayKey);
284
- } else {
289
+ if (!replayEvents && task.phase === 'terminal')
290
+ replayEvents = composition.runtime.terminalDecisionBatch(turn.taskId, replayKey);
291
+ }
292
+ else {
285
293
  const existing = turn.taskId ? composition.runtime.resumeTask(turn.taskId) : null;
286
- if (requestBinding && turn.kind === 'user.turn') composition.runtime.assertRequestBinding(browserRequestKey, turn, requestBinding);
294
+ if (requestBinding && turn.kind === 'user.turn')
295
+ composition.runtime.assertRequestBinding(browserRequestKey, turn, requestBinding);
287
296
  const requestReplayKey = existing && browserRequestKey ? `ingress:${existing.taskId}:${browserRequestKey}` : undefined;
288
297
  const directReplayKey = existing && turn.kind === 'user.turn' && turn.turnId ? `turn:${existing.taskId}:${turn.turnId}` : undefined;
289
298
  const stableReplayKey = requestReplayKey ?? directReplayKey;
290
299
  const stableReplay = stableReplayKey ? composition.runtime.readDecisionBatch(existing.taskId, stableReplayKey) : null;
291
300
  if (stableReplay) {
292
- if (existing && turn.kind === 'user.turn' && directReplayKey) composition.runtime.assertTurnBinding(existing.taskId, turn);
301
+ if (existing && turn.kind === 'user.turn' && directReplayKey)
302
+ composition.runtime.assertTurnBinding(existing.taskId, turn);
293
303
  task = existing;
294
304
  replayKey = stableReplayKey;
295
305
  replayEvents = stableReplay;
296
- } else if (existing?.phase === 'terminal' || existing?.phase === 'error') {
306
+ }
307
+ else if (existing?.phase === 'terminal' || existing?.phase === 'error') {
297
308
  throw new Error('task_terminal');
298
- } else if (existing?.phase === 'waiting_receipt') {
299
- if (turn.kind === 'user.turn' && turn.request.approval) composition.runtime.assertApprovalNotConsumed(existing.taskId, turn.request.approval);
309
+ }
310
+ else if (existing?.phase === 'waiting_receipt') {
311
+ if (turn.kind === 'user.turn' && turn.request.approval)
312
+ composition.runtime.assertApprovalNotConsumed(existing.taskId, turn.request.approval);
300
313
  const workspace = turn.workspace;
301
- if (workspace.contextRef !== existing.contextRef || workspace.surface !== existing.surface || workspace.revision !== existing.revision || workspace.capabilities.surface !== existing.surface || !sameCapabilities(workspace.capabilities.allowedActions, existing.allowedActions)) throw new Error('task_conflict:workspace_mismatch');
302
- if (!turn.turnId || existing.lastTurnId !== turn.turnId) throw new Error('receipt_required');
314
+ if (workspace.contextRef !== existing.contextRef || workspace.surface !== existing.surface || workspace.revision !== existing.revision || workspace.capabilities.surface !== existing.surface || !sameCapabilities(workspace.capabilities.allowedActions, existing.allowedActions))
315
+ throw new Error('task_conflict:workspace_mismatch');
316
+ // A waiting task can only be replayed with the caller's stable turn
317
+ // identity. Without it, an identical sentence is not distinguishable
318
+ // from a new user turn and must not receive an old operation batch.
319
+ if (!turn.turnId || existing.lastTurnId !== turn.turnId)
320
+ throw new Error('receipt_required');
303
321
  replayKey = browserRequestKey ? `ingress:${existing.taskId}:${browserRequestKey}` : `turn:${existing.taskId}:${existing.lastTurnId ?? existing.userTurnCount}`;
304
322
  replayEvents = composition.runtime.readDecisionBatch(existing.taskId, replayKey);
305
- if (!replayEvents) throw new Error('receipt_required');
323
+ if (!replayEvents)
324
+ throw new Error('receipt_required');
306
325
  task = existing;
307
- } else {
308
- if (existing && turn.kind === 'user.turn' && turn.request.approval) composition.runtime.assertApprovalNotConsumed(existing.taskId, turn.request.approval);
326
+ }
327
+ else {
328
+ if (existing && turn.kind === 'user.turn' && turn.request.approval)
329
+ composition.runtime.assertApprovalNotConsumed(existing.taskId, turn.request.approval);
309
330
  task = composition.runtime.startTask(turn, requestBinding);
310
331
  if (turn.kind === 'user.turn' && turn.taskId) {
311
332
  replayKey = browserRequestKey ? `ingress:${task.taskId}:${browserRequestKey}` : `turn:${task.taskId}:${turn.turnId ?? task.userTurnCount}`;
@@ -313,68 +334,49 @@ async function handleBookingCopilotRequest(req, res, composition, maxBodyBytes)
313
334
  }
314
335
  }
315
336
  }
316
- } catch (error) {
317
- sendJson(res, 409, {
318
- error: {
319
- code: normalizeBookingErrorCode(error)
320
- }
321
- });
337
+ }
338
+ catch (error) {
339
+ const code = normalizeBookingErrorCode(error);
340
+ console.error(JSON.stringify({ code, reason: extractBookingDispatchReason(error) }));
341
+ sendJson(res, 409, { error: { code } });
322
342
  return;
323
343
  }
324
- res.writeHead(200, {
325
- 'content-type': 'text/event-stream; charset=utf-8',
326
- 'cache-control': 'no-cache, no-transform',
327
- connection: 'keep-alive',
328
- 'x-content-type-options': 'nosniff',
329
- [BOOKING_SURFACE_VERSION_HEADER]: BOOKING_SURFACE_SCHEMA_VERSION,
330
- [BOOKING_SURFACE_SCHEMA_SHA256_HEADER]: BOOKING_SURFACE_SCHEMA_SHA256
331
- });
344
+ 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 });
332
345
  try {
333
346
  if (replayEvents) {
334
- for (const event of replayEvents)write(res, event);
335
- } else {
347
+ for (const event of replayEvents)
348
+ write(res, event);
349
+ }
350
+ else {
336
351
  const requestKey = replayKey ?? `turn:${task.taskId}:${task.userTurnCount}`;
337
352
  activeDecisionKey = requestKey;
338
353
  const approval = turn.kind === 'user.turn' ? turn.request.approval : undefined;
339
- const flightKey = JSON.stringify([
340
- task.taskId,
341
- approval ? `approval:${bookingDigest(approval)}` : requestKey
342
- ]);
343
- const decisionEvents = await runDecisionSingleFlight(composition, flightKey, async ()=>{
354
+ const flightKey = JSON.stringify([task.taskId, approval ? `approval:${bookingDigest(approval)}` : requestKey]);
355
+ const decisionEvents = await runDecisionSingleFlight(composition, flightKey, async () => {
344
356
  if (turn.kind === 'action.receipt.continuation' && task.awaitingApproval) {
345
- return composition.runtime.applyDecisionBatch(task.taskId, requestKey, [
346
- composition.runtime.approvalQuestion(task.taskId)
347
- ], false);
357
+ return composition.runtime.applyDecisionBatch(task.taskId, requestKey, [composition.runtime.approvalQuestion(task.taskId)], false);
348
358
  }
349
359
  const sessions = sessionsFor(composition);
350
360
  const session = sessions.get(task.taskId) ?? composition.plannerFactory(task);
351
361
  sessions.set(task.taskId, session);
352
- const decisions = await session.next({
353
- turn,
354
- task: composition.runtime.resumeTask(task.taskId) ?? task
355
- });
356
- const plannerDecisions = decisions.length ? decisions : [
357
- {
358
- kind: 'error',
359
- error: {
360
- code: 'PLANNER_NO_DECISION',
361
- message: 'Planner returned no typed decision.',
362
- retryable: false
363
- }
364
- }
365
- ];
362
+ const decisions = await session.next({ turn, task: composition.runtime.resumeTask(task.taskId) ?? task });
363
+ const plannerDecisions = decisions.length ? decisions : [{ kind: 'error', error: { code: 'PLANNER_NO_DECISION', message: 'Planner returned no typed decision.', retryable: false } }];
366
364
  return composition.runtime.applyDecisionBatch(task.taskId, requestKey, plannerDecisions, fresh);
367
365
  });
368
- for (const event of decisionEvents)write(res, event);
366
+ for (const event of decisionEvents)
367
+ write(res, event);
369
368
  }
370
- } catch (error) {
369
+ }
370
+ catch (error) {
371
371
  writeTypedError(res, composition, task, error, activeDecisionKey, fresh);
372
- } finally{
372
+ }
373
+ finally {
373
374
  res.end();
374
375
  }
375
376
  }
376
377
  export function startBookingCopilotServer(options) {
377
- if (!options.apiKey) return Promise.reject(new Error('booking_copilot_api_key_required'));
378
+ if (!options.apiKey)
379
+ return Promise.reject(new Error('booking_copilot_api_key_required'));
378
380
  if (options.artifactId !== undefined && !/^[0-9a-f]{40}$/.test(options.artifactId)) {
379
381
  return Promise.reject(new Error('booking_copilot_artifact_id_invalid'));
380
382
  }
@@ -386,7 +388,9 @@ export function startBookingCopilotServer(options) {
386
388
  if (hasIngressBinding !== hasPrincipal) {
387
389
  return Promise.reject(new Error('booking_copilot_ingress_binding_pair_required'));
388
390
  }
389
- if (hasIngressBinding && (typeof options.ingressBinding?.bind !== 'function' || typeof options.principal?.subject !== 'string' || options.principal.subject.length === 0 || options.principal.subject.length > 256 || typeof options.principal?.scope !== 'string' || options.principal.scope.length === 0 || options.principal.scope.length > 256)) {
391
+ if (hasIngressBinding && (typeof options.ingressBinding?.bind !== 'function'
392
+ || typeof options.principal?.subject !== 'string' || options.principal.subject.length === 0 || options.principal.subject.length > 256
393
+ || typeof options.principal?.scope !== 'string' || options.principal.scope.length === 0 || options.principal.scope.length > 256)) {
390
394
  return Promise.reject(new Error('booking_copilot_ingress_binding_invalid'));
391
395
  }
392
396
  const ingressMode = options.ingressMode ?? (hasIngressBinding ? 'bff-ingress-binding' : 'bff-bound-turn-only');
@@ -402,87 +406,32 @@ export function startBookingCopilotServer(options) {
402
406
  const composition = {
403
407
  runtime: options.runtime,
404
408
  plannerFactory: options.plannerFactory,
405
- ...hasIngressBinding ? {
406
- ingressBinding: options.ingressBinding,
407
- principal: options.principal
408
- } : {},
409
- ingressMode
409
+ ...(hasIngressBinding ? { ingressBinding: options.ingressBinding, principal: options.principal } : {}),
410
+ ingressMode,
410
411
  };
411
412
  const maxBodyBytes = options.maxBodyBytes ?? 1_000_000;
412
413
  const runningIdentity = runtimeIdentity();
413
- const server = createServer(async (req, res)=>{
414
- const isProbe = req.method === 'GET' && (req.url === '/healthz' || req.url === '/status');
415
- if (!isProbe && (req.method !== 'POST' || req.url !== '/a2a/booking-copilot/turn')) {
416
- sendJson(res, 404, {
417
- error: {
418
- code: 'not_found'
419
- }
420
- });
421
- return;
422
- }
423
- const auth = String(req.headers.authorization ?? '');
424
- if (!safeSecretEqual(auth, `Bearer ${options.apiKey}`)) {
425
- sendJson(res, 401, {
426
- error: {
427
- code: 'unauthorized'
428
- }
429
- });
430
- return;
431
- }
432
- if (isProbe) {
433
- res.setHeader(BOOKING_SURFACE_VERSION_HEADER, BOOKING_SURFACE_SCHEMA_VERSION);
434
- res.setHeader(BOOKING_SURFACE_SCHEMA_SHA256_HEADER, BOOKING_SURFACE_SCHEMA_SHA256);
435
- if (options.artifactId) res.setHeader('X-GoTry-Artifact-ID', options.artifactId);
436
- res.setHeader('X-GoTry-Node-Version', runningIdentity.nodeVersion);
437
- res.setHeader('X-GoTry-Node-Modules-ABI', runningIdentity.nodeModulesAbi);
438
- res.setHeader('X-GoTry-Release-Tuple', runningIdentity.releaseTuple);
439
- if (runningIdentity.glibcVersion) res.setHeader('X-GoTry-Glibc-Version', runningIdentity.glibcVersion);
440
- const acceptedTurnKinds = ingressMode === 'bff-ingress-binding' ? [
441
- ...BOOKING_COPILOT_ACCEPTED_TURN_KINDS,
442
- 'user.turn.ingress'
443
- ] : [
444
- ...BOOKING_COPILOT_ACCEPTED_TURN_KINDS
445
- ];
446
- const healthBody = {
447
- schemaVersion: BOOKING_SURFACE_SCHEMA_VERSION,
448
- schemaSha256: BOOKING_SURFACE_SCHEMA_SHA256,
449
- status: 'ready',
450
- ingressMode,
451
- acceptedTurnKinds
452
- };
453
- res.setHeader('X-GoTry-Ingress-Mode', ingressMode);
454
- res.setHeader('X-GoTry-Accepted-Turn-Kinds', acceptedTurnKinds.join(','));
455
- sendJson(res, 200, healthBody);
456
- return;
457
- }
458
- const schemaVersion = String(req.headers[BOOKING_SURFACE_VERSION_HEADER] ?? '');
459
- const schemaHash = String(req.headers[BOOKING_SURFACE_SCHEMA_SHA256_HEADER] ?? '');
460
- if (schemaVersion !== BOOKING_SURFACE_SCHEMA_VERSION || schemaHash !== BOOKING_SURFACE_SCHEMA_SHA256) {
461
- sendJson(res, 409, {
462
- error: {
463
- code: 'booking_surface_schema_mismatch',
464
- expectedVersion: BOOKING_SURFACE_SCHEMA_VERSION,
465
- expectedSchemaSha256: BOOKING_SURFACE_SCHEMA_SHA256
466
- }
467
- });
468
- return;
469
- }
470
- await handleBookingCopilotRequest(req, res, composition, maxBodyBytes);
414
+ const server = createServer((req, res) => {
415
+ void bookingCopilotTrafficHandler({
416
+ apiKey: options.apiKey,
417
+ composition,
418
+ maxBodyBytes,
419
+ ...(options.artifactId !== undefined ? { artifactId: options.artifactId } : {}),
420
+ ingressMode,
421
+ runningIdentity,
422
+ })(req, res);
471
423
  });
472
- return new Promise((resolve, reject)=>{
424
+ return new Promise((resolve, reject) => {
473
425
  server.once('error', reject);
474
- server.listen(options.port ?? 0, options.host ?? '127.0.0.1', ()=>{
426
+ server.listen(options.port ?? 0, options.host ?? '127.0.0.1', () => {
475
427
  server.off('error', reject);
476
428
  const address = server.address();
477
- const port = typeof address === 'object' && address ? address.port : options.port ?? 0;
429
+ const port = typeof address === 'object' && address ? address.port : (options.port ?? 0);
478
430
  resolve({
479
431
  server,
480
432
  port,
481
- close: ()=>new Promise((done, fail)=>server.close((error)=>error ? fail(error) : done()))
433
+ close: () => new Promise((done, fail) => server.close((error) => error ? fail(error) : done())),
482
434
  });
483
435
  });
484
436
  });
485
437
  }
486
-
487
-
488
- //# sourceURL=ts/src/booking-surface/server.ts