@danceiny/gotry 0.0.1-rc.16 → 0.0.1-rc.18

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 (229) hide show
  1. package/README.md +161 -116
  2. package/README.zh-CN.md +173 -144
  3. package/bin/gotry-booking-copilot.js +53 -0
  4. package/bin/gotry-bootstrap.js +113 -66
  5. package/bin/gotry-inner.js +436 -70
  6. package/bin/gotry-runtime-resolution.d.ts +27 -0
  7. package/bin/gotry-runtime-resolution.js +50 -0
  8. package/bin/gotry.js +1 -1
  9. package/cordis.gotry-patch.yml +5 -1
  10. package/dist/capabilities/agent-reach-deep.js +1 -1
  11. package/dist/capabilities/agent-reach.js +1 -1
  12. package/dist/capabilities/anything.js +1 -1
  13. package/dist/capabilities/artifacts.js +1 -1
  14. package/dist/capabilities/effect.js +1 -1
  15. package/dist/capabilities/fact-log.js +1 -1
  16. package/dist/capabilities/flyai.js +20 -6
  17. package/dist/capabilities/hbcli.js +1 -1
  18. package/dist/capabilities/incident-log.js +1 -1
  19. package/dist/capabilities/model-override.js +18 -0
  20. package/dist/capabilities/opensky.js +1 -1
  21. package/dist/capabilities/resilience.js +1 -1
  22. package/dist/capabilities/session/action-cache.js +1 -1
  23. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  24. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  25. package/dist/capabilities/session/benchmark.js +1 -1
  26. package/dist/capabilities/session/extension-bridge.js +19 -6
  27. package/dist/capabilities/session/extension-channel.js +3 -3
  28. package/dist/capabilities/session/extension-distribution.js +234 -0
  29. package/dist/capabilities/session/extract.js +1 -1
  30. package/dist/capabilities/session/golden-score.js +92 -0
  31. package/dist/capabilities/session/health-watch.js +1 -1
  32. package/dist/capabilities/session/read-guard.js +1 -1
  33. package/dist/capabilities/session/static-flight-golden.js +137 -0
  34. package/dist/capabilities/session/transport.js +1 -1
  35. package/dist/capabilities/session/wizard.js +21 -251
  36. package/dist/capabilities/session-consent.js +1 -1
  37. package/dist/capabilities/session-login.js +11 -5
  38. package/dist/capabilities/session-search.js +46 -4
  39. package/dist/capabilities/weather.js +168 -46
  40. package/dist/data/session-golden-20.json +25 -0
  41. package/dist/data/sf-golden-manifest.json +102 -0
  42. package/dist/data/sf-static-routes.json +91 -0
  43. package/dist/scripts/action-cache-tests.js +1 -1
  44. package/dist/scripts/agent-planning-budget-e2e.js +227 -0
  45. package/dist/scripts/agent-planning-budget-tests.js +173 -0
  46. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  47. package/dist/scripts/agent-reach-tests.js +1 -1
  48. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  49. package/dist/scripts/anything-tests.js +1 -1
  50. package/dist/scripts/async-collect.js +1 -1
  51. package/dist/scripts/benchmark-environment-bridge-e2e.js +981 -0
  52. package/dist/scripts/benchmark-environment-bridge-tests.js +2544 -0
  53. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +335 -0
  54. package/dist/scripts/booking-copilot-availability-policy-v2-tests.js +1355 -0
  55. package/dist/scripts/booking-copilot-bin-proof-tests.js +89 -0
  56. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +113 -0
  57. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +356 -0
  58. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +557 -0
  59. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +140 -0
  60. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +202 -0
  61. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +102 -0
  62. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +374 -0
  63. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +447 -0
  64. package/dist/scripts/booking-copilot-runtime-proof-tests.js +205 -0
  65. package/dist/scripts/booking-copilot-server-proof-tests.js +317 -0
  66. package/dist/scripts/booking-copilot-startup-proof-tests.js +283 -0
  67. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +3935 -0
  68. package/dist/scripts/booking-saga-tests.js +1 -1
  69. package/dist/scripts/booking-surface-contract-proof-tests.js +393 -0
  70. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +1174 -0
  71. package/dist/scripts/bootstrap-tests.js +45 -39
  72. package/dist/scripts/build-changelog.js +1 -1
  73. package/dist/scripts/changelog-tests.js +1 -1
  74. package/dist/scripts/companion-tests.js +1 -1
  75. package/dist/scripts/diff-test.js +1 -1
  76. package/dist/scripts/dsh-runtime-closure-tests.js +225 -0
  77. package/dist/scripts/dsh-runtime-closure.js +150 -0
  78. package/dist/scripts/effect-tests.js +1 -1
  79. package/dist/scripts/engine-run.js +1 -1
  80. package/dist/scripts/engine-tests.js +1 -1
  81. package/dist/scripts/evaluation-cadence-tests.js +347 -0
  82. package/dist/scripts/evaluation-contract-tests.js +574 -0
  83. package/dist/scripts/extension-distribution-cli.js +35 -0
  84. package/dist/scripts/extension-distribution-tests.js +341 -0
  85. package/dist/scripts/extension-tests.js +128 -23
  86. package/dist/scripts/fact-gate-tests.js +1 -1
  87. package/dist/scripts/flyai-tests.js +1 -1
  88. package/dist/scripts/hbcli-e2e-tests.js +1 -1
  89. package/dist/scripts/hbcli-tests.js +1 -1
  90. package/dist/scripts/health-watch-cli.js +1 -1
  91. package/dist/scripts/i18n-tests.js +1 -1
  92. package/dist/scripts/incident-tests.js +1 -1
  93. package/dist/scripts/journey-tests.js +1 -1
  94. package/dist/scripts/ledger-tests.js +1 -1
  95. package/dist/scripts/ledger-workflow-crash.js +1 -1
  96. package/dist/scripts/memory-capture-tests.js +1 -1
  97. package/dist/scripts/memory-decay-tests.js +1 -1
  98. package/dist/scripts/memory-metrics.js +1 -1
  99. package/dist/scripts/memory-value-report.js +1 -1
  100. package/dist/scripts/model-override-e2e.js +176 -0
  101. package/dist/scripts/nightly-evidence-tests.js +1 -1
  102. package/dist/scripts/nightly-evidence.js +1 -1
  103. package/dist/scripts/nudge-digest.js +1 -1
  104. package/dist/scripts/onboarding-tests.js +21 -53
  105. package/dist/scripts/opensky-check.js +1 -1
  106. package/dist/scripts/opensky-tests.js +1 -1
  107. package/dist/scripts/pnpm-dsh-closure-proof.js +20 -0
  108. package/dist/scripts/price-drift-tests.js +1 -1
  109. package/dist/scripts/price-drift-watch.js +1 -1
  110. package/dist/scripts/probe-poi-tests.js +1 -1
  111. package/dist/scripts/product-metrics.js +1 -1
  112. package/dist/scripts/publish-preverify.js +40 -4
  113. package/dist/scripts/realtime-pricing-tests.js +1 -1
  114. package/dist/scripts/replay-async.js +1 -1
  115. package/dist/scripts/replay-real.js +1 -1
  116. package/dist/scripts/replay.js +1 -1
  117. package/dist/scripts/session-attach-diagnose.js +1 -1
  118. package/dist/scripts/session-attach-poc.js +1 -1
  119. package/dist/scripts/session-benchmark.js +1 -1
  120. package/dist/scripts/session-extract-tests.js +1 -1
  121. package/dist/scripts/session-login.js +1 -1
  122. package/dist/scripts/session-tests.js +70 -20
  123. package/dist/scripts/sf-live-benchmark.js +338 -0
  124. package/dist/scripts/sf-live-cli-tests.js +21 -0
  125. package/dist/scripts/sf-soft-score-tests.js +108 -0
  126. package/dist/scripts/sf-summary.js +93 -0
  127. package/dist/scripts/skeleton-check.js +1 -1
  128. package/dist/scripts/skeleton-integration-test.js +1 -1
  129. package/dist/scripts/skills-contract-tests.js +1 -1
  130. package/dist/scripts/smoke-session-gate-tests.js +29 -0
  131. package/dist/scripts/smoke.js +79 -33
  132. package/dist/scripts/state-cli-tests.js +1 -1
  133. package/dist/scripts/state-cli.js +1 -1
  134. package/dist/scripts/static-golden-tests.js +299 -0
  135. package/dist/scripts/time-eval-tests.js +1 -1
  136. package/dist/scripts/travel-timeline-tests.js +1 -1
  137. package/dist/scripts/unified-tests.js +1 -1
  138. package/dist/scripts/weather-tests.js +694 -44
  139. package/dist/scripts/z3-race-tests.js +1 -1
  140. package/dist/src/artifact-gate.js +1 -1
  141. package/dist/src/benchmark-agent-conformance.js +370 -0
  142. package/dist/src/benchmark-environment-bridge.js +384 -0
  143. package/dist/src/benchmark-headless-child-diagnostics.js +173 -0
  144. package/dist/src/benchmark-tool-isolation.js +124 -0
  145. package/dist/src/bookable-facts.js +1 -1
  146. package/dist/src/booking-saga.js +1 -1
  147. package/dist/src/booking-surface/availability-policy-v2.js +830 -0
  148. package/dist/src/booking-surface/canonical-schema.js +113 -0
  149. package/dist/src/booking-surface/contracts-v2.js +89 -0
  150. package/dist/src/booking-surface/contracts.js +46 -0
  151. package/dist/src/booking-surface/dsh-planner.js +453 -0
  152. package/dist/src/booking-surface/dsh-plugin.js +93 -0
  153. package/dist/src/booking-surface/error-codes.js +94 -0
  154. package/dist/src/booking-surface/index.js +15 -0
  155. package/dist/src/booking-surface/profile.js +68 -0
  156. package/dist/src/booking-surface/runtime-v2.js +1771 -0
  157. package/dist/src/booking-surface/runtime.js +351 -0
  158. package/dist/src/booking-surface/server-v2.js +334 -0
  159. package/dist/src/booking-surface/server.js +302 -0
  160. package/dist/src/booking-surface/startup.js +159 -0
  161. package/dist/src/booking-surface/validation-v2.js +319 -0
  162. package/dist/src/booking-surface/validation.js +809 -0
  163. package/dist/src/bridge.js +1 -1
  164. package/dist/src/companions.js +1 -1
  165. package/dist/src/contracts.js +1 -1
  166. package/dist/src/dsh-llm.js +1 -1
  167. package/dist/src/engine.js +1 -1
  168. package/dist/src/evaluation-cadence.js +234 -0
  169. package/dist/src/evaluation-contracts.js +906 -0
  170. package/dist/src/i18n.js +1 -1
  171. package/dist/src/index.js +50 -19
  172. package/dist/src/journey.js +1 -1
  173. package/dist/src/loop.js +1 -1
  174. package/dist/src/memory-capture.js +1 -1
  175. package/dist/src/memory-decay.js +1 -1
  176. package/dist/src/memory-utility.js +1 -1
  177. package/dist/src/mock-llm.js +1 -1
  178. package/dist/src/model.js +1 -1
  179. package/dist/src/realtime-pricing.js +1 -1
  180. package/dist/src/slot-spec.js +1 -1
  181. package/dist/src/state-ledger.js +2 -1
  182. package/dist/src/time-anchor.js +1 -1
  183. package/dist/src/tool-budget.js +136 -0
  184. package/dist/src/tool-packet.js +1 -1
  185. package/dist/src/travel-slots.js +1 -1
  186. package/dist/src/travel-timeline.js +1 -1
  187. package/dist/src/unified.js +1 -1
  188. package/dist/src/wish-pool.js +1 -1
  189. package/dist/src/z3-shared.js +1 -1
  190. package/extension/README.md +31 -7
  191. package/package.json +286 -11
  192. package/schemas/booking.surface.v1.schema.json +927 -0
  193. package/schemas/booking.surface.v2.schema.json +61 -0
  194. package/ts/capabilities/flyai.ts +16 -3
  195. package/ts/capabilities/session/extension-bridge.ts +37 -14
  196. package/ts/capabilities/session/extension-channel.ts +6 -3
  197. package/ts/capabilities/session/extension-distribution.ts +264 -0
  198. package/ts/capabilities/session/golden-score.ts +139 -0
  199. package/ts/capabilities/session/health-watch.ts +1 -1
  200. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  201. package/ts/capabilities/session/wizard.ts +34 -176
  202. package/ts/capabilities/session-login.ts +15 -5
  203. package/ts/capabilities/session-search.ts +40 -3
  204. package/ts/capabilities/weather.ts +141 -52
  205. package/ts/package.json +3 -3
  206. package/ts/src/benchmark-agent-conformance.ts +448 -0
  207. package/ts/src/benchmark-environment-bridge.ts +348 -0
  208. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  209. package/ts/src/benchmark-tool-isolation.ts +166 -0
  210. package/ts/src/booking-surface/availability-policy-v2.ts +523 -0
  211. package/ts/src/booking-surface/canonical-schema.js +113 -0
  212. package/ts/src/booking-surface/contracts-v2.ts +118 -0
  213. package/ts/src/booking-surface/contracts.ts +380 -0
  214. package/ts/src/booking-surface/dsh-planner.ts +452 -0
  215. package/ts/src/booking-surface/dsh-plugin.js +93 -0
  216. package/ts/src/booking-surface/error-codes.ts +101 -0
  217. package/ts/src/booking-surface/index.ts +12 -0
  218. package/ts/src/booking-surface/profile.ts +42 -0
  219. package/ts/src/booking-surface/runtime-v2.ts +1466 -0
  220. package/ts/src/booking-surface/runtime.ts +483 -0
  221. package/ts/src/booking-surface/server-v2.ts +247 -0
  222. package/ts/src/booking-surface/server.ts +324 -0
  223. package/ts/src/booking-surface/startup.ts +196 -0
  224. package/ts/src/booking-surface/validation-v2.ts +205 -0
  225. package/ts/src/booking-surface/validation.ts +453 -0
  226. package/ts/src/index.ts +64 -11
  227. package/ts/src/state-ledger.ts +1 -0
  228. package/ts/src/tool-budget.ts +165 -0
  229. package/dist/scripts/wizard-bootstrap.js +0 -32
