@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.
- package/README.md +42 -16
- package/README.zh-CN.md +37 -15
- package/bin/gotry-bootstrap.js +309 -30
- package/bin/gotry-inner.js +74 -23
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +7 -8
- package/cordis.gotry-patch.yml +22 -8
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach.js +4 -4
- package/dist/capabilities/anything.js +62 -29
- package/dist/capabilities/channel-health.js +103 -0
- package/dist/capabilities/channel-registry.js +213 -0
- package/dist/capabilities/doctor.js +307 -0
- package/dist/capabilities/effect.js +178 -5
- package/dist/capabilities/flyai.js +13 -0
- package/dist/capabilities/hbcli.js +28 -2
- package/dist/capabilities/session/adapters/ctrip-hotel.js +207 -0
- package/dist/capabilities/session/adapters/rail-12306.js +285 -0
- package/dist/capabilities/session/extension-bridge.js +14 -1
- package/dist/capabilities/session/extension-channel.js +4 -2
- package/dist/capabilities/session/extension-distribution.js +1 -1
- package/dist/capabilities/session-search.js +311 -0
- package/dist/capabilities/visa-policy.js +126 -0
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +305 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +281 -0
- package/dist/scripts/anything-tests.js +70 -21
- package/dist/scripts/benchmark-environment-bridge-e2e.js +139 -37
- package/dist/scripts/benchmark-environment-bridge-tests.js +1099 -306
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +26 -26
- package/dist/scripts/{booking-copilot-availability-policy-v2-tests.js → booking-copilot-availability-policy-tests.js} +115 -115
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +41 -1
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +75 -120
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +370 -61
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +2 -2
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +7 -3
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +15 -9
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +19 -11
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +16 -52
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +3953 -155
- package/dist/scripts/booking-copilot-server-proof-tests.js +58 -23
- package/dist/scripts/booking-copilot-startup-proof-tests.js +10 -119
- package/dist/scripts/booking-surface-contract-proof-tests.js +1154 -326
- package/dist/scripts/bootstrap-tests.js +113 -2
- package/dist/scripts/build-metrics-report.js +339 -0
- package/dist/scripts/channel-probe-tests.js +148 -0
- package/dist/scripts/channel-probe.js +252 -0
- package/dist/scripts/channel-registry-tests.js +262 -0
- package/dist/scripts/doctor-tests.js +175 -0
- package/dist/scripts/effect-tests.js +274 -1
- package/dist/scripts/extension-tests.js +129 -11
- package/dist/scripts/fact-gate-tests.js +91 -2
- package/dist/scripts/flyai-tests.js +17 -1
- package/dist/scripts/hbcli-e2e-tests.js +1 -3
- package/dist/scripts/hbcli-tests.js +38 -1
- package/dist/scripts/metrics-report-tests.js +288 -0
- package/dist/scripts/persona-surface-guard-tests.js +19 -0
- package/dist/scripts/session-tests.js +401 -1
- package/dist/scripts/smoke.js +100 -83
- package/dist/scripts/turn-handoff-collect-tests.js +216 -0
- package/dist/scripts/turn-handoff-collect.js +189 -0
- package/dist/scripts/turn-policy-tests.js +56 -0
- package/dist/scripts/typed-contract-canary.js +261 -0
- package/dist/scripts/visa-policy-tests.js +75 -0
- package/dist/scripts/wish-channel-gate-tests.js +111 -0
- package/dist/src/artifact-gate.js +91 -3
- package/dist/src/benchmark-agent-conformance.js +39 -16
- package/dist/src/benchmark-environment-bridge.js +316 -52
- package/dist/src/bookable-facts.js +62 -3
- package/dist/src/booking-surface/{availability-policy-v2.js → availability-policy.js} +89 -89
- package/dist/src/booking-surface/canonical-schema.js +8 -22
- package/dist/src/booking-surface/contracts.js +70 -19
- package/dist/src/booking-surface/dsh-planner.js +310 -177
- package/dist/src/booking-surface/dsh-plugin.js +3 -3
- package/dist/src/booking-surface/index.js +1 -5
- package/dist/src/booking-surface/profile.js +1 -1
- package/dist/src/booking-surface/runtime.js +1682 -247
- package/dist/src/booking-surface/server.js +371 -185
- package/dist/src/booking-surface/startup.js +17 -27
- package/dist/src/booking-surface/validation.js +270 -760
- package/dist/src/index.js +739 -150
- package/dist/src/turn-deadline.js +292 -0
- package/dist/src/turn-policy.js +146 -0
- package/dist/src/wish-pool.js +5 -0
- package/extension/background.js +23 -4
- package/extension/content-main.js +47 -9
- package/extension/manifest.json +27 -9
- package/package.json +7 -26
- package/schemas/booking.surface.schema.json +2593 -0
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +89 -38
- package/ts/capabilities/flyai.ts +21 -3
- package/ts/capabilities/hbcli.ts +53 -4
- package/ts/capabilities/session/action-cache.ts +1 -1
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +293 -0
- package/ts/capabilities/session/extension-bridge.ts +32 -2
- package/ts/capabilities/session/extension-channel.ts +9 -8
- package/ts/capabilities/session/extension-distribution.ts +3 -1
- package/ts/capabilities/session/health-watch.ts +1 -1
- package/ts/capabilities/session/read-guard.ts +1 -1
- package/ts/capabilities/session/wizard.ts +1 -1
- package/ts/capabilities/session-search.ts +323 -1
- package/ts/dsh-runtime/vendor/README.md +60 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +200 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +202 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +216 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +371 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +56 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +126 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +109 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +27 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +170 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +53 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +20 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +91 -0
- package/ts/package.json +9 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +82 -2
- package/ts/src/benchmark-agent-conformance.ts +54 -15
- package/ts/src/benchmark-environment-bridge.ts +234 -46
- package/ts/src/bookable-facts.ts +109 -5
- package/ts/src/booking-saga.ts +1 -1
- package/ts/src/booking-surface/{availability-policy-v2.ts → availability-policy.ts} +124 -125
- package/ts/src/booking-surface/canonical-schema.js +8 -22
- package/ts/src/booking-surface/contracts.ts +278 -239
- package/ts/src/booking-surface/dsh-planner.ts +333 -194
- package/ts/src/booking-surface/dsh-plugin.js +3 -3
- package/ts/src/booking-surface/index.ts +1 -5
- package/ts/src/booking-surface/profile.ts +11 -11
- package/ts/src/booking-surface/runtime.ts +1351 -351
- package/ts/src/booking-surface/server.ts +282 -175
- package/ts/src/booking-surface/startup.ts +36 -48
- package/ts/src/booking-surface/validation.ts +194 -442
- package/ts/src/contracts.ts +1 -1
- package/ts/src/index.ts +492 -172
- package/ts/src/loop.ts +1 -1
- package/ts/src/state-ledger.ts +1 -1
- package/ts/src/turn-deadline.ts +365 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/wish-pool.ts +17 -4
- package/dist/scripts/agent-planning-budget-e2e.js +0 -227
- package/dist/scripts/agent-planning-budget-tests.js +0 -173
- package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +0 -3935
- package/dist/scripts/booking-surface-v2-contract-proof-tests.js +0 -1174
- package/dist/src/booking-surface/contracts-v2.js +0 -89
- package/dist/src/booking-surface/runtime-v2.js +0 -1771
- package/dist/src/booking-surface/server-v2.js +0 -334
- package/dist/src/booking-surface/validation-v2.js +0 -319
- package/dist/src/tool-budget.js +0 -136
- package/schemas/booking.surface.v1.schema.json +0 -927
- package/schemas/booking.surface.v2.schema.json +0 -61
- package/ts/src/booking-surface/contracts-v2.ts +0 -118
- package/ts/src/booking-surface/runtime-v2.ts +0 -1466
- package/ts/src/booking-surface/server-v2.ts +0 -247
- package/ts/src/booking-surface/validation-v2.ts +0 -205
- 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
|
-
|
|
52
|
-
const
|
|
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
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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}] ${
|
|
119
|
+
evidence: `[实时API:hbcli-anything@error@${ts}] ${raw}`,
|
|
88
120
|
latencyMs,
|
|
89
121
|
verdict: 'error',
|
|
90
|
-
error: r.
|
|
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' ||
|
|
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'
|
|
115
|
-
name:
|
|
116
|
-
score: it.
|
|
117
|
-
latitude:
|
|
118
|
-
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
|