@danceiny/gotry 0.0.1-rc.17 → 0.0.1-rc.19
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 +185 -148
- package/README.zh-CN.md +195 -154
- package/bin/gotry-bootstrap.js +246 -26
- package/bin/gotry-inner.js +68 -38
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +7 -8
- package/cordis.gotry-patch.yml +15 -5
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach.js +4 -4
- package/dist/capabilities/channel-health.js +103 -0
- package/dist/capabilities/channel-registry.js +213 -0
- package/dist/capabilities/doctor.js +285 -0
- package/dist/capabilities/effect.js +178 -5
- package/dist/capabilities/flyai.js +13 -0
- 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 +304 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +281 -0
- package/dist/scripts/benchmark-environment-bridge-e2e.js +5 -10
- package/dist/scripts/benchmark-environment-bridge-tests.js +148 -142
- 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 +115 -44
- 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 +121 -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/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 +7 -4
- package/dist/src/benchmark-environment-bridge.js +68 -7
- 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 +735 -149
- 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 +6 -26
- package/schemas/booking.surface.schema.json +2593 -0
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/flyai.ts +21 -3
- 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/package.json +1 -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 +14 -6
- package/ts/src/benchmark-environment-bridge.ts +29 -10
- 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 +489 -171
- package/ts/src/loop.ts +1 -1
- package/ts/src/state-ledger.ts +1 -1
- package/ts/src/turn-deadline.ts +361 -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,4 @@
|
|
|
1
|
-
import { flightClaimVerdict, itineraryInvariants, latestFactsForRouteDate } from './bookable-facts.js';
|
|
1
|
+
import { flightClaimVerdict, hotelClaimVerdict, itineraryInvariants, latestFactsForRouteDate } from './bookable-facts.js';
|
|
2
2
|
export const ARTIFACT_GATE_SCHEMA = 'gotry_artifact_gate.v1';
|
|
3
3
|
const FLIGHT_NO = /(?<![A-Za-z0-9])([A-Z0-9]{2}\d{3,4})(?![\d])/g;
|
|
4
4
|
const CARRIER_DIRECT = /(?<![A-Za-z0-9])([A-Z0-9]{2})(?!\d)(?:\s|[一-龥]){0,6}直飞/g;
|
|
@@ -26,6 +26,12 @@ function hasDirectAssertion(line) {
|
|
|
26
26
|
const before = line.slice(Math.max(0, idx - 3), idx);
|
|
27
27
|
return !(before.endsWith('无') || before.endsWith('不') || before.endsWith('没有') || before.endsWith('未见'));
|
|
28
28
|
}
|
|
29
|
+
function occ1(line, map) {
|
|
30
|
+
for (const city of Object.keys(map.city_alias)){
|
|
31
|
+
if (line.includes(city)) return city;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
29
35
|
function routeCtxOf(text, map, defaultYear) {
|
|
30
36
|
const occ = [];
|
|
31
37
|
for (const [city, iata] of Object.entries(map.city_alias)){
|
|
@@ -72,13 +78,21 @@ export function extractClaims(markdown, map, opts) {
|
|
|
72
78
|
flights: [],
|
|
73
79
|
policies: [],
|
|
74
80
|
airports: [],
|
|
75
|
-
direct_lines: []
|
|
81
|
+
direct_lines: [],
|
|
82
|
+
hotels: [],
|
|
83
|
+
anchors: new Map()
|
|
76
84
|
};
|
|
77
85
|
const lines = markdown.split('\n');
|
|
78
86
|
let section = {};
|
|
87
|
+
const ANCHOR = /fact:([0-9a-f]{16})/;
|
|
79
88
|
for(let i = 0; i < lines.length; i++){
|
|
80
89
|
const line = lines[i];
|
|
81
90
|
const lineNo = i + 1;
|
|
91
|
+
const anchorMatch = line.match(ANCHOR);
|
|
92
|
+
if (anchorMatch) {
|
|
93
|
+
claims.anchors.set(lineNo, anchorMatch[1].toLowerCase());
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
82
96
|
if (/^#{1,4}\s/.test(line)) {
|
|
83
97
|
const ctx = routeCtxOf(line, map, opts?.trip_year);
|
|
84
98
|
section = {
|
|
@@ -145,6 +159,20 @@ export function extractClaims(markdown, map, opts) {
|
|
|
145
159
|
has_as_of: AS_OF_WORD.test(line)
|
|
146
160
|
});
|
|
147
161
|
}
|
|
162
|
+
const HOTEL_ASSERT = /酒店|住宿|客栈|民宿/;
|
|
163
|
+
const HOTEL_BOOKABLE = /有房|可订|在架|已核验|已验证|可住|✓|✅/;
|
|
164
|
+
if (HOTEL_ASSERT.test(line) && HOTEL_BOOKABLE.test(line)) {
|
|
165
|
+
const stays = [
|
|
166
|
+
...line.matchAll(/(?<!\d)(\d{4}-\d{2}-\d{2})(?!\d)/g)
|
|
167
|
+
].map((m)=>m[1]);
|
|
168
|
+
claims.hotels.push({
|
|
169
|
+
line: lineNo,
|
|
170
|
+
text: line.trim().slice(0, 160),
|
|
171
|
+
destination: occ1(line, map),
|
|
172
|
+
check_in: stays[0],
|
|
173
|
+
check_out: stays[1]
|
|
174
|
+
});
|
|
175
|
+
}
|
|
148
176
|
for (const [city, carriers] of Object.entries(map.carrier_airport)){
|
|
149
177
|
const airports = [
|
|
150
178
|
...new Set(Object.values(carriers))
|
|
@@ -176,6 +204,66 @@ export function gateArtifact(markdown, facts, map, opts) {
|
|
|
176
204
|
const claims = extractClaims(markdown, map, opts);
|
|
177
205
|
const violations = [];
|
|
178
206
|
let traceable = 0;
|
|
207
|
+
for (const [lineNo, factId] of claims.anchors){
|
|
208
|
+
const f = facts.find((x)=>x.fact_id === factId);
|
|
209
|
+
if (!f) {
|
|
210
|
+
violations.push({
|
|
211
|
+
kind: 'fact_anchor_unknown',
|
|
212
|
+
line: lineNo,
|
|
213
|
+
detail: `渲染锚点 fact:${factId} 不在事实注册表——锚点被手改或伪造,产物不可信`
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (f.kind !== 'policy' && f.bookability === 'unavailable_exact_date') {
|
|
218
|
+
violations.push({
|
|
219
|
+
kind: 'not_in_source',
|
|
220
|
+
line: lineNo,
|
|
221
|
+
detail: `锚点事实为 exact-date 负事实(${f.fetched_at ?? ''})——负事实对应的可住/可订断言不得出现`
|
|
222
|
+
});
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
traceable++;
|
|
226
|
+
}
|
|
227
|
+
for (const c of claims.hotels){
|
|
228
|
+
if (claims.anchors.has(c.line)) continue;
|
|
229
|
+
if (!c.destination) {
|
|
230
|
+
violations.push({
|
|
231
|
+
kind: 'unverifiable_hotel_claim',
|
|
232
|
+
line: c.line,
|
|
233
|
+
detail: `酒店可住断言缺目的地上下文,无法回溯 exact-date 事实——fail closed 按未核验处理`
|
|
234
|
+
});
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const r = hotelClaimVerdict(facts, {
|
|
238
|
+
destination: c.destination,
|
|
239
|
+
check_in: c.check_in,
|
|
240
|
+
check_out: c.check_out
|
|
241
|
+
});
|
|
242
|
+
if (r.verdict === 'traceable') {
|
|
243
|
+
traceable++;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (r.verdict === 'unverified') {
|
|
247
|
+
violations.push({
|
|
248
|
+
kind: 'unverifiable_hotel_claim',
|
|
249
|
+
line: c.line,
|
|
250
|
+
detail: r.reason
|
|
251
|
+
});
|
|
252
|
+
} else {
|
|
253
|
+
violations.push({
|
|
254
|
+
kind: 'not_in_source',
|
|
255
|
+
line: c.line,
|
|
256
|
+
detail: r.reason
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (CHECK_MARK.test(c.text)) {
|
|
260
|
+
violations.push({
|
|
261
|
+
kind: 'unconditional_check',
|
|
262
|
+
line: c.line,
|
|
263
|
+
detail: '对未核验酒店使用无条件 ✓/✅——只有 bookable_exact_date 才允许确定性标记'
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
179
267
|
for (const c of claims.flights){
|
|
180
268
|
if (c.carrier_only) {
|
|
181
269
|
const hasCarrier = facts.some((f)=>f.kind === 'flight' && f.bookability === 'bookable_exact_date' && f.nonstop !== false && f.flight_no.startsWith(c.carrier_only) && (!c.origin || f.route.origin === c.origin || f.route.origin_airport === c.origin) && (!c.destination || f.route.destination === c.destination || f.route.dest_airport === c.destination) && (!c.date || f.date === c.date));
|
|
@@ -277,7 +365,7 @@ export function gateArtifact(markdown, facts, map, opts) {
|
|
|
277
365
|
const nos = [
|
|
278
366
|
...line.matchAll(FLIGHT_NO)
|
|
279
367
|
].map((m)=>m[1].toUpperCase());
|
|
280
|
-
const matched = facts.filter((f)=>f.kind
|
|
368
|
+
const matched = facts.filter((f)=>(f.kind === 'flight' || f.kind === 'train') && nos.includes(f.flight_no.toUpperCase()));
|
|
281
369
|
const allProtected = nos.length > 0 && matched.length === nos.length && matched.every((f)=>f.protected_connection === true);
|
|
282
370
|
if (!allProtected) {
|
|
283
371
|
violations.push({
|
|
@@ -29,10 +29,13 @@ function invalidTerminal() {
|
|
|
29
29
|
error: 'invalid_terminal_output'
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
function stripReasoningBlocks(raw) {
|
|
33
|
+
return raw.replace(/<think>[\s\S]*?<\/think>\s*/gi, '');
|
|
34
|
+
}
|
|
32
35
|
export function parseBenchmarkTerminal(raw, config) {
|
|
33
36
|
if (!validateTerminalOutputConfig(config)) return invalidTerminal();
|
|
34
37
|
if (Buffer.byteLength(raw, 'utf8') > config.max_bytes) return invalidTerminal();
|
|
35
|
-
const trimmed = raw.trim();
|
|
38
|
+
const trimmed = stripReasoningBlocks(raw).trim();
|
|
36
39
|
const opening = `<${config.tag}>`;
|
|
37
40
|
const closing = `</${config.tag}>`;
|
|
38
41
|
if (!trimmed.startsWith(opening) || !trimmed.endsWith(closing)) return invalidTerminal();
|
|
@@ -150,8 +153,8 @@ export function createBenchmarkAgentConformance(projection) {
|
|
|
150
153
|
}
|
|
151
154
|
if (typeof event.data.callId !== 'string') return;
|
|
152
155
|
const args = parseToolArguments(event.data.arguments);
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
156
|
+
const flat = args && plainObject(args) ? args : undefined;
|
|
157
|
+
if (flat?.action === 'call' && typeof flat.tool === 'string' && projection.allowedTools.includes(flat.tool)) {
|
|
155
158
|
state.validCallIds.add(event.data.callId);
|
|
156
159
|
}
|
|
157
160
|
return;
|
|
@@ -261,7 +264,7 @@ function systemSection(projection) {
|
|
|
261
264
|
text: [
|
|
262
265
|
'Benchmark execution contract:',
|
|
263
266
|
`- Translate every task instruction to use a CLI, shell, Python, or agent_env.cli into the native tool ${projection.toolName}; do not merely describe the intended command.`,
|
|
264
|
-
`- Call it with exactly {"
|
|
267
|
+
`- Call it with exactly {"action":"call","tool":"<one of: ${allowed}>","arguments":{...}}.`,
|
|
265
268
|
'- action:"tools" is discovery only and does not satisfy the required environment call.',
|
|
266
269
|
`- After a successful tool result, reply only <${projection.terminal.tag}>{...one JSON object...}</${projection.terminal.tag}> with no prose or code fence.`,
|
|
267
270
|
'- If a terminal-format correction arrives, reuse the existing result and do not call the tool again.'
|
|
@@ -211,18 +211,74 @@ function serializedArguments(value) {
|
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
+
export const BRIDGE_ERROR_CONTRACT = Object.freeze({
|
|
215
|
+
invalid_action: {
|
|
216
|
+
recoverable: true,
|
|
217
|
+
remedy: 'action 只允许 tools 或 call'
|
|
218
|
+
},
|
|
219
|
+
disallowed_tool: {
|
|
220
|
+
recoverable: true,
|
|
221
|
+
remedy: '先 action=tools 列出 allowed_tools 清单,再从清单内选工具'
|
|
222
|
+
},
|
|
223
|
+
invalid_arguments: {
|
|
224
|
+
recoverable: true,
|
|
225
|
+
remedy: 'arguments 必须是可序列化 JSON 对象(≤64KB、深度≤12);收窄后重试'
|
|
226
|
+
},
|
|
227
|
+
timed_out: {
|
|
228
|
+
recoverable: true,
|
|
229
|
+
remedy: '上游超时;可原样重试一次或换其他工具'
|
|
230
|
+
},
|
|
231
|
+
output_truncated: {
|
|
232
|
+
recoverable: true,
|
|
233
|
+
remedy: '输出超出上限;让被调工具收窄查询范围后重试'
|
|
234
|
+
},
|
|
235
|
+
invalid_json: {
|
|
236
|
+
recoverable: true,
|
|
237
|
+
remedy: '上游输出不是合法 JSON;重试一次或换工具'
|
|
238
|
+
},
|
|
239
|
+
invalid_output: {
|
|
240
|
+
recoverable: true,
|
|
241
|
+
remedy: '上游输出结构不符(如原始字符串);重试或换工具'
|
|
242
|
+
},
|
|
243
|
+
runner_failed: {
|
|
244
|
+
recoverable: true,
|
|
245
|
+
remedy: '上游进程非零退出;可重试一次,持续失败换工具'
|
|
246
|
+
},
|
|
247
|
+
spawn_failed: {
|
|
248
|
+
recoverable: false,
|
|
249
|
+
remedy: '可执行文件不可用——环境问题,调用方不可恢复'
|
|
250
|
+
},
|
|
251
|
+
forbidden_output: {
|
|
252
|
+
recoverable: false,
|
|
253
|
+
remedy: '输出含未授权键(策略边界)——不可恢复,换工具或放弃'
|
|
254
|
+
}
|
|
255
|
+
});
|
|
214
256
|
export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
215
257
|
const bridge = loadBenchmarkEnvironmentConfig(path);
|
|
216
258
|
if (!bridge) throw new Error('benchmark environment bridge configuration unavailable');
|
|
217
259
|
if (!subprocess) throw new Error('benchmark environment bridge subprocess unavailable');
|
|
218
260
|
register(defineTool({
|
|
219
261
|
name: 'gotry_benchmark_environment',
|
|
220
|
-
description: 'When a prompt asks to run agent_env.cli, use action=call here with the mapped tool; arbitrary shell is not exposed.',
|
|
262
|
+
description: 'When a prompt asks to run agent_env.cli, use action=call here with the mapped tool; arbitrary shell is not exposed. ' + 'Recoverable domain-error contract: every failure returns { ok:false, error:<code>, ... } from a closed vocabulary; ' + 'call action=errors to fetch the per-code recoverable flag and remedy before retrying.',
|
|
221
263
|
parameters: {
|
|
222
|
-
|
|
223
|
-
type: '
|
|
264
|
+
action: {
|
|
265
|
+
type: 'string',
|
|
266
|
+
enum: [
|
|
267
|
+
'tools',
|
|
268
|
+
'call',
|
|
269
|
+
'errors'
|
|
270
|
+
],
|
|
224
271
|
required: true,
|
|
225
|
-
description: '
|
|
272
|
+
description: 'tools=列出可调工具;call=执行被映射工具;errors=拉取可恢复错误契约表'
|
|
273
|
+
},
|
|
274
|
+
tool: {
|
|
275
|
+
type: 'string',
|
|
276
|
+
description: '被调工具名(必须在 allowed_tools 清单内;action=call 时必填)'
|
|
277
|
+
},
|
|
278
|
+
arguments: {
|
|
279
|
+
type: 'object',
|
|
280
|
+
additionalProperties: true,
|
|
281
|
+
description: '传给被调工具的参数对象(action=call 时可选;≤64KB、深度≤12)'
|
|
226
282
|
}
|
|
227
283
|
},
|
|
228
284
|
output: {
|
|
@@ -237,15 +293,20 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
|
237
293
|
]
|
|
238
294
|
},
|
|
239
295
|
async execute (args) {
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
296
|
+
const action = args?.action;
|
|
297
|
+
if (action === 'errors') return jsonObject({
|
|
298
|
+
ok: true,
|
|
299
|
+
errors: BRIDGE_ERROR_CONTRACT
|
|
300
|
+
});
|
|
301
|
+
if (action === 'tools') return jsonObject({
|
|
242
302
|
ok: true,
|
|
243
303
|
tools: bridge.allowed_tools
|
|
244
304
|
});
|
|
245
|
-
if (
|
|
305
|
+
if (action !== 'call') return jsonObject({
|
|
246
306
|
ok: false,
|
|
247
307
|
error: 'invalid_action'
|
|
248
308
|
});
|
|
309
|
+
const query = args;
|
|
249
310
|
if (typeof query.tool !== 'string' || !bridge.allowed_tools.includes(query.tool)) {
|
|
250
311
|
return jsonObject({
|
|
251
312
|
ok: false,
|
|
@@ -52,7 +52,7 @@ export function factsFromFlyai(q, r, fetchedAt, alias) {
|
|
|
52
52
|
const queryId = makeQueryId(source, q.kind, q.origin, q.destination, q.date);
|
|
53
53
|
const origin = iataOf(alias, q.origin);
|
|
54
54
|
const destination = iataOf(alias, q.destination);
|
|
55
|
-
if (r.verdict === 'error') return [];
|
|
55
|
+
if (r.verdict === 'error' || r.verdict === 'needs-setup') return [];
|
|
56
56
|
if (r.verdict === 'miss') return [
|
|
57
57
|
negativeFact(queryId, q.kind, q.origin, q.destination, q.date, source, fetchedAt, alias)
|
|
58
58
|
];
|
|
@@ -159,7 +159,7 @@ export function dedupeFacts(facts) {
|
|
|
159
159
|
].sort((a, b)=>a.fetched_at.localeCompare(b.fetched_at) || a.fact_id.localeCompare(b.fact_id));
|
|
160
160
|
}
|
|
161
161
|
export function latestFactsForRouteDate(facts, origin, destination, date) {
|
|
162
|
-
const hits = facts.filter((f)=>f.kind
|
|
162
|
+
const hits = facts.filter((f)=>(f.kind === 'flight' || f.kind === 'train') && f.route.origin === origin && f.route.destination === destination && f.date === date);
|
|
163
163
|
const latestQueryTs = new Map();
|
|
164
164
|
for (const f of hits){
|
|
165
165
|
const prev = latestQueryTs.get(f.query_id);
|
|
@@ -173,7 +173,7 @@ export function latestFactsForRouteDate(facts, origin, destination, date) {
|
|
|
173
173
|
}
|
|
174
174
|
export function flightClaimVerdict(facts, claim) {
|
|
175
175
|
const no = claim.flight_no.toUpperCase().replace(/\s+/g, '');
|
|
176
|
-
const sameNo = facts.filter((f)=>f.kind
|
|
176
|
+
const sameNo = facts.filter((f)=>(f.kind === 'flight' || f.kind === 'train') && f.flight_no.toUpperCase() === no && f.bookability === 'bookable_exact_date');
|
|
177
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
178
|
if (routeMatched.length > 0) return {
|
|
179
179
|
verdict: 'traceable',
|
|
@@ -310,6 +310,10 @@ export function defaultReviewBy(tripDate) {
|
|
|
310
310
|
return d.toISOString().slice(0, 10);
|
|
311
311
|
}
|
|
312
312
|
export function renderFlightFact(f) {
|
|
313
|
+
return renderFlightFactInner(f) + ` <!-- fact:${f.fact_id} -->`;
|
|
314
|
+
}
|
|
315
|
+
function renderFlightFactInner(f) {
|
|
316
|
+
const anchor = ` <!-- fact:${f.fact_id} -->`;
|
|
313
317
|
const route = `${f.route.origin}→${f.route.destination}`;
|
|
314
318
|
const evidence = `[${f.source}@${f.fetched_at} #${f.query_id}]`;
|
|
315
319
|
if (f.bookability === 'unavailable_exact_date') {
|
|
@@ -344,6 +348,61 @@ export function renderNightsLine(it) {
|
|
|
344
348
|
const flightLegs = it.od_segments.filter((s)=>s.mode === 'flight').reduce((acc, s)=>acc + s.legs, 0);
|
|
345
349
|
return `共 ${daysBetween(it.trip_start, it.trip_end) + 1} 天 ${totalNights} 晚 = ${hotelNights} 个酒店夜 + ${it.onboard_nights} 个机上夜;航班按 O&D ${flightOd} 段(共 ${flightLegs} 个 flight legs)`;
|
|
346
350
|
}
|
|
351
|
+
export function factsFromHotel(input) {
|
|
352
|
+
if (!input.checkIn || !input.checkOut) return [];
|
|
353
|
+
if (input.verdict !== 'hit' && input.verdict !== 'miss') return [];
|
|
354
|
+
const queryId = `hotel:${input.source}:${input.destination}:${input.checkIn}_${input.checkOut}`;
|
|
355
|
+
return [
|
|
356
|
+
{
|
|
357
|
+
schema: BOOKABLE_FACT_SCHEMA,
|
|
358
|
+
kind: 'hotel',
|
|
359
|
+
fact_id: makeFactId([
|
|
360
|
+
input.destination,
|
|
361
|
+
input.checkIn,
|
|
362
|
+
input.checkOut,
|
|
363
|
+
queryId
|
|
364
|
+
]),
|
|
365
|
+
destination: input.destination,
|
|
366
|
+
check_in: input.checkIn,
|
|
367
|
+
check_out: input.checkOut,
|
|
368
|
+
verdict: input.verdict,
|
|
369
|
+
bookability: input.verdict === 'hit' ? 'bookable_exact_date' : 'unavailable_exact_date',
|
|
370
|
+
options_masked: input.options,
|
|
371
|
+
source: input.source,
|
|
372
|
+
query_id: queryId,
|
|
373
|
+
fetched_at: input.fetchedAt,
|
|
374
|
+
as_of: input.fetchedAt.slice(0, 10),
|
|
375
|
+
tier: 'live_inventory'
|
|
376
|
+
}
|
|
377
|
+
];
|
|
378
|
+
}
|
|
379
|
+
export function hotelClaimVerdict(facts, claim) {
|
|
380
|
+
const scoped = facts.filter((f)=>f.kind === 'hotel' && f.destination === claim.destination && (!claim.check_in || f.check_in === claim.check_in) && (!claim.check_out || f.check_out === claim.check_out));
|
|
381
|
+
if (scoped.length === 0) {
|
|
382
|
+
return {
|
|
383
|
+
verdict: 'unverified',
|
|
384
|
+
reason: `酒店 claim(${claim.destination}${claim.check_in ? ` ${claim.check_in}→${claim.check_out ?? '?'}` : ''})未经 exact-date 源核验——先检索再写结论`
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const hit = scoped.find((f)=>f.bookability === 'bookable_exact_date');
|
|
388
|
+
if (hit) return {
|
|
389
|
+
verdict: 'traceable',
|
|
390
|
+
fact: hit,
|
|
391
|
+
reason: 'exact-date 命中'
|
|
392
|
+
};
|
|
393
|
+
const latest = scoped[scoped.length - 1];
|
|
394
|
+
return {
|
|
395
|
+
verdict: 'not_in_source',
|
|
396
|
+
fact: latest,
|
|
397
|
+
reason: `该档期 exact-date 源正常返回 0 条(${latest.fetched_at})——不得写有房/可订`
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
export function renderHotelFact(f) {
|
|
401
|
+
if (f.verdict !== 'hit') {
|
|
402
|
+
return `- ${f.destination} ${f.check_in}→${f.check_out}:exact-date 源 0 条在架(不可订) <!-- fact:${f.fact_id} -->`;
|
|
403
|
+
}
|
|
404
|
+
return `- ${f.destination} ${f.check_in}→${f.check_out}:住宿已核验(在架 ${f.options_masked} 家;价格上游打码,真实价以落地页为准) <!-- fact:${f.fact_id} -->`;
|
|
405
|
+
}
|
|
347
406
|
|
|
348
407
|
|
|
349
408
|
//# sourceURL=ts/src/bookable-facts.ts
|