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

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 (139) hide show
  1. package/README.md +10 -6
  2. package/README.zh-CN.md +10 -6
  3. package/bin/gotry-bootstrap.js +294 -5
  4. package/cordis.gotry-patch.yml +10 -0
  5. package/data/airline-airports.json +39 -0
  6. package/dist/capabilities/agent-reach-deep.js +1 -1
  7. package/dist/capabilities/agent-reach.js +1 -1
  8. package/dist/capabilities/anything.js +1 -1
  9. package/dist/capabilities/artifacts.js +1 -1
  10. package/dist/capabilities/effect.js +293 -0
  11. package/dist/capabilities/fact-log.js +41 -0
  12. package/dist/capabilities/flyai.js +1 -1
  13. package/dist/capabilities/hbcli.js +43 -1
  14. package/dist/capabilities/incident-log.js +1 -1
  15. package/dist/capabilities/opensky.js +1 -1
  16. package/dist/capabilities/resilience.js +90 -0
  17. package/dist/capabilities/session/action-cache.js +1 -1
  18. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  19. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  20. package/dist/capabilities/session/benchmark.js +2 -1
  21. package/dist/capabilities/session/extension-bridge.js +345 -0
  22. package/dist/capabilities/session/extension-channel.js +93 -0
  23. package/dist/capabilities/session/extract.js +1 -1
  24. package/dist/capabilities/session/health-watch.js +216 -0
  25. package/dist/capabilities/session/read-guard.js +1 -1
  26. package/dist/capabilities/session/transport.js +1 -1
  27. package/dist/capabilities/session/wizard.js +377 -0
  28. package/dist/capabilities/session-consent.js +1 -1
  29. package/dist/capabilities/session-login.js +77 -1
  30. package/dist/capabilities/session-search.js +69 -2
  31. package/dist/capabilities/weather.js +1 -1
  32. package/dist/scripts/action-cache-tests.js +1 -1
  33. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  34. package/dist/scripts/agent-reach-tests.js +1 -1
  35. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  36. package/dist/scripts/anything-tests.js +1 -1
  37. package/dist/scripts/async-collect.js +1 -1
  38. package/dist/scripts/booking-saga-tests.js +1 -1
  39. package/dist/scripts/bootstrap-tests.js +39 -3
  40. package/dist/scripts/build-changelog.js +279 -0
  41. package/dist/scripts/changelog-tests.js +165 -0
  42. package/dist/scripts/companion-tests.js +1 -1
  43. package/dist/scripts/diff-test.js +1 -1
  44. package/dist/scripts/effect-tests.js +474 -0
  45. package/dist/scripts/engine-run.js +1 -1
  46. package/dist/scripts/engine-tests.js +1 -1
  47. package/dist/scripts/extension-tests.js +468 -0
  48. package/dist/scripts/fact-gate-tests.js +318 -0
  49. package/dist/scripts/flyai-tests.js +1 -1
  50. package/dist/scripts/hbcli-e2e-tests.js +180 -0
  51. package/dist/scripts/hbcli-tests.js +1 -1
  52. package/dist/scripts/health-watch-cli.js +51 -0
  53. package/dist/scripts/i18n-tests.js +1 -1
  54. package/dist/scripts/incident-tests.js +1 -1
  55. package/dist/scripts/journey-tests.js +1 -1
  56. package/dist/scripts/ledger-tests.js +1 -1
  57. package/dist/scripts/ledger-workflow-crash.js +1 -1
  58. package/dist/scripts/memory-capture-tests.js +1 -1
  59. package/dist/scripts/memory-decay-tests.js +1 -1
  60. package/dist/scripts/memory-metrics.js +1 -1
  61. package/dist/scripts/memory-value-report.js +1 -1
  62. package/dist/scripts/nightly-evidence-tests.js +67 -4
  63. package/dist/scripts/nightly-evidence.js +59 -3
  64. package/dist/scripts/nudge-digest.js +1 -1
  65. package/dist/scripts/onboarding-tests.js +218 -0
  66. package/dist/scripts/opensky-check.js +1 -1
  67. package/dist/scripts/opensky-tests.js +1 -1
  68. package/dist/scripts/price-drift-tests.js +441 -0
  69. package/dist/scripts/price-drift-watch.js +491 -0
  70. package/dist/scripts/probe-poi-tests.js +1 -1
  71. package/dist/scripts/product-metrics.js +1 -1
  72. package/dist/scripts/publish-preverify.js +1 -1
  73. package/dist/scripts/realtime-pricing-tests.js +1 -1
  74. package/dist/scripts/replay-async.js +1 -1
  75. package/dist/scripts/replay-real.js +1 -1
  76. package/dist/scripts/replay.js +1 -1
  77. package/dist/scripts/session-attach-diagnose.js +1 -1
  78. package/dist/scripts/session-attach-poc.js +1 -1
  79. package/dist/scripts/session-benchmark.js +1 -1
  80. package/dist/scripts/session-extract-tests.js +1 -1
  81. package/dist/scripts/session-login.js +1 -1
  82. package/dist/scripts/session-tests.js +1 -1
  83. package/dist/scripts/skeleton-check.js +1 -1
  84. package/dist/scripts/skeleton-integration-test.js +1 -1
  85. package/dist/scripts/skills-contract-tests.js +1 -1
  86. package/dist/scripts/smoke.js +76 -1
  87. package/dist/scripts/state-cli-tests.js +1 -1
  88. package/dist/scripts/state-cli.js +1 -1
  89. package/dist/scripts/time-eval-tests.js +1 -1
  90. package/dist/scripts/travel-timeline-tests.js +1 -1
  91. package/dist/scripts/unified-tests.js +1 -1
  92. package/dist/scripts/weather-tests.js +1 -1
  93. package/dist/scripts/wizard-bootstrap.js +32 -0
  94. package/dist/scripts/z3-race-tests.js +1 -1
  95. package/dist/src/artifact-gate.js +310 -0
  96. package/dist/src/bookable-facts.js +349 -0
  97. package/dist/src/booking-saga.js +1 -1
  98. package/dist/src/bridge.js +1 -1
  99. package/dist/src/companions.js +1 -1
  100. package/dist/src/contracts.js +1 -1
  101. package/dist/src/dsh-llm.js +1 -1
  102. package/dist/src/engine.js +1 -1
  103. package/dist/src/i18n.js +1 -1
  104. package/dist/src/index.js +189 -46
  105. package/dist/src/journey.js +1 -1
  106. package/dist/src/loop.js +1 -1
  107. package/dist/src/memory-capture.js +1 -1
  108. package/dist/src/memory-decay.js +1 -1
  109. package/dist/src/memory-utility.js +1 -1
  110. package/dist/src/mock-llm.js +1 -1
  111. package/dist/src/model.js +1 -1
  112. package/dist/src/realtime-pricing.js +14 -7
  113. package/dist/src/slot-spec.js +1 -1
  114. package/dist/src/state-ledger.js +1 -1
  115. package/dist/src/time-anchor.js +1 -1
  116. package/dist/src/tool-packet.js +1 -1
  117. package/dist/src/travel-slots.js +1 -1
  118. package/dist/src/travel-timeline.js +1 -1
  119. package/dist/src/unified.js +1 -1
  120. package/dist/src/wish-pool.js +1 -1
  121. package/dist/src/z3-shared.js +1 -1
  122. package/extension/README.md +26 -0
  123. package/extension/background.js +160 -0
  124. package/extension/content-bridge.js +41 -0
  125. package/extension/content-main.js +62 -0
  126. package/extension/manifest.json +32 -0
  127. package/package.json +6 -2
  128. package/ts/capabilities/fact-log.ts +54 -0
  129. package/ts/capabilities/hbcli.ts +46 -0
  130. package/ts/capabilities/session/benchmark.ts +4 -1
  131. package/ts/capabilities/session/extension-bridge.ts +354 -0
  132. package/ts/capabilities/session/extension-channel.ts +92 -0
  133. package/ts/capabilities/session/health-watch.ts +257 -0
  134. package/ts/capabilities/session/wizard.ts +282 -0
  135. package/ts/capabilities/session-login.ts +53 -2
  136. package/ts/capabilities/session-search.ts +68 -3
  137. package/ts/src/artifact-gate.ts +365 -0
  138. package/ts/src/bookable-facts.ts +557 -0
  139. package/ts/src/index.ts +102 -8