@@ -0,0 +1,302 @@
1
+ import { timingSafeEqual } from 'node:crypto';
2
+ import { createServer } from 'node:http';
3
+ import { BOOKING_SURFACE_SCHEMA_SHA256, BOOKING_SURFACE_SCHEMA_SHA256_HEADER, BOOKING_SURFACE_SCHEMA_VERSION, BOOKING_SURFACE_VERSION_HEADER } from './contracts.js';
4
+ import { BookingCopilotTaskRuntime } from './runtime.js';
5
+ import { validateBookingCopilotTurnV1 } from './validation.js';
6
+ import { handleBookingCopilotV2Request } from './server-v2.js';
7
+ import { normalizeBookingErrorCode, safeBookingErrorMessage } from './error-codes.js';
8
+ import { BOOKING_COPILOT_V2_ACCEPTED_TURN_KINDS, BOOKING_COPILOT_V2_INGRESS_TURN_KIND, BOOKING_COPILOT_V2_INGRESS_MODES, BOOKING_SURFACE_SCHEMA_V2_SHA256, BOOKING_SURFACE_SCHEMA_VERSION_V2 } from './contracts-v2.js';
9
+ function runtimeIdentity() {
10
+ const report = process.report?.getReport?.();
11
+ const header = report?.header;
12
+ const glibcVersion = header?.glibcVersionRuntime ?? '';
13
+ return {
14
+ nodeVersion: process.version,
15
+ nodeModulesAbi: process.versions.modules ?? '',
16
+ releaseTuple: `${process.platform}-${process.arch}-${glibcVersion ? 'glibc' : 'unknown'}`,
17
+ glibcVersion
18
+ };
19
+ }
20
+ function safeSecretEqual(actual, expected) {
21
+ const actualBytes = Buffer.from(actual);
22
+ const expectedBytes = Buffer.from(expected);
23
+ return actualBytes.length === expectedBytes.length && timingSafeEqual(actualBytes, expectedBytes);
24
+ }
25
+ function sendJson(res, status, body) {
26
+ res.writeHead(status, {
27
+ 'content-type': 'application/json; charset=utf-8'
28
+ });
29
+ res.end(JSON.stringify(body));
30
+ }
31
+ function readBody(req, maxBodyBytes) {
32
+ return new Promise((resolve, reject)=>{
33
+ const chunks = [];
34
+ let size = 0;
35
+ let exceeded = false;
36
+ req.on('data', (chunk)=>{
37
+ size += chunk.length;
38
+ if (size > maxBodyBytes) {
39
+ exceeded = true;
40
+ return;
41
+ }
42
+ chunks.push(chunk);
43
+ });
44
+ req.on('end', ()=>exceeded ? reject(new Error('payload_too_large')) : resolve(Buffer.concat(chunks).toString('utf8')));
45
+ req.on('error', reject);
46
+ });
47
+ }
48
+ function writeSse(res, event) {
49
+ res.write(`event: ${event.kind}\ndata: ${JSON.stringify(event)}\n\n`);
50
+ }
51
+ export function startBookingCopilotServer(options) {
52
+ if (!options.apiKey) return Promise.reject(new Error('booking_copilot_api_key_required'));
53
+ if (options.artifactId !== undefined && !/^[0-9a-f]{40}$/.test(options.artifactId)) {
54
+ return Promise.reject(new Error('booking_copilot_artifact_id_invalid'));
55
+ }
56
+ const hasIngressBinding = Boolean(options.v2?.ingressBinding);
57
+ const hasPrincipal = Boolean(options.v2?.principal);
58
+ if (options.v2 && hasIngressBinding !== hasPrincipal) {
59
+ return Promise.reject(new Error('booking_copilot_v2_ingress_binding_pair_required'));
60
+ }
61
+ if (options.v2 && hasIngressBinding && (typeof options.v2.ingressBinding?.bind !== 'function' || typeof options.v2.principal?.subject !== 'string' || options.v2.principal.subject.length === 0 || options.v2.principal.subject.length > 256 || typeof options.v2.principal?.scope !== 'string' || options.v2.principal.scope.length === 0 || options.v2.principal.scope.length > 256)) {
62
+ return Promise.reject(new Error('booking_copilot_v2_ingress_binding_invalid'));
63
+ }
64
+ const ingressMode = options.v2 ? options.v2.ingressMode ?? (hasIngressBinding ? 'bff-ingress-binding' : 'bff-bound-turn-only') : undefined;
65
+ if (options.v2 && !BOOKING_COPILOT_V2_INGRESS_MODES.includes(ingressMode)) {
66
+ return Promise.reject(new Error('booking_copilot_v2_ingress_mode_invalid'));
67
+ }
68
+ if (options.v2 && ingressMode === 'bff-ingress-binding' && !hasIngressBinding) {
69
+ return Promise.reject(new Error('booking_copilot_v2_ingress_binding_required'));
70
+ }
71
+ if (options.v2 && ingressMode === 'bff-bound-turn-only' && hasIngressBinding) {
72
+ return Promise.reject(new Error('booking_copilot_v2_ingress_mode_conflict'));
73
+ }
74
+ const v2 = options.v2 ? {
75
+ ...options.v2,
76
+ ingressMode
77
+ } : undefined;
78
+ const v2Only = Boolean(v2 && !options.runtime && !options.plannerFactory);
79
+ const sessions = new Map();
80
+ const maxBodyBytes = options.maxBodyBytes ?? 1_000_000;
81
+ const runningIdentity = runtimeIdentity();
82
+ const server = createServer(async (req, res)=>{
83
+ const isProbe = req.method === 'GET' && (req.url === '/healthz' || req.url === '/status');
84
+ if (!isProbe && (req.method !== 'POST' || req.url !== '/a2a/booking-copilot/turn')) {
85
+ sendJson(res, 404, {
86
+ error: {
87
+ code: 'not_found'
88
+ }
89
+ });
90
+ return;
91
+ }
92
+ const auth = String(req.headers.authorization ?? '');
93
+ if (!safeSecretEqual(auth, `Bearer ${options.apiKey}`)) {
94
+ sendJson(res, 401, {
95
+ error: {
96
+ code: 'unauthorized'
97
+ }
98
+ });
99
+ return;
100
+ }
101
+ if (isProbe) {
102
+ const activeV2Only = Boolean(v2 && !options.runtime && !options.plannerFactory);
103
+ res.setHeader(BOOKING_SURFACE_VERSION_HEADER, activeV2Only ? BOOKING_SURFACE_SCHEMA_VERSION_V2 : BOOKING_SURFACE_SCHEMA_VERSION);
104
+ res.setHeader(BOOKING_SURFACE_SCHEMA_SHA256_HEADER, activeV2Only ? BOOKING_SURFACE_SCHEMA_V2_SHA256 : BOOKING_SURFACE_SCHEMA_SHA256);
105
+ if (options.artifactId) res.setHeader('X-GoTry-Artifact-ID', options.artifactId);
106
+ res.setHeader('X-GoTry-Node-Version', runningIdentity.nodeVersion);
107
+ res.setHeader('X-GoTry-Node-Modules-ABI', runningIdentity.nodeModulesAbi);
108
+ res.setHeader('X-GoTry-Release-Tuple', runningIdentity.releaseTuple);
109
+ if (runningIdentity.glibcVersion) res.setHeader('X-GoTry-Glibc-Version', runningIdentity.glibcVersion);
110
+ const healthBody = {
111
+ schemaVersion: activeV2Only ? BOOKING_SURFACE_SCHEMA_VERSION_V2 : BOOKING_SURFACE_SCHEMA_VERSION,
112
+ schemaSha256: activeV2Only ? BOOKING_SURFACE_SCHEMA_V2_SHA256 : BOOKING_SURFACE_SCHEMA_SHA256,
113
+ status: 'ready'
114
+ };
115
+ if (activeV2Only || v2) healthBody.supportedSchemaVersions = activeV2Only ? [
116
+ BOOKING_SURFACE_SCHEMA_VERSION_V2
117
+ ] : [
118
+ BOOKING_SURFACE_SCHEMA_VERSION,
119
+ BOOKING_SURFACE_SCHEMA_VERSION_V2
120
+ ];
121
+ if (v2) {
122
+ const acceptedTurnKinds = v2.ingressMode === 'bff-ingress-binding' ? [
123
+ ...BOOKING_COPILOT_V2_ACCEPTED_TURN_KINDS,
124
+ BOOKING_COPILOT_V2_INGRESS_TURN_KIND
125
+ ] : [
126
+ ...BOOKING_COPILOT_V2_ACCEPTED_TURN_KINDS
127
+ ];
128
+ healthBody.ingressMode = v2.ingressMode;
129
+ healthBody.acceptedTurnKinds = acceptedTurnKinds;
130
+ res.setHeader('X-GoTry-Ingress-Mode', v2.ingressMode);
131
+ res.setHeader('X-GoTry-Accepted-Turn-Kinds', acceptedTurnKinds.join(','));
132
+ }
133
+ sendJson(res, 200, healthBody);
134
+ return;
135
+ }
136
+ const schemaVersion = String(req.headers[BOOKING_SURFACE_VERSION_HEADER] ?? '');
137
+ const schemaHash = String(req.headers[BOOKING_SURFACE_SCHEMA_SHA256_HEADER] ?? '');
138
+ if (schemaVersion === BOOKING_SURFACE_SCHEMA_VERSION_V2) {
139
+ if (!v2 || schemaHash !== BOOKING_SURFACE_SCHEMA_V2_SHA256) {
140
+ sendJson(res, 409, {
141
+ error: {
142
+ code: 'booking_surface_schema_mismatch',
143
+ expectedVersion: BOOKING_SURFACE_SCHEMA_VERSION_V2,
144
+ expectedSchemaSha256: BOOKING_SURFACE_SCHEMA_V2_SHA256
145
+ }
146
+ });
147
+ return;
148
+ }
149
+ await handleBookingCopilotV2Request(req, res, v2, maxBodyBytes);
150
+ return;
151
+ }
152
+ if (schemaVersion !== BOOKING_SURFACE_SCHEMA_VERSION || schemaHash !== BOOKING_SURFACE_SCHEMA_SHA256) {
153
+ sendJson(res, 409, {
154
+ error: {
155
+ code: 'booking_surface_schema_mismatch',
156
+ expectedVersion: BOOKING_SURFACE_SCHEMA_VERSION,
157
+ expectedSchemaSha256: BOOKING_SURFACE_SCHEMA_SHA256
158
+ }
159
+ });
160
+ return;
161
+ }
162
+ if (!options.runtime || !options.plannerFactory) {
163
+ sendJson(res, 503, {
164
+ error: {
165
+ code: 'booking_surface_v1_not_configured'
166
+ }
167
+ });
168
+ return;
169
+ }
170
+ let turn;
171
+ try {
172
+ const raw = await readBody(req, maxBodyBytes);
173
+ const parsed = JSON.parse(raw);
174
+ const validation = validateBookingCopilotTurnV1(parsed);
175
+ if (!validation.ok) {
176
+ sendJson(res, 400, {
177
+ error: {
178
+ code: 'invalid_booking_surface_turn',
179
+ details: validation.errors
180
+ }
181
+ });
182
+ return;
183
+ }
184
+ turn = parsed;
185
+ } catch (error) {
186
+ const code = normalizeBookingErrorCode(error);
187
+ sendJson(res, code === 'PAYLOAD_TOO_LARGE' ? 413 : 400, {
188
+ error: {
189
+ code
190
+ }
191
+ });
192
+ return;
193
+ }
194
+ let task;
195
+ let isNewTask = false;
196
+ try {
197
+ const taskId = turn.taskId;
198
+ if (v2 && taskId && v2.runtime.resumeTask(taskId)) throw new Error('task_conflict:protocol_version');
199
+ if (turn.kind === 'user.turn') {
200
+ const suppliedTaskId = turn.taskId;
201
+ isNewTask = !suppliedTaskId || options.runtime.resumeTask(suppliedTaskId) === null;
202
+ task = options.runtime.startTask(turn);
203
+ } else {
204
+ task = options.runtime.continueWithReceipt(turn);
205
+ }
206
+ } catch (error) {
207
+ sendJson(res, 409, {
208
+ error: {
209
+ code: normalizeBookingErrorCode(error)
210
+ }
211
+ });
212
+ return;
213
+ }
214
+ res.writeHead(200, {
215
+ 'content-type': 'text/event-stream; charset=utf-8',
216
+ 'cache-control': 'no-cache, no-transform',
217
+ connection: 'keep-alive',
218
+ 'x-content-type-options': 'nosniff',
219
+ [BOOKING_SURFACE_VERSION_HEADER]: BOOKING_SURFACE_SCHEMA_VERSION,
220
+ [BOOKING_SURFACE_SCHEMA_SHA256_HEADER]: BOOKING_SURFACE_SCHEMA_SHA256
221
+ });
222
+ try {
223
+ if (isNewTask) writeSse(res, options.runtime.emitEvent(task.taskId, {
224
+ kind: 'status',
225
+ status: 'submitted'
226
+ }));
227
+ writeSse(res, options.runtime.emitEvent(task.taskId, {
228
+ kind: 'status',
229
+ status: 'working'
230
+ }));
231
+ task = options.runtime.resumeTask(task.taskId) ?? task;
232
+ let session = sessions.get(task.taskId);
233
+ if (!session) {
234
+ session = options.plannerFactory(task);
235
+ sessions.set(task.taskId, session);
236
+ }
237
+ const decisions = await session.next({
238
+ turn,
239
+ task
240
+ });
241
+ if (decisions.length === 0) {
242
+ writeSse(res, options.runtime.emitEvent(task.taskId, {
243
+ kind: 'error',
244
+ error: {
245
+ code: 'PLANNER_NO_DECISION',
246
+ message: 'Planner returned no typed decision.',
247
+ retryable: false
248
+ }
249
+ }));
250
+ } else {
251
+ for (const decision of decisions){
252
+ const event = decision.kind === 'operation' ? options.runtime.issueOperation(task.taskId, decision.action) : options.runtime.emitEvent(task.taskId, decision);
253
+ writeSse(res, event);
254
+ }
255
+ }
256
+ } catch (error) {
257
+ try {
258
+ writeSse(res, options.runtime.emitEvent(task.taskId, {
259
+ kind: 'error',
260
+ error: {
261
+ code: normalizeBookingErrorCode(error),
262
+ message: safeBookingErrorMessage(normalizeBookingErrorCode(error)),
263
+ retryable: false
264
+ }
265
+ }));
266
+ } catch {
267
+ writeSse(res, {
268
+ schemaVersion: 'booking.surface.v1',
269
+ eventId: `error-${task.taskId}-${task.lastSequence + 1}`,
270
+ taskId: task.taskId,
271
+ contextRef: task.contextRef,
272
+ sequence: task.lastSequence + 1,
273
+ emittedAt: new Date().toISOString(),
274
+ kind: 'error',
275
+ error: {
276
+ code: normalizeBookingErrorCode(error),
277
+ message: safeBookingErrorMessage(normalizeBookingErrorCode(error)),
278
+ retryable: false
279
+ }
280
+ });
281
+ }
282
+ } finally{
283
+ res.end();
284
+ }
285
+ });
286
+ return new Promise((resolve, reject)=>{
287
+ server.once('error', reject);
288
+ server.listen(options.port ?? 0, options.host ?? '127.0.0.1', ()=>{
289
+ server.off('error', reject);
290
+ const address = server.address();
291
+ const port = typeof address === 'object' && address ? address.port : options.port ?? 0;
292
+ resolve({
293
+ server,
294
+ port,
295
+ close: ()=>new Promise((done, fail)=>server.close((error)=>error ? fail(error) : done()))
296
+ });
297
+ });
298
+ });
299
+ }
300
+
301
+
302
+ //# sourceURL=ts/src/booking-surface/server.ts
@@ -0,0 +1,159 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { isAbsolute, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { ensureLedger } from '../state-ledger.js';
5
+ import { buildDshPlannerEnvironment, createDshEmbeddedBookingPlanner, createDshEmbeddedBookingPlannerV2 } from './dsh-planner.js';
6
+ import { BookingCopilotTaskRuntime } from './runtime.js';
7
+ import { BookingCopilotTaskRuntimeV2 } from './runtime-v2.js';
8
+ import { BOOKING_COPILOT_V2_INGRESS_MODES } from './contracts-v2.js';
9
+ import { startBookingCopilotServer } from './server.js';
10
+ const DEFAULT_DEPENDENCIES = {
11
+ ensureLedger,
12
+ runtimeFactory: (ledger)=>new BookingCopilotTaskRuntime(ledger),
13
+ createPlanner: createDshEmbeddedBookingPlanner,
14
+ runtimeFactoryV2: (ledger)=>new BookingCopilotTaskRuntimeV2(ledger),
15
+ createPlannerV2: createDshEmbeddedBookingPlannerV2,
16
+ startServer: startBookingCopilotServer
17
+ };
18
+ function parsePort(raw) {
19
+ if (raw === undefined || raw === '') return 3_082;
20
+ const value = Number(raw);
21
+ if (!Number.isSafeInteger(value) || value < 0 || value > 65_535) throw new Error('booking_copilot_invalid_port');
22
+ return value;
23
+ }
24
+ export function resolveBookingCopilotStartupConfig(env = process.env) {
25
+ const apiKey = env.GOTRY_BOOKING_COPILOT_API_KEY ?? '';
26
+ if (!apiKey) throw new Error('booking_copilot_api_key_required');
27
+ const stateRoot = env.GOTRY_BOOKING_COPILOT_STATE_ROOT ?? '';
28
+ if (!stateRoot) throw new Error('booking_copilot_state_root_required');
29
+ if (!isAbsolute(stateRoot)) throw new Error('booking_copilot_state_root_must_be_absolute');
30
+ const artifactId = env.GOTRY_BOOKING_COPILOT_ARTIFACT_ID || undefined;
31
+ if (artifactId !== undefined && !/^[0-9a-f]{40}$/.test(artifactId)) {
32
+ throw new Error('booking_copilot_artifact_id_invalid');
33
+ }
34
+ const rawIngressMode = env.GOTRY_BOOKING_COPILOT_V2_INGRESS_MODE;
35
+ const ingressMode = rawIngressMode === undefined || rawIngressMode === '' ? 'bff-bound-turn-only' : rawIngressMode;
36
+ if (!BOOKING_COPILOT_V2_INGRESS_MODES.includes(ingressMode)) {
37
+ throw new Error('booking_copilot_v2_ingress_mode_invalid');
38
+ }
39
+ return {
40
+ apiKey,
41
+ stateRoot,
42
+ host: env.GOTRY_BOOKING_COPILOT_HOST || '127.0.0.1',
43
+ port: parsePort(env.GOTRY_BOOKING_COPILOT_PORT),
44
+ artifactId,
45
+ ingressMode: ingressMode
46
+ };
47
+ }
48
+ async function closeAll(server, planner, plannerV2, ledger) {
49
+ const failures = [];
50
+ for (const close of [
51
+ server ? ()=>server.close() : undefined,
52
+ planner ? ()=>planner.close() : undefined,
53
+ plannerV2 ? ()=>plannerV2.close() : undefined,
54
+ ledger ? ()=>Promise.resolve(ledger.close()) : undefined
55
+ ]){
56
+ if (!close) continue;
57
+ try {
58
+ await close();
59
+ } catch (error) {
60
+ failures.push(error);
61
+ }
62
+ }
63
+ if (failures.length === 1) throw failures[0];
64
+ if (failures.length > 1) throw new AggregateError(failures, 'booking_copilot_shutdown_failed');
65
+ }
66
+ export async function startBookingCopilotFromEnvironment(env = process.env, dependencies = DEFAULT_DEPENDENCIES) {
67
+ const config = resolveBookingCopilotStartupConfig(env);
68
+ const activeDependencies = dependencies;
69
+ const hasIngressBinding = Boolean(activeDependencies.ingressBinding);
70
+ const hasPrincipal = Boolean(activeDependencies.principal);
71
+ if (hasIngressBinding !== hasPrincipal) throw new Error('booking_copilot_v2_ingress_binding_pair_required');
72
+ const ingressPair = hasIngressBinding && hasPrincipal;
73
+ if (ingressPair && env.GOTRY_BOOKING_COPILOT_V2_INGRESS_MODE !== undefined && config.ingressMode === 'bff-bound-turn-only') {
74
+ throw new Error('booking_copilot_v2_ingress_mode_conflict');
75
+ }
76
+ if (!ingressPair && config.ingressMode === 'bff-ingress-binding') throw new Error('booking_copilot_v2_ingress_binding_required');
77
+ if (ingressPair && (typeof activeDependencies.ingressBinding?.bind !== 'function' || typeof activeDependencies.principal?.subject !== 'string' || activeDependencies.principal.subject.length === 0 || activeDependencies.principal.subject.length > 256 || typeof activeDependencies.principal?.scope !== 'string' || activeDependencies.principal.scope.length === 0 || activeDependencies.principal.scope.length > 256)) {
78
+ throw new Error('booking_copilot_v2_ingress_binding_invalid');
79
+ }
80
+ const activeIngressMode = ingressPair ? 'bff-ingress-binding' : 'bff-bound-turn-only';
81
+ mkdirSync(config.stateRoot, {
82
+ recursive: true
83
+ });
84
+ let ledger;
85
+ let planner;
86
+ let plannerV2;
87
+ let server;
88
+ try {
89
+ ledger = dependencies.ensureLedger(config.stateRoot);
90
+ const plannerOptions = {
91
+ stateRoot: config.stateRoot,
92
+ env: buildDshPlannerEnvironment(env)
93
+ };
94
+ if (!activeDependencies.runtimeFactoryV2 || !activeDependencies.createPlannerV2) throw new Error('booking_copilot_v2_startup_dependency_missing');
95
+ const runtime = activeDependencies.runtimeFactory(ledger);
96
+ planner = await activeDependencies.createPlanner(plannerOptions);
97
+ const runtimeV2 = activeDependencies.runtimeFactoryV2(ledger);
98
+ plannerV2 = await activeDependencies.createPlannerV2(plannerOptions);
99
+ server = await activeDependencies.startServer({
100
+ apiKey: config.apiKey,
101
+ runtime,
102
+ plannerFactory: planner.plannerFactory,
103
+ v2: {
104
+ runtime: runtimeV2,
105
+ plannerFactory: plannerV2.plannerFactory,
106
+ ingressBinding: activeDependencies.ingressBinding,
107
+ principal: activeDependencies.principal,
108
+ ingressMode: activeIngressMode
109
+ },
110
+ host: config.host,
111
+ port: config.port,
112
+ artifactId: config.artifactId
113
+ });
114
+ } catch (error) {
115
+ try {
116
+ await closeAll(server, planner, plannerV2, ledger);
117
+ } catch (cleanupError) {
118
+ throw new AggregateError([
119
+ error,
120
+ cleanupError
121
+ ], 'booking_copilot_startup_and_cleanup_failed');
122
+ }
123
+ throw error;
124
+ }
125
+ let closed = false;
126
+ return {
127
+ port: server.port,
128
+ async close () {
129
+ if (closed) return;
130
+ closed = true;
131
+ await closeAll(server, planner, plannerV2, ledger);
132
+ }
133
+ };
134
+ }
135
+ async function runCli() {
136
+ const handle = await startBookingCopilotFromEnvironment();
137
+ process.stderr.write(`[gotry-booking-copilot] listening on http://127.0.0.1:${handle.port}/a2a/booking-copilot/turn\n`);
138
+ let stopping = false;
139
+ const stop = ()=>{
140
+ if (stopping) return;
141
+ stopping = true;
142
+ void handle.close().then(()=>process.exit(0), (error)=>{
143
+ process.stderr.write(`[gotry-booking-copilot] shutdown failed: ${error instanceof Error ? error.message : String(error)}\n`);
144
+ process.exit(1);
145
+ });
146
+ };
147
+ process.once('SIGINT', stop);
148
+ process.once('SIGTERM', stop);
149
+ }
150
+ const invokedPath = process.argv[1] ? resolve(process.argv[1]) : '';
151
+ if (invokedPath === fileURLToPath(import.meta.url)) {
152
+ runCli().catch((error)=>{
153
+ process.stderr.write(`[gotry-booking-copilot] startup failed: ${error instanceof Error ? error.message : String(error)}\n`);
154
+ process.exit(1);
155
+ });
156
+ }
157
+
158
+
159
+ //# sourceURL=ts/src/booking-surface/startup.ts