@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.
Files changed (327) hide show
  1. package/README.md +102 -194
  2. package/README.zh-CN.md +115 -210
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-bootstrap.js +466 -35
  5. package/bin/gotry-inner.js +255 -36
  6. package/bin/gotry-process-liveness.d.ts +27 -0
  7. package/bin/gotry-process-liveness.js +97 -0
  8. package/bin/gotry-runtime-resolution.d.ts +1 -1
  9. package/bin/gotry-runtime-resolution.js +2 -2
  10. package/client/client.js +174 -0
  11. package/cordis.gotry-patch.yml +70 -93
  12. package/dist/capabilities/agent-reach-deep.js +45 -122
  13. package/dist/capabilities/agent-reach.js +63 -148
  14. package/dist/capabilities/anything.js +89 -75
  15. package/dist/capabilities/artifacts.js +123 -104
  16. package/dist/capabilities/channel-health.js +65 -45
  17. package/dist/capabilities/channel-registry.js +108 -87
  18. package/dist/capabilities/doctor.js +357 -235
  19. package/dist/capabilities/effect.js +223 -309
  20. package/dist/capabilities/fact-log.js +28 -14
  21. package/dist/capabilities/flyai.js +245 -156
  22. package/dist/capabilities/ground-transfer.js +509 -0
  23. package/dist/capabilities/hbcli.js +144 -148
  24. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  25. package/dist/capabilities/incident-log.js +131 -61
  26. package/dist/capabilities/model-override.js +30 -10
  27. package/dist/capabilities/opensky.js +54 -43
  28. package/dist/capabilities/resilience.js +50 -32
  29. package/dist/capabilities/session/action-cache.js +50 -44
  30. package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
  31. package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
  32. package/dist/capabilities/session/adapters/dida-portal.js +206 -0
  33. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  34. package/dist/capabilities/session/adapters/rail-12306.js +169 -112
  35. package/dist/capabilities/session/benchmark.js +85 -149
  36. package/dist/capabilities/session/extension-bridge.js +320 -259
  37. package/dist/capabilities/session/extension-channel.js +57 -76
  38. package/dist/capabilities/session/extension-distribution.js +115 -119
  39. package/dist/capabilities/session/extract.js +31 -21
  40. package/dist/capabilities/session/golden-score.js +55 -33
  41. package/dist/capabilities/session/health-watch.js +146 -154
  42. package/dist/capabilities/session/read-guard.js +55 -47
  43. package/dist/capabilities/session/static-flight-golden.js +49 -36
  44. package/dist/capabilities/session/transport.js +54 -61
  45. package/dist/capabilities/session/wizard.js +63 -93
  46. package/dist/capabilities/session-consent.js +143 -47
  47. package/dist/capabilities/session-login.js +95 -126
  48. package/dist/capabilities/session-search.js +391 -253
  49. package/dist/capabilities/sponsor-plugin.js +341 -0
  50. package/dist/capabilities/visa-policy.js +87 -73
  51. package/dist/capabilities/weather.js +163 -235
  52. package/dist/scripts/action-cache-tests.js +24 -59
  53. package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
  54. package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
  55. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  56. package/dist/scripts/agent-reach-tests.js +26 -18
  57. package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
  58. package/dist/scripts/anything-tests.js +58 -62
  59. package/dist/scripts/artifact-client-contract-tests.js +69 -0
  60. package/dist/scripts/artifacts-capability-tests.js +235 -0
  61. package/dist/scripts/async-collect.js +26 -15
  62. package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
  63. package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
  64. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
  65. package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
  66. package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
  67. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
  68. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
  69. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
  70. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
  71. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
  72. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
  73. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
  74. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
  75. package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
  76. package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
  77. package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
  78. package/dist/scripts/booking-executor-tests.js +80 -0
  79. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  80. package/dist/scripts/booking-saga-tests.js +61 -123
  81. package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
  82. package/dist/scripts/bootstrap-tests.js +1383 -128
  83. package/dist/scripts/build-changelog.js +89 -130
  84. package/dist/scripts/build-metrics-report.js +135 -143
  85. package/dist/scripts/changelog-tests.js +35 -82
  86. package/dist/scripts/channel-probe-tests.js +44 -116
  87. package/dist/scripts/channel-probe.js +84 -167
  88. package/dist/scripts/channel-registry-tests.js +97 -185
  89. package/dist/scripts/companion-tests.js +25 -70
  90. package/dist/scripts/dida-cookie-probe.js +87 -0
  91. package/dist/scripts/dida-sw-probe.js +114 -0
  92. package/dist/scripts/diff-test.js +17 -12
  93. package/dist/scripts/doctor-tests.js +428 -120
  94. package/dist/scripts/dsh-artifact-e2e.js +150 -0
  95. package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
  96. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  97. package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
  98. package/dist/scripts/dsh-runtime-closure.js +75 -63
  99. package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
  100. package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
  101. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  102. package/dist/scripts/effect-tests.js +165 -552
  103. package/dist/scripts/engine-run.js +1 -3
  104. package/dist/scripts/engine-tests.js +16 -10
  105. package/dist/scripts/evaluation-cadence-tests.js +69 -242
  106. package/dist/scripts/evaluation-contract-tests.js +193 -499
  107. package/dist/scripts/extension-distribution-cli.js +19 -15
  108. package/dist/scripts/extension-distribution-tests.js +98 -211
  109. package/dist/scripts/extension-tests.js +366 -389
  110. package/dist/scripts/fact-gate-tests.js +725 -263
  111. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  112. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  113. package/dist/scripts/flight-malformed-tests.js +159 -0
  114. package/dist/scripts/flyai-tests.js +325 -68
  115. package/dist/scripts/g5-guard-tests.js +131 -0
  116. package/dist/scripts/g5-guard.js +189 -0
  117. package/dist/scripts/gen-lunar-anchors.js +106 -0
  118. package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
  119. package/dist/scripts/ground-transfer-tests.js +956 -0
  120. package/dist/scripts/hbcli-e2e-tests.js +71 -90
  121. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  122. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  123. package/dist/scripts/hbcli-tests.js +41 -115
  124. package/dist/scripts/health-watch-cli.js +42 -28
  125. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  126. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  127. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  128. package/dist/scripts/i18n-tests.js +16 -9
  129. package/dist/scripts/incident-tests.js +281 -87
  130. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  131. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  132. package/dist/scripts/issue-271-liveness-tests.js +547 -0
  133. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  134. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  135. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  136. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  137. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  138. package/dist/scripts/journey-tests.js +22 -27
  139. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  140. package/dist/scripts/kernel-manifest-gate.js +126 -0
  141. package/dist/scripts/kernel-manifest-tests.js +212 -0
  142. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  143. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  144. package/dist/scripts/kernel-manifest.js +369 -0
  145. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  146. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  147. package/dist/scripts/ledger-tests.js +325 -340
  148. package/dist/scripts/ledger-workflow-crash.js +10 -20
  149. package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
  150. package/dist/scripts/memory-capture-tests.js +242 -61
  151. package/dist/scripts/memory-decay-tests.js +18 -52
  152. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  153. package/dist/scripts/memory-lifecycle.js +196 -0
  154. package/dist/scripts/memory-metrics.js +24 -12
  155. package/dist/scripts/memory-value-report.js +474 -222
  156. package/dist/scripts/memory-value-tests.js +328 -0
  157. package/dist/scripts/metrics-report-tests.js +62 -199
  158. package/dist/scripts/model-override-e2e.js +66 -105
  159. package/dist/scripts/nightly-evidence-tests.js +64 -89
  160. package/dist/scripts/nightly-evidence.js +108 -111
  161. package/dist/scripts/nudge-digest.js +39 -28
  162. package/dist/scripts/onboarding-tests.js +63 -88
  163. package/dist/scripts/opensky-check.js +18 -13
  164. package/dist/scripts/opensky-tests.js +28 -28
  165. package/dist/scripts/persona-surface-guard-tests.js +37 -12
  166. package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
  167. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  168. package/dist/scripts/price-drift-tests.js +126 -227
  169. package/dist/scripts/price-drift-watch.js +209 -253
  170. package/dist/scripts/probe-poi-tests.js +52 -45
  171. package/dist/scripts/product-metrics.js +187 -273
  172. package/dist/scripts/publish-preverify.js +47 -41
  173. package/dist/scripts/realtime-pricing-tests.js +37 -76
  174. package/dist/scripts/replay-async.js +20 -18
  175. package/dist/scripts/replay-real.js +16 -18
  176. package/dist/scripts/replay.js +52 -82
  177. package/dist/scripts/session-attach-diagnose.js +16 -12
  178. package/dist/scripts/session-attach-poc.js +34 -37
  179. package/dist/scripts/session-benchmark.js +66 -147
  180. package/dist/scripts/session-dida-live-e2e.js +151 -0
  181. package/dist/scripts/session-extract-tests.js +14 -35
  182. package/dist/scripts/session-login.js +14 -13
  183. package/dist/scripts/session-tests.js +545 -583
  184. package/dist/scripts/sf-live-benchmark.js +92 -110
  185. package/dist/scripts/sf-live-cli-tests.js +2 -9
  186. package/dist/scripts/sf-soft-score-tests.js +19 -60
  187. package/dist/scripts/sf-summary-tests.js +149 -0
  188. package/dist/scripts/sf-summary.js +466 -84
  189. package/dist/scripts/skeleton-check.js +16 -14
  190. package/dist/scripts/skeleton-integration-test.js +11 -10
  191. package/dist/scripts/skills-contract-tests.js +51 -46
  192. package/dist/scripts/smoke-session-gate-tests.js +12 -20
  193. package/dist/scripts/smoke.js +541 -422
  194. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  195. package/dist/scripts/state-cli-tests.js +226 -101
  196. package/dist/scripts/state-cli.js +549 -212
  197. package/dist/scripts/static-golden-tests.js +46 -180
  198. package/dist/scripts/time-eval-tests.js +210 -192
  199. package/dist/scripts/travel-timeline-tests.js +29 -93
  200. package/dist/scripts/turn-handoff-collect-tests.js +46 -93
  201. package/dist/scripts/turn-handoff-collect.js +71 -99
  202. package/dist/scripts/turn-policy-tests.js +28 -29
  203. package/dist/scripts/typed-contract-canary.js +77 -162
  204. package/dist/scripts/tz-resolver-sanity.js +50 -0
  205. package/dist/scripts/tz-resolver-tests.js +584 -0
  206. package/dist/scripts/unified-tests.js +23 -19
  207. package/dist/scripts/visa-policy-tests.js +31 -44
  208. package/dist/scripts/weather-tests.js +121 -543
  209. package/dist/scripts/wish-channel-gate-tests.js +29 -86
  210. package/dist/scripts/write-gate-tests.js +522 -0
  211. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  212. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  213. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  214. package/dist/scripts/z3-race-tests.js +18 -12
  215. package/dist/src/artifact-gate.js +652 -221
  216. package/dist/src/backend/kernel.js +91 -0
  217. package/dist/src/backend/modules/booking-copilot.js +55 -0
  218. package/dist/src/backend/modules/booking-executor.js +124 -0
  219. package/dist/src/backend/modules/session-search.js +194 -0
  220. package/dist/src/benchmark-agent-conformance.js +296 -262
  221. package/dist/src/benchmark-environment-bridge.js +298 -413
  222. package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
  223. package/dist/src/benchmark-tool-isolation.js +94 -67
  224. package/dist/src/bookable-facts.js +299 -174
  225. package/dist/src/booking-saga.js +64 -120
  226. package/dist/src/booking-surface/availability-policy.js +443 -576
  227. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  228. package/dist/src/booking-surface/contracts.js +24 -80
  229. package/dist/src/booking-surface/dsh-planner.js +510 -281
  230. package/dist/src/booking-surface/dsh-plugin.js +17 -1
  231. package/dist/src/booking-surface/error-codes.js +82 -10
  232. package/dist/src/booking-surface/index.js +0 -3
  233. package/dist/src/booking-surface/profile.js +10 -54
  234. package/dist/src/booking-surface/recovery-chain.js +367 -0
  235. package/dist/src/booking-surface/runtime.js +765 -891
  236. package/dist/src/booking-surface/server.js +200 -251
  237. package/dist/src/booking-surface/startup.js +58 -54
  238. package/dist/src/booking-surface/validation.js +150 -218
  239. package/dist/src/bridge.js +20 -13
  240. package/dist/src/companions.js +48 -74
  241. package/dist/src/contracts.js +21 -43
  242. package/dist/src/dsh-llm.js +82 -120
  243. package/dist/src/engine.js +253 -239
  244. package/dist/src/evaluation-cadence.js +72 -209
  245. package/dist/src/evaluation-contracts.js +220 -843
  246. package/dist/src/flight-pack-adapter.js +54 -0
  247. package/dist/src/flight-pack-contract.js +2 -0
  248. package/dist/src/gotry-backend.js +70 -0
  249. package/dist/src/hotel-date-gate.js +118 -0
  250. package/dist/src/i18n.js +18 -7
  251. package/dist/src/index.js +1425 -1629
  252. package/dist/src/journey.js +132 -81
  253. package/dist/src/ledger-repair-apply.js +538 -0
  254. package/dist/src/ledger-repair-plan.js +463 -0
  255. package/dist/src/loop.js +257 -166
  256. package/dist/src/memory-capture.js +180 -32
  257. package/dist/src/memory-decay.js +30 -14
  258. package/dist/src/memory-lifecycle.js +1310 -0
  259. package/dist/src/memory-utility.js +27 -40
  260. package/dist/src/mock-llm.js +36 -61
  261. package/dist/src/model.js +81 -36
  262. package/dist/src/realtime-pricing.js +63 -72
  263. package/dist/src/slot-spec.js +84 -106
  264. package/dist/src/state-ledger.js +310 -471
  265. package/dist/src/subagent-job-id-guard.js +58 -0
  266. package/dist/src/time-anchor.js +231 -33
  267. package/dist/src/tool-packet.js +20 -8
  268. package/dist/src/travel-slots.js +58 -35
  269. package/dist/src/travel-timeline.js +44 -63
  270. package/dist/src/turn-deadline.js +121 -127
  271. package/dist/src/turn-policy.js +61 -89
  272. package/dist/src/tz-resolver.js +143 -0
  273. package/dist/src/unified.js +504 -381
  274. package/dist/src/wish-pool.js +39 -14
  275. package/dist/src/write-gate.js +495 -0
  276. package/dist/src/z3-shared.js +273 -7
  277. package/extension/README.md +4 -3
  278. package/extension/background.js +125 -14
  279. package/extension/content-bridge.js +23 -1
  280. package/extension/content-main.js +15 -4
  281. package/extension/manifest.json +16 -8
  282. package/package.json +251 -220
  283. package/ts/capabilities/agent-reach.ts +4 -4
  284. package/ts/capabilities/anything.ts +1 -1
  285. package/ts/capabilities/artifacts.ts +46 -9
  286. package/ts/capabilities/flyai.ts +156 -37
  287. package/ts/capabilities/hbcli.ts +1 -1
  288. package/ts/capabilities/incident-log.ts +88 -59
  289. package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
  290. package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
  291. package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
  292. package/ts/capabilities/session/extension-bridge.ts +226 -101
  293. package/ts/capabilities/session/extension-channel.ts +22 -9
  294. package/ts/capabilities/session/wizard.ts +2 -2
  295. package/ts/capabilities/session-consent.ts +98 -14
  296. package/ts/capabilities/session-login.ts +1 -0
  297. package/ts/capabilities/session-search.ts +315 -31
  298. package/ts/dsh-runtime/vendor/README.md +8 -4
  299. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
  300. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
  301. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
  302. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
  303. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
  304. package/ts/package.json +236 -16
  305. package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
  306. package/ts/scripts/state-cli.ts +418 -51
  307. package/ts/src/artifact-gate.ts +561 -15
  308. package/ts/src/benchmark-environment-bridge.ts +37 -20
  309. package/ts/src/bookable-facts.ts +183 -7
  310. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  311. package/ts/src/booking-surface/dsh-planner.ts +240 -32
  312. package/ts/src/booking-surface/dsh-plugin.js +17 -1
  313. package/ts/src/booking-surface/error-codes.ts +75 -0
  314. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  315. package/ts/src/booking-surface/server.ts +84 -51
  316. package/ts/src/booking-surface/validation.ts +2 -0
  317. package/ts/src/contracts.ts +12 -2
  318. package/ts/src/dsh-llm.ts +4 -8
  319. package/ts/src/index.ts +609 -90
  320. package/ts/src/loop.ts +116 -7
  321. package/ts/src/memory-capture.ts +227 -3
  322. package/ts/src/mock-llm.ts +4 -8
  323. package/ts/src/model.ts +88 -2
  324. package/ts/src/state-ledger.ts +47 -29
  325. package/ts/src/subagent-job-id-guard.ts +72 -0
  326. package/ts/src/time-anchor.ts +217 -13
  327. package/ts/src/unified.ts +316 -54
