@danceiny/gotry 0.0.1-rc.8 → 0.2.0-rc.27

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 (506) hide show
  1. package/README.md +172 -217
  2. package/README.zh-CN.md +222 -0
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-booking-copilot.js +53 -0
  5. package/bin/gotry-bootstrap.js +1744 -0
  6. package/bin/gotry-inner.js +733 -83
  7. package/bin/gotry-process-liveness.d.ts +27 -0
  8. package/bin/gotry-process-liveness.js +97 -0
  9. package/bin/gotry-runtime-resolution.d.ts +27 -0
  10. package/bin/gotry-runtime-resolution.js +49 -0
  11. package/bin/gotry.js +1 -1
  12. package/client/client.js +203 -0
  13. package/cordis.gotry-patch.yml +80 -49
  14. package/data/airline-airports.json +39 -0
  15. package/data/stations-12306-verify.json +524 -0
  16. package/dist/capabilities/agent-reach-deep.js +45 -122
  17. package/dist/capabilities/agent-reach.js +101 -146
  18. package/dist/capabilities/anything.js +135 -94
  19. package/dist/capabilities/artifacts.js +370 -0
  20. package/dist/capabilities/channel-health.js +131 -0
  21. package/dist/capabilities/channel-registry.js +256 -0
  22. package/dist/capabilities/doctor.js +546 -0
  23. package/dist/capabilities/effect.js +394 -0
  24. package/dist/capabilities/external-event.js +149 -0
  25. package/dist/capabilities/fact-log.js +55 -0
  26. package/dist/capabilities/flyai-config.js +337 -0
  27. package/dist/capabilities/flyai.js +968 -0
  28. package/dist/capabilities/fx-contract.js +293 -0
  29. package/dist/capabilities/geo-atlas.js +246 -0
  30. package/dist/capabilities/ground-transfer.js +509 -0
  31. package/dist/capabilities/hbcli.js +260 -123
  32. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  33. package/dist/capabilities/incident-log.js +131 -61
  34. package/dist/capabilities/itinerary-artifact.js +190 -0
  35. package/dist/capabilities/itinerary-deck-artifact.js +194 -0
  36. package/dist/capabilities/itinerary-deck-export.js +339 -0
  37. package/dist/capabilities/lavish-local.js +924 -0
  38. package/dist/capabilities/model-override.js +38 -0
  39. package/dist/capabilities/opensky.js +54 -43
  40. package/dist/capabilities/resilience.js +191 -0
  41. package/dist/capabilities/session/action-cache.js +126 -0
  42. package/dist/capabilities/session/adapters/ctrip-flight.js +141 -0
  43. package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
  44. package/dist/capabilities/session/adapters/dida-portal.js +274 -0
  45. package/dist/capabilities/session/adapters/meituan-local.js +54 -0
  46. package/dist/capabilities/session/adapters/rail-12306.js +342 -0
  47. package/dist/capabilities/session/benchmark.js +189 -0
  48. package/dist/capabilities/session/bridge-ledger.js +224 -0
  49. package/dist/capabilities/session/extension-bridge.js +636 -0
  50. package/dist/capabilities/session/extension-channel.js +109 -0
  51. package/dist/capabilities/session/extension-distribution.js +230 -0
  52. package/dist/capabilities/session/extract.js +50 -0
  53. package/dist/capabilities/session/golden-score.js +114 -0
  54. package/dist/capabilities/session/health-watch.js +215 -0
  55. package/dist/capabilities/session/read-guard.js +107 -0
  56. package/dist/capabilities/session/static-flight-golden.js +150 -0
  57. package/dist/capabilities/session/transport.js +109 -0
  58. package/dist/capabilities/session/wizard.js +117 -0
  59. package/dist/capabilities/session-consent.js +178 -0
  60. package/dist/capabilities/session-login.js +170 -0
  61. package/dist/capabilities/session-search.js +686 -0
  62. package/dist/capabilities/spawn-bounded.js +203 -0
  63. package/dist/capabilities/sponsor-plugin.js +341 -0
  64. package/dist/capabilities/visa-policy.js +140 -0
  65. package/dist/capabilities/weather.js +243 -138
  66. package/dist/data/session-golden-20.json +25 -0
  67. package/dist/data/sf-golden-manifest.json +103 -0
  68. package/dist/data/sf-static-routes.json +91 -0
  69. package/dist/scripts/action-cache-tests.js +72 -0
  70. package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
  71. package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
  72. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  73. package/dist/scripts/agent-reach-error-tests.js +354 -0
  74. package/dist/scripts/agent-reach-tests.js +26 -18
  75. package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
  76. package/dist/scripts/anything-tests.js +116 -60
  77. package/dist/scripts/artifact-client-contract-tests.js +220 -0
  78. package/dist/scripts/artifacts-capability-tests.js +727 -0
  79. package/dist/scripts/async-collect.js +68 -10
  80. package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
  81. package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
  82. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
  83. package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
  84. package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
  85. package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
  86. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
  87. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
  88. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
  89. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
  90. package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
  91. package/dist/scripts/booking-copilot-dsh-warmer-proof.js +123 -0
  92. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
  93. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
  94. package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
  95. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
  96. package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
  97. package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
  98. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
  99. package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
  100. package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
  101. package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
  102. package/dist/scripts/booking-executor-tests.js +80 -0
  103. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  104. package/dist/scripts/booking-saga-tests.js +125 -0
  105. package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
  106. package/dist/scripts/bootstrap-tests.js +1479 -0
  107. package/dist/scripts/bridge-events-tests.js +239 -0
  108. package/dist/scripts/bridge-ledger-tests.js +428 -0
  109. package/dist/scripts/build-changelog.js +238 -0
  110. package/dist/scripts/build-metrics-report.js +331 -0
  111. package/dist/scripts/changelog-tests.js +118 -0
  112. package/dist/scripts/channel-probe-tests.js +103 -0
  113. package/dist/scripts/channel-probe.js +169 -0
  114. package/dist/scripts/channel-registry-tests.js +208 -0
  115. package/dist/scripts/companion-tests.js +67 -0
  116. package/dist/scripts/dida-cookie-probe.js +87 -0
  117. package/dist/scripts/dida-runner-stop-tests.js +311 -0
  118. package/dist/scripts/dida-sw-probe.js +114 -0
  119. package/dist/scripts/diff-test.js +17 -12
  120. package/dist/scripts/doctor-tests.js +564 -0
  121. package/dist/scripts/dsh-artifact-e2e.js +279 -0
  122. package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
  123. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  124. package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
  125. package/dist/scripts/dsh-runtime-closure.js +162 -0
  126. package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
  127. package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
  128. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  129. package/dist/scripts/effect-tests.js +539 -0
  130. package/dist/scripts/engine-run.js +1 -3
  131. package/dist/scripts/engine-tests.js +16 -10
  132. package/dist/scripts/evaluation-cadence-tests.js +174 -0
  133. package/dist/scripts/evaluation-contract-tests.js +268 -0
  134. package/dist/scripts/extension-distribution-cli.js +39 -0
  135. package/dist/scripts/extension-distribution-tests.js +443 -0
  136. package/dist/scripts/extension-tests.js +988 -0
  137. package/dist/scripts/external-event-tests.js +350 -0
  138. package/dist/scripts/fact-gate-tests.js +869 -0
  139. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  140. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  141. package/dist/scripts/flight-malformed-tests.js +159 -0
  142. package/dist/scripts/flyai-setup-tests.js +228 -0
  143. package/dist/scripts/flyai-setup-tool-tests.js +54 -0
  144. package/dist/scripts/flyai-tests.js +692 -0
  145. package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
  146. package/dist/scripts/fx-contract-tests.js +201 -0
  147. package/dist/scripts/g5-guard-tests.js +131 -0
  148. package/dist/scripts/g5-guard.js +189 -0
  149. package/dist/scripts/gen-lunar-anchors.js +106 -0
  150. package/dist/scripts/geo-atlas-tests.js +283 -0
  151. package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
  152. package/dist/scripts/gotry-try-demo-tests.js +79 -0
  153. package/dist/scripts/gotry-try-demo.js +49 -0
  154. package/dist/scripts/ground-transfer-tests.js +956 -0
  155. package/dist/scripts/hbcli-e2e-tests.js +159 -0
  156. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  157. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  158. package/dist/scripts/hbcli-tests.js +121 -69
  159. package/dist/scripts/health-watch-cli.js +73 -0
  160. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  161. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  162. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  163. package/dist/scripts/i18n-tests.js +41 -0
  164. package/dist/scripts/incident-tests.js +281 -87
  165. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  166. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  167. package/dist/scripts/issue-271-liveness-tests.js +569 -0
  168. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  169. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  170. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  171. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  172. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  173. package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
  174. package/dist/scripts/itinerary-artifact-tests.js +368 -0
  175. package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
  176. package/dist/scripts/itinerary-deck-export-tests.js +552 -0
  177. package/dist/scripts/itinerary-deck-tests.js +435 -0
  178. package/dist/scripts/itinerary-html-tests.js +426 -0
  179. package/dist/scripts/journey-tests.js +22 -27
  180. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  181. package/dist/scripts/kernel-manifest-gate.js +126 -0
  182. package/dist/scripts/kernel-manifest-tests.js +212 -0
  183. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  184. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  185. package/dist/scripts/kernel-manifest.js +369 -0
  186. package/dist/scripts/lavish-local-tests.js +1226 -0
  187. package/dist/scripts/lavish-product-tools-tests.js +537 -0
  188. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  189. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  190. package/dist/scripts/ledger-tests.js +480 -0
  191. package/dist/scripts/ledger-workflow-crash.js +32 -0
  192. package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
  193. package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
  194. package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
  195. package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
  196. package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
  197. package/dist/scripts/memory-capture-tests.js +242 -61
  198. package/dist/scripts/memory-decay-tests.js +49 -0
  199. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  200. package/dist/scripts/memory-lifecycle.js +196 -0
  201. package/dist/scripts/memory-metrics.js +29 -31
  202. package/dist/scripts/memory-value-report.js +631 -0
  203. package/dist/scripts/memory-value-tests.js +328 -0
  204. package/dist/scripts/metrics-report-tests.js +151 -0
  205. package/dist/scripts/model-override-e2e.js +137 -0
  206. package/dist/scripts/nightly-evidence-tests.js +161 -0
  207. package/dist/scripts/nightly-evidence.js +286 -0
  208. package/dist/scripts/nudge-digest.js +43 -42
  209. package/dist/scripts/observability-tests.js +381 -0
  210. package/dist/scripts/onboarding-tests.js +161 -0
  211. package/dist/scripts/opensky-check.js +18 -13
  212. package/dist/scripts/opensky-tests.js +28 -28
  213. package/dist/scripts/persona-surface-guard-tests.js +49 -0
  214. package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
  215. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  216. package/dist/scripts/price-drift-tests.js +340 -0
  217. package/dist/scripts/price-drift-watch.js +447 -0
  218. package/dist/scripts/probe-poi-tests.js +53 -45
  219. package/dist/scripts/product-metrics.js +483 -0
  220. package/dist/scripts/publish-preverify.js +110 -0
  221. package/dist/scripts/realtime-pricing-tests.js +76 -0
  222. package/dist/scripts/recall-tests.js +513 -0
  223. package/dist/scripts/replay-async.js +20 -18
  224. package/dist/scripts/replay-real.js +18 -18
  225. package/dist/scripts/replay.js +52 -82
  226. package/dist/scripts/session-attach-diagnose.js +31 -0
  227. package/dist/scripts/session-attach-poc.js +62 -0
  228. package/dist/scripts/session-benchmark.js +180 -0
  229. package/dist/scripts/session-dida-live-e2e.js +199 -0
  230. package/dist/scripts/session-extract-tests.js +60 -0
  231. package/dist/scripts/session-link-tests.js +478 -0
  232. package/dist/scripts/session-login.js +29 -0
  233. package/dist/scripts/session-tests.js +862 -0
  234. package/dist/scripts/session-zones-tests.js +358 -0
  235. package/dist/scripts/sf-live-benchmark.js +367 -0
  236. package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
  237. package/dist/scripts/sf-live-cli-tests.js +92 -0
  238. package/dist/scripts/sf-live-options.js +46 -0
  239. package/dist/scripts/sf-manifest-tests.js +30 -0
  240. package/dist/scripts/sf-manifest.js +33 -0
  241. package/dist/scripts/sf-soft-score-tests.js +67 -0
  242. package/dist/scripts/sf-summary-tests.js +324 -0
  243. package/dist/scripts/sf-summary.js +525 -0
  244. package/dist/scripts/share-tests.js +432 -0
  245. package/dist/scripts/skeleton-check.js +16 -14
  246. package/dist/scripts/skeleton-integration-test.js +28 -11
  247. package/dist/scripts/skills-contract-tests.js +51 -46
  248. package/dist/scripts/smoke-session-gate-tests.js +21 -0
  249. package/dist/scripts/smoke.js +703 -219
  250. package/dist/scripts/spawn-bounded-abort-tests.js +241 -0
  251. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  252. package/dist/scripts/state-cli-tests.js +261 -0
  253. package/dist/scripts/state-cli.js +585 -0
  254. package/dist/scripts/state-repair-cli.js +66 -0
  255. package/dist/scripts/state-repair-tests.js +140 -0
  256. package/dist/scripts/static-golden-tests.js +165 -0
  257. package/dist/scripts/time-eval-tests.js +210 -192
  258. package/dist/scripts/travel-timeline-tests.js +59 -0
  259. package/dist/scripts/turn-handoff-collect-tests.js +169 -0
  260. package/dist/scripts/turn-handoff-collect.js +161 -0
  261. package/dist/scripts/turn-policy-tests.js +55 -0
  262. package/dist/scripts/typed-contract-canary.js +176 -0
  263. package/dist/scripts/tz-resolver-sanity.js +50 -0
  264. package/dist/scripts/tz-resolver-tests.js +584 -0
  265. package/dist/scripts/unified-tests.js +23 -19
  266. package/dist/scripts/visa-policy-tests.js +62 -0
  267. package/dist/scripts/weather-tests.js +276 -41
  268. package/dist/scripts/wish-channel-gate-tests.js +54 -0
  269. package/dist/scripts/write-gate-tests.js +522 -0
  270. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  271. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  272. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  273. package/dist/scripts/z3-race-tests.js +41 -0
  274. package/dist/src/artifact-gate.js +829 -0
  275. package/dist/src/backend/kernel.js +91 -0
  276. package/dist/src/backend/modules/booking-copilot.js +92 -0
  277. package/dist/src/backend/modules/booking-executor.js +124 -0
  278. package/dist/src/backend/modules/session-search.js +551 -0
  279. package/dist/src/benchmark-agent-conformance.js +578 -0
  280. package/dist/src/benchmark-environment-bridge.js +515 -0
  281. package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
  282. package/dist/src/benchmark-tool-isolation.js +151 -0
  283. package/dist/src/bookable-facts.js +533 -0
  284. package/dist/src/booking-saga.js +97 -0
  285. package/dist/src/booking-surface/availability-policy.js +697 -0
  286. package/dist/src/booking-surface/booking-intent.js +43 -0
  287. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  288. package/dist/src/booking-surface/canonical-schema.js +116 -0
  289. package/dist/src/booking-surface/contracts.js +75 -0
  290. package/dist/src/booking-surface/dsh-planner.js +1854 -0
  291. package/dist/src/booking-surface/dsh-plugin.js +378 -0
  292. package/dist/src/booking-surface/dsh-worker.js +98 -0
  293. package/dist/src/booking-surface/error-codes.js +231 -0
  294. package/dist/src/booking-surface/index.js +9 -0
  295. package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
  296. package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
  297. package/dist/src/booking-surface/profile.js +24 -0
  298. package/dist/src/booking-surface/recovery-chain.js +367 -0
  299. package/dist/src/booking-surface/runtime.js +1968 -0
  300. package/dist/src/booking-surface/server.js +674 -0
  301. package/dist/src/booking-surface/startup.js +168 -0
  302. package/dist/src/booking-surface/validation.js +261 -0
  303. package/dist/src/bridge.js +20 -13
  304. package/dist/src/companions.js +86 -0
  305. package/dist/src/contracts.js +21 -43
  306. package/dist/src/dsh-llm.js +97 -111
  307. package/dist/src/engine.js +276 -193
  308. package/dist/src/evaluation-cadence.js +97 -0
  309. package/dist/src/evaluation-contracts.js +283 -0
  310. package/dist/src/flight-pack-adapter.js +54 -0
  311. package/dist/src/flight-pack-contract.js +2 -0
  312. package/dist/src/flyai-setup-tool.js +70 -0
  313. package/dist/src/gotry-backend.js +125 -0
  314. package/dist/src/hotel-date-gate.js +118 -0
  315. package/dist/src/i18n.js +88 -0
  316. package/dist/src/index.js +2305 -731
  317. package/dist/src/itinerary-deck.js +139 -0
  318. package/dist/src/itinerary-doc-shared.js +661 -0
  319. package/dist/src/itinerary-html.js +131 -0
  320. package/dist/src/journey.js +155 -108
  321. package/dist/src/lavish-tools.js +400 -0
  322. package/dist/src/ledger-repair-apply.js +538 -0
  323. package/dist/src/ledger-repair-plan.js +463 -0
  324. package/dist/src/loop.js +358 -162
  325. package/dist/src/memory-capture.js +180 -32
  326. package/dist/src/memory-decay.js +47 -0
  327. package/dist/src/memory-lifecycle.js +1310 -0
  328. package/dist/src/memory-utility.js +27 -40
  329. package/dist/src/mock-llm.js +36 -61
  330. package/dist/src/model.js +81 -36
  331. package/dist/src/realtime-pricing.js +103 -0
  332. package/dist/src/recall/card.js +74 -0
  333. package/dist/src/recall/evaluator.js +140 -0
  334. package/dist/src/recall/integration.js +17 -0
  335. package/dist/src/recall/tick.js +166 -0
  336. package/dist/src/session-link/plan-it-action.js +48 -0
  337. package/dist/src/session-link/session-link.js +248 -0
  338. package/dist/src/session-zones.js +507 -0
  339. package/dist/src/share/adapters.js +51 -0
  340. package/dist/src/share/share-consent.js +76 -0
  341. package/dist/src/share/share-deck.js +58 -0
  342. package/dist/src/share/share-token.js +146 -0
  343. package/dist/src/slot-spec.js +84 -106
  344. package/dist/src/state-ledger.js +767 -0
  345. package/dist/src/state-repair.js +299 -0
  346. package/dist/src/subagent-job-id-guard.js +58 -0
  347. package/dist/src/time-anchor.js +231 -33
  348. package/dist/src/tool-packet.js +20 -8
  349. package/dist/src/travel-slots.js +58 -35
  350. package/dist/src/travel-timeline.js +59 -0
  351. package/dist/src/turn-deadline.js +287 -0
  352. package/dist/src/turn-policy.js +118 -0
  353. package/dist/src/tz-resolver.js +143 -0
  354. package/dist/src/unified.js +538 -360
  355. package/dist/src/wish-pool.js +42 -12
  356. package/dist/src/write-gate.js +495 -0
  357. package/dist/src/z3-shared.js +284 -0
  358. package/extension/README.md +21 -0
  359. package/extension/README.zh-CN.md +21 -0
  360. package/extension/background.js +463 -0
  361. package/extension/content-bridge.js +270 -0
  362. package/extension/content-main.js +111 -0
  363. package/extension/manifest.json +60 -0
  364. package/package.json +407 -14
  365. package/schemas/booking.intent.schema.json +193 -0
  366. package/schemas/booking.surface.schema.json +2622 -0
  367. package/ts/capabilities/agent-reach-deep.ts +114 -0
  368. package/ts/capabilities/agent-reach.ts +198 -0
  369. package/ts/capabilities/anything.ts +255 -0
  370. package/ts/capabilities/artifacts.ts +428 -0
  371. package/ts/capabilities/fact-log.ts +54 -0
  372. package/ts/capabilities/flyai-config.ts +388 -0
  373. package/ts/capabilities/flyai.ts +1274 -0
  374. package/ts/capabilities/hbcli.ts +253 -71
  375. package/ts/capabilities/incident-log.ts +88 -59
  376. package/ts/capabilities/itinerary-artifact.ts +214 -0
  377. package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
  378. package/ts/capabilities/itinerary-deck-export.ts +378 -0
  379. package/ts/capabilities/opensky.ts +121 -0
  380. package/ts/capabilities/session/action-cache.ts +148 -0
  381. package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
  382. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  383. package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
  384. package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
  385. package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
  386. package/ts/capabilities/session/benchmark.ts +276 -0
  387. package/ts/capabilities/session/bridge-ledger.ts +333 -0
  388. package/ts/capabilities/session/extension-bridge.ts +794 -0
  389. package/ts/capabilities/session/extension-channel.ts +144 -0
  390. package/ts/capabilities/session/extension-distribution.ts +266 -0
  391. package/ts/capabilities/session/extract.ts +54 -0
  392. package/ts/capabilities/session/golden-score.ts +139 -0
  393. package/ts/capabilities/session/health-watch.ts +264 -0
  394. package/ts/capabilities/session/read-guard.ts +141 -0
  395. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  396. package/ts/capabilities/session/transport.ts +146 -0
  397. package/ts/capabilities/session/wizard.ts +140 -0
  398. package/ts/capabilities/session-consent.ts +211 -0
  399. package/ts/capabilities/session-login.ts +208 -0
  400. package/ts/capabilities/session-search.ts +893 -0
  401. package/ts/capabilities/weather.ts +330 -0
  402. package/ts/dsh-runtime/vendor/README.md +68 -0
  403. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  404. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
  405. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
  406. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
  407. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  408. package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
  409. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
  410. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  411. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
  412. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  413. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  414. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
  415. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  416. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
  417. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
  418. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
  419. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
  420. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  421. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  422. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
  423. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
  424. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  425. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  426. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  427. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  428. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
  429. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  430. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
  431. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  432. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  433. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
  434. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  435. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  436. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
  437. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
  438. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  439. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
  440. package/ts/package.json +296 -3
  441. package/ts/scripts/async-collect.ts +91 -0
  442. package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
  443. package/ts/scripts/skeleton-check.ts +1 -1
  444. package/ts/scripts/skeleton-integration-test.ts +23 -1
  445. package/ts/scripts/state-cli.ts +568 -0
  446. package/ts/scripts/turn-handoff-collect.ts +178 -0
  447. package/ts/src/artifact-gate.ts +991 -0
  448. package/ts/src/benchmark-agent-conformance.ts +619 -0
  449. package/ts/src/benchmark-environment-bridge.ts +538 -0
  450. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  451. package/ts/src/benchmark-tool-isolation.ts +166 -0
  452. package/ts/src/bookable-facts.ts +837 -0
  453. package/ts/src/booking-saga.ts +133 -0
  454. package/ts/src/booking-surface/availability-policy.ts +522 -0
  455. package/ts/src/booking-surface/booking-intent.ts +86 -0
  456. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  457. package/ts/src/booking-surface/canonical-schema.js +116 -0
  458. package/ts/src/booking-surface/contracts.ts +475 -0
  459. package/ts/src/booking-surface/dsh-planner.ts +1949 -0
  460. package/ts/src/booking-surface/dsh-plugin.js +378 -0
  461. package/ts/src/booking-surface/dsh-worker.ts +110 -0
  462. package/ts/src/booking-surface/error-codes.ts +241 -0
  463. package/ts/src/booking-surface/index.ts +9 -0
  464. package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
  465. package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
  466. package/ts/src/booking-surface/profile.ts +42 -0
  467. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  468. package/ts/src/booking-surface/runtime.ts +1793 -0
  469. package/ts/src/booking-surface/server.ts +733 -0
  470. package/ts/src/booking-surface/startup.ts +201 -0
  471. package/ts/src/booking-surface/validation.ts +217 -0
  472. package/ts/src/companions.ts +113 -0
  473. package/ts/src/contracts.ts +13 -3
  474. package/ts/src/dsh-llm.ts +44 -11
  475. package/ts/src/flyai-setup-tool.ts +77 -0
  476. package/ts/src/index.ts +2107 -224
  477. package/ts/src/itinerary-deck.ts +189 -0
  478. package/ts/src/itinerary-doc-shared.ts +741 -0
  479. package/ts/src/itinerary-html.ts +171 -0
  480. package/ts/src/loop.ts +278 -40
  481. package/ts/src/memory-capture.ts +283 -0
  482. package/ts/src/memory-decay.ts +55 -0
  483. package/ts/src/memory-utility.ts +70 -0
  484. package/ts/src/mock-llm.ts +4 -8
  485. package/ts/src/model.ts +88 -2
  486. package/ts/src/recall/card.ts +106 -0
  487. package/ts/src/recall/evaluator.ts +170 -0
  488. package/ts/src/recall/integration.ts +35 -0
  489. package/ts/src/recall/tick.ts +213 -0
  490. package/ts/src/session-link/plan-it-action.ts +78 -0
  491. package/ts/src/session-link/session-link.ts +275 -0
  492. package/ts/src/share/adapters.ts +93 -0
  493. package/ts/src/share/share-consent.ts +105 -0
  494. package/ts/src/share/share-deck.ts +85 -0
  495. package/ts/src/share/share-token.ts +166 -0
  496. package/ts/src/slot-spec.ts +172 -0
  497. package/ts/src/state-ledger.ts +912 -0
  498. package/ts/src/subagent-job-id-guard.ts +72 -0
  499. package/ts/src/time-anchor.ts +338 -0
  500. package/ts/src/tool-packet.ts +32 -0
  501. package/ts/src/travel-slots.ts +219 -0
  502. package/ts/src/travel-timeline.ts +83 -0
  503. package/ts/src/turn-deadline.ts +366 -0
  504. package/ts/src/turn-policy.ts +154 -0
  505. package/ts/src/unified.ts +360 -89
  506. package/ts/src/wish-pool.ts +79 -0