@@ -0,0 +1,349 @@
1
+ import { createHash } from 'node:crypto';
2
+ export const BOOKABLE_FACT_SCHEMA = 'gotry_bookable_fact.v1';
3
+ function sha16(text) {
4
+ return createHash('sha256').update(text).digest('hex').slice(0, 16);
5
+ }
6
+ export function makeQueryId(source, kind, origin, destination, date) {
7
+ return `${source}:${kind}:${origin}-${destination}:${date}`;
8
+ }
9
+ export function makeFactId(parts) {
10
+ return sha16(parts.map((p)=>p ?? '').join('|'));
11
+ }
12
+ function iataOf(alias, city) {
13
+ return alias?.[city] ?? city;
14
+ }
15
+ const YMD = /^\d{4}-\d{2}-\d{2}$/;
16
+ function hmOf(dateTime) {
17
+ const m = dateTime?.match(/T?(\d{2}:\d{2})/);
18
+ return m?.[1];
19
+ }
20
+ function dateOf(dateTime, fallback) {
21
+ const m = dateTime?.match(/^(\d{4}-\d{2}-\d{2})/);
22
+ return m?.[1] ?? fallback;
23
+ }
24
+ export function negativeFact(queryId, kind, origin, destination, date, source, fetchedAt, alias) {
25
+ return {
26
+ schema: BOOKABLE_FACT_SCHEMA,
27
+ fact_id: makeFactId([
28
+ 'neg',
29
+ kind,
30
+ iataOf(alias, origin),
31
+ iataOf(alias, destination),
32
+ date,
33
+ queryId
34
+ ]),
35
+ kind,
36
+ route: {
37
+ origin: iataOf(alias, origin),
38
+ destination: iataOf(alias, destination)
39
+ },
40
+ date,
41
+ flight_no: '',
42
+ tier: 'live_inventory',
43
+ bookability: 'unavailable_exact_date',
44
+ source,
45
+ query_id: queryId,
46
+ fetched_at: fetchedAt,
47
+ as_of: fetchedAt.slice(0, 10)
48
+ };
49
+ }
50
+ export function factsFromFlyai(q, r, fetchedAt, alias) {
51
+ const source = 'flyai';
52
+ const queryId = makeQueryId(source, q.kind, q.origin, q.destination, q.date);
53
+ const origin = iataOf(alias, q.origin);
54
+ const destination = iataOf(alias, q.destination);
55
+ if (r.verdict === 'error') return [];
56
+ if (r.verdict === 'miss') return [
57
+ negativeFact(queryId, q.kind, q.origin, q.destination, q.date, source, fetchedAt, alias)
58
+ ];
59
+ const facts = [];
60
+ for (const o of r.options ?? []){
61
+ if (!o.no || !o.depDateTime) continue;
62
+ const numeric = typeof o.price === 'number' && Number.isFinite(o.price) && o.price > 0 ? o.price : undefined;
63
+ facts.push({
64
+ schema: BOOKABLE_FACT_SCHEMA,
65
+ fact_id: makeFactId([
66
+ q.kind,
67
+ origin,
68
+ destination,
69
+ q.date,
70
+ o.no,
71
+ queryId
72
+ ]),
73
+ kind: q.kind,
74
+ route: {
75
+ origin,
76
+ destination,
77
+ origin_airport: o.depStation,
78
+ dest_airport: o.arrStation
79
+ },
80
+ date: dateOf(o.depDateTime, q.date),
81
+ flight_no: o.no,
82
+ marketing_carrier: o.name || undefined,
83
+ operating_carrier: undefined,
84
+ dep_local: hmOf(o.depDateTime),
85
+ arr_local: hmOf(o.arrDateTime),
86
+ nonstop: o.nonstop !== false,
87
+ price: numeric,
88
+ currency: numeric !== undefined ? 'CNY' : undefined,
89
+ tier: 'live_inventory',
90
+ bookability: 'bookable_exact_date',
91
+ source,
92
+ query_id: queryId,
93
+ fetched_at: fetchedAt,
94
+ as_of: fetchedAt.slice(0, 10)
95
+ });
96
+ }
97
+ return facts.length > 0 ? facts : [
98
+ negativeFact(queryId, q.kind, q.origin, q.destination, q.date, source, fetchedAt, alias)
99
+ ];
100
+ }
101
+ export function factsFromSession(q, r, fetchedAt, alias) {
102
+ const source = 'session:ctrip-flight';
103
+ const queryId = makeQueryId(source, 'flight', q.origin, q.destination, q.date);
104
+ const origin = iataOf(alias, q.origin);
105
+ const destination = iataOf(alias, q.destination);
106
+ if (r.verdict !== 'hit') {
107
+ return r.verdict === 'miss' ? [
108
+ negativeFact(queryId, 'flight', q.origin, q.destination, q.date, source, fetchedAt, alias)
109
+ ] : [];
110
+ }
111
+ const facts = [];
112
+ for (const o of r.options ?? []){
113
+ if (!o.flightNo || !o.depDateTime) continue;
114
+ const numeric = typeof o.price === 'number' && Number.isFinite(o.price) && o.price > 0 ? o.price : undefined;
115
+ facts.push({
116
+ schema: BOOKABLE_FACT_SCHEMA,
117
+ fact_id: makeFactId([
118
+ 'flight',
119
+ origin,
120
+ destination,
121
+ q.date,
122
+ o.flightNo,
123
+ queryId
124
+ ]),
125
+ kind: 'flight',
126
+ route: {
127
+ origin,
128
+ destination,
129
+ origin_airport: o.depAirport,
130
+ dest_airport: o.arrAirport
131
+ },
132
+ date: dateOf(o.depDateTime, q.date),
133
+ flight_no: o.flightNo,
134
+ marketing_carrier: o.airline || undefined,
135
+ operating_carrier: undefined,
136
+ dep_local: hmOf(o.depDateTime),
137
+ arr_local: hmOf(o.arrDateTime),
138
+ nonstop: true,
139
+ price: numeric,
140
+ currency: numeric !== undefined ? 'CNY' : undefined,
141
+ tier: 'live_inventory',
142
+ bookability: 'bookable_exact_date',
143
+ source,
144
+ query_id: queryId,
145
+ fetched_at: fetchedAt,
146
+ as_of: fetchedAt.slice(0, 10)
147
+ });
148
+ }
149
+ return facts;
150
+ }
151
+ export function dedupeFacts(facts) {
152
+ const byId = new Map();
153
+ for (const f of facts){
154
+ const prev = byId.get(f.fact_id);
155
+ if (!prev || prev.fetched_at <= f.fetched_at) byId.set(f.fact_id, f);
156
+ }
157
+ return [
158
+ ...byId.values()
159
+ ].sort((a, b)=>a.fetched_at.localeCompare(b.fetched_at) || a.fact_id.localeCompare(b.fact_id));
160
+ }
161
+ export function latestFactsForRouteDate(facts, origin, destination, date) {
162
+ const hits = facts.filter((f)=>f.kind !== 'policy' && f.route.origin === origin && f.route.destination === destination && f.date === date);
163
+ const latestQueryTs = new Map();
164
+ for (const f of hits){
165
+ const prev = latestQueryTs.get(f.query_id);
166
+ if (!prev || prev < f.fetched_at) latestQueryTs.set(f.query_id, f.fetched_at);
167
+ }
168
+ const latestTs = [
169
+ ...latestQueryTs.values()
170
+ ].sort().pop();
171
+ if (!latestTs) return [];
172
+ return hits.filter((f)=>f.fetched_at === latestTs);
173
+ }
174
+ export function flightClaimVerdict(facts, claim) {
175
+ const no = claim.flight_no.toUpperCase().replace(/\s+/g, '');
176
+ const sameNo = facts.filter((f)=>f.kind !== 'policy' && f.flight_no.toUpperCase() === no && f.bookability === 'bookable_exact_date');
177
+ const routeMatched = sameNo.filter((f)=>(!claim.origin || f.route.origin === claim.origin || f.route.origin_airport === claim.origin) && (!claim.destination || f.route.destination === claim.destination || f.route.dest_airport === claim.destination) && (!claim.date || f.date === claim.date));
178
+ if (routeMatched.length > 0) return {
179
+ verdict: 'traceable',
180
+ fact: routeMatched[0],
181
+ reason: `回溯 ${routeMatched[0].query_id}`
182
+ };
183
+ if (claim.origin && claim.destination && claim.date) {
184
+ const scoped = latestFactsForRouteDate(facts, claim.origin, claim.destination, claim.date);
185
+ if (scoped.length > 0) {
186
+ return {
187
+ verdict: 'not_in_source',
188
+ reason: scoped.some((f)=>f.bookability === 'unavailable_exact_date') ? `${claim.origin}→${claim.destination} ${claim.date} exact-date 源返回 0 条(${scoped[0].query_id})——不得用历史班期/相邻日期填充` : `${claim.origin}→${claim.destination} ${claim.date} exact-date 源在架 ${scoped.filter((f)=>f.bookability === 'bookable_exact_date').map((f)=>f.flight_no).join('/')}——无 ${no}`
189
+ };
190
+ }
191
+ return {
192
+ verdict: 'route_unqueried',
193
+ reason: `${claim.origin}→${claim.destination} ${claim.date} 从未经 exact-date 源核验`
194
+ };
195
+ }
196
+ return {
197
+ verdict: 'route_unqueried',
198
+ reason: `航班 ${no} 无可回溯的 exact-date 事实(缺 route/date 上下文)`
199
+ };
200
+ }
201
+ function hmToMin(hm) {
202
+ const [h, m] = hm.split(':').map(Number);
203
+ return (h ?? 0) * 60 + (m ?? 0);
204
+ }
205
+ function minToHm(min) {
206
+ return `${String(Math.floor(min / 60)).padStart(2, '0')}:${String(min % 60).padStart(2, '0')}`;
207
+ }
208
+ export function checkSameDayDeparture(facts, q, c) {
209
+ const earliest = hmToMin(c.errand_end_local) + c.airport_transit_min + c.checkin_deadline_min;
210
+ const scoped = latestFactsForRouteDate(facts, q.origin, q.destination, q.date);
211
+ const nonstopBookable = scoped.filter((f)=>f.bookability === 'bookable_exact_date' && f.nonstop !== false);
212
+ const feasible = [];
213
+ const excluded = [];
214
+ for (const f of nonstopBookable){
215
+ const dep = f.dep_local ? hmToMin(f.dep_local) : null;
216
+ if (dep === null) {
217
+ excluded.push({
218
+ flight_no: f.flight_no,
219
+ reason: '缺起飞时刻,不参与衔接判定'
220
+ });
221
+ } else if (dep >= earliest) {
222
+ feasible.push(f);
223
+ } else {
224
+ excluded.push({
225
+ flight_no: f.flight_no,
226
+ reason: `${f.dep_local} 起飞早于最早可接时刻 ${minToHm(earliest)}(${c.errand_end_local} 结束事务 + ${c.airport_transit_min}m 到机场 + ${c.checkin_deadline_min}m 值机)`
227
+ });
228
+ }
229
+ }
230
+ const ok = feasible.length > 0;
231
+ const suggestions = [];
232
+ if (!ok) {
233
+ const inStock = nonstopBookable.map((f)=>`${f.flight_no} ${f.dep_local ?? '?'}`).join('、');
234
+ suggestions.push(`增加 1 个${q.origin}住宿夜:改乘次日早班${inStock ? `(当日在架:${inStock})` : ''}`);
235
+ suggestions.push('或提前一天完成地面事务/提前一天出发,把同日衔接改为隔夜衔接');
236
+ }
237
+ return {
238
+ feasible: ok,
239
+ earliest_departure_local: minToHm(earliest),
240
+ feasible_flights: feasible,
241
+ excluded,
242
+ suggestions
243
+ };
244
+ }
245
+ function daysBetween(a, b) {
246
+ return Math.round((Date.parse(`${b}T00:00:00Z`) - Date.parse(`${a}T00:00:00Z`)) / 86_400_000);
247
+ }
248
+ export function itineraryInvariants(it) {
249
+ const violations = [];
250
+ const totalNights = daysBetween(it.trip_start, it.trip_end);
251
+ let hotelNights = 0;
252
+ for (const s of it.stays){
253
+ const n = daysBetween(s.check_in, s.check_out);
254
+ if (n <= 0) violations.push({
255
+ kind: 'date_order',
256
+ detail: `${s.place} 住宿 ${s.check_in}→${s.check_out} 晚数 ≤0`
257
+ });
258
+ else hotelNights += n;
259
+ }
260
+ if (hotelNights + it.onboard_nights !== totalNights) {
261
+ violations.push({
262
+ kind: 'nights_inconsistent',
263
+ detail: `酒店夜 ${hotelNights} + 机上夜 ${it.onboard_nights} = ${hotelNights + it.onboard_nights} ≠ 行程总夜数 ${totalNights}(${it.trip_start}→${it.trip_end})——夜数口径必须由结构化住宿反算,不许自然语言估算`
264
+ });
265
+ }
266
+ for (const seg of it.od_segments){
267
+ if (seg.mode === 'flight' && seg.legs < 1) {
268
+ violations.push({
269
+ kind: 'legs_inconsistent',
270
+ detail: `${seg.from}→${seg.to}(${seg.date})flight O&D 段 legs=${seg.legs}——每段至少 1 leg`
271
+ });
272
+ }
273
+ if (seg.date < it.trip_start || seg.date > it.trip_end) {
274
+ violations.push({
275
+ kind: 'date_order',
276
+ detail: `${seg.from}→${seg.to} 日期 ${seg.date} 落在行程窗 ${it.trip_start}→${it.trip_end} 之外`
277
+ });
278
+ }
279
+ }
280
+ const minSum = it.budget_items.reduce((acc, b)=>acc + b.min_cny, 0);
281
+ if (it.claimed_floor_cny !== undefined && it.claimed_floor_cny < minSum) {
282
+ violations.push({
283
+ kind: 'budget_floor_inconsistent',
284
+ detail: `预算分项最低合计 ¥${minSum.toLocaleString()} > 产物声称可压到 ¥${it.claimed_floor_cny.toLocaleString()}——下限话术不得低于分项最低合计`
285
+ });
286
+ }
287
+ return violations;
288
+ }
289
+ export function detectBacktrack(stops, nonstopEdges) {
290
+ const findings = [];
291
+ for(let i = 0; i + 2 < stops.length; i++){
292
+ const a = stops[i], b = stops[i + 1], c = stops[i + 2];
293
+ if (a !== c) continue;
294
+ const d = stops[i + 3];
295
+ const suggestion = d && nonstopEdges.has(`${b}-${d}`) ? `${b}→${d} 有直飞:改为 ${a}→${b} 后直接 ${b}→${d},消除 ${a}→${b}→${a} 回头路` : undefined;
296
+ findings.push({
297
+ kind: 'backtrack',
298
+ path: [
299
+ a,
300
+ b,
301
+ c
302
+ ],
303
+ suggestion
304
+ });
305
+ }
306
+ return findings;
307
+ }
308
+ export function defaultReviewBy(tripDate) {
309
+ const d = new Date(Date.parse(`${tripDate}T00:00:00Z`) - 30 * 86_400_000);
310
+ return d.toISOString().slice(0, 10);
311
+ }
312
+ export function renderFlightFact(f) {
313
+ const route = `${f.route.origin}→${f.route.destination}`;
314
+ const evidence = `[${f.source}@${f.fetched_at} #${f.query_id}]`;
315
+ if (f.bookability === 'unavailable_exact_date') {
316
+ const review = f.review_by ?? defaultReviewBy(f.date);
317
+ return `- ${route} ${f.date}:**未确认/当前不可售**——exact-date 源 ${f.source} 于 ${f.fetched_at.slice(0, 10)} 返回 0 条;到 ${review} 复核,不得用历史班期/相邻日期填充 ${evidence}`;
318
+ }
319
+ if (f.bookability !== 'bookable_exact_date') {
320
+ return `- ${route} ${f.date} ${f.flight_no || ''}:**未核验**(${f.bookability})——仅作参考,不得作为可下单方案呈现 ${evidence}`;
321
+ }
322
+ const carrier = f.operating_carrier && f.operating_carrier !== f.marketing_carrier ? `营销 ${f.marketing_carrier ?? '?'} / 实际承运 ${f.operating_carrier}` : f.marketing_carrier ?? '';
323
+ const times = f.dep_local && f.arr_local ? ` ${f.dep_local}→${f.arr_local}` : '';
324
+ const nonstop = f.nonstop === false ? '(中转)' : ' 直飞';
325
+ const airportNote = f.route.dest_airport && f.route.dest_airport !== f.route.destination ? `(落 ${f.route.dest_airport})` : '';
326
+ const price = f.price !== undefined ? ` ¥${f.price}` : ' 价待询';
327
+ const baggage = f.baggage_included === false ? ',不含托运行李' : '';
328
+ return `- ${route} ${f.date} ${f.flight_no}${carrier ? `(${carrier})` : ''}${times}${nonstop}${airportNote}${price}${baggage} ${evidence}`;
329
+ }
330
+ export function renderConnection(legA, legB) {
331
+ const through = legA.protected_connection === true && legB.protected_connection === true;
332
+ const head = `${legA.route.origin}→${legA.route.destination} ${legA.flight_no} + ${legB.route.origin}→${legB.route.destination} ${legB.flight_no}`;
333
+ return through ? `- ${head}:联程(同一票号/行李直挂/误机保护)` : `- ${head}:⚠️ **分票/自助转机**——两段接得上 ≠ 联程;无同一票号、行李需自取重挂、误机无保护,缓冲与风险自担`;
334
+ }
335
+ export function renderPolicyFact(p, tripStart) {
336
+ const review = p.review_by ?? (tripStart ? defaultReviewBy(tripStart) : undefined);
337
+ const reviewNote = review ? `;远期政策须复核——到 ${review} 再核验一次` : '';
338
+ return `- ${p.subject}:截至 ${p.as_of} 的现行政策——${p.statement}${reviewNote} [${p.source}@${p.fetched_at} #${p.query_id}]`;
339
+ }
340
+ export function renderNightsLine(it) {
341
+ const totalNights = daysBetween(it.trip_start, it.trip_end);
342
+ const hotelNights = it.stays.reduce((acc, s)=>acc + Math.max(0, daysBetween(s.check_in, s.check_out)), 0);
343
+ const flightOd = it.od_segments.filter((s)=>s.mode === 'flight').length;
344
+ const flightLegs = it.od_segments.filter((s)=>s.mode === 'flight').reduce((acc, s)=>acc + s.legs, 0);
345
+ return `共 ${daysBetween(it.trip_start, it.trip_end) + 1} 天 ${totalNights} 晚 = ${hotelNights} 个酒店夜 + ${it.onboard_nights} 个机上夜;航班按 O&D ${flightOd} 段(共 ${flightLegs} 个 flight legs)`;
346
+ }
347
+
348
+
349
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/bookable-facts.ts
@@ -150,4 +150,4 @@ export function sagaLegalPaths() {
150
150
  }