@@ -1,7 +1,31 @@
1
- import { CircuitBreaker, withRetry } from './resilience.js';
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
- continent: p.continent ?? 'yz_645708',
21
- country: p.country
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) return {
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) return {
36
- ok: false,
37
- via: 'visa-policy-error',
38
- evidence: `[visa-policy@${fetchedAt}] 洲列表抓取失败: ${page.error ?? '?'}`,
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) continue;
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) continue;
62
+ if (!page.ok || !page.html)
63
+ continue;
55
64
  const section = extractVisaSection(page.html);
56
- if (!section) continue;
57
- facts.push(...policyFactsFromMfa({
58
- countryLabel: t.country,
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 ? `[visa-policy:cs-mfa@${fetchedAt}] ${facts.length} 条政策事实(抓取 ${countries.length} 国)` : `[visa-policy@${fetchedAt}] 0 条(页面缺「签证入境」章节或抓取失败;不落负事实)`;
65
- return {
66
- ok: true,
67
- via: 'cs-mfa',
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
- FLYAI_SEARCH: (p)=>flyaiSearch(p),
76
- HBCLI_HOTEL_SEARCH: (p)=>searchHotels({
77
- destination: p.destination,
78
- checkIn: p.checkIn,
79
- checkOut: p.checkOut,
80
- adults: p.adults
81
- }, {
82
- hbcliBin: p.hbcliBin,
83
- timeoutMs: p.timeoutMs,
84
- fallbackPath: p.fallbackPath
85
- }),
86
- HBCLI_HOTEL_RATES: (p)=>hotelRates({
87
- hotelId: p.hotelId,
88
- checkIn: p.checkIn,
89
- checkOut: p.checkOut,
90
- adults: p.adults,
91
- countryCode: p.countryCode,
92
- nationalityCode: p.nationalityCode,
93
- residencyCode: p.residencyCode
94
- }, {
95
- hbcliBin: p.hbcliBin,
96
- timeoutMs: p.timeoutMs
97
- }),
98
- HBCLI_CHECK_AVAIL: (p)=>checkAvail({
99
- ratePkgId: p.ratePkgId
100
- }, {
101
- hbcliBin: p.hbcliBin,
102
- timeoutMs: p.timeoutMs
103
- }),
104
- SESSION_FLIGHT_SEARCH: (p)=>sessionFlightSearch(p),
105
- SESSION_HOTEL_SEARCH: (p)=>sessionHotelSearch(p),
106
- SESSION_TRAIN_SEARCH: (p)=>sessionTrainSearch(p),
107
- WEATHER_GEOCODE: (p)=>geocodePlace(p.name, {
108
- count: p.count,
109
- timeoutMs: p.timeoutMs
110
- }),
111
- WEATHER_FORECAST: (p)=>getForecast({
112
- latitude: p.latitude,
113
- longitude: p.longitude
114
- }, {
115
- days: p.days,
116
- timeoutMs: p.timeoutMs
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 API_BREAKER = {
151
- failureThreshold: 3,
152
- openMs: 30_000
153
- };
154
- const hbcliTimeout = (r)=>{
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
- maxAttempts: 2,
160
- baseDelayMs: 300,
161
- maxDelayMs: 1_000
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
- maxAttempts: 2,
168
- baseDelayMs: 500,
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: (r)=>hbcliTimeout(r),
185
- breaker: {
186
- failureThreshold: 3,
187
- openMs: 60_000
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
- failureThreshold: 3,
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
- failureThreshold: 3,
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
- WEATHER_CLIMATE: {
242
- channel: 'api',
243
- retry: API_RETRY,
244
- breaker: API_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: (r)=>r.via === 'opensky-error'
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
- failureThreshold: 3,
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: (r)=>r.via === 'r.jina.ai-error'
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
- failureThreshold: 3,
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
- failureThreshold: 3,
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: (r)=>r.verdict === 'error'
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
- failureThreshold: 2,
313
- openMs: 300_000
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' ? `${effect} 通道被断路器开启保护(连续失败达到阈值,冷却中)——不要立即重试,换其他工具或稍后再试;原因见 trace 证据链` : `${effect} 未登记效应(effect_interpreter.v1 注册表外,生产解译器拒绝)`,
323
- evidence: trace.evidence.join(';')
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) return null;
315
+ const br = (() => {
316
+ if (!spec.breaker)
317
+ return null;
357
318
  const existing = breakers.get(fx.effect);
358
- if (existing) return existing;
359
- const created = new CircuitBreaker({
360
- ...spec.breaker,
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
- effect: fx.effect,
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: spec.retry.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) failed ? br.onFailure() : br.onSuccess();
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