@@ -12,10 +12,12 @@
12
12
  * 估算必须显式标记。这是 L4 与 L1 透明卡片的接缝。
13
13
  */
14
14
 
15
- import { spawn } from 'node:child_process'
15
+ import { homedir } from 'node:os'
16
+ import { join } from 'node:path'
17
+ import { spawnBounded } from './spawn-bounded.ts'
16
18
 
17
19
  export interface HbcliCallOptions {
18
- /** hbcli 二进制路径(默认 'hbcli',依赖 PATH) */
20
+ /** hbcli 二进制路径(默认 'hbcli',依赖 PATH;~/.local/bin 等已知安装位自动回退) */
19
21
  hbcliBin?: string
20
22
  /** 超时(ms) */
21
23
  timeoutMs?: number
@@ -23,6 +25,8 @@ export interface HbcliCallOptions {
23
25
  token?: string
24
26
  /** 环境(uat/prod/dev);默认 uat */
25
27
  env?: 'uat' | 'prod' | 'dev'
28
+ /** 宿主取消信号(exec.signal);abort → SIGTERM,500ms 后 SIGKILL 有界回收 */
29
+ signal?: AbortSignal
26
30
  }
27
31
 
28
32
  export interface HbcliCallResult {
@@ -43,76 +47,121 @@ export interface HbcliCallResult {
43
47
  error?: string
44
48
  }
45
49
 
50
+ /**
51
+ * hbcli 二进制候选路径(gotry setup 按官方脚本装到 ~/.local/bin/hbcli,
52
+ * symlink 指向 ~/.staicli/current/hbcli——当 PATH 不含 ~/.local/bin 时裸名
53
+ * spawn 仍 ENOENT,按已知安装位回退)。仅对默认名 'hbcli' 扩展;显式自定义
54
+ * 名(如测试注入的不存在路径)不扩展,保持配置即所用的可测性。
55
+ */
56
+ export function hbcliBinCandidates(bin: string, homeDir: string = homedir()): string[] {
57
+ if (bin !== 'hbcli') return [bin]
58
+ return [bin, join(homeDir, '.local/bin/hbcli'), join(homeDir, '.staicli/current/hbcli')]
59
+ }
60
+
61
+ /** 单个候选的一次 spawn 封装:失败不抛,返回降级结果(spawnError 标记 ENOENT 类失败供上层换候选) */
62
+ async function attemptHbcli(
63
+ bin: string,
64
+ args: string[],
65
+ opts: Required<Pick<HbcliCallOptions, 'timeoutMs' | 'env'>> & { envVars: Record<string, string>; signal?: AbortSignal },
66
+ ): Promise<HbcliCallResult & { spawnError?: boolean }> {
67
+ const started = Date.now()
68
+ const r = await spawnBounded(bin, args, {
69
+ env: { ...process.env, ...opts.envVars },
70
+ timeoutMs: opts.timeoutMs,
71
+ signal: opts.signal,
72
+ })
73
+ const latencyMs = Date.now() - started
74
+ const ts = new Date().toISOString()
75
+ const cleanupError = r.groupReaped === false ? 'process group cleanup incomplete' : undefined
76
+ if (r.aborted) {
77
+ return {
78
+ via: 'hbcli-error', exitCode: r.code ?? -1, result: null,
79
+ evidence: `[实时API:hbcli@abort@${ts}]`,
80
+ stderr: r.stderr.slice(0, 2000), latencyMs,
81
+ error: ['aborted by host signal', cleanupError].filter(Boolean).join('; '),
82
+ }
83
+ }
84
+ if (r.error || r.groupReaped === false || r.code !== 0) {
85
+ const error = [r.error, r.stderr.trim().slice(0, 200), cleanupError].filter(Boolean).join('; ')
86
+ const failureKind = r.timedOut ? 'timeout' : /ENOENT/.test(r.error ?? '') ? 'spawn_error' : 'error'
87
+ return {
88
+ via: 'hbcli-error', exitCode: r.code ?? -1, result: null,
89
+ evidence: `[实时API:hbcli@${failureKind}@${ts}]`,
90
+ stderr: r.stderr.slice(0, 2000),
91
+ latencyMs, error: error || `exit ${r.code}`,
92
+ spawnError: /ENOENT/.test(r.error ?? ''),
93
+ }
94
+ }
95
+ // 尝试 JSON 解析
96
+ const jStart = r.stdout.search(/[\{\[]/)
97
+ const jsonStr = jStart >= 0 ? r.stdout.slice(jStart) : ''
98
+ let result: unknown = null
99
+ if (jsonStr) {
100
+ try { result = JSON.parse(jsonStr) } catch { /* 非 JSON 输出,留给调用方处理 */ }
101
+ }
102
+ return {
103
+ via: 'hbcli-realtime', exitCode: 0, result,
104
+ evidence: `[实时API:hbcli@${ts}]`,
105
+ stdout: r.stdout.slice(0, 2000), latencyMs,
106
+ }
107
+ }
108
+
46
109
  /** 通用 hbcli JSON 调用封装:失败不抛,而是返回降级结果 */
47
110
  export async function callHbcliJson(
48
111
  args: string[],
49
112
  opts: HbcliCallOptions = {},
50
113
  ): Promise<HbcliCallResult> {
51
- const started = Date.now()
52
- const bin = opts.hbcliBin ?? 'hbcli'
53
- const timeoutMs = opts.timeoutMs ?? 15_000
54
114
  const env = opts.env ?? 'uat'
55
115
  const envVars: Record<string, string> = { HOTELBYTE_ENV: env }
56
116
  if (opts.token) envVars['HOTELBYTE_TOKEN'] = opts.token
117
+ const callOpts = { timeoutMs: opts.timeoutMs ?? 15_000, env, envVars, signal: opts.signal }
118
+ const candidates = hbcliBinCandidates(opts.hbcliBin ?? 'hbcli')
119
+ // abort 状态不可触发 fallback 候选;spawn 前的 pre-aborted 调用整体零 spawn
120
+ if (opts.signal?.aborted) {
121
+ return attemptHbcli(candidates[0]!, args, callOpts)
122
+ }
123
+ let last: HbcliCallResult & { spawnError?: boolean } | undefined
124
+ for (const bin of candidates) {
125
+ last = await attemptHbcli(bin, args, callOpts)
126
+ // spawn 级失败(ENOENT 等)且还有候选 → 换下一个已知安装位;其余失败(退码/超时/abort)无重试意义
127
+ if (!(last.spawnError && candidates.indexOf(bin) < candidates.length - 1)) return last
128
+ }
129
+ return last!
130
+ }
57
131
 
58
- return new Promise((resolve) => {
59
- let stdout = ''
60
- let stderr = ''
61
- let settled = false
62
- const child = spawn(bin, args, { env: { ...process.env, ...envVars } })
63
- const timer = setTimeout(() => {
64
- if (!settled) {
65
- settled = true
66
- child.kill('SIGKILL')
67
- resolve({
68
- via: 'hbcli-error', exitCode: -1, result: null,
69
- evidence: `[实时API:hbcli@timeout@${new Date().toISOString()}]`,
70
- latencyMs: Date.now() - started, error: `timeout after ${timeoutMs}ms`,
71
- })
72
- }
73
- }, timeoutMs)
74
- child.stdout.on('data', (d: Buffer) => { stdout += d.toString() })
75
- child.stderr.on('data', (d: Buffer) => { stderr += d.toString() })
76
- child.on('close', (code) => {
77
- if (settled) return
78
- settled = true
79
- clearTimeout(timer)
80
- const latencyMs = Date.now() - started
81
- if (code !== 0) {
82
- // 不抛错,降级返回。证据链显式标注:实时 API 调用失败+原因+时间戳。
83
- resolve({
84
- via: 'hbcli-error', exitCode: code ?? -1, result: null,
85
- evidence: `[实时API:hbcli@error@${new Date().toISOString()}]`,
86
- stderr: stderr.slice(0, 2000),
87
- latencyMs, error: stderr.trim().slice(0, 200) || `exit ${code}`,
88
- })
89
- return
90
- }
91
- // 尝试 JSON 解析
92
- const jStart = stdout.search(/[\{\[]/)
93
- const jsonStr = jStart >= 0 ? stdout.slice(jStart) : ''
94
- let result: unknown = null
95
- if (jsonStr) {
96
- try { result = JSON.parse(jsonStr) } catch { /* 非 JSON 输出,留给调用方处理 */ }
97
- }
98
- resolve({
99
- via: 'hbcli-realtime', exitCode: 0, result,
100
- evidence: `[实时API:hbcli@${new Date().toISOString()}]`,
101
- stdout: stdout.slice(0, 2000), latencyMs,
102
- })
103
- })
104
- child.on('error', (e) => {
105
- if (settled) return
106
- settled = true
107
- clearTimeout(timer)
108
- // ENOENT (二进制不存在) 等也走降级路径
109
- resolve({
110
- via: 'hbcli-error', exitCode: -1, result: null,
111
- evidence: `[实时API:hbcli@spawn_error@${new Date().toISOString()}]`,
112
- latencyMs: Date.now() - started, error: (e as Error).message,
113
- })
114
- })
115
- })
132
+ /** 上游 i18n 名称容错读取(zh 优先,中文优先产品口径;回退 en/ar) */
133
+ function i18nName(v: { zh?: string; en?: string; ar?: string } | string | undefined): string {
134
+ if (!v) return ''
135
+ if (typeof v === 'string') return v
136
+ return v.zh || v.en || v.ar || ''
137
+ }
138
+
139
+ /**
140
+ * 实时 hotelList 响应 → 模型可读紧凑行(id/名称/星级/最低价)。
141
+ * issue #195:render 面只透 summary,若 summary 不含数据,模型只看到
142
+ * 「实时返回」四个字——工具调用等于白调。id 必须在行内,hotel-rates 的入参
143
+ * 就来自这里。上游 wire:HotelListResp{list:[Hotel{id,name:{en,zh},star,minPrice:{amount,currency}}]}。
144
+ */
145
+ function hotelLines(result: unknown, max = 8): string {
146
+ const list = (result as { list?: unknown } | null)?.list
147
+ if (!Array.isArray(list) || list.length === 0) return ''
148
+ const lines: string[] = []
149
+ for (const item of list.slice(0, max)) {
150
+ const h = item as {
151
+ id?: string | number
152
+ name?: { zh?: string; en?: string; ar?: string }
153
+ star?: number
154
+ minPrice?: { amount?: number; currency?: string }
155
+ }
156
+ const name = i18nName(h.name) || `hotel ${h.id ?? '?'}`
157
+ const star = typeof h.star === 'number' && h.star > 0 ? ` ${h.star}★` : ''
158
+ const price = h.minPrice && typeof h.minPrice.amount === 'number'
159
+ ? ` 低至 ${h.minPrice.amount}${h.minPrice.currency ? ` ${h.minPrice.currency}` : ''}`
160
+ : ''
161
+ lines.push(`- id=${h.id ?? '?'} ${name}${star}${price}`)
162
+ }
163
+ if (list.length > max) lines.push(`…(共 ${list.length} 家,仅列前 ${max})`)
164
+ return lines.join('\n')
116
165
  }
117
166
 
118
167
  /** 高层语义化封装:酒店列表查询(down-tier to 静态包 + 证据链标注) */
@@ -120,29 +169,70 @@ export async function searchHotels(
120
169
  query: { destination: string; checkIn?: string; checkOut?: string; adults?: number },
121
170
  opts: HbcliCallOptions & { fallbackPath?: string } = {},
122
171
  ): Promise<HbcliCallResult & { hotels?: unknown; summary: string }> {
123
- // 旗标对齐上游 CLI v0.3.0(命令树扁平化后):--destination-name + --room-occupancies;
124
- // hotel-list 无日期旗标(房价按上游当前窗口),checkIn/checkOut 留给 hotel-rates 跟进
172
+ // 旗标对齐上游 CLI(hotelbyte-com/hotelbyte-cli):--destination-name + --room-occupancies;
173
+ // hotel-list 支持日期旗标(--check-in/--check-out,上游帮助文档在册)——issue #195 教训:
174
+ // 「日期不传上游」会让实时价退化为当前窗口价,用户要下周五的房给的是今天的价。
125
175
  const hbArgs = ['search', 'hotel-list', '--json', '--page-size', '10']
126
176
  if (query.destination) hbArgs.push('--destination-name', query.destination)
177
+ if (query.checkIn) hbArgs.push('--check-in', query.checkIn)
178
+ if (query.checkOut) hbArgs.push('--check-out', query.checkOut)
127
179
  if (query.adults) hbArgs.push('--room-occupancies', JSON.stringify([{ adultCount: query.adults, childrenAges: [] }]))
128
180
  const live = await callHbcliJson(hbArgs, opts)
181
+ // 取消立即结束:不读静态包、不产出命中/无结果/估算,也不计上游故障
182
+ if (opts.signal?.aborted || /abort/i.test(live.error ?? '')) {
183
+ const ts = new Date().toISOString()
184
+ const cleanupError = /cleanup incomplete/i.test(live.error ?? '') ? `; ${live.error}` : ''
185
+ return {
186
+ via: 'hbcli-error', exitCode: live.exitCode, result: null,
187
+ evidence: `[实时API:hbcli@abort@${ts}]`,
188
+ latencyMs: live.latencyMs, error: `aborted by host signal${cleanupError}`, summary: `酒店「${query.destination}」检索已取消,未产生事实`,
189
+ }
190
+ }
129
191
  if (live.via === 'hbcli-realtime') {
130
- return { ...live, hotels: live.result, summary: `${query.destination}:hbcli 实时返回${query.checkIn || query.checkOut ? '(日期不传上游 list,以当前窗口房价返回)' : ''}` }
192
+ // summary 即模型面(render 只透 summary):必须自带紧凑数据行 + 证据链,
193
+ // 否则模型只看到「实时返回」占位一句话(issue #195 实锤)。
194
+ const list = (live.result as { list?: unknown[] } | null)?.list
195
+ const n = Array.isArray(list) ? list.length : 0
196
+ const dateTag = query.checkIn
197
+ ? `入住 ${query.checkIn}${query.checkOut ? ` → 退房 ${query.checkOut}` : ''}`
198
+ : '未传日期(上游按当前窗口价)'
199
+ const lines = hotelLines(live.result)
200
+ const summary = `${query.destination}:hbcli 实时 ${n} 家(${dateTag})${lines ? `\n${lines}` : ''}\n${live.evidence}`
201
+ return { ...live, hotels: live.result, summary }
131
202
  }
132
- // 降级:读静态包
203
+ // 降级原因人话化(issue #24):hbcli 未安装时按 gotry setup 指引(npm 安装期已
204
+ // 自动跑过官方脚本;PATH 未含 ~/.local/bin 时上方候选路径也已兜住),
205
+ // 裸 "spawn hbcli ENOENT" 读起来像工具坏了——实际静态包降级是设计行为
206
+ const rawReason = live.error ?? live.via
207
+ const reason = /ENOENT/i.test(rawReason) ? '未安装 hbcli(可选实时源;npx @danceiny/gotry setup 可按官方脚本安装)' : rawReason
208
+ // 降级:读静态包,按目的地过滤命中的住宿块(issue #24)——整包倾倒会把无关场景
209
+ // (深圳/普吉/曼谷/云南/大理混装)灌给模型且不指明哪块相关;包内无该目的地时明示
210
+ // 「无数据」而不是伪装成可用结果。
133
211
  const fallback = opts.fallbackPath
134
212
  if (fallback) {
135
213
  try {
136
214
  const { readFile } = await import('node:fs/promises')
137
- const pack = JSON.parse(await readFile(fallback, 'utf-8')) as Record<string, unknown>
215
+ const pack = JSON.parse(await readFile(fallback, 'utf-8')) as { stays?: unknown[] } & Record<string, unknown>
216
+ const kw = query.destination.trim()
217
+ const stays = Array.isArray(pack.stays) ? pack.stays : []
218
+ const matched = kw ? stays.filter(s => JSON.stringify(s).includes(kw)) : stays
219
+ if (matched.length) {
220
+ return {
221
+ ...live,
222
+ hotels: { stays: matched },
223
+ // summary 即模型面:静态块内容(note/options/recommendation)直接内联,
224
+ // 模型不必再猜「命中 N 块」里有什么(issue #195)
225
+ summary: `${query.destination}:hbcli 实时源不可用(${reason}),已降级到静态包(公开渠道估算,非实时),命中 ${matched.length} 个住宿块:\n${JSON.stringify(matched).slice(0, 1600)}`,
226
+ }
227
+ }
138
228
  return {
139
229
  ...live,
140
- hotels: pack,
141
- summary: `${query.destination}:hbcli 不可用(${live.error ?? live.via}),降级到静态包`,
230
+ hotels: null,
231
+ summary: `${query.destination}:hbcli 实时源不可用(${reason}),且静态包无「${query.destination}」住宿数据(静态包仅覆盖内置场景)`,
142
232
  }
143
233
  } catch { /* 静态包读不到也优雅降级 */ }
144
234
  }
145
- return { ...live, summary: `${query.destination}:hbcli 不可用且无静态包(仅返回错误)` }
235
+ return { ...live, summary: `${query.destination}:hbcli 实时源不可用(${reason})且无静态包(仅返回错误)` }
146
236
  }
147
237
 
148
238
  /** 高层语义化封装:目的地列表(无数据依赖,通常 hbcli dest 命令可独立调通) */
@@ -150,3 +240,95 @@ export async function listDestinations(opts: HbcliCallOptions = {}): Promise<Hbc
150
240
  const r = await callHbcliJson(['search', 'destinations', '--json'], opts)
151
241
  return { ...r, destinations: r.result }
152
242
  }
243
+
244
+ /**
245
+ * 高层语义化封装:房型报价(hotel-rates;上游 hotelRates 创建后端 session,
246
+ * 产出带 RatePkgId 的房型列表——check-avail 与 book 的入参来源)。
247
+ *
248
+ * 价格面无静态降级(fail-closed):hotel-list 的静态包降级是定性住宿信息,
249
+ * 房价是钱——估算价格冒充实时价是红线;不可用就诚实失败,证据链标注,
250
+ * 与 bookable-facts 证据分级(live_inventory 才可进确认卡)同口径。
251
+ */
252
+ export async function hotelRates(
253
+ query: {
254
+ hotelId: string
255
+ checkIn?: string
256
+ checkOut?: string
257
+ adults?: number
258
+ countryCode?: string
259
+ nationalityCode?: string
260
+ residencyCode?: string
261
+ },
262
+ opts: HbcliCallOptions = {},
263
+ ): Promise<HbcliCallResult & { rates?: unknown; summary: string }> {
264
+ const hbArgs = ['search', 'hotel-rates', '--json', '--hotel-id', query.hotelId]
265
+ if (query.checkIn) hbArgs.push('--check-in', query.checkIn)
266
+ if (query.checkOut) hbArgs.push('--check-out', query.checkOut)
267
+ if (query.countryCode) hbArgs.push('--country-code', query.countryCode)
268
+ if (query.nationalityCode) hbArgs.push('--nationality-code', query.nationalityCode)
269
+ if (query.residencyCode) hbArgs.push('--residency-code', query.residencyCode)
270
+ if (query.adults) hbArgs.push('--room-occupancies', JSON.stringify([{ adultCount: query.adults, childrenAges: [] }]))
271
+ const live = await callHbcliJson(hbArgs, opts)
272
+ const summary = live.via === 'hbcli-realtime'
273
+ ? `hotel ${query.hotelId}:hbcli 实时报价${query.checkIn ? `(${query.checkIn} → ${query.checkOut ?? '?'})` : ''}`
274
+ : `hotel ${query.hotelId}:hbcli 实时报价不可用(${live.error ?? live.via});价格面无静态降级,fail-closed 不估算`
275
+ return { ...live, rates: live.result, summary }
276
+ }
277
+
278
+ /** 高层语义化封装:下单前实时验价(check-avail;入参 RatePkgId 来自 hotel-rates 产物)。价格面同上:不可用即诚实失败。 */
279
+ export async function checkAvail(
280
+ query: { ratePkgId: string },
281
+ opts: HbcliCallOptions = {},
282
+ ): Promise<HbcliCallResult & { avail?: unknown; summary: string }> {
283
+ const live = await callHbcliJson(['search', 'check-avail', '--json', '--rate-pkg-id', query.ratePkgId], opts)
284
+ const summary = live.via === 'hbcli-realtime'
285
+ ? `ratePkg ${query.ratePkgId}:hbcli 实时验价(库存与价格以后端为准)`
286
+ : `ratePkg ${query.ratePkgId}:hbcli 实时验价不可用(${live.error ?? live.via});价格面无静态降级,fail-closed`
287
+ return { ...live, avail: live.result, summary }
288
+ }
289
+
290
+ /**
291
+ * C 端客户邮箱验证码登录(hotelbyte-cli auth customer-* 两步流;后端
292
+ * loginByCustomerEmailCode:验证通过即登录,新邮箱即注册,无需密码):
293
+ * step 1 customerSendCode —— 邮箱收码(外发邮件动作,仅用户明示要登录/注册时调用);
294
+ * step 2 customerLogin —— 用户从收件箱报码,验证即建档/登录。
295
+ *
296
+ * 与检索族的本质差异:customerLogin 写 hbcli 共享凭据仓(customer 档,
297
+ * ~/.staicli/credentials.json)——hbcli 侧 customer 档是最低优先级凭据(无
298
+ * portal/API-key 凭据时才生效),登录不劫持既有凭据。本层不透出 token 值
299
+ * (CLI 只回 token_saved,凭据留在 CLI 仓内,不进 gotry 会话/日志)。
300
+ * auth 命令族要求 hbcli 带 register 支持(npm 0.0.4 起;0.0.3 报 unknown
301
+ * command,经 hbcli-error 面优雅失败)。
302
+ */
303
+ export async function customerSendCode(
304
+ email: string,
305
+ opts: HbcliCallOptions = {},
306
+ ): Promise<HbcliCallResult & { summary: string }> {
307
+ const live = await callHbcliJson(['--json', 'auth', 'customer-send-code', '--email', email], opts)
308
+ const r = live.result as { expiresIn?: number; nextTime?: string } | null
309
+ const summary = live.via === 'hbcli-realtime'
310
+ ? `验证码已发送到 ${email}${r?.expiresIn ? `(有效期 ${r.expiresIn} 秒)` : ''}——请让用户从邮箱查收并报码,再走 gotry_hotel_auth_login`
311
+ : `验证码发送失败(${live.error ?? live.via});服务端有发码限流,勿立即重试`
312
+ return { ...live, summary }
313
+ }
314
+
315
+ export async function customerLogin(
316
+ query: { email: string; code: string; ttl?: number; attributionToken?: string },
317
+ opts: HbcliCallOptions = {},
318
+ ): Promise<HbcliCallResult & { summary: string }> {
319
+ const hbArgs = ['--json', 'auth', 'customer-login', '--email', query.email, '--code', query.code]
320
+ if (query.ttl && query.ttl > 0) hbArgs.push('--ttl', String(query.ttl))
321
+ if (query.attributionToken) hbArgs.push('--attribution-token', query.attributionToken)
322
+ const live = await callHbcliJson(hbArgs, opts)
323
+ const r = live.result as { status?: string; attributionBound?: boolean } | null
324
+ let summary: string
325
+ if (live.via === 'hbcli-realtime' && r?.status === 'logged_in') {
326
+ const attribution = typeof r.attributionBound === 'boolean'
327
+ ? `;顾问归因绑定${r.attributionBound ? '成功' : '未成(登录不受影响,归因可重试)'}`
328
+ : ''
329
+ summary = `hotelbyte 客户登录成功(${query.email};新邮箱即注册${attribution})——凭据已存入 hbcli customer 档,后续酒店检索/报价走实时源`
330
+ } else {
331
+ summary = `hotelbyte 客户登录失败(${live.error ?? live.via});验证码是一次性凭据,失败后需重新发码`
332
+ }
333
+ return { ...live, summary }
334
+ }
@@ -1,20 +1,17 @@
1
1
  /**
2
- * 进程级事故日志(D-NEW 护栏):fsync append-only JSONL。
2
+ * 进程级事故日志(D-NEW):同步、durable 的 JSONL 诊断记录。
3
3
  *
4
- * 目的: dsh 0.1.1-rc.1 只接 SIGINT/SIGTERM,缺 uncaughtException/unhandledRejection——
5
- * 任一插件 wasm/runtime 异常穿透到 node 层都会让整个 web UI 进程死亡,
6
- * 现场不存。这次(Z3 WASM mk_bool_var 崩溃)教训:连个取证都没有。
7
- *
8
- * 设计: 进程级 handler 同步写盘(fsync),尽量在进程被杀前留下事故证据。
9
- * handler 自身不再抛——再次抛出会绕过 handler,变成无声死亡。
4
+ * `uncaughtExceptionMonitor` 只观察 fatal 事故并留下证据;宿主仍决定
5
+ * 是否退出。GoTry 不吞掉 fatal 事件、不调用 process.exit、也不隐式重启。
6
+ * 工具 execute 的业务失败仍由 guardToolExecute 转成结构化失败返回。
10
7
  *
11
8
  * §11 状态面同步:事故日志是 gotry-state/incidents.jsonl,
12
- * 用户可见(README 第 x 节),可清理。
9
+ * 用户可见、可清理。
13
10
  */
14
11
 
15
- import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'
12
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, writeFileSync } from 'node:fs'
16
13
  import { dirname, isAbsolute, join } from 'node:path'
17
- import { fileURLToPath } from 'node:url'
14
+ import { pathToFileURL } from 'node:url'
18
15
  import type { ToolFailure } from '../src/tool-packet.ts'
19
16
 
20
17
  export type IncidentKind = 'uncaughtException' | 'unhandledRejection' | 'plugin_error' | 'tool_execute_error'
@@ -43,68 +40,104 @@ function ensureDir(path: string): void {
43
40
 
44
41
  let installed = false
45
42
  let logPath: string | null = null
43
+ let activeMonitor: ((err: Error, origin: NodeJS.UncaughtExceptionOrigin) => void) | null = null
44
+
45
+ function safeErrorMessage(value: unknown): string {
46
+ try {
47
+ if (value instanceof Error) return typeof value.message === 'string' ? value.message : '[error-without-message]'
48
+ if (typeof value === 'string') return value
49
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint' || typeof value === 'symbol') return String(value)
50
+ const json = JSON.stringify(value)
51
+ return typeof json === 'string' ? json : '[unprintable-error]'
52
+ } catch {
53
+ return '[unprintable-error]'
54
+ }
55
+ }
56
+
57
+ function safeErrorStack(value: unknown): string {
58
+ try {
59
+ const stack = (value as { stack?: unknown } | null | undefined)?.stack
60
+ return typeof stack === 'string' ? stack : ''
61
+ } catch {
62
+ return ''
63
+ }
64
+ }
46
65
 
47
66
  /**
48
- * 写入一条事故并 fsync。同步执行——handler 内异步无效。
49
- * 自身 try/catch 自保,不让写入失败再抛一次把进程杀掉。
67
+ * 写入一条事故并 fsync。整个 append 使用同一个 fd,并保证所有失败路径
68
+ * 都尝试 close;返回 true 只表示写入、fsync、close 全部成功。
50
69
  */
51
70
  export function recordIncident(inc: Incident, stateRoot?: string): boolean {
52
71
  const target = stateRoot ? resolveIncidentsPath(stateRoot) : (logPath ?? resolveIncidentsPath('.'))
72
+ let fd: number | undefined
73
+ let closeAttempted = false
53
74
  try {
54
75
  ensureDir(target)
55
- appendFileSync(target, JSON.stringify(inc) + '\n', { flag: 'a' })
56
- // fsync: 让盘数据落定,免得被杀时缓冲丢
57
- const fd = require('node:fs').openSync(target, 'a')
58
- require('node:fs').fsyncSync(fd)
59
- require('node:fs').closeSync(fd)
76
+ fd = openSync(target, 'a')
77
+ writeFileSync(fd, JSON.stringify(inc) + '\n', { encoding: 'utf8' })
78
+ fsyncSync(fd)
79
+ closeAttempted = true
80
+ closeSync(fd)
81
+ fd = undefined
60
82
  return true
61
83
  } catch {
62
- // 绝对不能在这里抛——我们正在处理已经抛过一次的进程级错误
84
+ // 事故处理中的诊断失败绝不能再次抛出。
63
85
  return false
86
+ } finally {
87
+ if (fd !== undefined && !closeAttempted) {
88
+ closeAttempted = true
89
+ try { closeSync(fd) } catch { /* best effort close on a failed write */ }
90
+ }
64
91
  }
65
92
  }
66
93
 
67
94
  /**
68
- * 安装进程级护栏:进程整个生命周期仅生效一次(重复调用安全)。
69
- * 必须同步执行——异步发生在事件循环下一次,而 wasm 崩溃后已无下一轮。
70
- *
71
- * @returns 一个 disposer,卸载所有监听(测试场景使用)
95
+ * 安装进程级事故观察器。只挂 monitor,不改变 Node/dsh 的 fatal 行为。
96
+ * 进程整个生命周期仅生效一次;disposer 供宿主卸载和测试使用。
72
97
  */
73
98
  export function installProcessGuards(stateRoot: string, labels?: { uncaughtException?: string; unhandledRejection?: string }): () => void {
74
99
  if (installed) return () => { /* 已装,no-op */ }
75
100
  installed = true
76
101
  logPath = resolveIncidentsPath(stateRoot)
77
102
 
78
- const uncaughtHandler = (err: Error, origin: NodeJS.UncaughtExceptionOrigin | 'uncaughtException') => {
79
- recordIncident({
80
- ts: new Date().toISOString(),
81
- kind: 'uncaughtException',
82
- message: (err?.message ?? String(err)).slice(0, 2000),
83
- stack: (err?.stack ?? '').slice(0, 4000),
84
- source: labels?.uncaughtException ?? String(origin),
85
- }, stateRoot)
86
- // 不调用 process.exit——让 dsh/上级容器的重启策略负责;
87
- // 如果 dsh 不接住,我们至少留下事故记录。
88
- }
89
-
90
- const rejectionHandler = (reason: unknown, promise: Promise<unknown>) => {
91
- const msg = reason instanceof Error ? reason.message : String(reason)
92
- const stack = reason instanceof Error ? (reason.stack ?? '') : ''
93
- recordIncident({
94
- ts: new Date().toISOString(),
95
- kind: 'unhandledRejection',
96
- message: msg.slice(0, 2000),
97
- stack: stack.slice(0, 4000),
98
- source: labels?.unhandledRejection ?? 'promise',
99
- }, stateRoot)
103
+ const monitor = (err: Error, origin: NodeJS.UncaughtExceptionOrigin) => {
104
+ try {
105
+ const kind: IncidentKind = origin === 'unhandledRejection' ? 'unhandledRejection' : 'uncaughtException'
106
+ const source = kind === 'unhandledRejection'
107
+ ? (labels?.unhandledRejection ?? 'promise')
108
+ : (labels?.uncaughtException ?? origin)
109
+ recordIncident({
110
+ ts: new Date().toISOString(),
111
+ kind,
112
+ message: safeErrorMessage(err).slice(0, 2000),
113
+ stack: safeErrorStack(err).slice(0, 4000),
114
+ source,
115
+ }, stateRoot)
116
+ } catch {
117
+ // Formatting must never replace the original fatal event. Keep a final
118
+ // plain fallback attempt for diagnostics, while the host retains control.
119
+ try {
120
+ recordIncident({
121
+ ts: new Date().toISOString(),
122
+ kind: origin === 'unhandledRejection' ? 'unhandledRejection' : 'uncaughtException',
123
+ message: '[incident-observer-fallback]',
124
+ source: 'incident-observer',
125
+ }, stateRoot)
126
+ } catch { /* observer is strictly best effort */ }
127
+ }
100
128
  }
101
129
 
102
- process.on('uncaughtException', uncaughtHandler)
103
- process.on('unhandledRejection', rejectionHandler)
130
+ process.on('uncaughtExceptionMonitor', monitor)
131
+ activeMonitor = monitor
132
+ let active = true
104
133
  return () => {
105
- process.off('uncaughtException', uncaughtHandler)
106
- process.off('unhandledRejection', rejectionHandler)
134
+ if (!active) return
135
+ active = false
136
+ if (activeMonitor !== monitor) return
137
+ process.off('uncaughtExceptionMonitor', monitor)
107
138
  installed = false
139
+ logPath = null
140
+ activeMonitor = null
108
141
  }
109
142
  }
110
143
 
@@ -112,24 +145,23 @@ export function installProcessGuards(stateRoot: string, labels?: { uncaughtExcep
112
145
  * 工具执行面异常隔离(D-NEW gotry 侧收尾):dsh 的一个工具 execute 抛错/拒绝
113
146
  * 会沿 cordis 传到主循环,拖垮整个会话。包装后:降级为结构化错误返回给 LLM、
114
147
  * 事故落盘,永不向上抛。落盘失败也不抛(双保险,仍返回结构化错误)。
115
- * 失败形状 = ToolFailure(RFC S1 observation envelope 的失败分支,编译期对齐)。
116
148
  */
117
149
  export function guardToolExecute<A, R>(name: string, stateRoot: string, execute: (args: A, exec: unknown) => R | Promise<R>): (args: A, exec: unknown) => Promise<R> {
118
150
  return async (args: A, exec: unknown): Promise<R> => {
119
151
  try {
120
152
  return await execute(args, exec)
121
153
  } catch (e) {
122
- const err = e instanceof Error ? e : new Error(String(e))
154
+ const message = safeErrorMessage(e)
123
155
  recordIncident({
124
156
  ts: new Date().toISOString(),
125
157
  kind: 'tool_execute_error',
126
- message: `${name}: ${err.message}`.slice(0, 2000),
127
- stack: (err.stack ?? '').slice(0, 4000),
158
+ message: `${name}: ${message}`.slice(0, 2000),
159
+ stack: safeErrorStack(e).slice(0, 4000),
128
160
  source: name,
129
161
  }, stateRoot)
130
162
  const failure: ToolFailure = {
131
163
  ok: false,
132
- summary: `gotry_${name} 内部错误(已隔离,会话继续): ${err.message.slice(0, 300)}`,
164
+ summary: `gotry_${name} 内部错误(已隔离,会话继续): ${message.slice(0, 300)}`,
133
165
  evidence: `[incident:tool_execute_error@${new Date().toISOString()}]`,
134
166
  }
135
167
  return failure as R
@@ -137,12 +169,9 @@ export function guardToolExecute<A, R>(name: string, stateRoot: string, execute:
137
169
  }
138
170
  }
139
171
 
140
- /** CLI 调试:从命令行单独跑查看当前进程是否已挂护栏 */
141
- if (import.meta.url === `file://${fileURLToPath(import.meta.url)}` && process.argv[2] === '--smoke') {
172
+ /** CLI 调试:最小 fatal 子进程会留下事故证据并按 Node 原生语义非零退出。 */
173
+ if (process.argv[1] !== undefined && pathToFileURL(process.argv[1]).href === import.meta.url && process.argv[2] === '--smoke') {
142
174
  const stateRoot = process.argv[3] ?? '.'
143
175
  installProcessGuards(stateRoot, { uncaughtException: 'smoke', unhandledRejection: 'smoke' })
144
- writeFileSync(resolveIncidentsPath(stateRoot) + '.smoke', 'ok')
145
- // 触发一次未捕获,应被写盘(不真正杀进程——进程继续跑完)
146
- setTimeout(() => Promise.reject(new Error('intentional smoke-rejection')), 50)
147
- setTimeout(() => { console.log('SMOKE OK'); process.exit(0) }, 500)
176
+ setTimeout(() => { throw new Error('intentional smoke-uncaught') }, 50)
148
177
  }