151
151
 
152
152
 
153
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/booking-saga.ts
153
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/booking-saga.ts
@@ -31,4 +31,4 @@ export async function recordLatency(logPath, latencyMs, kind) {
31
31
  }
32
32
 
33
33
 
34
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/bridge.ts
34
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/bridge.ts
@@ -109,4 +109,4 @@ export function upsertCompanion(profiles, patch) {
109
109
  }
110
110
 
111
111
 
112
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/companions.ts
112
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/companions.ts
@@ -61,4 +61,4 @@ export const TRIP_STATE_JSON_SCHEMA = {
61
61
  };
62
62
 
63
63
 
64
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/contracts.ts
64
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/contracts.ts
@@ -195,4 +195,4 @@ export function createOpenAICompatLlm(flightPackPath, clock = ()=>new Date()) {
195
195
  }
196
196
 
197
197
 
198
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/dsh-llm.ts
198
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/dsh-llm.ts
@@ -397,4 +397,4 @@ function renderMarkdown(req, verdicts, recommended) {
397
397
  }
398
398
 
399
399
 
400
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/engine.ts
400
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/engine.ts
package/dist/src/i18n.js CHANGED
@@ -74,4 +74,4 @@ export function enCoverage() {
74
74
  }
75
75
 
76
76
 
77
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/i18n.ts
77
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/i18n.ts