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

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 (176) hide show
  1. package/README.md +42 -16
  2. package/README.zh-CN.md +37 -15
  3. package/bin/gotry-bootstrap.js +309 -30
  4. package/bin/gotry-inner.js +74 -23
  5. package/bin/gotry-runtime-resolution.d.ts +1 -1
  6. package/bin/gotry-runtime-resolution.js +7 -8
  7. package/cordis.gotry-patch.yml +22 -8
  8. package/data/stations-12306-verify.json +524 -0
  9. package/dist/capabilities/agent-reach.js +4 -4
  10. package/dist/capabilities/anything.js +62 -29
  11. package/dist/capabilities/channel-health.js +103 -0
  12. package/dist/capabilities/channel-registry.js +213 -0
  13. package/dist/capabilities/doctor.js +307 -0
  14. package/dist/capabilities/effect.js +178 -5
  15. package/dist/capabilities/flyai.js +13 -0
  16. package/dist/capabilities/hbcli.js +28 -2
  17. package/dist/capabilities/session/adapters/ctrip-hotel.js +207 -0
  18. package/dist/capabilities/session/adapters/rail-12306.js +285 -0
  19. package/dist/capabilities/session/extension-bridge.js +14 -1
  20. package/dist/capabilities/session/extension-channel.js +4 -2
  21. package/dist/capabilities/session/extension-distribution.js +1 -1
  22. package/dist/capabilities/session-search.js +311 -0
  23. package/dist/capabilities/visa-policy.js +126 -0
  24. package/dist/scripts/agent-planning-turn-deadline-e2e.js +305 -0
  25. package/dist/scripts/agent-planning-turn-deadline-tests.js +281 -0
  26. package/dist/scripts/anything-tests.js +70 -21
  27. package/dist/scripts/benchmark-environment-bridge-e2e.js +139 -37
  28. package/dist/scripts/benchmark-environment-bridge-tests.js +1099 -306
  29. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +26 -26
  30. package/dist/scripts/{booking-copilot-availability-policy-v2-tests.js → booking-copilot-availability-policy-tests.js} +115 -115
  31. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +41 -1
  32. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +75 -120
  33. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +370 -61
  34. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +2 -2
  35. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +7 -3
  36. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +15 -9
  37. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +19 -11
  38. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +16 -52
  39. package/dist/scripts/booking-copilot-runtime-proof-tests.js +3953 -155
  40. package/dist/scripts/booking-copilot-server-proof-tests.js +58 -23
  41. package/dist/scripts/booking-copilot-startup-proof-tests.js +10 -119
  42. package/dist/scripts/booking-surface-contract-proof-tests.js +1154 -326
  43. package/dist/scripts/bootstrap-tests.js +113 -2
  44. package/dist/scripts/build-metrics-report.js +339 -0
  45. package/dist/scripts/channel-probe-tests.js +148 -0
  46. package/dist/scripts/channel-probe.js +252 -0
  47. package/dist/scripts/channel-registry-tests.js +262 -0
  48. package/dist/scripts/doctor-tests.js +175 -0
  49. package/dist/scripts/effect-tests.js +274 -1
  50. package/dist/scripts/extension-tests.js +129 -11
  51. package/dist/scripts/fact-gate-tests.js +91 -2
  52. package/dist/scripts/flyai-tests.js +17 -1
  53. package/dist/scripts/hbcli-e2e-tests.js +1 -3
  54. package/dist/scripts/hbcli-tests.js +38 -1
  55. package/dist/scripts/metrics-report-tests.js +288 -0
  56. package/dist/scripts/persona-surface-guard-tests.js +19 -0
  57. package/dist/scripts/session-tests.js +401 -1
  58. package/dist/scripts/smoke.js +100 -83
  59. package/dist/scripts/turn-handoff-collect-tests.js +216 -0
  60. package/dist/scripts/turn-handoff-collect.js +189 -0
  61. package/dist/scripts/turn-policy-tests.js +56 -0
  62. package/dist/scripts/typed-contract-canary.js +261 -0
  63. package/dist/scripts/visa-policy-tests.js +75 -0
  64. package/dist/scripts/wish-channel-gate-tests.js +111 -0
  65. package/dist/src/artifact-gate.js +91 -3
  66. package/dist/src/benchmark-agent-conformance.js +39 -16
  67. package/dist/src/benchmark-environment-bridge.js +316 -52
  68. package/dist/src/bookable-facts.js +62 -3
  69. package/dist/src/booking-surface/{availability-policy-v2.js → availability-policy.js} +89 -89
  70. package/dist/src/booking-surface/canonical-schema.js +8 -22
  71. package/dist/src/booking-surface/contracts.js +70 -19
  72. package/dist/src/booking-surface/dsh-planner.js +310 -177
  73. package/dist/src/booking-surface/dsh-plugin.js +3 -3
  74. package/dist/src/booking-surface/index.js +1 -5
  75. package/dist/src/booking-surface/profile.js +1 -1
  76. package/dist/src/booking-surface/runtime.js +1682 -247
  77. package/dist/src/booking-surface/server.js +371 -185
  78. package/dist/src/booking-surface/startup.js +17 -27
  79. package/dist/src/booking-surface/validation.js +270 -760
  80. package/dist/src/index.js +739 -150
  81. package/dist/src/turn-deadline.js +292 -0
  82. package/dist/src/turn-policy.js +146 -0
  83. package/dist/src/wish-pool.js +5 -0
  84. package/extension/background.js +23 -4
  85. package/extension/content-main.js +47 -9
  86. package/extension/manifest.json +27 -9
  87. package/package.json +7 -26
  88. package/schemas/booking.surface.schema.json +2593 -0
  89. package/ts/capabilities/agent-reach.ts +4 -4
  90. package/ts/capabilities/anything.ts +89 -38
  91. package/ts/capabilities/flyai.ts +21 -3
  92. package/ts/capabilities/hbcli.ts +53 -4
  93. package/ts/capabilities/session/action-cache.ts +1 -1
  94. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  95. package/ts/capabilities/session/adapters/rail-12306.ts +293 -0
  96. package/ts/capabilities/session/extension-bridge.ts +32 -2
  97. package/ts/capabilities/session/extension-channel.ts +9 -8
  98. package/ts/capabilities/session/extension-distribution.ts +3 -1
  99. package/ts/capabilities/session/health-watch.ts +1 -1
  100. package/ts/capabilities/session/read-guard.ts +1 -1
  101. package/ts/capabilities/session/wizard.ts +1 -1
  102. package/ts/capabilities/session-search.ts +323 -1
  103. package/ts/dsh-runtime/vendor/README.md +60 -0
  104. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  105. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +200 -0
  106. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +202 -0
  107. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +216 -0
  108. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  109. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +371 -0
  110. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  111. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +56 -0
  112. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  113. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  114. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +126 -0
  115. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  116. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +109 -0
  117. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +27 -0
  118. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  119. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  120. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +170 -0
  121. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +53 -0
  122. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  123. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  124. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  125. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  126. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +20 -0
  127. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  128. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  129. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  130. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  131. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  132. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +16 -0
  133. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +55 -0
  134. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  135. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +91 -0
  136. package/ts/package.json +9 -0
  137. package/ts/scripts/turn-handoff-collect.ts +178 -0
  138. package/ts/src/artifact-gate.ts +82 -2
  139. package/ts/src/benchmark-agent-conformance.ts +54 -15
  140. package/ts/src/benchmark-environment-bridge.ts +234 -46
  141. package/ts/src/bookable-facts.ts +109 -5
  142. package/ts/src/booking-saga.ts +1 -1
  143. package/ts/src/booking-surface/{availability-policy-v2.ts → availability-policy.ts} +124 -125
  144. package/ts/src/booking-surface/canonical-schema.js +8 -22
  145. package/ts/src/booking-surface/contracts.ts +278 -239
  146. package/ts/src/booking-surface/dsh-planner.ts +333 -194
  147. package/ts/src/booking-surface/dsh-plugin.js +3 -3
  148. package/ts/src/booking-surface/index.ts +1 -5
  149. package/ts/src/booking-surface/profile.ts +11 -11
  150. package/ts/src/booking-surface/runtime.ts +1351 -351
  151. package/ts/src/booking-surface/server.ts +282 -175
  152. package/ts/src/booking-surface/startup.ts +36 -48
  153. package/ts/src/booking-surface/validation.ts +194 -442
  154. package/ts/src/contracts.ts +1 -1
  155. package/ts/src/index.ts +492 -172
  156. package/ts/src/loop.ts +1 -1
  157. package/ts/src/state-ledger.ts +1 -1
  158. package/ts/src/turn-deadline.ts +365 -0
  159. package/ts/src/turn-policy.ts +154 -0
  160. package/ts/src/wish-pool.ts +17 -4
  161. package/dist/scripts/agent-planning-budget-e2e.js +0 -227
  162. package/dist/scripts/agent-planning-budget-tests.js +0 -173
  163. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +0 -3935
  164. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +0 -1174
  165. package/dist/src/booking-surface/contracts-v2.js +0 -89
  166. package/dist/src/booking-surface/runtime-v2.js +0 -1771
  167. package/dist/src/booking-surface/server-v2.js +0 -334
  168. package/dist/src/booking-surface/validation-v2.js +0 -319
  169. package/dist/src/tool-budget.js +0 -136
  170. package/schemas/booking.surface.v1.schema.json +0 -927
  171. package/schemas/booking.surface.v2.schema.json +0 -61
  172. package/ts/src/booking-surface/contracts-v2.ts +0 -118
  173. package/ts/src/booking-surface/runtime-v2.ts +0 -1466
  174. package/ts/src/booking-surface/server-v2.ts +0 -247
  175. package/ts/src/booking-surface/validation-v2.ts +0 -205
  176. package/ts/src/tool-budget.ts +0 -165
