@danceiny/gotry 0.0.1-rc.15 → 0.0.1-rc.17
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 +51 -13
- package/README.zh-CN.md +26 -12
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +357 -8
- package/bin/gotry-inner.js +442 -60
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +50 -0
- package/bin/gotry.js +1 -1
- package/cordis.gotry-patch.yml +15 -1
- package/data/airline-airports.json +39 -0
- package/dist/capabilities/agent-reach-deep.js +1 -1
- package/dist/capabilities/agent-reach.js +1 -1
- package/dist/capabilities/anything.js +1 -1
- package/dist/capabilities/artifacts.js +1 -1
- package/dist/capabilities/effect.js +293 -0
- package/dist/capabilities/fact-log.js +41 -0
- package/dist/capabilities/flyai.js +20 -6
- package/dist/capabilities/hbcli.js +43 -1
- package/dist/capabilities/incident-log.js +1 -1
- package/dist/capabilities/model-override.js +18 -0
- package/dist/capabilities/opensky.js +1 -1
- package/dist/capabilities/resilience.js +90 -0
- package/dist/capabilities/session/action-cache.js +1 -1
- package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
- package/dist/capabilities/session/adapters/meituan-local.js +1 -1
- package/dist/capabilities/session/benchmark.js +2 -1
- package/dist/capabilities/session/extension-bridge.js +358 -0
- package/dist/capabilities/session/extension-channel.js +93 -0
- package/dist/capabilities/session/extension-distribution.js +234 -0
- package/dist/capabilities/session/extract.js +1 -1
- package/dist/capabilities/session/golden-score.js +92 -0
- package/dist/capabilities/session/health-watch.js +216 -0
- package/dist/capabilities/session/read-guard.js +1 -1
- package/dist/capabilities/session/static-flight-golden.js +137 -0
- package/dist/capabilities/session/transport.js +1 -1
- package/dist/capabilities/session/wizard.js +147 -0
- package/dist/capabilities/session-consent.js +1 -1
- package/dist/capabilities/session-login.js +83 -1
- package/dist/capabilities/session-search.js +111 -2
- package/dist/capabilities/weather.js +168 -46
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +102 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +1 -1
- package/dist/scripts/agent-planning-budget-e2e.js +227 -0
- package/dist/scripts/agent-planning-budget-tests.js +173 -0
- package/dist/scripts/agent-reach-deep-tests.js +1 -1
- package/dist/scripts/agent-reach-tests.js +1 -1
- package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
- package/dist/scripts/anything-tests.js +1 -1
- package/dist/scripts/async-collect.js +1 -1
- package/dist/scripts/benchmark-environment-bridge-e2e.js +981 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +2544 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +335 -0
- package/dist/scripts/booking-copilot-availability-policy-v2-tests.js +1355 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +89 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +113 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +356 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +557 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +140 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +202 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +102 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +374 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +447 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +205 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +317 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +283 -0
- package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +3935 -0
- package/dist/scripts/booking-saga-tests.js +1 -1
- package/dist/scripts/booking-surface-contract-proof-tests.js +393 -0
- package/dist/scripts/booking-surface-v2-contract-proof-tests.js +1174 -0
- package/dist/scripts/bootstrap-tests.js +59 -3
- package/dist/scripts/build-changelog.js +279 -0
- package/dist/scripts/changelog-tests.js +165 -0
- package/dist/scripts/companion-tests.js +1 -1
- package/dist/scripts/diff-test.js +1 -1
- package/dist/scripts/dsh-runtime-closure-tests.js +225 -0
- package/dist/scripts/dsh-runtime-closure.js +150 -0
- package/dist/scripts/effect-tests.js +474 -0
- package/dist/scripts/engine-run.js +1 -1
- package/dist/scripts/engine-tests.js +1 -1
- package/dist/scripts/evaluation-cadence-tests.js +347 -0
- package/dist/scripts/evaluation-contract-tests.js +574 -0
- package/dist/scripts/extension-distribution-cli.js +35 -0
- package/dist/scripts/extension-distribution-tests.js +341 -0
- package/dist/scripts/extension-tests.js +573 -0
- package/dist/scripts/fact-gate-tests.js +318 -0
- package/dist/scripts/flyai-tests.js +1 -1
- package/dist/scripts/hbcli-e2e-tests.js +180 -0
- package/dist/scripts/hbcli-tests.js +1 -1
- package/dist/scripts/health-watch-cli.js +51 -0
- package/dist/scripts/i18n-tests.js +1 -1
- package/dist/scripts/incident-tests.js +1 -1
- package/dist/scripts/journey-tests.js +1 -1
- package/dist/scripts/ledger-tests.js +1 -1
- package/dist/scripts/ledger-workflow-crash.js +1 -1
- package/dist/scripts/memory-capture-tests.js +1 -1
- package/dist/scripts/memory-decay-tests.js +1 -1
- package/dist/scripts/memory-metrics.js +1 -1
- package/dist/scripts/memory-value-report.js +1 -1
- package/dist/scripts/model-override-e2e.js +176 -0
- package/dist/scripts/nightly-evidence-tests.js +67 -4
- package/dist/scripts/nightly-evidence.js +59 -3
- package/dist/scripts/nudge-digest.js +1 -1
- package/dist/scripts/onboarding-tests.js +186 -0
- package/dist/scripts/opensky-check.js +1 -1
- package/dist/scripts/opensky-tests.js +1 -1
- package/dist/scripts/pnpm-dsh-closure-proof.js +20 -0
- package/dist/scripts/price-drift-tests.js +441 -0
- package/dist/scripts/price-drift-watch.js +491 -0
- package/dist/scripts/probe-poi-tests.js +1 -1
- package/dist/scripts/product-metrics.js +1 -1
- package/dist/scripts/publish-preverify.js +40 -4
- package/dist/scripts/realtime-pricing-tests.js +1 -1
- package/dist/scripts/replay-async.js +1 -1
- package/dist/scripts/replay-real.js +1 -1
- package/dist/scripts/replay.js +1 -1
- package/dist/scripts/session-attach-diagnose.js +1 -1
- package/dist/scripts/session-attach-poc.js +1 -1
- package/dist/scripts/session-benchmark.js +1 -1
- package/dist/scripts/session-extract-tests.js +1 -1
- package/dist/scripts/session-login.js +1 -1
- package/dist/scripts/session-tests.js +70 -20
- package/dist/scripts/sf-live-benchmark.js +338 -0
- package/dist/scripts/sf-live-cli-tests.js +21 -0
- package/dist/scripts/sf-soft-score-tests.js +108 -0
- package/dist/scripts/sf-summary.js +93 -0
- package/dist/scripts/skeleton-check.js +1 -1
- package/dist/scripts/skeleton-integration-test.js +1 -1
- package/dist/scripts/skills-contract-tests.js +1 -1
- package/dist/scripts/smoke-session-gate-tests.js +29 -0
- package/dist/scripts/smoke.js +154 -33
- package/dist/scripts/state-cli-tests.js +1 -1
- package/dist/scripts/state-cli.js +1 -1
- package/dist/scripts/static-golden-tests.js +299 -0
- package/dist/scripts/time-eval-tests.js +1 -1
- package/dist/scripts/travel-timeline-tests.js +1 -1
- package/dist/scripts/unified-tests.js +1 -1
- package/dist/scripts/weather-tests.js +694 -44
- package/dist/scripts/z3-race-tests.js +1 -1
- package/dist/src/artifact-gate.js +310 -0
- package/dist/src/benchmark-agent-conformance.js +370 -0
- package/dist/src/benchmark-environment-bridge.js +384 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +173 -0
- package/dist/src/benchmark-tool-isolation.js +124 -0
- package/dist/src/bookable-facts.js +349 -0
- package/dist/src/booking-saga.js +1 -1
- package/dist/src/booking-surface/availability-policy-v2.js +830 -0
- package/dist/src/booking-surface/canonical-schema.js +113 -0
- package/dist/src/booking-surface/contracts-v2.js +89 -0
- package/dist/src/booking-surface/contracts.js +46 -0
- package/dist/src/booking-surface/dsh-planner.js +453 -0
- package/dist/src/booking-surface/dsh-plugin.js +93 -0
- package/dist/src/booking-surface/error-codes.js +94 -0
- package/dist/src/booking-surface/index.js +15 -0
- package/dist/src/booking-surface/profile.js +68 -0
- package/dist/src/booking-surface/runtime-v2.js +1771 -0
- package/dist/src/booking-surface/runtime.js +351 -0
- package/dist/src/booking-surface/server-v2.js +334 -0
- package/dist/src/booking-surface/server.js +302 -0
- package/dist/src/booking-surface/startup.js +159 -0
- package/dist/src/booking-surface/validation-v2.js +319 -0
- package/dist/src/booking-surface/validation.js +809 -0
- package/dist/src/bridge.js +1 -1
- package/dist/src/companions.js +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/dsh-llm.js +1 -1
- package/dist/src/engine.js +1 -1
- package/dist/src/evaluation-cadence.js +234 -0
- package/dist/src/evaluation-contracts.js +906 -0
- package/dist/src/i18n.js +1 -1
- package/dist/src/index.js +234 -60
- package/dist/src/journey.js +1 -1
- package/dist/src/loop.js +1 -1
- package/dist/src/memory-capture.js +1 -1
- package/dist/src/memory-decay.js +1 -1
- package/dist/src/memory-utility.js +1 -1
- package/dist/src/mock-llm.js +1 -1
- package/dist/src/model.js +1 -1
- package/dist/src/realtime-pricing.js +14 -7
- package/dist/src/slot-spec.js +1 -1
- package/dist/src/state-ledger.js +2 -1
- package/dist/src/time-anchor.js +1 -1
- package/dist/src/tool-budget.js +136 -0
- package/dist/src/tool-packet.js +1 -1
- package/dist/src/travel-slots.js +1 -1
- package/dist/src/travel-timeline.js +1 -1
- package/dist/src/unified.js +1 -1
- package/dist/src/wish-pool.js +1 -1
- package/dist/src/z3-shared.js +1 -1
- package/extension/README.md +50 -0
- package/extension/background.js +160 -0
- package/extension/content-bridge.js +41 -0
- package/extension/content-main.js +62 -0
- package/extension/manifest.json +32 -0
- package/package.json +291 -12
- package/schemas/booking.surface.v1.schema.json +927 -0
- package/schemas/booking.surface.v2.schema.json +61 -0
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/flyai.ts +16 -3
- package/ts/capabilities/hbcli.ts +46 -0
- package/ts/capabilities/session/benchmark.ts +4 -1
- package/ts/capabilities/session/extension-bridge.ts +377 -0
- package/ts/capabilities/session/extension-channel.ts +95 -0
- package/ts/capabilities/session/extension-distribution.ts +264 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +257 -0
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/wizard.ts +140 -0
- package/ts/capabilities/session-login.ts +63 -2
- package/ts/capabilities/session-search.ts +105 -3
- package/ts/capabilities/weather.ts +141 -52
- package/ts/package.json +3 -3
- package/ts/src/artifact-gate.ts +365 -0
- package/ts/src/benchmark-agent-conformance.ts +448 -0
- package/ts/src/benchmark-environment-bridge.ts +348 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/bookable-facts.ts +557 -0
- package/ts/src/booking-surface/availability-policy-v2.ts +523 -0
- package/ts/src/booking-surface/canonical-schema.js +113 -0
- package/ts/src/booking-surface/contracts-v2.ts +118 -0
- package/ts/src/booking-surface/contracts.ts +380 -0
- package/ts/src/booking-surface/dsh-planner.ts +452 -0
- package/ts/src/booking-surface/dsh-plugin.js +93 -0
- package/ts/src/booking-surface/error-codes.ts +101 -0
- package/ts/src/booking-surface/index.ts +12 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/runtime-v2.ts +1466 -0
- package/ts/src/booking-surface/runtime.ts +483 -0
- package/ts/src/booking-surface/server-v2.ts +247 -0
- package/ts/src/booking-surface/server.ts +324 -0
- package/ts/src/booking-surface/startup.ts +196 -0
- package/ts/src/booking-surface/validation-v2.ts +205 -0
- package/ts/src/booking-surface/validation.ts +453 -0
- package/ts/src/index.ts +161 -14
- package/ts/src/state-ledger.ts +1 -0
- package/ts/src/tool-budget.ts +165 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
|
+
|
|
4
|
+
export const REQUIRED_BENCHMARK_DSH_VERSION = '0.1.2-alpha.3'
|
|
5
|
+
|
|
6
|
+
export function readDshPackage(bin) {
|
|
7
|
+
try {
|
|
8
|
+
const packageJson = join(dirname(dirname(bin)), 'package.json')
|
|
9
|
+
const metadata = JSON.parse(readFileSync(packageJson, 'utf8'))
|
|
10
|
+
return typeof metadata.version === 'string' ? { bin, version: metadata.version } : null
|
|
11
|
+
} catch {
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resolveDshPackage(resolver) {
|
|
17
|
+
try {
|
|
18
|
+
return readDshPackage(resolver.resolve('@deepseek-ai/dsh/lib/bin.js'))
|
|
19
|
+
} catch {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function supportsNodeVersion(version) {
|
|
25
|
+
const match = /^(\d+)\.(\d+)/.exec(version)
|
|
26
|
+
if (!match) return false
|
|
27
|
+
const major = Number(match[1])
|
|
28
|
+
const minor = Number(match[2])
|
|
29
|
+
return major > 22 || (major === 22 && minor >= 15)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function selectDshRuntime({ repoRoot, rootResolver, benchmark }) {
|
|
33
|
+
const root = resolveDshPackage(rootResolver)
|
|
34
|
+
if (root) return { ...root, source: 'root' }
|
|
35
|
+
if (benchmark) return null
|
|
36
|
+
|
|
37
|
+
const vendoredBin = join(repoRoot, 'ts/dsh-runtime/node_modules/@deepseek-ai/dsh/lib/bin.js')
|
|
38
|
+
if (!existsSync(vendoredBin)) return null
|
|
39
|
+
const vendored = readDshPackage(vendoredBin)
|
|
40
|
+
return vendored ? { ...vendored, source: 'legacy-vendored' } : null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function benchmarkRuntimeSupported(runtime) {
|
|
44
|
+
return runtime?.version === REQUIRED_BENCHMARK_DSH_VERSION
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function selectDshCwd({ repoRoot, invocationCwd, sourceCheckoutMode, benchmark }) {
|
|
48
|
+
if (sourceCheckoutMode && !benchmark) return join(repoRoot, 'ts/dsh-runtime')
|
|
49
|
+
return invocationCwd
|
|
50
|
+
}
|
package/bin/gotry.js
CHANGED
|
@@ -17,7 +17,7 @@ if [ -z "$conf" ] || [ ! -x "$conf" ]; then
|
|
|
17
17
|
fi
|
|
18
18
|
if [ -z "$conf" ] || [ ! -x "$conf" ]; then
|
|
19
19
|
cat >&2 <<'EOF'
|
|
20
|
-
gotry: node not found in PATH. Install Node 22+:
|
|
20
|
+
gotry: node not found in PATH. Install Node 22.15+:
|
|
21
21
|
macOS: brew install node@22 (or use nvm)
|
|
22
22
|
Linux: https://nodejs.org/
|
|
23
23
|
|
package/cordis.gotry-patch.yml
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
# 工具,结构化澄清卡);解析失败整块剔除
|
|
19
19
|
|
|
20
20
|
# 注:insert 里 dsh-map-tools 的 name 是占位符,bin/gotry-inner.js 运行时重写为
|
|
21
|
-
# 绝对路径(
|
|
21
|
+
# 绝对路径(source/package 优先依赖解析;legacy vendor 仅作兼容解析,裸包名绕
|
|
22
|
+
# exports 子路径限制);
|
|
22
23
|
# 依赖缺失时该条目整块剔除,不挡启动。地图/路线/POI 工具,零 key 走 OSRM/OSRM。
|
|
23
24
|
|
|
24
25
|
# GoTry 成品人格:行为契约=产品语义
|
|
@@ -91,3 +92,16 @@
|
|
|
91
92
|
开页探测;返回 challenged→立即停手如实告知,绝不重试
|
|
92
93
|
绝不绕过。跨源结论各带来源标注([实时API:flyai@ts]/[会话:ctrip-flight@ts]/
|
|
93
94
|
[静态包:估算]),分桶比对直达/中转,不比裸最低价;证据链随结论必达用户。
|
|
95
|
+
(20)事实闸(可下单事实 fail-closed,issue #46):航班号/时刻/机场/价格/签证政策
|
|
96
|
+
只能来自本轮 exact-date 工具结果;exact-date 返回 miss = 「未确认/当前不可售,
|
|
97
|
+
到 D-xx 复核」,**不得**用航线页当日班期、历史航季、相邻日期或记忆填充;
|
|
98
|
+
「只验证航线存在」永不升级为「该日期有该航班」;「联程」仅用于同票保护
|
|
99
|
+
(protected_connection),分票/自助转机必须显式标红(无行李直挂/误机无保护);
|
|
100
|
+
政策只能表述为「截至 YYYY-MM-DD 的现行政策」并附复核日期,不用无条件 ✓;
|
|
101
|
+
双机场城市的航司落点不互换(FD 落 DMK、VZ 落 BKK)。
|
|
102
|
+
**交付任何含可下单事实的行程产物(markdown 文件或长方案)前,必须调
|
|
103
|
+
gotry_fact_gate**;闸 blocked 时逐条修正为可回溯事实或降级措辞,
|
|
104
|
+
不得宣称「已验证方案」。
|
|
105
|
+
(21)工具循环收敛:第 16 次工具调用收到 TOOL_BUDGET_SOFT 提示后立即整理并给出最终答复;
|
|
106
|
+
每个对话轮次最多允许 18 次真实工具派发,第 19 次起收到结构化 TOOL_BUDGET_EXHAUSTED
|
|
107
|
+
拒绝结果且不得再调用工具。达到闸值后只能基于已有结果输出最终答复,不得重试或改调其他工具。
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"schema": "gotry_airline_airports.v1",
|
|
4
|
+
"as_of": "2026-08-29",
|
|
5
|
+
"review_by": "2026-09-28",
|
|
6
|
+
"source": "issue #46 exact-date audit snapshot (trip.com 2027 日期可售快照)",
|
|
7
|
+
"note": "两张表职责不同:city_alias 是稳定词汇(解析航线上下文);carrier_airport 是 dated 快照,只作冲突检测——可下单事实的唯一来源永远是 exact-date 工具结果,本表不产事实。双机场城市的航司→机场映射过期风险高,过 review_by 后只降级使用(仍作冲突检测,结论标注快照日)。"
|
|
8
|
+
},
|
|
9
|
+
"city_alias": {
|
|
10
|
+
"香港": "HKG",
|
|
11
|
+
"普吉": "HKT",
|
|
12
|
+
"甲米": "KBV",
|
|
13
|
+
"奥南": "KBV",
|
|
14
|
+
"曼谷": "BKK",
|
|
15
|
+
"深圳": "SZX",
|
|
16
|
+
"广州": "CAN",
|
|
17
|
+
"迪拜": "DXB",
|
|
18
|
+
"昆明": "KMG",
|
|
19
|
+
"大理": "DLU",
|
|
20
|
+
"丽江": "LJG",
|
|
21
|
+
"南京": "NKG",
|
|
22
|
+
"上海": "PVG",
|
|
23
|
+
"北京": "PEK",
|
|
24
|
+
"成都": "CTU",
|
|
25
|
+
"西安": "XIY",
|
|
26
|
+
"杭州": "HGH",
|
|
27
|
+
"多哈": "DOH"
|
|
28
|
+
},
|
|
29
|
+
"carrier_airport": {
|
|
30
|
+
"BKK": {
|
|
31
|
+
"FD": "DMK",
|
|
32
|
+
"VZ": "BKK",
|
|
33
|
+
"SL": "DMK",
|
|
34
|
+
"DD": "DMK",
|
|
35
|
+
"PG": "BKK",
|
|
36
|
+
"TG": "BKK"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { CircuitBreaker, withRetry } from './resilience.js';
|
|
2
|
+
import { flyaiSearch } from './flyai.js';
|
|
3
|
+
import { checkAvail, hotelRates, searchHotels } from './hbcli.js';
|
|
4
|
+
import { sessionFlightSearch } from './session-search.js';
|
|
5
|
+
import { geocodePlace, getClimate, getForecast } from './weather.js';
|
|
6
|
+
import { verifyFlight } from './opensky.js';
|
|
7
|
+
const DEFAULT_HANDLERS = {
|
|
8
|
+
FLYAI_SEARCH: (p)=>flyaiSearch(p),
|
|
9
|
+
HBCLI_HOTEL_SEARCH: (p)=>searchHotels({
|
|
10
|
+
destination: p.destination,
|
|
11
|
+
checkIn: p.checkIn,
|
|
12
|
+
checkOut: p.checkOut,
|
|
13
|
+
adults: p.adults
|
|
14
|
+
}, {
|
|
15
|
+
hbcliBin: p.hbcliBin,
|
|
16
|
+
timeoutMs: p.timeoutMs,
|
|
17
|
+
fallbackPath: p.fallbackPath
|
|
18
|
+
}),
|
|
19
|
+
HBCLI_HOTEL_RATES: (p)=>hotelRates({
|
|
20
|
+
hotelId: p.hotelId,
|
|
21
|
+
checkIn: p.checkIn,
|
|
22
|
+
checkOut: p.checkOut,
|
|
23
|
+
adults: p.adults,
|
|
24
|
+
countryCode: p.countryCode,
|
|
25
|
+
nationalityCode: p.nationalityCode,
|
|
26
|
+
residencyCode: p.residencyCode
|
|
27
|
+
}, {
|
|
28
|
+
hbcliBin: p.hbcliBin,
|
|
29
|
+
timeoutMs: p.timeoutMs
|
|
30
|
+
}),
|
|
31
|
+
HBCLI_CHECK_AVAIL: (p)=>checkAvail({
|
|
32
|
+
ratePkgId: p.ratePkgId
|
|
33
|
+
}, {
|
|
34
|
+
hbcliBin: p.hbcliBin,
|
|
35
|
+
timeoutMs: p.timeoutMs
|
|
36
|
+
}),
|
|
37
|
+
SESSION_FLIGHT_SEARCH: (p)=>sessionFlightSearch(p),
|
|
38
|
+
WEATHER_GEOCODE: (p)=>geocodePlace(p.name, {
|
|
39
|
+
count: p.count,
|
|
40
|
+
timeoutMs: p.timeoutMs
|
|
41
|
+
}),
|
|
42
|
+
WEATHER_FORECAST: (p)=>getForecast({
|
|
43
|
+
latitude: p.latitude,
|
|
44
|
+
longitude: p.longitude
|
|
45
|
+
}, {
|
|
46
|
+
days: p.days,
|
|
47
|
+
timeoutMs: p.timeoutMs
|
|
48
|
+
}),
|
|
49
|
+
WEATHER_CLIMATE: (p)=>getClimate({
|
|
50
|
+
latitude: p.latitude,
|
|
51
|
+
longitude: p.longitude
|
|
52
|
+
}, p.month, {
|
|
53
|
+
timeoutMs: p.timeoutMs
|
|
54
|
+
}),
|
|
55
|
+
OPENSKY_FLIGHT_VERIFY: (p)=>verifyFlight(p)
|
|
56
|
+
};
|
|
57
|
+
const defaultIsFailure = (r)=>r?.ok === false;
|
|
58
|
+
const flyaiTransient = (msg)=>msg.length > 0 && !/Sentinel/i.test(msg) && /timeout|timed out|ECONNRESET|ECONNREFUSED|ETIMEDOUT|socket|HTTP 5/i.test(msg);
|
|
59
|
+
const API_RETRY = {
|
|
60
|
+
maxAttempts: 2,
|
|
61
|
+
baseDelayMs: 400,
|
|
62
|
+
maxDelayMs: 1_600
|
|
63
|
+
};
|
|
64
|
+
const API_BREAKER = {
|
|
65
|
+
failureThreshold: 3,
|
|
66
|
+
openMs: 30_000
|
|
67
|
+
};
|
|
68
|
+
const SPECS = {
|
|
69
|
+
FLYAI_SEARCH: {
|
|
70
|
+
channel: 'cli',
|
|
71
|
+
retry: {
|
|
72
|
+
maxAttempts: 2,
|
|
73
|
+
baseDelayMs: 500,
|
|
74
|
+
maxDelayMs: 2_000
|
|
75
|
+
},
|
|
76
|
+
breaker: {
|
|
77
|
+
failureThreshold: 3,
|
|
78
|
+
openMs: 60_000
|
|
79
|
+
},
|
|
80
|
+
isRetryable: (_r, e)=>{
|
|
81
|
+
const msg = e != null ? String(e.message ?? e) : String((_r ?? {}).error ?? '');
|
|
82
|
+
return flyaiTransient(msg);
|
|
83
|
+
},
|
|
84
|
+
isFailure: defaultIsFailure
|
|
85
|
+
},
|
|
86
|
+
HBCLI_HOTEL_SEARCH: {
|
|
87
|
+
channel: 'cli',
|
|
88
|
+
retry: null,
|
|
89
|
+
breaker: {
|
|
90
|
+
failureThreshold: 3,
|
|
91
|
+
openMs: 60_000
|
|
92
|
+
},
|
|
93
|
+
isFailure: (r)=>r.via === 'hbcli-error'
|
|
94
|
+
},
|
|
95
|
+
HBCLI_HOTEL_RATES: {
|
|
96
|
+
channel: 'cli',
|
|
97
|
+
retry: null,
|
|
98
|
+
breaker: {
|
|
99
|
+
failureThreshold: 3,
|
|
100
|
+
openMs: 60_000
|
|
101
|
+
},
|
|
102
|
+
isFailure: (r)=>r.via === 'hbcli-error'
|
|
103
|
+
},
|
|
104
|
+
HBCLI_CHECK_AVAIL: {
|
|
105
|
+
channel: 'cli',
|
|
106
|
+
retry: null,
|
|
107
|
+
breaker: {
|
|
108
|
+
failureThreshold: 3,
|
|
109
|
+
openMs: 60_000
|
|
110
|
+
},
|
|
111
|
+
isFailure: (r)=>r.via === 'hbcli-error'
|
|
112
|
+
},
|
|
113
|
+
SESSION_FLIGHT_SEARCH: {
|
|
114
|
+
channel: 'browser',
|
|
115
|
+
retry: null,
|
|
116
|
+
breaker: null,
|
|
117
|
+
isFailure: defaultIsFailure
|
|
118
|
+
},
|
|
119
|
+
WEATHER_GEOCODE: {
|
|
120
|
+
channel: 'api',
|
|
121
|
+
retry: API_RETRY,
|
|
122
|
+
breaker: API_BREAKER,
|
|
123
|
+
isFailure: defaultIsFailure
|
|
124
|
+
},
|
|
125
|
+
WEATHER_FORECAST: {
|
|
126
|
+
channel: 'api',
|
|
127
|
+
retry: API_RETRY,
|
|
128
|
+
breaker: API_BREAKER,
|
|
129
|
+
isFailure: defaultIsFailure
|
|
130
|
+
},
|
|
131
|
+
WEATHER_CLIMATE: {
|
|
132
|
+
channel: 'api',
|
|
133
|
+
retry: API_RETRY,
|
|
134
|
+
breaker: API_BREAKER,
|
|
135
|
+
isFailure: defaultIsFailure
|
|
136
|
+
},
|
|
137
|
+
OPENSKY_FLIGHT_VERIFY: {
|
|
138
|
+
channel: 'api',
|
|
139
|
+
retry: API_RETRY,
|
|
140
|
+
breaker: API_BREAKER,
|
|
141
|
+
isRetryable: (_r, e)=>e != null || (_r ?? {}).verdict === 'unavailable',
|
|
142
|
+
isFailure: (r)=>r.via === 'opensky-error'
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
export function declinedObservation(effect, trace) {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
verdict: 'error',
|
|
149
|
+
summary: trace.declined === 'circuit-open' ? `${effect} 通道被断路器开启保护(连续失败达到阈值,冷却中)——不要立即重试,换其他工具或稍后再试;原因见 trace 证据链` : `${effect} 未登记效应(effect_interpreter.v1 注册表外,生产解译器拒绝)`,
|
|
150
|
+
evidence: trace.evidence.join(';')
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const defaultBreakers = new Map();
|
|
154
|
+
const defaultSleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
155
|
+
export function __resetEffectBreakersForTest() {
|
|
156
|
+
defaultBreakers.clear();
|
|
157
|
+
}
|
|
158
|
+
export function makeProductionInterpreter(opts = {}) {
|
|
159
|
+
const breakers = opts.breakers ?? defaultBreakers;
|
|
160
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
161
|
+
return async (fx)=>{
|
|
162
|
+
const ts = new Date().toISOString();
|
|
163
|
+
const name = fx.effect;
|
|
164
|
+
const spec = SPECS[name];
|
|
165
|
+
const handler = opts.handlers?.[fx.effect] ?? DEFAULT_HANDLERS[name];
|
|
166
|
+
if (!spec || !handler) {
|
|
167
|
+
return {
|
|
168
|
+
result: null,
|
|
169
|
+
trace: {
|
|
170
|
+
effect: fx.effect,
|
|
171
|
+
channel: 'api',
|
|
172
|
+
attempts: 0,
|
|
173
|
+
backoffMs: 0,
|
|
174
|
+
breaker: 'off',
|
|
175
|
+
declined: 'unknown-effect',
|
|
176
|
+
evidence: [
|
|
177
|
+
`[效应:${fx.effect}@${ts}] unknown effect`
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const br = (()=>{
|
|
183
|
+
if (!spec.breaker) return null;
|
|
184
|
+
const existing = breakers.get(fx.effect);
|
|
185
|
+
if (existing) return existing;
|
|
186
|
+
const created = new CircuitBreaker({
|
|
187
|
+
...spec.breaker,
|
|
188
|
+
...opts.now ? {
|
|
189
|
+
now: opts.now
|
|
190
|
+
} : {}
|
|
191
|
+
});
|
|
192
|
+
breakers.set(fx.effect, created);
|
|
193
|
+
return created;
|
|
194
|
+
})();
|
|
195
|
+
const gate = br?.canAttempt() ?? {
|
|
196
|
+
allowed: true,
|
|
197
|
+
state: 'closed'
|
|
198
|
+
};
|
|
199
|
+
if (!gate.allowed) {
|
|
200
|
+
const trace = {
|
|
201
|
+
effect: fx.effect,
|
|
202
|
+
channel: spec.channel,
|
|
203
|
+
attempts: 0,
|
|
204
|
+
backoffMs: 0,
|
|
205
|
+
breaker: gate.state,
|
|
206
|
+
declined: 'circuit-open',
|
|
207
|
+
evidence: [
|
|
208
|
+
`[效应:${fx.effect}@${ts}] breaker=${gate.state} 拒绝(冷却中,不重试)`
|
|
209
|
+
]
|
|
210
|
+
};
|
|
211
|
+
return {
|
|
212
|
+
result: null,
|
|
213
|
+
trace
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const policy = spec.retry ? {
|
|
217
|
+
maxAttempts: spec.retry.maxAttempts,
|
|
218
|
+
baseDelayMs: spec.retry.baseDelayMs,
|
|
219
|
+
maxDelayMs: spec.retry.maxDelayMs,
|
|
220
|
+
isRetryable: spec.isRetryable,
|
|
221
|
+
sleep
|
|
222
|
+
} : {
|
|
223
|
+
maxAttempts: 1,
|
|
224
|
+
baseDelayMs: 0,
|
|
225
|
+
maxDelayMs: 0,
|
|
226
|
+
sleep
|
|
227
|
+
};
|
|
228
|
+
const dispatch = handler;
|
|
229
|
+
const outcome = await withRetry(()=>dispatch(fx.params), policy);
|
|
230
|
+
const failed = outcome.error != null || spec.isFailure(outcome.result);
|
|
231
|
+
if (br) failed ? br.onFailure() : br.onSuccess();
|
|
232
|
+
const breakerState = br?.state() ?? 'off';
|
|
233
|
+
const evidence = [
|
|
234
|
+
`[效应:${fx.effect}@${ts}] attempts=${outcome.attempts} backoff=${outcome.backoffMs}ms breaker=${breakerState}`
|
|
235
|
+
];
|
|
236
|
+
return {
|
|
237
|
+
result: outcome.error != null ? null : outcome.result,
|
|
238
|
+
trace: {
|
|
239
|
+
effect: fx.effect,
|
|
240
|
+
channel: spec.channel,
|
|
241
|
+
attempts: outcome.attempts,
|
|
242
|
+
backoffMs: outcome.backoffMs,
|
|
243
|
+
breaker: breakerState,
|
|
244
|
+
evidence
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export const productionInterpreter = makeProductionInterpreter();
|
|
250
|
+
export async function interpretEffect(fx) {
|
|
251
|
+
return productionInterpreter(fx);
|
|
252
|
+
}
|
|
253
|
+
export function makeMockInterpreter(fixtures) {
|
|
254
|
+
return async (fx)=>{
|
|
255
|
+
const ts = new Date().toISOString();
|
|
256
|
+
const registered = Object.prototype.hasOwnProperty.call(fixtures, fx.effect);
|
|
257
|
+
if (!registered) {
|
|
258
|
+
return {
|
|
259
|
+
result: null,
|
|
260
|
+
trace: {
|
|
261
|
+
effect: fx.effect,
|
|
262
|
+
channel: 'mock',
|
|
263
|
+
attempts: 0,
|
|
264
|
+
backoffMs: 0,
|
|
265
|
+
breaker: 'off',
|
|
266
|
+
declined: 'unknown-effect',
|
|
267
|
+
evidence: [
|
|
268
|
+
`[效应:mock@${ts}] ${fx.effect} 未登记夹具`
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
result: fixtures[fx.effect],
|
|
275
|
+
trace: {
|
|
276
|
+
effect: fx.effect,
|
|
277
|
+
channel: 'mock',
|
|
278
|
+
attempts: 1,
|
|
279
|
+
backoffMs: 0,
|
|
280
|
+
breaker: 'off',
|
|
281
|
+
evidence: [
|
|
282
|
+
`[效应:mock@${ts}] ${fx.effect} 夹具回放(确定性,无网络)`
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
export function selectInterpreter(env, opts = {}) {
|
|
289
|
+
return env === 'mock' ? makeMockInterpreter(opts.fixtures ?? {}) : makeProductionInterpreter(opts);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
//# sourceURL=ts/capabilities/effect.ts
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { dedupeFacts } from '../src/bookable-facts.js';
|
|
4
|
+
export const FACT_LOG_NAME = 'bookable-facts.jsonl';
|
|
5
|
+
export function factLogPath(stateRoot) {
|
|
6
|
+
const root = stateRoot === '.' ? process.cwd() : stateRoot;
|
|
7
|
+
return join(root, 'gotry-state', FACT_LOG_NAME);
|
|
8
|
+
}
|
|
9
|
+
export async function appendFacts(stateRoot, facts) {
|
|
10
|
+
if (facts.length === 0) return true;
|
|
11
|
+
try {
|
|
12
|
+
const p = factLogPath(stateRoot);
|
|
13
|
+
await mkdir(join(p, '..'), {
|
|
14
|
+
recursive: true
|
|
15
|
+
});
|
|
16
|
+
await appendFile(p, facts.map((f)=>JSON.stringify(f)).join('\n') + '\n', 'utf-8');
|
|
17
|
+
return true;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function loadFactRegistry(stateRoot) {
|
|
23
|
+
try {
|
|
24
|
+
const raw = await readFile(factLogPath(stateRoot), 'utf-8');
|
|
25
|
+
const facts = [];
|
|
26
|
+
for (const line of raw.split('\n')){
|
|
27
|
+
const t = line.trim();
|
|
28
|
+
if (!t) continue;
|
|
29
|
+
try {
|
|
30
|
+
const f = JSON.parse(t);
|
|
31
|
+
if (f.schema === 'gotry_bookable_fact.v1' && f.fact_id) facts.push(f);
|
|
32
|
+
} catch {}
|
|
33
|
+
}
|
|
34
|
+
return dedupeFacts(facts);
|
|
35
|
+
} catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
//# sourceURL=ts/capabilities/fact-log.ts
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { closeSync, openSync, readFileSync, unlinkSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
2
5
|
export function parseFlyaiItemList(stdout) {
|
|
3
6
|
let start = -1;
|
|
4
7
|
let depth = 0;
|
|
@@ -40,11 +43,17 @@ export function parseFlyaiItemList(stdout) {
|
|
|
40
43
|
throw new Error(sawIncompleteObject ? 'incomplete FlyAI JSON object' : 'no complete FlyAI itemList JSON object');
|
|
41
44
|
}
|
|
42
45
|
function sh(cmd, args, opts) {
|
|
46
|
+
const outFile = join(tmpdir(), `gotry-flyai-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.out`);
|
|
47
|
+
const outFd = openSync(outFile, 'w');
|
|
43
48
|
const child = spawn(cmd, args, {
|
|
44
49
|
env: process.env,
|
|
45
|
-
cwd: process.cwd()
|
|
50
|
+
cwd: process.cwd(),
|
|
51
|
+
stdio: [
|
|
52
|
+
'ignore',
|
|
53
|
+
outFd,
|
|
54
|
+
'pipe'
|
|
55
|
+
]
|
|
46
56
|
});
|
|
47
|
-
let stdout = '';
|
|
48
57
|
let stderr = '';
|
|
49
58
|
let error;
|
|
50
59
|
const timer = setTimeout(()=>{
|
|
@@ -53,9 +62,6 @@ function sh(cmd, args, opts) {
|
|
|
53
62
|
} catch {}
|
|
54
63
|
}, opts.timeoutMs);
|
|
55
64
|
return new Promise((resolve)=>{
|
|
56
|
-
child.stdout?.on('data', (d)=>{
|
|
57
|
-
stdout += d.toString();
|
|
58
|
-
});
|
|
59
65
|
child.stderr?.on('data', (d)=>{
|
|
60
66
|
stderr += d.toString();
|
|
61
67
|
});
|
|
@@ -64,6 +70,14 @@ function sh(cmd, args, opts) {
|
|
|
64
70
|
});
|
|
65
71
|
child.on('close', (code)=>{
|
|
66
72
|
clearTimeout(timer);
|
|
73
|
+
closeSync(outFd);
|
|
74
|
+
let stdout = '';
|
|
75
|
+
try {
|
|
76
|
+
stdout = readFileSync(outFile, 'utf8');
|
|
77
|
+
} catch {}
|
|
78
|
+
try {
|
|
79
|
+
unlinkSync(outFile);
|
|
80
|
+
} catch {}
|
|
67
81
|
resolve({
|
|
68
82
|
code: code ?? -1,
|
|
69
83
|
stdout,
|
|
@@ -233,4 +247,4 @@ function parseHotelItems(items) {
|
|
|
233
247
|
}
|
|
234
248
|
|
|
235
249
|
|
|
236
|
-
//# sourceURL
|
|
250
|
+
//# sourceURL=ts/capabilities/flyai.ts
|
|
@@ -177,6 +177,48 @@ export async function listDestinations(opts = {}) {
|
|
|
177
177
|
destinations: r.result
|
|
178
178
|
};
|
|
179
179
|
}
|
|
180
|
+
export async function hotelRates(query, opts = {}) {
|
|
181
|
+
const hbArgs = [
|
|
182
|
+
'search',
|
|
183
|
+
'hotel-rates',
|
|
184
|
+
'--json',
|
|
185
|
+
'--hotel-id',
|
|
186
|
+
query.hotelId
|
|
187
|
+
];
|
|
188
|
+
if (query.checkIn) hbArgs.push('--check-in', query.checkIn);
|
|
189
|
+
if (query.checkOut) hbArgs.push('--check-out', query.checkOut);
|
|
190
|
+
if (query.countryCode) hbArgs.push('--country-code', query.countryCode);
|
|
191
|
+
if (query.nationalityCode) hbArgs.push('--nationality-code', query.nationalityCode);
|
|
192
|
+
if (query.residencyCode) hbArgs.push('--residency-code', query.residencyCode);
|
|
193
|
+
if (query.adults) hbArgs.push('--room-occupancies', JSON.stringify([
|
|
194
|
+
{
|
|
195
|
+
adultCount: query.adults,
|
|
196
|
+
childrenAges: []
|
|
197
|
+
}
|
|
198
|
+
]));
|
|
199
|
+
const live = await callHbcliJson(hbArgs, opts);
|
|
200
|
+
const summary = live.via === 'hbcli-realtime' ? `hotel ${query.hotelId}:hbcli 实时报价${query.checkIn ? `(${query.checkIn} → ${query.checkOut ?? '?'})` : ''}` : `hotel ${query.hotelId}:hbcli 实时报价不可用(${live.error ?? live.via});价格面无静态降级,fail-closed 不估算`;
|
|
201
|
+
return {
|
|
202
|
+
...live,
|
|
203
|
+
rates: live.result,
|
|
204
|
+
summary
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
export async function checkAvail(query, opts = {}) {
|
|
208
|
+
const live = await callHbcliJson([
|
|
209
|
+
'search',
|
|
210
|
+
'check-avail',
|
|
211
|
+
'--json',
|
|
212
|
+
'--rate-pkg-id',
|
|
213
|
+
query.ratePkgId
|
|
214
|
+
], opts);
|
|
215
|
+
const summary = live.via === 'hbcli-realtime' ? `ratePkg ${query.ratePkgId}:hbcli 实时验价(库存与价格以后端为准)` : `ratePkg ${query.ratePkgId}:hbcli 实时验价不可用(${live.error ?? live.via});价格面无静态降级,fail-closed`;
|
|
216
|
+
return {
|
|
217
|
+
...live,
|
|
218
|
+
avail: live.result,
|
|
219
|
+
summary
|
|
220
|
+
};
|
|
221
|
+
}
|
|
180
222
|
|
|
181
223
|
|
|
182
|
-
//# sourceURL
|
|
224
|
+
//# sourceURL=ts/capabilities/hbcli.ts
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const MODEL_OVERRIDE_ENV = 'GOTRY_LLM_MODEL';
|
|
2
|
+
export function installModelOverride(ctx, env = process.env) {
|
|
3
|
+
const model = env[MODEL_OVERRIDE_ENV];
|
|
4
|
+
if (!model || typeof ctx.on !== 'function') return false;
|
|
5
|
+
ctx.on('agent/request', async (_payload, next)=>{
|
|
6
|
+
const resolved = await next();
|
|
7
|
+
const { reasoningEffort: _inherited, ...rest } = resolved;
|
|
8
|
+
return {
|
|
9
|
+
...rest,
|
|
10
|
+
provider: 'deepseek-official',
|
|
11
|
+
model
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
//# sourceURL=ts/capabilities/model-override.ts
|