@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24
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 +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +16 -9
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +547 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- package/ts/src/unified.ts +316 -54
|
@@ -1,7 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 效应解译器 effect_interpreter.v1(issue #16 采纳,ADR-18)。
|
|
3
|
+
*
|
|
4
|
+
* 「效应描述 + 解译器」:业务/编排层对基础设施的依赖收敛为一个**纯数据的效应值**
|
|
5
|
+
* (effect 名 + params),不存在对渠道模块的直接 import——agent 不调用 Ctrip.Query(),
|
|
6
|
+
* 它描述 `SEARCH_FLIGHT`;把意图换算成真实副作用的是可替换的解译器:
|
|
7
|
+
*
|
|
8
|
+
* - 生产解译器 makeProductionInterpreter:查注册表 → 韧性横切(指数退避重试/
|
|
9
|
+
* 断路器,per-效应策略表)→ 渠道 handler(现能力层函数,零改写);
|
|
10
|
+
* - mock 解译器 makeMockInterpreter:夹具回放(CI/本地无网确定性);
|
|
11
|
+
* - 浏览器解译 = SESSION_* 效应(用户本人登录态的扩展桥车道,2026-08-29 起默认传输:
|
|
12
|
+
* MV3 GoTry Session Bridge + 本地桥长轮询,零系统弹窗;ReadGuard+授权闸不变,
|
|
13
|
+
* 非 CUA 视觉点击——本仓已按零 Python 依赖与 a11y/DOM 优先判死后者,
|
|
14
|
+
* docs/design/effect-interpreter.md §4)。
|
|
15
|
+
*
|
|
16
|
+
* 解译产物:`{ result, trace }`——result 是渠道自有 observation **原样透传**
|
|
17
|
+
* (ADR-13 平铺 envelope 在工具层不受扰);trace 是解译层横切证据
|
|
18
|
+
* {attempts, backoffMs, breaker, declined, evidence},拼进工具层 summary/evidence。
|
|
19
|
+
* unknown-effect / 断路拒绝时 result=null + declinedObservation() 给出平铺失败面。
|
|
20
|
+
*
|
|
21
|
+
* 多渠道比价口径:解译器**不做**渠道间自动路由/降级排序——OTA 工具面平铺是
|
|
22
|
+
* founder 判定(persona 19),比价发生在 agent 层(多工具并调+证据链逐源标注);
|
|
23
|
+
* 本层只让「每条通道」更可替换、可熔断、可 mock。
|
|
24
|
+
*/
|
|
25
|
+
import { CircuitBreaker, withRetry, } from './resilience.js';
|
|
2
26
|
import { flyaiSearch } from './flyai.js';
|
|
3
27
|
import { checkAvail, hotelRates, searchHotels } from './hbcli.js';
|
|
4
|
-
import { sessionFlightSearch, sessionHotelSearch, sessionTrainSearch } from './session-search.js';
|
|
28
|
+
import { sessionFlightSearch, sessionHotelSearch, sessionTrainSearch, sessionDidaSearch } from './session-search.js';
|
|
5
29
|
import { geocodePlace, getClimate, getForecast } from './weather.js';
|
|
6
30
|
import { verifyFlight } from './opensky.js';
|
|
7
31
|
import { anythingSearch } from './anything.js';
|
|
@@ -9,6 +33,7 @@ import { continentListUrl, countryPageUrl, extractVisaSection, fetchVisaPolicyPa
|
|
|
9
33
|
import { readUrl, reach, reachStatus } from './agent-reach.js';
|
|
10
34
|
import { githubSearch, videoSubtitle } from './agent-reach-deep.js';
|
|
11
35
|
import { sessionLogin } from './session-login.js';
|
|
36
|
+
/** C 档中国领事服务网抓取编排:country 直抓 / continent 列表遍历(≤limit,默认 10;抓取纪律=礼貌间隔,不并发) */
|
|
12
37
|
async function fetchVisaPolicyEntries(p) {
|
|
13
38
|
const started = Date.now();
|
|
14
39
|
const fetchedAt = new Date().toISOString();
|
|
@@ -16,322 +41,267 @@ async function fetchVisaPolicyEntries(p) {
|
|
|
16
41
|
const facts = [];
|
|
17
42
|
const targets = [];
|
|
18
43
|
if (p.country) {
|
|
19
|
-
targets.push({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
} else if (p.continent) {
|
|
44
|
+
targets.push({ continent: p.continent ?? 'yz_645708', country: p.country });
|
|
45
|
+
}
|
|
46
|
+
else if (p.continent) {
|
|
24
47
|
const listUrl = continentListUrl(p.continent);
|
|
25
|
-
if (!listUrl)
|
|
26
|
-
ok: false,
|
|
27
|
-
via: 'visa-policy-error',
|
|
28
|
-
evidence: `[visa-policy@${fetchedAt}] 非法洲路径`,
|
|
29
|
-
latencyMs: 0,
|
|
30
|
-
facts: [],
|
|
31
|
-
countries: [],
|
|
32
|
-
error: 'invalid_continent'
|
|
33
|
-
};
|
|
48
|
+
if (!listUrl)
|
|
49
|
+
return { ok: false, via: 'visa-policy-error', evidence: `[visa-policy@${fetchedAt}] 非法洲路径`, latencyMs: 0, facts: [], countries: [], error: 'invalid_continent' };
|
|
34
50
|
const page = await fetchVisaPolicyPage(listUrl, p.timeoutMs);
|
|
35
|
-
if (!page.ok || !page.html)
|
|
36
|
-
ok: false,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
latencyMs: 0,
|
|
40
|
-
facts: [],
|
|
41
|
-
countries: [],
|
|
42
|
-
error: page.error
|
|
43
|
-
};
|
|
44
|
-
for (const c of listCountryPaths(page.html, p.continent).slice(0, Math.max(1, Math.min(10, p.limit ?? 10))))targets.push({
|
|
45
|
-
continent: p.continent,
|
|
46
|
-
country: c
|
|
47
|
-
});
|
|
51
|
+
if (!page.ok || !page.html)
|
|
52
|
+
return { ok: false, via: 'visa-policy-error', evidence: `[visa-policy@${fetchedAt}] 洲列表抓取失败: ${page.error ?? '?'}`, latencyMs: 0, facts: [], countries: [], error: page.error };
|
|
53
|
+
for (const c of listCountryPaths(page.html, p.continent).slice(0, Math.max(1, Math.min(10, p.limit ?? 10))))
|
|
54
|
+
targets.push({ continent: p.continent, country: c });
|
|
48
55
|
}
|
|
49
|
-
for (const t of targets){
|
|
56
|
+
for (const t of targets) {
|
|
50
57
|
const url = countryPageUrl(t.continent, t.country);
|
|
51
|
-
if (!url)
|
|
58
|
+
if (!url)
|
|
59
|
+
continue;
|
|
52
60
|
const page = await fetchVisaPolicyPage(url, p.timeoutMs);
|
|
53
61
|
countries.push(t.country);
|
|
54
|
-
if (!page.ok || !page.html)
|
|
62
|
+
if (!page.ok || !page.html)
|
|
63
|
+
continue;
|
|
55
64
|
const section = extractVisaSection(page.html);
|
|
56
|
-
if (!section)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
countryPath: t.country,
|
|
60
|
-
section,
|
|
61
|
-
fetchedAt
|
|
62
|
-
}));
|
|
65
|
+
if (!section)
|
|
66
|
+
continue;
|
|
67
|
+
facts.push(...policyFactsFromMfa({ countryLabel: t.country, countryPath: t.country, section, fetchedAt }));
|
|
63
68
|
}
|
|
64
|
-
const evidence = facts.length > 0
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
evidence,
|
|
69
|
-
latencyMs: Date.now() - started,
|
|
70
|
-
facts,
|
|
71
|
-
countries
|
|
72
|
-
};
|
|
69
|
+
const evidence = facts.length > 0
|
|
70
|
+
? `[visa-policy:cs-mfa@${fetchedAt}] ${facts.length} 条政策事实(抓取 ${countries.length} 国)`
|
|
71
|
+
: `[visa-policy@${fetchedAt}] 0 条(页面缺「签证入境」章节或抓取失败;不落负事实)`;
|
|
72
|
+
return { ok: true, via: 'cs-mfa', evidence, latencyMs: Date.now() - started, facts, countries };
|
|
73
73
|
}
|
|
74
|
+
/** 默认渠道实现(生产解译器的 dispatch 目标;全部满足「永不抛错」能力层契约) */
|
|
74
75
|
const DEFAULT_HANDLERS = {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
WEATHER_CLIMATE: (p)=>getClimate({
|
|
119
|
-
latitude: p.latitude,
|
|
120
|
-
longitude: p.longitude
|
|
121
|
-
}, p.month, {
|
|
122
|
-
timeoutMs: p.timeoutMs
|
|
123
|
-
}),
|
|
124
|
-
OPENSKY_FLIGHT_VERIFY: (p)=>verifyFlight(p),
|
|
125
|
-
ANYTHING_SEARCH: (p)=>anythingSearch(p),
|
|
126
|
-
WEB_READ: (p)=>readUrl({
|
|
127
|
-
url: p.url,
|
|
128
|
-
timeoutMs: p.timeoutMs
|
|
129
|
-
}),
|
|
130
|
-
GITHUB_SEARCH: (p)=>githubSearch(p),
|
|
131
|
-
VIDEO_SUBTITLE: (p)=>videoSubtitle(p),
|
|
132
|
-
AGENT_REACH: (p)=>p.action === 'status' || !p.action && !p.channel ? reachStatus(p.timeoutMs) : reach({
|
|
133
|
-
channel: p.channel ?? '',
|
|
134
|
-
method: p.method ?? '',
|
|
135
|
-
args: p.args,
|
|
136
|
-
timeoutMs: p.timeoutMs
|
|
137
|
-
}),
|
|
138
|
-
SESSION_LOGIN: (p)=>sessionLogin({
|
|
139
|
-
waitMs: typeof p.waitSeconds === 'number' ? p.waitSeconds * 1000 : undefined
|
|
140
|
-
}),
|
|
141
|
-
VISA_POLICY_FETCH: (p)=>fetchVisaPolicyEntries(p)
|
|
142
|
-
};
|
|
143
|
-
const defaultIsFailure = (r)=>r?.ok === false;
|
|
144
|
-
const flyaiTransient = (msg)=>msg.length > 0 && !/Sentinel/i.test(msg) && /timeout|timed out|ECONNRESET|ECONNREFUSED|ETIMEDOUT|socket|HTTP 5/i.test(msg);
|
|
145
|
-
const API_RETRY = {
|
|
146
|
-
maxAttempts: 2,
|
|
147
|
-
baseDelayMs: 400,
|
|
148
|
-
maxDelayMs: 1_600
|
|
76
|
+
/** 飞猪官方只读通道(机/火/酒店;spawn CLI) */
|
|
77
|
+
FLYAI_SEARCH: (p) => flyaiSearch(p),
|
|
78
|
+
/** hotelbyte 桥(实时 hbcli → 静态包降级;spawn CLI) */
|
|
79
|
+
HBCLI_HOTEL_SEARCH: (p) => searchHotels({ destination: p.destination, checkIn: p.checkIn, checkOut: p.checkOut, adults: p.adults }, { hbcliBin: p.hbcliBin, timeoutMs: p.timeoutMs, fallbackPath: p.fallbackPath }),
|
|
80
|
+
/** hotelbyte 桥·房型报价(M0 预订链;spawn CLI;价格面无静态降级 fail-closed) */
|
|
81
|
+
HBCLI_HOTEL_RATES: (p) => hotelRates({
|
|
82
|
+
hotelId: p.hotelId, checkIn: p.checkIn, checkOut: p.checkOut, adults: p.adults,
|
|
83
|
+
countryCode: p.countryCode, nationalityCode: p.nationalityCode, residencyCode: p.residencyCode,
|
|
84
|
+
}, { hbcliBin: p.hbcliBin, timeoutMs: p.timeoutMs }),
|
|
85
|
+
/** hotelbyte 桥·下单前实时验价(M0 预订链;spawn CLI;同价格面红线,不可用即诚实失败) */
|
|
86
|
+
HBCLI_CHECK_AVAIL: (p) => checkAvail({ ratePkgId: p.ratePkgId }, { hbcliBin: p.hbcliBin, timeoutMs: p.timeoutMs }),
|
|
87
|
+
/** 用户本人登录态会话检索(浏览器解译通道:扩展桥默认 + cdp 显式后备;ReadGuard+节律闸) */
|
|
88
|
+
SESSION_FLIGHT_SEARCH: (p) => sessionFlightSearch(p),
|
|
89
|
+
/** 用户本人登录态会话酒店检索(2026-09-03 实装;同浏览器解译通道,风控红线同款) */
|
|
90
|
+
SESSION_HOTEL_SEARCH: (p) => sessionHotelSearch(p),
|
|
91
|
+
/** 12306 余票会话检索(2026-09-03 实装;公开查询面,无登录闸;同浏览器解译通道) */
|
|
92
|
+
SESSION_TRAIN_SEARCH: (p) => sessionTrainSearch(p),
|
|
93
|
+
/** Dida 供应商门户实时价会话检索(2026-09-09 实装;hotel-be portal integration 迁移线) */
|
|
94
|
+
SESSION_DIDA_SEARCH: (p) => sessionDidaSearch(p),
|
|
95
|
+
/** 地名 → 坐标(open-meteo → nominatim 双源,免费) */
|
|
96
|
+
WEATHER_GEOCODE: (p) => geocodePlace(p.name, { count: p.count, timeoutMs: p.timeoutMs }),
|
|
97
|
+
/** 预报(≤16 天,免费无 key) */
|
|
98
|
+
WEATHER_FORECAST: (p) => getForecast({ latitude: p.latitude, longitude: p.longitude }, { days: p.days, timeoutMs: p.timeoutMs }),
|
|
99
|
+
/** 历史气候(季节性基线,免费无 key) */
|
|
100
|
+
WEATHER_CLIMATE: (p) => getClimate({ latitude: p.latitude, longitude: p.longitude }, p.month, { timeoutMs: p.timeoutMs }),
|
|
101
|
+
/** OpenSky ADS-B 实时印证(免费匿名,~400 credits/天) */
|
|
102
|
+
OPENSKY_FLIGHT_VERIFY: (p) => verifyFlight(p),
|
|
103
|
+
/** hotel-be Anything 目的地/酒店候选(hbcli CLI;gotry_anything_search 同源,D-23 收编) */
|
|
104
|
+
ANYTHING_SEARCH: (p) => anythingSearch(p),
|
|
105
|
+
/** 网页读取兜底(r.jina.ai 免费公共源;gotry_web_search 同源,D-23 收编) */
|
|
106
|
+
WEB_READ: (p) => readUrl({ url: p.url, timeoutMs: p.timeoutMs }),
|
|
107
|
+
/** GitHub 仓库搜索(gh CLI;agent-reach-deep 执行面,gotry_github_search 同源,D-23 收编) */
|
|
108
|
+
GITHUB_SEARCH: (p) => githubSearch(p),
|
|
109
|
+
/** 视频字幕拉取(yt-dlp;agent-reach-deep 执行面,gotry_video_subtitle 同源,D-23 收编) */
|
|
110
|
+
VIDEO_SUBTITLE: (p) => videoSubtitle(p),
|
|
111
|
+
/** agent-reach 反射桥(status=上游 doctor;reach=渠道×方法透传,gotry_agent_reach 同源,D-23 收编) */
|
|
112
|
+
AGENT_REACH: (p) => (p.action === 'status' || (!p.action && !p.channel))
|
|
113
|
+
? reachStatus(p.timeoutMs)
|
|
114
|
+
: reach({ channel: p.channel ?? '', method: p.method ?? '', args: p.args, timeoutMs: p.timeoutMs }),
|
|
115
|
+
/** 账号会话登录引导(浏览器通道;票据名只读 0 值过手,gotry_session_login 同源,D-23 收编) */
|
|
116
|
+
SESSION_LOGIN: (p) => sessionLogin({ waitMs: typeof p.waitSeconds === 'number' ? p.waitSeconds * 1000 : undefined }),
|
|
117
|
+
/** 政策事实生产端 v1(issue #141,D-26):C 档中国领事服务网国家指南树(礼貌抓取:永不重试+断路器护站) */
|
|
118
|
+
VISA_POLICY_FETCH: (p) => fetchVisaPolicyEntries(p),
|
|
149
119
|
};
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
120
|
+
const defaultIsFailure = (r) => r?.ok === false;
|
|
121
|
+
/** FlyAI 错误原话里瞬时类(超时/网络断)才值得重试;Sentinel 限流与 ENOENT 永不 */
|
|
122
|
+
const flyaiTransient = (msg) => msg.length > 0
|
|
123
|
+
&& !/Sentinel/i.test(msg)
|
|
124
|
+
&& /timeout|timed out|ECONNRESET|ECONNREFUSED|ETIMEDOUT|socket|HTTP 5/i.test(msg);
|
|
125
|
+
const API_RETRY = { maxAttempts: 2, baseDelayMs: 400, maxDelayMs: 1_600 };
|
|
126
|
+
const API_BREAKER = { failureThreshold: 3, openMs: 30_000 };
|
|
127
|
+
/**
|
|
128
|
+
* HBCLI timeout 专用瞬时判定(2026-09-02 迪拜 session 实况:hotel-list 首调
|
|
129
|
+
* 30s 超时,LLM 立即手动重试即成功——上游冷启动建后端 session 可超 30s,而
|
|
130
|
+
* 「候选路径是切换不是重试」的契约只针对 ENOENT 类 spawn 失败,不覆盖超时)。
|
|
131
|
+
* 只有 timeout 类失败值得重试一次;ENOENT/退码类上游明确说「不」的失败永不。
|
|
132
|
+
*/
|
|
133
|
+
const hbcliTimeout = (r) => {
|
|
155
134
|
const msg = String((r ?? {}).error ?? '');
|
|
156
135
|
return msg.length > 0 && /timeout/i.test(msg);
|
|
157
136
|
};
|
|
158
|
-
const HBCLI_RETRY = {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
137
|
+
const HBCLI_RETRY = { maxAttempts: 2, baseDelayMs: 300, maxDelayMs: 1_000 };
|
|
138
|
+
/**
|
|
139
|
+
* 渠道韧性策略表(权威面;docs/design/effect-interpreter.md §3 同表逐行有依据):
|
|
140
|
+
* - FLYAI:瞬时代码级错误重试 1 次;连续 3 次 error(含 Sentinel)熔断 60s 保护配额;
|
|
141
|
+
* 试用额度达限(429)归 needs-setup,永不重试;
|
|
142
|
+
* - HBCLI:仅 timeout 类失败重试 1 次(冷启动建后端 session 可超时,重试即恢复);
|
|
143
|
+
* ENOENT/退码类永不重试(上游契约「候选路径是切换不是重试」),熔断同上;
|
|
144
|
+
* RATES/CHECK_AVAIL 同族,且价格面无静态降级(fail-closed,不估算房价——
|
|
145
|
+
* 与 bookable-facts 证据分级同口径,live_inventory 才可进确认卡);
|
|
146
|
+
* - SESSION:永不重试、不熔断——风控/挑战是「上游说不」,重试即红线;
|
|
147
|
+
* 节律闸(≥30s)在渠道内(session-search §3.4),不在本层重复。
|
|
148
|
+
* - WEATHER/OPENSKY:免费源,瞬时网络抖动可重试,熔断防免费配额空转。
|
|
149
|
+
* - ANYTHING:同 HBCLI 族(timeout 类瞬时重试 1 次;ENOENT/退码类永不);
|
|
150
|
+
* - WEB_READ:r.jina.ai 免费公共源,瞬时抖动可重试,熔断防公共配额空转;
|
|
151
|
+
* - GITHUB/VIDEO(gh/yt-dlp):verdict=timeout 瞬时重试 1 次;not-installed=配置态不重试;
|
|
152
|
+
* - AGENT_REACH:反射桥透传(wrapper 不是 router)——永不重试不熔断,上游超时自管;
|
|
153
|
+
* - SESSION_LOGIN:同 SESSION 浏览器族(风控红线)。
|
|
154
|
+
* (D-23 收尾,issue #115:六渠道入表,没有策略表行就没有效应。)
|
|
155
|
+
*/
|
|
163
156
|
const SPECS = {
|
|
164
157
|
FLYAI_SEARCH: {
|
|
165
158
|
channel: 'cli',
|
|
166
|
-
retry: {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
maxDelayMs: 2_000
|
|
170
|
-
},
|
|
171
|
-
breaker: {
|
|
172
|
-
failureThreshold: 3,
|
|
173
|
-
openMs: 60_000
|
|
174
|
-
},
|
|
175
|
-
isRetryable: (_r, e)=>{
|
|
159
|
+
retry: { maxAttempts: 2, baseDelayMs: 500, maxDelayMs: 2_000 },
|
|
160
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
161
|
+
isRetryable: (_r, e) => {
|
|
176
162
|
const msg = e != null ? String(e.message ?? e) : String((_r ?? {}).error ?? '');
|
|
177
163
|
return flyaiTransient(msg);
|
|
178
164
|
},
|
|
179
|
-
isFailure: defaultIsFailure
|
|
165
|
+
isFailure: defaultIsFailure,
|
|
180
166
|
},
|
|
181
167
|
HBCLI_HOTEL_SEARCH: {
|
|
182
168
|
channel: 'cli',
|
|
183
169
|
retry: HBCLI_RETRY,
|
|
184
|
-
isRetryable
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
isFailure: (r)=>r.via === 'hbcli-error'
|
|
170
|
+
// spec 级 isRetryable 是解译器组装 policy 的权威源(FLYAI 同款):仅 timeout 类
|
|
171
|
+
// 瞬时(冷启动建后端 session);ENOENT/退码类永不(「切换不是重试」契约不变)
|
|
172
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
173
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
174
|
+
isFailure: r => r.via === 'hbcli-error',
|
|
190
175
|
},
|
|
191
176
|
HBCLI_HOTEL_RATES: {
|
|
192
177
|
channel: 'cli',
|
|
193
178
|
retry: HBCLI_RETRY,
|
|
194
|
-
isRetryable: (r)=>hbcliTimeout(r),
|
|
195
|
-
breaker: {
|
|
196
|
-
|
|
197
|
-
openMs: 60_000
|
|
198
|
-
},
|
|
199
|
-
isFailure: (r)=>r.via === 'hbcli-error'
|
|
179
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
180
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
181
|
+
isFailure: r => r.via === 'hbcli-error',
|
|
200
182
|
},
|
|
201
183
|
HBCLI_CHECK_AVAIL: {
|
|
202
184
|
channel: 'cli',
|
|
203
185
|
retry: HBCLI_RETRY,
|
|
204
|
-
isRetryable: (r)=>hbcliTimeout(r),
|
|
205
|
-
breaker: {
|
|
206
|
-
|
|
207
|
-
openMs: 60_000
|
|
208
|
-
},
|
|
209
|
-
isFailure: (r)=>r.via === 'hbcli-error'
|
|
186
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
187
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
188
|
+
isFailure: r => r.via === 'hbcli-error',
|
|
210
189
|
},
|
|
211
190
|
SESSION_FLIGHT_SEARCH: {
|
|
212
191
|
channel: 'browser',
|
|
213
192
|
retry: null,
|
|
214
193
|
breaker: null,
|
|
215
|
-
isFailure: defaultIsFailure
|
|
194
|
+
isFailure: defaultIsFailure,
|
|
216
195
|
},
|
|
217
196
|
SESSION_HOTEL_SEARCH: {
|
|
218
197
|
channel: 'browser',
|
|
219
198
|
retry: null,
|
|
220
199
|
breaker: null,
|
|
221
|
-
isFailure: defaultIsFailure
|
|
200
|
+
isFailure: defaultIsFailure,
|
|
222
201
|
},
|
|
223
202
|
SESSION_TRAIN_SEARCH: {
|
|
224
203
|
channel: 'browser',
|
|
225
204
|
retry: null,
|
|
226
205
|
breaker: null,
|
|
227
|
-
isFailure: defaultIsFailure
|
|
228
|
-
},
|
|
229
|
-
WEATHER_GEOCODE: {
|
|
230
|
-
channel: 'api',
|
|
231
|
-
retry: API_RETRY,
|
|
232
|
-
breaker: API_BREAKER,
|
|
233
|
-
isFailure: defaultIsFailure
|
|
234
|
-
},
|
|
235
|
-
WEATHER_FORECAST: {
|
|
236
|
-
channel: 'api',
|
|
237
|
-
retry: API_RETRY,
|
|
238
|
-
breaker: API_BREAKER,
|
|
239
|
-
isFailure: defaultIsFailure
|
|
206
|
+
isFailure: defaultIsFailure,
|
|
240
207
|
},
|
|
241
|
-
|
|
242
|
-
channel: '
|
|
243
|
-
retry:
|
|
244
|
-
breaker:
|
|
245
|
-
isFailure: defaultIsFailure
|
|
208
|
+
SESSION_DIDA_SEARCH: {
|
|
209
|
+
channel: 'browser',
|
|
210
|
+
retry: null,
|
|
211
|
+
breaker: null,
|
|
212
|
+
isFailure: defaultIsFailure,
|
|
246
213
|
},
|
|
214
|
+
WEATHER_GEOCODE: { channel: 'api', retry: API_RETRY, breaker: API_BREAKER, isFailure: defaultIsFailure },
|
|
215
|
+
WEATHER_FORECAST: { channel: 'api', retry: API_RETRY, breaker: API_BREAKER, isFailure: defaultIsFailure },
|
|
216
|
+
WEATHER_CLIMATE: { channel: 'api', retry: API_RETRY, breaker: API_BREAKER, isFailure: defaultIsFailure },
|
|
247
217
|
OPENSKY_FLIGHT_VERIFY: {
|
|
248
218
|
channel: 'api',
|
|
249
219
|
retry: API_RETRY,
|
|
250
220
|
breaker: API_BREAKER,
|
|
251
|
-
isRetryable: (_r, e)=>e != null || (_r ?? {}).verdict === 'unavailable',
|
|
252
|
-
isFailure:
|
|
221
|
+
isRetryable: (_r, e) => e != null || (_r ?? {}).verdict === 'unavailable',
|
|
222
|
+
isFailure: r => r.via === 'opensky-error',
|
|
253
223
|
},
|
|
224
|
+
// --- D-23 收尾(issue #115):六渠道入注册表——没有策略表行就没有效应 ---
|
|
225
|
+
// ANYTHING:同 HBCLI 族(hbcli CLI):仅 timeout 类瞬时(冷启动建后端 session)重试 1 次;
|
|
226
|
+
// ENOENT/退码类永不(「切换不是重试」契约);熔断防上游空转
|
|
254
227
|
ANYTHING_SEARCH: {
|
|
255
228
|
channel: 'cli',
|
|
256
229
|
retry: HBCLI_RETRY,
|
|
257
|
-
isRetryable: (r)=>hbcliTimeout(r),
|
|
258
|
-
breaker: {
|
|
259
|
-
|
|
260
|
-
openMs: 60_000
|
|
261
|
-
},
|
|
262
|
-
isFailure: (r)=>r.via === 'hbcli-anything-error'
|
|
230
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
231
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
232
|
+
isFailure: r => r.via === 'hbcli-anything-error',
|
|
263
233
|
},
|
|
234
|
+
// WEB_READ:r.jina.ai 免费公共源(同 WEATHER/OPENSKY 族):瞬时网络抖动可重试,
|
|
235
|
+
// 熔断防公共配额空转;非法 URL 是用户输入错(isFailure 不含,不重试不熔断)
|
|
264
236
|
WEB_READ: {
|
|
265
237
|
channel: 'api',
|
|
266
238
|
retry: API_RETRY,
|
|
267
239
|
breaker: API_BREAKER,
|
|
268
|
-
isFailure:
|
|
240
|
+
isFailure: r => r.via === 'r.jina.ai-error',
|
|
269
241
|
},
|
|
242
|
+
// GITHUB/VIDEO(gh/yt-dlp CLI):verdict=timeout 瞬时重试 1 次(进程冷启动超时同 HBCLI
|
|
243
|
+
// 2026-09-02 实况);not-installed 是配置态(needs-setup 族,重试无意义),error 是上游说不;
|
|
244
|
+
// 两者都计入熔断(防坏环境空转)
|
|
270
245
|
GITHUB_SEARCH: {
|
|
271
246
|
channel: 'cli',
|
|
272
247
|
retry: HBCLI_RETRY,
|
|
273
|
-
isRetryable: (r)=>r.verdict === 'timeout',
|
|
274
|
-
breaker: {
|
|
275
|
-
|
|
276
|
-
openMs: 60_000
|
|
277
|
-
},
|
|
278
|
-
isFailure: (r)=>{
|
|
279
|
-
const v = r.verdict;
|
|
280
|
-
return v === 'error' || v === 'timeout';
|
|
281
|
-
}
|
|
248
|
+
isRetryable: (r) => r.verdict === 'timeout',
|
|
249
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
250
|
+
isFailure: r => { const v = r.verdict; return v === 'error' || v === 'timeout'; },
|
|
282
251
|
},
|
|
283
252
|
VIDEO_SUBTITLE: {
|
|
284
253
|
channel: 'cli',
|
|
285
254
|
retry: HBCLI_RETRY,
|
|
286
|
-
isRetryable: (r)=>r.verdict === 'timeout',
|
|
287
|
-
breaker: {
|
|
288
|
-
|
|
289
|
-
openMs: 60_000
|
|
290
|
-
},
|
|
291
|
-
isFailure: (r)=>{
|
|
292
|
-
const v = r.verdict;
|
|
293
|
-
return v === 'error' || v === 'timeout';
|
|
294
|
-
}
|
|
255
|
+
isRetryable: (r) => r.verdict === 'timeout',
|
|
256
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
257
|
+
isFailure: r => { const v = r.verdict; return v === 'error' || v === 'timeout'; },
|
|
295
258
|
},
|
|
259
|
+
// AGENT_REACH:反射桥透传(wrapper 不是 router,D-4a')——上游超时自管,重试会放大
|
|
260
|
+
// 上游配额消耗,永不重试不熔断;needs-setup/not-installed 是「上游说不」同 SESSION 族
|
|
296
261
|
AGENT_REACH: {
|
|
297
262
|
channel: 'cli',
|
|
298
263
|
retry: null,
|
|
299
264
|
breaker: null,
|
|
300
|
-
isFailure:
|
|
265
|
+
isFailure: r => r.verdict === 'error',
|
|
301
266
|
},
|
|
267
|
+
// SESSION_LOGIN:同 SESSION_* 浏览器族——风控/挑战红线,永不重试不熔断;
|
|
268
|
+
// pending 是「等用户」不是失败
|
|
302
269
|
SESSION_LOGIN: {
|
|
303
270
|
channel: 'browser',
|
|
304
271
|
retry: null,
|
|
305
272
|
breaker: null,
|
|
306
|
-
isFailure: defaultIsFailure
|
|
273
|
+
isFailure: defaultIsFailure,
|
|
307
274
|
},
|
|
275
|
+
// VISA_POLICY(issue #141,D-26 v1):C 档中国领事服务网——政府权威源礼貌抓取:
|
|
276
|
+
// 永不重试(抓取纪律)+ 断路器防 hammering 政府站点;页面缺章节=无结论不落负事实
|
|
308
277
|
VISA_POLICY_FETCH: {
|
|
309
278
|
channel: 'api',
|
|
310
279
|
retry: null,
|
|
311
|
-
breaker: {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
},
|
|
315
|
-
isFailure: defaultIsFailure
|
|
316
|
-
}
|
|
280
|
+
breaker: { failureThreshold: 2, openMs: 300_000 },
|
|
281
|
+
isFailure: defaultIsFailure,
|
|
282
|
+
},
|
|
317
283
|
};
|
|
284
|
+
/** 断路/未注册时的平铺失败观察(ADR-13 ToolFailure 兼容形态,工具层可直接返回) */
|
|
318
285
|
export function declinedObservation(effect, trace) {
|
|
319
286
|
return {
|
|
320
287
|
ok: false,
|
|
321
288
|
verdict: 'error',
|
|
322
|
-
summary: trace.declined === 'circuit-open'
|
|
323
|
-
|
|
289
|
+
summary: trace.declined === 'circuit-open'
|
|
290
|
+
? `${effect} 通道被断路器开启保护(连续失败达到阈值,冷却中)——不要立即重试,换其他工具或稍后再试;原因见 trace 证据链`
|
|
291
|
+
: `${effect} 未登记效应(effect_interpreter.v1 注册表外,生产解译器拒绝)`,
|
|
292
|
+
evidence: trace.evidence.join(';'),
|
|
324
293
|
};
|
|
325
294
|
}
|
|
326
295
|
const defaultBreakers = new Map();
|
|
327
|
-
const defaultSleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
296
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
297
|
+
/** 测试隔离:清空产品单例断路器(断路状态是进程内瞬态,同 rate limiter 先例) */
|
|
328
298
|
export function __resetEffectBreakersForTest() {
|
|
329
299
|
defaultBreakers.clear();
|
|
330
300
|
}
|
|
331
301
|
export function makeProductionInterpreter(opts = {}) {
|
|
332
302
|
const breakers = opts.breakers ?? defaultBreakers;
|
|
333
303
|
const sleep = opts.sleep ?? defaultSleep;
|
|
334
|
-
return async (fx)=>{
|
|
304
|
+
return async (fx) => {
|
|
335
305
|
const ts = new Date().toISOString();
|
|
336
306
|
const name = fx.effect;
|
|
337
307
|
const spec = SPECS[name];
|
|
@@ -339,128 +309,72 @@ export function makeProductionInterpreter(opts = {}) {
|
|
|
339
309
|
if (!spec || !handler) {
|
|
340
310
|
return {
|
|
341
311
|
result: null,
|
|
342
|
-
trace: {
|
|
343
|
-
effect: fx.effect,
|
|
344
|
-
channel: 'api',
|
|
345
|
-
attempts: 0,
|
|
346
|
-
backoffMs: 0,
|
|
347
|
-
breaker: 'off',
|
|
348
|
-
declined: 'unknown-effect',
|
|
349
|
-
evidence: [
|
|
350
|
-
`[效应:${fx.effect}@${ts}] unknown effect`
|
|
351
|
-
]
|
|
352
|
-
}
|
|
312
|
+
trace: { effect: fx.effect, channel: 'api', attempts: 0, backoffMs: 0, breaker: 'off', declined: 'unknown-effect', evidence: [`[效应:${fx.effect}@${ts}] unknown effect`] },
|
|
353
313
|
};
|
|
354
314
|
}
|
|
355
|
-
const br = (()=>{
|
|
356
|
-
if (!spec.breaker)
|
|
315
|
+
const br = (() => {
|
|
316
|
+
if (!spec.breaker)
|
|
317
|
+
return null;
|
|
357
318
|
const existing = breakers.get(fx.effect);
|
|
358
|
-
if (existing)
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
...opts.now ? {
|
|
362
|
-
now: opts.now
|
|
363
|
-
} : {}
|
|
364
|
-
});
|
|
319
|
+
if (existing)
|
|
320
|
+
return existing;
|
|
321
|
+
const created = new CircuitBreaker({ ...spec.breaker, ...(opts.now ? { now: opts.now } : {}) });
|
|
365
322
|
breakers.set(fx.effect, created);
|
|
366
323
|
return created;
|
|
367
324
|
})();
|
|
368
|
-
const gate = br?.canAttempt() ?? {
|
|
369
|
-
allowed: true,
|
|
370
|
-
state: 'closed'
|
|
371
|
-
};
|
|
325
|
+
const gate = br?.canAttempt() ?? { allowed: true, state: 'closed' };
|
|
372
326
|
if (!gate.allowed) {
|
|
373
|
-
const trace = {
|
|
374
|
-
|
|
375
|
-
channel: spec.channel,
|
|
376
|
-
attempts: 0,
|
|
377
|
-
backoffMs: 0,
|
|
378
|
-
breaker: gate.state,
|
|
379
|
-
declined: 'circuit-open',
|
|
380
|
-
evidence: [
|
|
381
|
-
`[效应:${fx.effect}@${ts}] breaker=${gate.state} 拒绝(冷却中,不重试)`
|
|
382
|
-
]
|
|
383
|
-
};
|
|
384
|
-
return {
|
|
385
|
-
result: null,
|
|
386
|
-
trace
|
|
387
|
-
};
|
|
327
|
+
const trace = { effect: fx.effect, channel: spec.channel, attempts: 0, backoffMs: 0, breaker: gate.state, declined: 'circuit-open', evidence: [`[效应:${fx.effect}@${ts}] breaker=${gate.state} 拒绝(冷却中,不重试)`] };
|
|
328
|
+
return { result: null, trace };
|
|
388
329
|
}
|
|
389
|
-
const policy = spec.retry
|
|
390
|
-
maxAttempts: spec.retry.maxAttempts,
|
|
391
|
-
baseDelayMs:
|
|
392
|
-
maxDelayMs: spec.retry.maxDelayMs,
|
|
393
|
-
isRetryable: spec.isRetryable,
|
|
394
|
-
sleep
|
|
395
|
-
} : {
|
|
396
|
-
maxAttempts: 1,
|
|
397
|
-
baseDelayMs: 0,
|
|
398
|
-
maxDelayMs: 0,
|
|
399
|
-
sleep
|
|
400
|
-
};
|
|
330
|
+
const policy = spec.retry
|
|
331
|
+
? { maxAttempts: spec.retry.maxAttempts, baseDelayMs: spec.retry.baseDelayMs, maxDelayMs: spec.retry.maxDelayMs, isRetryable: spec.isRetryable, sleep }
|
|
332
|
+
: { maxAttempts: 1, baseDelayMs: 0, maxDelayMs: 0, sleep };
|
|
401
333
|
const dispatch = handler;
|
|
402
|
-
const outcome = await withRetry(()=>dispatch(fx.params), policy);
|
|
334
|
+
const outcome = await withRetry(() => dispatch(fx.params), policy);
|
|
403
335
|
const failed = outcome.error != null || spec.isFailure(outcome.result);
|
|
404
|
-
if (br)
|
|
336
|
+
if (br)
|
|
337
|
+
failed ? br.onFailure() : br.onSuccess();
|
|
405
338
|
const breakerState = br?.state() ?? 'off';
|
|
406
|
-
const evidence = [
|
|
407
|
-
`[效应:${fx.effect}@${ts}] attempts=${outcome.attempts} backoff=${outcome.backoffMs}ms breaker=${breakerState}`
|
|
408
|
-
];
|
|
339
|
+
const evidence = [`[效应:${fx.effect}@${ts}] attempts=${outcome.attempts} backoff=${outcome.backoffMs}ms breaker=${breakerState}`];
|
|
409
340
|
return {
|
|
410
341
|
result: outcome.error != null ? null : outcome.result,
|
|
411
|
-
trace: {
|
|
412
|
-
effect: fx.effect,
|
|
413
|
-
channel: spec.channel,
|
|
414
|
-
attempts: outcome.attempts,
|
|
415
|
-
backoffMs: outcome.backoffMs,
|
|
416
|
-
breaker: breakerState,
|
|
417
|
-
evidence
|
|
418
|
-
}
|
|
342
|
+
trace: { effect: fx.effect, channel: spec.channel, attempts: outcome.attempts, backoffMs: outcome.backoffMs, breaker: breakerState, evidence },
|
|
419
343
|
};
|
|
420
344
|
};
|
|
421
345
|
}
|
|
346
|
+
/** 产品面解译器单例(断路器状态随进程存活;同 session-search 节律闸先例) */
|
|
422
347
|
export const productionInterpreter = makeProductionInterpreter();
|
|
348
|
+
/** 类型化入口:tool 层 `interpretEffect({ effect: 'FLYAI_SEARCH', params })` */
|
|
423
349
|
export async function interpretEffect(fx) {
|
|
424
350
|
return productionInterpreter(fx);
|
|
425
351
|
}
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
// mock 解译器:CI/本地无网确定性(夹具 = 渠道 observation 的录制形态)
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
/**
|
|
356
|
+
* mock 解译器(禁止网络,0 延迟,确定性):给同一效应返回夹具原样——
|
|
357
|
+
* 渠道 observation 的录制就长什么样回放什么样,证据链由夹具自带
|
|
358
|
+
* (录制期已带 [实时API:…]),trace 落 [效应:mock@ts] 夹具回放标注。
|
|
359
|
+
* CI / 离线单测 / 巡检「不联网跑全链」即此通道(与 mock-llm 同思想的第三个成员)。
|
|
360
|
+
*/
|
|
426
361
|
export function makeMockInterpreter(fixtures) {
|
|
427
|
-
return async (fx)=>{
|
|
362
|
+
return async (fx) => {
|
|
428
363
|
const ts = new Date().toISOString();
|
|
429
364
|
const registered = Object.prototype.hasOwnProperty.call(fixtures, fx.effect);
|
|
430
365
|
if (!registered) {
|
|
431
366
|
return {
|
|
432
367
|
result: null,
|
|
433
|
-
trace: {
|
|
434
|
-
effect: fx.effect,
|
|
435
|
-
channel: 'mock',
|
|
436
|
-
attempts: 0,
|
|
437
|
-
backoffMs: 0,
|
|
438
|
-
breaker: 'off',
|
|
439
|
-
declined: 'unknown-effect',
|
|
440
|
-
evidence: [
|
|
441
|
-
`[效应:mock@${ts}] ${fx.effect} 未登记夹具`
|
|
442
|
-
]
|
|
443
|
-
}
|
|
368
|
+
trace: { effect: fx.effect, channel: 'mock', attempts: 0, backoffMs: 0, breaker: 'off', declined: 'unknown-effect', evidence: [`[效应:mock@${ts}] ${fx.effect} 未登记夹具`] },
|
|
444
369
|
};
|
|
445
370
|
}
|
|
446
371
|
return {
|
|
447
372
|
result: fixtures[fx.effect],
|
|
448
|
-
trace: {
|
|
449
|
-
effect: fx.effect,
|
|
450
|
-
channel: 'mock',
|
|
451
|
-
attempts: 1,
|
|
452
|
-
backoffMs: 0,
|
|
453
|
-
breaker: 'off',
|
|
454
|
-
evidence: [
|
|
455
|
-
`[效应:mock@${ts}] ${fx.effect} 夹具回放(确定性,无网络)`
|
|
456
|
-
]
|
|
457
|
-
}
|
|
373
|
+
trace: { effect: fx.effect, channel: 'mock', attempts: 1, backoffMs: 0, breaker: 'off', evidence: [`[效应:mock@${ts}] ${fx.effect} 夹具回放(确定性,无网络)`] },
|
|
458
374
|
};
|
|
459
375
|
};
|
|
460
376
|
}
|
|
377
|
+
/** 按环境选择解译器('production' 默认;'mock' 供 CI/离线) */
|
|
461
378
|
export function selectInterpreter(env, opts = {}) {
|
|
462
379
|
return env === 'mock' ? makeMockInterpreter(opts.fixtures ?? {}) : makeProductionInterpreter(opts);
|
|
463
380
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
//# sourceURL=ts/capabilities/effect.ts
|