@@ -1,4 +1,24 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import { hbcliBinCandidates } from './hbcli.js';
3
+ function i18nText(v) {
4
+ if (!v) return '';
5
+ if (typeof v === 'string') return v;
6
+ return v.zh || v.en || v.ar || '';
7
+ }
8
+ function latlngOf(coord) {
9
+ for (const src of [
10
+ coord?.google,
11
+ coord?.gaode
12
+ ]){
13
+ const lat = typeof src?.lat === 'number' ? src.lat : undefined;
14
+ const lng = typeof src?.lng === 'number' ? src.lng : undefined;
15
+ if (lat !== undefined && lng !== undefined && (lat !== 0 || lng !== 0)) return {
16
+ latitude: lat,
17
+ longitude: lng
18
+ };
19
+ }
20
+ return {};
21
+ }
2
22
  function sh(cmd, args, opts) {
3
23
  const ctrl = new AbortController();
4
24
  const timer = setTimeout(()=>{
@@ -48,8 +68,20 @@ function sh(cmd, args, opts) {
48
68
  });
49
69
  });
50
70
  }
51
- const upperFirst = (s)=>s ? s[0].toUpperCase() + s.slice(1) : s;
52
- const lowerFirst = (s)=>s ? s[0].toLowerCase() + s.slice(1) : s;
71
+ function buildArgs(keyword, q) {
72
+ const args = [
73
+ '--json',
74
+ 'search',
75
+ 'anything',
76
+ keyword
77
+ ];
78
+ if (q.contentType) args.push('--content-type', q.contentType);
79
+ if (q.parentDestinationId !== undefined) args.push('--destination-id', String(q.parentDestinationId));
80
+ return args;
81
+ }
82
+ function upgradeHint(stderr) {
83
+ return /unknown command/i.test(stderr) ? 'hbcli 版本过旧(无 search anything 子命令)——请升级:hbcli update,或重跑 npx gotry setup' : null;
84
+ }
53
85
  export async function anythingSearch(q) {
54
86
  const started = Date.now();
55
87
  const ts = new Date().toISOString();
@@ -64,30 +96,30 @@ export async function anythingSearch(q) {
64
96
  error: 'keyword is required'
65
97
  };
66
98
  }
67
- const bin = q.hbcliBin ?? 'hbcli';
68
- const args = [
69
- bin,
70
- 'search',
71
- 'anything',
72
- kw
73
- ];
74
- if (q.contentType) args.push('--content-type', q.contentType);
75
- if (q.parentDestinationId !== undefined) {
76
- args.push('--parent-destination-id', String(q.parentDestinationId));
99
+ const args = buildArgs(kw, q);
100
+ const candidates = hbcliBinCandidates(q.hbcliBin ?? 'hbcli');
101
+ let r = {
102
+ code: -1,
103
+ stdout: '',
104
+ stderr: ''
105
+ };
106
+ for(let i = 0; i < candidates.length; i++){
107
+ r = await sh(candidates[i], args, {
108
+ timeoutMs: q.timeoutMs ?? 12_000,
109
+ env: process.env
110
+ });
111
+ if (!(r.error && i < candidates.length - 1)) break;
77
112
  }
78
- const r = await sh(bin, args.slice(1), {
79
- timeoutMs: q.timeoutMs ?? 12_000,
80
- env: process.env
81
- });
82
113
  const latencyMs = Date.now() - started;
83
114
  if (r.error || r.code !== 0) {
115
+ const raw = r.error ?? `${r.stderr.slice(0, 200)} (exit ${r.code})`;
84
116
  return {
85
117
  ok: false,
86
118
  via: 'hbcli-anything-error',
87
- evidence: `[实时API:hbcli-anything@error@${ts}] ${r.error ?? `exit ${r.code}`}`,
119
+ evidence: `[实时API:hbcli-anything@error@${ts}] ${raw}`,
88
120
  latencyMs,
89
121
  verdict: 'error',
90
- error: r.error ?? `${r.stderr.slice(0, 200)} (exit ${r.code})`
122
+ error: upgradeHint(r.stderr) ?? raw
91
123
  };
92
124
  }
93
125
  let data;
@@ -109,15 +141,19 @@ export async function anythingSearch(q) {
109
141
  const hits = rawItems.map((it)=>{
110
142
  const region = it.region ?? {};
111
143
  const hotel = it.hotel ?? {};
112
- const isHotel = it.type === 'hotel' || it.type === 'SupplierHotelList' || Boolean(hotel.id);
144
+ const isHotel = it.type === 'hotel' || Boolean(hotel.id);
145
+ const hotelCoords = latlngOf(hotel.latlngCoordinator);
146
+ const placeCoords = latlngOf(it.place?.latlngCoordinator);
147
+ const center = region.coordinates ?? {};
113
148
  return {
114
- type: isHotel ? 'hotel' : it.type === 'place' || it.type === 'City' ? lowerFirst(it.type) : 'city',
115
- name: it.name ?? region.name_en ?? region.name ?? hotel.name ?? '?',
116
- score: it.score ?? region.typeScore,
117
- latitude: hotel.latitude ?? region.latitude,
118
- longitude: hotel.longitude ?? region.longitude,
119
- destinationId: region.id,
120
- hotelId: hotel.id
149
+ type: isHotel ? 'hotel' : it.type === 'place' ? 'place' : 'city',
150
+ name: i18nText(hotel.name) || i18nText(region.name) || '?',
151
+ score: it.matchScore,
152
+ latitude: hotelCoords.latitude ?? placeCoords.latitude ?? (typeof center.centerLat === 'number' ? center.centerLat : undefined),
153
+ longitude: hotelCoords.longitude ?? placeCoords.longitude ?? (typeof center.centerLng === 'number' ? center.centerLng : undefined),
154
+ destinationId: hotel.destinationId !== undefined ? String(hotel.destinationId) : region.id !== undefined ? String(region.id) : undefined,
155
+ hotelId: hotel.id !== undefined ? String(hotel.id) : undefined,
156
+ star: typeof hotel.star === 'number' && hotel.star > 0 ? hotel.star : undefined
121
157
  };
122
158
  });
123
159
  const verdict = hits.length > 0 ? 'hit' : 'miss';
@@ -131,9 +167,6 @@ export async function anythingSearch(q) {
131
167
  totalCandidates: rawItems.length
132
168
  };
133
169
  }
134
- export const _ = {
135
- upperFirst
136
- };
137
170
 
138
171
 
139
172
  //# sourceURL=ts/capabilities/anything.ts
@@ -0,0 +1,103 @@
1
+ const session = new Map();
2
+ export const DEFAULT_COOLDOWN_MS = 30_000;
3
+ export function markChannelDown(channel, reason, now = Date.now()) {
4
+ session.set(channel, {
5
+ state: 'down',
6
+ reason,
7
+ since: now
8
+ });
9
+ return {
10
+ channel,
11
+ state: 'down',
12
+ reason,
13
+ at: new Date(now).toISOString()
14
+ };
15
+ }
16
+ export function markChannelCooldown(channel, cooldownMs = DEFAULT_COOLDOWN_MS, now = Date.now()) {
17
+ session.set(channel, {
18
+ state: 'cooldown',
19
+ until: now + cooldownMs,
20
+ since: now
21
+ });
22
+ return {
23
+ channel,
24
+ state: 'cooldown',
25
+ at: new Date(now).toISOString()
26
+ };
27
+ }
28
+ export function clearChannel(channel) {
29
+ session.delete(channel);
30
+ }
31
+ export function channelState(channel, now = Date.now()) {
32
+ const e = session.get(channel);
33
+ if (!e) return undefined;
34
+ if (e.state === 'cooldown' && (e.until ?? 0) <= now) {
35
+ session.delete(channel);
36
+ return undefined;
37
+ }
38
+ return {
39
+ state: e.state,
40
+ reason: e.reason
41
+ };
42
+ }
43
+ export function resetChannelHealth() {
44
+ session.clear();
45
+ }
46
+ export function noteChannelVerdict(channel, verdict, opts = {}) {
47
+ const now = opts.now ?? Date.now();
48
+ switch(verdict){
49
+ case 'hit':
50
+ case 'found':
51
+ clearChannel(channel);
52
+ return undefined;
53
+ case 'needs-setup':
54
+ case 'needs-login':
55
+ case 'needs-extension':
56
+ case 'needs-attach':
57
+ case 'challenged':
58
+ return markChannelDown(channel, verdict, now);
59
+ case 'cooldown':
60
+ return markChannelCooldown(channel, opts.cooldownMs ?? DEFAULT_COOLDOWN_MS, now);
61
+ default:
62
+ return undefined;
63
+ }
64
+ }
65
+ export async function recordChannelEvent(stateRoot, event) {
66
+ try {
67
+ const { mkdir, appendFile } = await import('node:fs/promises');
68
+ const dir = `${stateRoot.replace(/\/+$/, '')}/gotry-state`;
69
+ await mkdir(dir, {
70
+ recursive: true
71
+ });
72
+ await appendFile(`${dir}/channel-health.jsonl`, `${JSON.stringify(event)}\n`, 'utf-8');
73
+ } catch {}
74
+ }
75
+ export async function readLatestChannelEvents(stateRoot, opts = {}) {
76
+ const latest = new Map();
77
+ try {
78
+ const { readFile } = await import('node:fs/promises');
79
+ const raw = await readFile(`${stateRoot.replace(/\/+$/, '')}/gotry-state/channel-health.jsonl`, 'utf-8');
80
+ const nowMs = opts.now ?? Date.now();
81
+ const cutoff = nowMs - (opts.limitDays ?? 30) * 86_400_000;
82
+ for (const line of raw.split('\n')){
83
+ const t = line.trim();
84
+ if (!t) continue;
85
+ try {
86
+ const ev = JSON.parse(t);
87
+ if (!ev || typeof ev.channel !== 'string' || typeof ev.state !== 'string') continue;
88
+ if (![
89
+ 'down',
90
+ 'cooldown',
91
+ 'ok'
92
+ ].includes(ev.state)) continue;
93
+ const atMs = Date.parse(ev.at ?? '');
94
+ if (Number.isFinite(atMs) && atMs < cutoff) continue;
95
+ latest.set(ev.channel, ev);
96
+ } catch {}
97
+ }
98
+ } catch {}
99
+ return latest;
100
+ }
101
+
102
+
103
+ //# sourceURL=ts/capabilities/channel-health.ts
@@ -0,0 +1,213 @@
1
+ import { channelState } from './channel-health.js';
2
+ export const CHANNELS = [
3
+ {
4
+ id: 'flyai',
5
+ label: '飞猪官方 API',
6
+ tool: 'gotry_flyai_search',
7
+ intents: [
8
+ 'search-flight',
9
+ 'search-train',
10
+ 'search-hotel'
11
+ ],
12
+ quotaClass: 'anonymous-trial',
13
+ evidenceTag: '[实时API:flyai@ts]',
14
+ tier: 'realtime-api',
15
+ efficiencyRank: 0,
16
+ setup: '零 setup(匿名试用共享池,易达限;正式 key 配 FLYAI_API_KEY 解除)'
17
+ },
18
+ {
19
+ id: 'hbcli-hotel',
20
+ label: 'hbcli 酒店实时源',
21
+ tool: 'gotry_hotel_search',
22
+ intents: [
23
+ 'search-hotel'
24
+ ],
25
+ quotaClass: 'user-key',
26
+ evidenceTag: '[实时API:hbcli@ts]',
27
+ tier: 'realtime-api',
28
+ efficiencyRank: 1,
29
+ setup: 'hbcli 凭证(demo 沙箱一键);未装自动降静态包'
30
+ },
31
+ {
32
+ id: 'session:ctrip-flight',
33
+ label: '携程·用户登录态',
34
+ tool: 'gotry_session_search',
35
+ intents: [
36
+ 'search-flight'
37
+ ],
38
+ quotaClass: 'user-session',
39
+ evidenceTag: '[会话:ctrip-flight@ts]',
40
+ tier: 'session',
41
+ efficiencyRank: 2,
42
+ setup: '一次性装 Session Bridge 扩展 + 授权卡 + (如需)官网登录',
43
+ hint: 'kind=flight'
44
+ },
45
+ {
46
+ id: 'session:ctrip-hotel',
47
+ label: '携程·用户登录态',
48
+ tool: 'gotry_session_search',
49
+ intents: [
50
+ 'search-hotel'
51
+ ],
52
+ quotaClass: 'user-session',
53
+ evidenceTag: '[会话:ctrip-hotel@ts]',
54
+ tier: 'session',
55
+ efficiencyRank: 2,
56
+ setup: '一次性装 Session Bridge 扩展 + 授权卡 + (如需)官网登录',
57
+ hint: 'kind=hotel'
58
+ },
59
+ {
60
+ id: 'session:12306-train',
61
+ label: '12306 公开查询面',
62
+ tool: 'gotry_session_search',
63
+ intents: [
64
+ 'search-train'
65
+ ],
66
+ quotaClass: 'user-session',
67
+ evidenceTag: '[会话:train-12306@ts]',
68
+ tier: 'session',
69
+ efficiencyRank: 2,
70
+ setup: '一次性装 Session Bridge 扩展(公开查询面,无需登录)',
71
+ hint: 'kind=train'
72
+ },
73
+ {
74
+ id: 'anything-geo',
75
+ label: '目的地/酒店候选(hotel-be)',
76
+ tool: 'gotry_anything_search',
77
+ intents: [
78
+ 'search-geo'
79
+ ],
80
+ quotaClass: 'user-key',
81
+ evidenceTag: '[实时API:hbcli-anything@ts]',
82
+ tier: 'realtime-api',
83
+ efficiencyRank: 1,
84
+ setup: 'hbcli 可达即可(随 hbcli 安装)'
85
+ },
86
+ {
87
+ id: 'open-meteo',
88
+ label: '天气(open-meteo)',
89
+ tool: 'gotry_weather_check',
90
+ intents: [
91
+ 'weather'
92
+ ],
93
+ quotaClass: 'free-public',
94
+ evidenceTag: '[实时API:open-meteo@ts]',
95
+ tier: 'realtime-api',
96
+ efficiencyRank: 0
97
+ },
98
+ {
99
+ id: 'opensky',
100
+ label: '航班实时印证(OpenSky)',
101
+ tool: 'gotry_flight_verify',
102
+ intents: [
103
+ 'verify-flight'
104
+ ],
105
+ quotaClass: 'free-public',
106
+ evidenceTag: '[实时API:opensky@ts]',
107
+ tier: 'realtime-api',
108
+ efficiencyRank: 0
109
+ },
110
+ {
111
+ id: 'web-read',
112
+ label: '网页读取兜底(agent-reach)',
113
+ tool: 'gotry_web_search',
114
+ intents: [
115
+ 'read-web',
116
+ 'search-flight',
117
+ 'search-train',
118
+ 'search-hotel'
119
+ ],
120
+ quotaClass: 'user-key',
121
+ evidenceTag: '[agent-reach:web.read@ts]',
122
+ tier: 'best-effort',
123
+ efficiencyRank: 3,
124
+ setup: 'gotry .venv 装 agent-reach(npx gotry doctor --fix)'
125
+ },
126
+ {
127
+ id: 'static-hotel',
128
+ label: '内置静态数据包',
129
+ tool: 'gotry_hotel_search',
130
+ intents: [
131
+ 'search-hotel'
132
+ ],
133
+ quotaClass: 'static',
134
+ evidenceTag: '[静态包:估算]',
135
+ tier: 'static',
136
+ efficiencyRank: 9,
137
+ routable: false
138
+ }
139
+ ];
140
+ const TIER_RANK = {
141
+ 'realtime-api': 0,
142
+ session: 1,
143
+ 'best-effort': 2,
144
+ static: 3
145
+ };
146
+ const TIER_WHY = {
147
+ 'realtime-api': '官方结构化数据,证据链最强',
148
+ 'session': '用户本人登录态的真实价/库存',
149
+ 'best-effort': '非结构化兜底(读页汇总),证据弱于 API',
150
+ 'static': '内置估算,仅覆盖内置场景'
151
+ };
152
+ export const INTENT_LABELS = {
153
+ 'search-flight': '机票',
154
+ 'search-train': '火车',
155
+ 'search-hotel': '酒店',
156
+ 'search-geo': '目的地候选',
157
+ 'read-web': '网页/社媒',
158
+ 'weather': '天气',
159
+ 'verify-flight': '航班印证'
160
+ };
161
+ export function channelsForIntent(intent) {
162
+ return CHANNELS.filter((c)=>c.routable !== false && c.intents.includes(intent)).slice().sort((a, b)=>TIER_RANK[a.tier] - TIER_RANK[b.tier] || a.efficiencyRank - b.efficiencyRank);
163
+ }
164
+ export function routingAdvice(intent, opts = {}) {
165
+ const alternatives = channelsForIntent(intent).filter((c)=>c.id !== opts.excludeChannel).filter((c)=>channelState(c.id, opts.now)?.state !== 'down').slice(0, opts.limit ?? 3).map((c)=>({
166
+ tool: c.tool,
167
+ channel: c.id,
168
+ label: c.label,
169
+ why: TIER_WHY[c.tier],
170
+ ...c.setup ? {
171
+ setup: c.setup
172
+ } : {},
173
+ ...c.hint ? {
174
+ hint: c.hint
175
+ } : {}
176
+ }));
177
+ return {
178
+ intent,
179
+ alternatives
180
+ };
181
+ }
182
+ export function toolRoutingHeadline(tool) {
183
+ const intents = new Set();
184
+ for (const c of CHANNELS){
185
+ if (c.routable === false || c.tool !== tool) continue;
186
+ for (const i of c.intents)intents.add(i);
187
+ }
188
+ if (intents.size === 0) return '';
189
+ const lines = [
190
+ ...intents
191
+ ].map((intent)=>`- ${INTENT_LABELS[intent]}: ${channelsForIntent(intent).map((c)=>`${c.label}(${c.tool}${c.hint ? ` ${c.hint}` : ''})`).join(' → ')}`);
192
+ return [
193
+ '本工具服务的检索意图与通道顺位(证据级降序):',
194
+ ...lines,
195
+ 'verdict≠hit 时按结果内 routing 字段改道下一通道,勿盲试同通道。'
196
+ ].join('\n');
197
+ }
198
+ export function renderRoutingCard(now = new Date()) {
199
+ const multiChannel = Object.keys(INTENT_LABELS).map((intent)=>({
200
+ intent,
201
+ channels: channelsForIntent(intent)
202
+ })).filter((x)=>x.channels.length >= 2);
203
+ const lines = multiChannel.map(({ intent, channels })=>`- ${INTENT_LABELS[intent]}: ${channels.map((c)=>`${c.label}(${c.tool}${c.hint ? ` ${c.hint}` : ''})`).join(' → ')}`);
204
+ return [
205
+ '检索通道顺位(官方API > 用户会话 > 网页兜底;某次检索 verdict≠hit 时,按该结果内 routing 字段改道下一通道,同通道本会话不盲试第二次):',
206
+ ...lines,
207
+ '额度口径:匿名试用=首次体验导流(达限即改道并提示配 key);正式 key(FLYAI_API_KEY/hbcli 凭证)与账号会话=正式用量;静态包=估算必标注。',
208
+ `卡生成于 ${now.toISOString()}(通道状态变化不改变顺位表本身,只影响 routing 建议的可用性过滤)。`
209
+ ].join('\n');
210
+ }
211
+
212
+
213
+ //# sourceURL=ts/capabilities/channel-registry.ts