@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
@@ -0,0 +1,171 @@
1
+ /**
2
+ * 行程 HTML 渲染器(issue #442,父 #438;共享契约层抽取见 issue #564)。
3
+ *
4
+ * 纯有界渲染器:拒绝畸形输入,产出单文件自包含 HTML。本模块不执行 IO、
5
+ * 不启动服务、不解析 Markdown、不做夜数/预算/时间运算——只把显式行程结构
6
+ * 与已注册事实投影成可导航、可展开的安全文档。
7
+ *
8
+ * 输入校验、事实归一化与证据卡渲染的单一事实源在 `itinerary-doc-shared.ts`
9
+ * (两个文档投影共用,防第二实现漂移);本模块只保留单页文档的排版与组装。
10
+ *
11
+ * 两条永不合流的证据面:
12
+ * 1. 计划面(planner:trip_start/trip_end/stays/od_segments)——用户排期意图;
13
+ * 2. 证据面(facts:调用方从注册表选出的 BookableFact)——exact-date 查询结论,
14
+ * 逐条保留 tier/bookability/source/query_id/fetched_at/as_of。
15
+ * 目的地级酒店库存绝不宣称「某家酒店有房/可订」;无任何整体 verified 徽章。
16
+ *
17
+ * 渲染闸锚点纪律:本模块不嵌入 `<!-- fact:<id> -->` 锚点(那属 Markdown 产物闸),
18
+ * 事实 id 只作为可见文本展示,避免整篇 HTML 被当作锚点行解析。
19
+ */
20
+
21
+ import {
22
+ DOC_BASE_CSS,
23
+ DOC_NARROW_CSS,
24
+ EVIDENCE_BOUNDARY_NOTICE,
25
+ MODE_LABEL,
26
+ TWO_PLANE_STATEMENT,
27
+ buildDateIndex,
28
+ docBytesError,
29
+ escapeHtml,
30
+ factIdSlug,
31
+ hotelFactsForStay,
32
+ leftoverFacts,
33
+ normalizeDocInput,
34
+ renderDateList,
35
+ renderFact,
36
+ renderFactCountsLine,
37
+ renderHotelEvidenceSection,
38
+ renderHotelNote,
39
+ renderLeftoverBlock,
40
+ renderPlannedStay,
41
+ renderPolicyNote,
42
+ renderSegment,
43
+ routeFactsFor,
44
+ timeEl,
45
+ type ItineraryDocResult,
46
+ type NormalizedFact,
47
+ type NormalizedFlight,
48
+ type NormalizedHotel,
49
+ type NormalizedItinerary,
50
+ type NormalizedPolicy,
51
+ } from './itinerary-doc-shared.ts'
52
+
53
+ // API 面稳定性:既有消费方(itinerary-artifact.ts / 测试)继续从本模块导入
54
+ export { ITINERARY_DOC_LIMITS as ITINERARY_HTML_LIMITS } from './itinerary-doc-shared.ts'
55
+ export type { ItineraryDocInput as ItineraryHtmlInput, ItineraryDocResult as ItineraryHtmlResult } from './itinerary-doc-shared.ts'
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // 单页文档排版(组装共享层的内容原语;CSS 为本形态私有)
59
+ // ---------------------------------------------------------------------------
60
+
61
+ const CSS = DOC_BASE_CSS + `
62
+ .wrap{max-width:64rem;margin:0 auto;padding:1rem}
63
+ header.doc{border-bottom:1px solid var(--line);padding-bottom:.75rem}
64
+ nav.toc{border:1px solid var(--line);border-radius:6px;padding:.6rem .8rem;margin:1rem 0;background:var(--card)}
65
+ nav.toc h2{font-size:1rem}
66
+ nav.toc ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:.35rem .9rem}
67
+ nav.toc ul ul{margin-left:.9rem;font-size:.92rem;display:block}
68
+ section{margin:1.75rem 0}
69
+ ` + DOC_NARROW_CSS + `
70
+ @media (max-width:480px){
71
+ .wrap{padding:.6rem}
72
+ }`
73
+
74
+ function render(input: { title: string; itinerary: NormalizedItinerary; facts: NormalizedFact[] }): string {
75
+ const { title, itinerary, facts } = input
76
+ const stays = itinerary.stays
77
+ const segs = itinerary.od_segments
78
+ const hotelFacts = facts.filter((f): f is NormalizedHotel => f.kind === 'hotel')
79
+ const policyFacts = facts.filter((f): f is NormalizedPolicy => f.kind === 'policy')
80
+ const flightFacts = facts.filter((f): f is NormalizedFlight => f.kind === 'flight' || f.kind === 'train')
81
+
82
+ const stayBlocks = stays.map((stay, i) => renderPlannedStay(stay, `stay-${i + 1}`, hotelFactsForStay(hotelFacts, stay)))
83
+ const segBlocks = segs.map((seg, i) => renderSegment(seg, `seg-${i + 1}`, routeFactsFor(facts, seg), itinerary.trip_start))
84
+
85
+ const stayDateItems = stays.filter((s, i) => stays.findIndex(x => x.check_in === s.check_in) === i)
86
+ .map(s => `<li>${timeEl(s.check_in)} — <a href="#stay-${stays.indexOf(s) + 1}">计划住宿 ${escapeHtml(s.place)}</a></li>`)
87
+ .join('')
88
+ const segDateItems = [...new Set(segs.map(s => s.date))].sort()
89
+ .map(d => `<li>${timeEl(d)} — <a href="#segments">交通段</a></li>`)
90
+ .join('')
91
+
92
+ const dateList = renderDateList(buildDateIndex(itinerary))
93
+ const leftovers = leftoverFacts(facts, itinerary)
94
+ const leftoverBlock = renderLeftoverBlock(leftovers, itinerary.trip_start)
95
+ const hotelNote = renderHotelNote(hotelFacts)
96
+ const policyNote = renderPolicyNote(policyFacts)
97
+
98
+ return `<!DOCTYPE html>
99
+ <html lang="zh-CN">
100
+ <head>
101
+ <meta charset="utf-8">
102
+ <meta name="viewport" content="width=device-width, initial-scale=1">
103
+ <meta name="referrer" content="no-referrer">
104
+ <title>${escapeHtml(title)}</title>
105
+ <style>${CSS}</style>
106
+ </head>
107
+ <body>
108
+ <a class="skip" href="#main">跳到主要内容</a>
109
+ <div class="wrap">
110
+ <header class="doc">
111
+ <h1>${escapeHtml(title)}</h1>
112
+ <p>行程 ${timeEl(itinerary.trip_start)} → ${timeEl(itinerary.trip_end)}</p>
113
+ ${TWO_PLANE_STATEMENT}
114
+ </header>
115
+ <main id="main">
116
+ ${EVIDENCE_BOUNDARY_NOTICE}
117
+ <nav class="toc" aria-label="行程导航">
118
+ <h2>导航</h2>
119
+ <ul>
120
+ <li><a href="#dates">按日期</a><ul>${dateList.startsWith('<p') ? '<li class="muted">无</li>' : ''}</ul></li>
121
+ <li><a href="#segments">航班/交通段</a><ul>${segDateItems || '<li class="muted">无</li>'}</ul></li>
122
+ <li><a href="#stays">住宿</a><ul>${stayDateItems || '<li class="muted">无</li>'}</ul></li>
123
+ <li><a href="#facts">事实与证据</a></li>
124
+ </ul>
125
+ </nav>
126
+ <section id="dates"><h2>按日期行程</h2>
127
+ ${dateList}
128
+ </section>
129
+ <section id="segments"><h2>航班/交通段</h2>
130
+ <p class="muted">下列条目是计划段(用户排期意图);每段内另列可回溯到该 route + date 的已注册事实。计划段不因存在事实而变成可下单。</p>
131
+ ${segBlocks.join('') || '<p class="muted missing">没有显式交通段。</p>'}
132
+ </section>
133
+ <section id="stays"><h2>住宿</h2>
134
+ ${hotelNote}
135
+ ${stayBlocks.join('') || '<p class="muted missing">没有显式住宿段。</p>'}
136
+ ${renderHotelEvidenceSection(hotelFacts, itinerary.trip_start)}
137
+ </section>
138
+ <section id="facts"><h2>事实与证据</h2>
139
+ ${renderFactCountsLine(facts)}
140
+ ${policyFacts.map(f => renderFact(f, `policy-${factIdSlug(f.fact.fact_id)}`, itinerary.trip_start)).join('')}
141
+ ${policyNote}
142
+ ${leftoverBlock}
143
+ <h3>全部航班/车次事实(含负事实与冲突)</h3>
144
+ ${flightFacts.map(f => renderFact(f, `flight-${factIdSlug(f.fact.fact_id)}`, itinerary.trip_start)).join('') || '<p class="muted missing">本次输入没有航班/车次事实。</p>'}
145
+ </section>
146
+ </main>
147
+ </div>
148
+ </body>
149
+ </html>
150
+ `
151
+ }
152
+
153
+ // ---------------------------------------------------------------------------
154
+ // 入口
155
+ // ---------------------------------------------------------------------------
156
+
157
+ /**
158
+ * 纯渲染入口。`input` 是任意运行时数据:结构、枚举、真实日历日期、有限数值、
159
+ * 容量与文档大小全部现场校验;任何一项不合法即返回 `ok:false` + 结构化 errors,
160
+ * 绝不静默丢弃核心行程后仍返回「成功」。校验与归一化的单一事实源在
161
+ * `itinerary-doc-shared.ts`,与 deck 投影共用同一拒绝语义。
162
+ */
163
+ export function renderItineraryHtml(input: unknown): ItineraryDocResult {
164
+ const normalized = normalizeDocInput(input)
165
+ if (!normalized.ok) return normalized
166
+ const html = render(normalized)
167
+ // 输出大小硬上限:超限即整体失败(检查实现于共享层)
168
+ const bytesError = docBytesError(html)
169
+ if (bytesError !== undefined) return { ok: false, errors: bytesError }
170
+ return { ok: true, html }
171
+ }
package/ts/src/loop.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Stage 1 对话循环(S2 段1,docs/stage1-top-down-design.md §2.3)。
2
+ * Stage 1 对话循环(S2 段1,docs/design/stage1-top-down-design.md §2.3)。
3
3
  *
4
4
  * 自顶向下:循环只依赖两个端口——LlmPort(mock/真)与确定性工具(interview_next)。
5
5
  * 求解/渲染接线是 S2 后续段;本段验收:重放用户开场白,系统一轮内完成
@@ -8,6 +8,7 @@
8
8
 
9
9
  import type { InterviewQuestion, TripState, TravelerProfile, Turn, CalendarState, SolveResult } from './contracts.ts'
10
10
  import type { JourneySpecTS } from './unified.ts'
11
+ import { buildTimeAnchor, isRealIsoDate, planningWindowBounds, parsePlanningWindow, type PlanningWindow, type TimeAnchor } from './time-anchor.ts'
11
12
  import type { TravelSlotExtraction } from './travel-slots.ts'
12
13
  import { anythingSearch } from '../capabilities/anything.ts'
13
14
 
@@ -95,6 +96,98 @@ export function validateSpec(spec: JourneySpecTS): string | null {
95
96
  return null
96
97
  }
97
98
 
99
+ export interface PlanningWindowRejection {
100
+ segmentId: string
101
+ optionId: string
102
+ date: string
103
+ }
104
+
105
+ export interface PlanningWindowCheck {
106
+ spec: JourneySpecTS
107
+ rejected: PlanningWindowRejection[]
108
+ error?: string
109
+ }
110
+
111
+ export interface PlanningWindowOptions {
112
+ /** Registered feasibility tool opts into the dated-candidate default future floor. */
113
+ defaultDatedFuture?: boolean
114
+ }
115
+
116
+ function isoDateInBounds(date: string, bounds: { start: string; end: string }): boolean {
117
+ const m = date.match(/^(\d{4})-(\d{2})-(\d{2})$/)
118
+ if (!m || !isRealIsoDate(Number(m[1]), Number(m[2]), Number(m[3]))) return false
119
+ return date >= bounds.start && date <= bounds.end
120
+ }
121
+
122
+ function hasDatedCandidates(spec: JourneySpecTS): boolean {
123
+ return spec.segments.some(segment => segment.date !== undefined || segment.options.some(option => option.date !== undefined))
124
+ }
125
+
126
+ /**
127
+ * Enforce a named-year future window at the planner boundary. Historical
128
+ * lookup/backtesting remains an explicit bypass; an expired named year fails
129
+ * instead of being silently rolled into a different year.
130
+ */
131
+ export function applyPlanningWindow(
132
+ spec: JourneySpecTS,
133
+ window: PlanningWindow | null,
134
+ anchor: TimeAnchor,
135
+ options: PlanningWindowOptions = {},
136
+ ): PlanningWindowCheck {
137
+ // Dated recommendations are future-oriented even without optional named-year context.
138
+ // Fully dateless legacy calculations remain untouched; historical bypass is explicit.
139
+ if (window?.intent === 'historical') return { spec, rejected: [] }
140
+ const implicitFutureFloor = !window && options.defaultDatedFuture === true && hasDatedCandidates(spec)
141
+ if (!window && !implicitFutureFloor) return { spec, rejected: [] }
142
+
143
+ const currentYear = Number(anchor.today.slice(0, 4))
144
+ if (window && window.requestedYear < currentYear) {
145
+ return {
146
+ spec,
147
+ rejected: [],
148
+ error: `${window.requestedYear} 年已结束(当前规划参考日 ${anchor.today}),不会自动改用其他年份;如需历史查询请明确说明“历史/回测”。`,
149
+ }
150
+ }
151
+
152
+ const bounds = window
153
+ ? planningWindowBounds(window, anchor)
154
+ : { start: anchor.today, end: '9999-12-31' }
155
+ if (!bounds) return { spec, rejected: [], error: `无法建立 ${window!.requestedYear} 年的未来规划窗口。` }
156
+
157
+ const rejected: PlanningWindowRejection[] = []
158
+ const guardedSegments = spec.segments.map(segment => ({
159
+ ...segment,
160
+ options: segment.options.filter(option => {
161
+ const date = option.date ?? segment.date
162
+ if (date && isoDateInBounds(date, bounds)) return true
163
+ rejected.push({ segmentId: segment.id, optionId: option.id, date: date ?? '未提供明确日期' })
164
+ return false
165
+ }),
166
+ }))
167
+
168
+ if (guardedSegments.some(segment => segment.options.length === 0)) {
169
+ return {
170
+ spec: { ...spec, segments: guardedSegments },
171
+ rejected,
172
+ error: implicitFutureFloor
173
+ ? `带日期的未来推荐必须不早于当前规划参考日 ${bounds.start};没有带明确日期且在未来窗口内的可接受方案。`
174
+ : `${window!.requestedYear} 年未来规划窗口为 ${bounds.start} 至 ${bounds.end};没有带明确日期且落在窗口内的可接受方案。`,
175
+ }
176
+ }
177
+ return { spec: { ...spec, segments: guardedSegments }, rejected }
178
+ }
179
+
180
+ function latestPlanningWindow(history: Turn[], userMsg: string, anchor: TimeAnchor): PlanningWindow | null {
181
+ const turns = [...history, { role: 'user' as const, text: userMsg }]
182
+ for (let i = turns.length - 1; i >= 0; i--) {
183
+ const turn = turns[i]
184
+ if (turn?.role !== 'user') continue
185
+ const parsed = parsePlanningWindow(turn.text, anchor)
186
+ if (parsed) return parsed
187
+ }
188
+ return null
189
+ }
190
+
98
191
  /** 求解端口:S3 起由 unified 求解器实现;循环只认此签名(确定性责任)。 */
99
192
  export type SolvePort = (spec: JourneySpecTS) => Promise<SolveResult>
100
193
 
@@ -110,6 +203,51 @@ export interface AsyncTicket {
110
203
  etaLabel: string
111
204
  }
112
205
 
206
+ export const ASYNC_TERMINAL_SCHEMA = 'gotry_async_terminal.v1' as const
207
+
208
+ export interface AsyncTerminalOutcome {
209
+ schema: typeof ASYNC_TERMINAL_SCHEMA
210
+ ticket_id: string
211
+ status: 'succeeded' | 'failed'
212
+ passed: number
213
+ total: 4
214
+ checks: Record<string, boolean>
215
+ failed_checks: string[]
216
+ }
217
+
218
+ function asyncTerminalOutcome(ticketId: string, checks: Record<string, boolean>): AsyncTerminalOutcome {
219
+ const failedChecks = Object.entries(checks).filter(([, passed]) => !passed).map(([name]) => name)
220
+ return {
221
+ schema: ASYNC_TERMINAL_SCHEMA,
222
+ ticket_id: ticketId,
223
+ status: failedChecks.length === 0 ? 'succeeded' : 'failed',
224
+ passed: Object.values(checks).filter(Boolean).length,
225
+ total: 4,
226
+ checks,
227
+ failed_checks: failedChecks,
228
+ }
229
+ }
230
+
231
+ function terminalOutcomeFromState(ticketId: string, state: TripState): AsyncTerminalOutcome {
232
+ if (!state.spec || !state.solve) {
233
+ return asyncTerminalOutcome(ticketId, {
234
+ '1_承诺时间后必有明确产物': false,
235
+ '2_产物通过自检清单': false,
236
+ '3_待决问题全部是简单选择题': false,
237
+ '4_做不到的诚实说': false,
238
+ })
239
+ }
240
+
241
+ return asyncTerminalOutcome(ticketId, {
242
+ '1_承诺时间后必有明确产物': Boolean(state.solve.legs?.length || state.solve.verdicts?.length),
243
+ '2_产物通过自检清单': state.solve.feasible
244
+ ? (state.solve.legs?.every(l => (l as Record<string, unknown>)['energy_pct'] !== undefined) ?? false)
245
+ : Boolean(state.solve.unsat_core?.length),
246
+ '3_待决问题全部是简单选择题': state.gates.every(g => g.id === 'budget' || g.options.length >= 2),
247
+ '4_做不到的诚实说': state.solve.feasible || Boolean(state.solve.suggestions?.length),
248
+ })
249
+ }
250
+
113
251
  export function isComplex(state: TripState): boolean {
114
252
  /** 复杂度判据(架构占位,后续可细化):多段或多约束即深规划 */
115
253
  const p = state.profile
@@ -134,21 +272,19 @@ export async function collectDeepPlanning(
134
272
  state: TripState,
135
273
  ticket: AsyncTicket,
136
274
  solve: SolvePort,
137
- ): Promise<{ reply: string; state: TripState }> {
275
+ ): Promise<{ reply: string; state: TripState; outcome: AsyncTerminalOutcome }> {
138
276
  const spec = state.spec
139
- if (!spec) return { reply: '(内部状态缺失 spec——深度规划未就绪,这是不应发生的路径)', state }
277
+ if (!spec) {
278
+ const outcome = terminalOutcomeFromState(ticket.id, state)
279
+ return { reply: '(内部状态缺失 spec——深度规划未就绪,这是不应发生的路径)', state, outcome }
280
+ }
140
281
  state.solve = await solve(spec)
141
282
  state.gates = state.gates.filter(g => !g.id.startsWith('async-'))
142
283
 
143
284
  // 不失望四条自检(交付物自带,总纲 3.6)
144
- const checks = {
145
- '1_承诺时间后必有明确产物': Boolean(state.solve.legs?.length || state.solve.verdicts?.length),
146
- '2_产物通过自检清单': state.solve.feasible ? (state.solve.legs?.every(l => (l as Record<string, unknown>)['energy_pct'] !== undefined) ?? false) : Boolean(state.solve.unsat_core?.length),
147
- '3_待决问题全部是简单选择题': state.gates.every(g => g.id === 'budget' || g.options.length >= 2),
148
- '4_做不到的诚实说': state.solve.feasible || Boolean(state.solve.suggestions?.length),
149
- }
150
- const head = `# 回访交付:${ticket.objective}\n(工单 ${ticket.id},不失望四条:${Object.values(checks).every(Boolean) ? '4/4 ✅' : '有未达项 ❌'})`
151
- return { reply: `${head}\n\n${renderSolve(state)}`, state }
285
+ const outcome = terminalOutcomeFromState(ticket.id, state)
286
+ const head = `# 回访交付:${ticket.objective}\n(工单 ${ticket.id},不失望四条:${outcome.status === 'succeeded' ? '4/4 ✅' : '有未达项 ❌'})`
287
+ return { reply: `${head}\n\n${renderSolve(state)}`, state, outcome }
152
288
  }
153
289
 
154
290
  /** 求解结果 → 人话(模板;S4 可由 LLM 润色,数字与判定不可改) */
@@ -158,13 +294,20 @@ export function renderSolve(state: TripState): string {
158
294
  const lines: string[] = []
159
295
  if (s.feasible) {
160
296
  lines.push(`**方案可行,机票合计 ¥${s.money_cny}**`)
161
- for (const lg of s.legs ?? []) {
162
- const l = lg as Record<string, string | number>
163
- lines.push(`- ${l['leg']} ${l['service']}:${l['dep']} 起飞,${l['wake']} 出发,${l['arrive_stay']} 到,`
164
- + `门到门 ${l['door_to_door']},落地精力 ${l['energy_pct']}%,¥${l['price_cny']}`)
297
+ // §7-1 issue #559 卡点 4:从 s.skeleton_notes 字符串编码里推断 unscaffolded 的 seg.id,
298
+ // 不动 unified.ts/journey.ts(内核冻结面 #234),用户面给 ⚠ 前缀与「枢纽对未在骨架覆盖」补充。
299
+ // skeleton_notes 单条形态:`<seg.id>: [骨架:openflights] ❌ ...` 或 `<seg.id>: [骨架:openflights] ✅ ...`
300
+ const unscaffoldedSegIds = new Set<string>()
301
+ for (const n of s.skeleton_notes ?? []) {
302
+ const m = /^([^:]+):\s*\[骨架:openflights\]\s*❌/.exec(n)
303
+ if (m) unscaffoldedSegIds.add(m[1])
165
304
  }
166
- for (const e of s.work_window_exclusions ?? []) {
167
- lines.push(`- 已排除 ${e.option}(工作窗口):${e.reason}`)
305
+ for (const lg of s.legs ?? []) {
306
+ const l = lg as Record<string, string | number | boolean>
307
+ const unscaffoldedFlag = unscaffoldedSegIds.has(String(l['leg'])) ? '⚠ ' : ''
308
+ const unscaffoldedNote = unscaffoldedSegIds.has(String(l['leg'])) ? '(枢纽对未在骨架覆盖,实时源以新航线为准)' : ''
309
+ lines.push(`- ${unscaffoldedFlag}${l['leg']} ${l['service']}:${l['dep']} 起飞,${l['wake']} 出发,${l['arrive_stay']} 到,`
310
+ + `门到门 ${l['door_to_door']},落地精力 ${l['energy_pct']}%,¥${l['price_cny']}${unscaffoldedNote ? ` ${unscaffoldedNote}` : ''}`)
168
311
  }
169
312
  for (const n of s.skeleton_notes ?? []) lines.push(`- ${n}`)
170
313
  for (const f of s.red_flags ?? []) lines.push(`- ⚠️ ${f}`)
@@ -172,6 +315,9 @@ export function renderSolve(state: TripState): string {
172
315
  lines.push(`**当前约束下不可行——冲突:${(s.unsat_core ?? []).join('、')}`)
173
316
  for (const sg of s.suggestions ?? []) lines.push(`- 放宽「${sg.relax}」可解(约 ¥${sg.money_cny})`)
174
317
  }
318
+ for (const e of s.work_window_exclusions ?? []) {
319
+ lines.push(`- 已排除 ${e.option}(工作窗口):${e.reason}`)
320
+ }
175
321
  if (state.gates.length) {
176
322
  lines.push('', '**待你决定(选择题)**')
177
323
  for (const g of state.gates) {
@@ -188,7 +334,9 @@ export async function runTurn(
188
334
  llm: LlmPort,
189
335
  history: Turn[] = [],
190
336
  solve: SolvePort | null = null,
337
+ now: Date = new Date(),
191
338
  ): Promise<{ reply: string; state: TripState }> {
339
+ const anchor = buildTimeAnchor(now)
192
340
  const facts = await llm.extractFacts([...history, { role: 'user', text: userMsg }], state)
193
341
 
194
342
  const conflicts: string[] = []
@@ -236,7 +384,10 @@ export async function runTurn(
236
384
  } else if (ar.verdict === 'miss') {
237
385
  parts.push(`**${poiProbe} → miss** (酒店-be Anything 一切正常但无候选)\n${ar.evidence}`)
238
386
  } else {
239
- parts.push(`**${poiProbe} → unavailable** (${ar.error ?? 'hbcli 不可达'})\n${ar.evidence}`)
387
+ // 用户面只给人话原因 + 来源 tag:evidence 里的上游原话是溯源契约(能力层测试锁),
388
+ // 其中的进程噪音(如 `(exit null)`)不该渲染进回复。
389
+ const sourceTag = ar.evidence.match(/^\[[^\]]+\]/)?.[0]
390
+ parts.push(`**${poiProbe} → 实时查询不可用**(${ar.error ?? 'hbcli 不可达'})——这条不阻塞本轮规划,只影响实时数据源,其余结论照常带来源标注${sourceTag ? `\n${sourceTag}` : ''}`)
240
391
  }
241
392
  }
242
393
 
@@ -244,7 +395,22 @@ export async function runTurn(
244
395
 
245
396
  // 约束齐备(无阻塞问题)→ 翻译 spec → **校验闸 + 日期一致性闸** → 求解 → 渲染
246
397
  if (blocking.length === 0 && solve) {
247
- const spec = await llm.extractSpec([...history, { role: 'user', text: userMsg }], state)
398
+ const extractedSpec = await llm.extractSpec([...history, { role: 'user', text: userMsg }], state)
399
+ const planning = latestPlanningWindow(history, userMsg, anchor)
400
+ const planningCheck = extractedSpec ? applyPlanningWindow(extractedSpec, planning, anchor) : null
401
+ if (planningCheck?.error) {
402
+ state.spec = undefined
403
+ state.solve = undefined
404
+ parts.push(`⚠️ ${planningCheck.error}`)
405
+ return { reply: parts.join('\n\n'), state }
406
+ }
407
+ if (planningCheck?.rejected.length) {
408
+ const rejected = planningCheck.rejected
409
+ .map(r => `${r.segmentId}/${r.optionId}(${r.date})`)
410
+ .join('、')
411
+ parts.push(`已排除规划窗口外的候选:${rejected}`)
412
+ }
413
+ const spec = planningCheck?.spec ?? extractedSpec
248
414
  // 场景路由:erhai 候选标记 → 候选求解(洱海金标准的引擎判定)——纯 TS unify 路径
249
415
  if (spec && (spec as unknown as { note?: string }).note === 'erhai-candidates') {
250
416
  const { readFile } = await import('node:fs/promises')
@@ -268,11 +434,10 @@ export async function runTurn(
268
434
  // D-10 切片 C:spec↔槽位日期一致性闸(ADR-10 翻译≠造数的执行点)。
269
435
  // LLM 翻译的 spec 日期与代码层换算的槽位日期分歧时不求解、不猜,追问确认——
270
436
  // 日期是求解关键输入,错日期 = 错判决。槽位缺失/unresolved 不参与(不造假阳性)。
271
- const ext = await llm.extractSlots([...history, { role: 'user', text: userMsg }])
437
+ const ext = await llm.extractSlots([...history, { role: 'user', text: userMsg }], now)
272
438
  if (ext) {
273
439
  const { resolveSlots, specDateMismatches } = await import('./slot-spec.ts')
274
- const { buildTimeAnchor } = await import('./time-anchor.ts')
275
- const mismatches = specDateMismatches(spec, resolveSlots(ext, buildTimeAnchor()))
440
+ const mismatches = specDateMismatches(spec, resolveSlots(ext, anchor))
276
441
  if (mismatches.length > 0) {
277
442
  const lines = mismatches.map(m => `- ${m.specAt}:翻译说 ${m.specDate},你的原话换算是 ${m.slotDate}(${m.field})`)
278
443
  parts.push(`⚠️ 日期分歧,确认后我再算(以你为准):\n${lines.join('\n')}`)
@@ -292,42 +457,112 @@ export async function runTurn(
292
457
  return { reply: parts.join('\n\n'), state }
293
458
  }
294
459
 
295
- // ---- 异步工单持久化(S5 编排半段):真正的「一小时后」必须跨进程存续 ----
296
- // 请求时落盘(ticket+state 快照);任意后续进程(如 loopx 驱动的 tick)执行
297
- // async-collect 加载、求解、写回交付物。状态目录属用户数据(红线 6)。
460
+ // ---- 异步工单持久化(S5 编排半段 → ADR-15 durable 工单):真正的「一小时后」必须跨进程存续 ----
461
+ // 权威 = 账本 workflow_runs/workflow_steps(单事务;崩溃后 done 步骤不重执行);
462
+ // {id}.json / {id}.deliverable.md 降级为视图(AGENTS.md 清扫合同 + 人工检视),
463
+ // tmp+rename 原子写——此前裸 writeFile 的非原子缺口(RFC §1.1)就此关闭。
298
464
 
299
465
  import { join } from 'node:path'
300
- import { mkdir } from 'node:fs/promises'
301
- import { readFile, writeFile } from 'node:fs/promises'
466
+ import { mkdir, rename, writeFile, readFile } from 'node:fs/promises'
467
+ import { ensureLedger, openLedgerIfExists, type StateLedger } from './state-ledger.ts'
302
468
 
303
- const ASYNC_DIR = 'gotry-state/async'
469
+ async function asyncDir(stateRoot: string): Promise<string> {
470
+ const root = stateRoot === '.' ? process.cwd() : stateRoot
471
+ const dir = join(root, 'gotry-state', 'async')
472
+ await mkdir(dir, { recursive: true })
473
+ return dir
474
+ }
304
475
 
305
- async function asyncPath(name: string): Promise<string> {
306
- await mkdir(ASYNC_DIR, { recursive: true })
307
- return join(ASYNC_DIR, name)
476
+ async function atomicWrite(path: string, text: string): Promise<void> {
477
+ const tmp = `${path}.tmp`
478
+ await writeFile(tmp, text, 'utf-8')
479
+ await rename(tmp, path)
308
480
  }
309
481
 
310
- export async function persistAsyncTicket(ticket: AsyncTicket, state: TripState): Promise<string> {
311
- const p = await asyncPath(`${ticket.id}.json`)
312
- await writeFile(p, JSON.stringify({ ticket, state }, null, 2), 'utf-8')
482
+ export async function persistAsyncTicket(ticket: AsyncTicket, state: TripState, stateRoot = '.'): Promise<string> {
483
+ const ledger = ensureLedger(stateRoot)
484
+ ledger.createWorkflowRun({ id: ticket.id, goal: ticket.objective, ticket, state })
485
+ const dir = await asyncDir(stateRoot)
486
+ const p = join(dir, `${ticket.id}.json`)
487
+ await atomicWrite(p, JSON.stringify({ ticket, state }, null, 2))
313
488
  return p
314
489
  }
315
490
 
316
- export async function loadAsyncTicket(ticketId: string): Promise<{ ticket: AsyncTicket; state: TripState } | null> {
491
+ export async function loadAsyncTicket(ticketId: string, stateRoot = '.'): Promise<{ ticket: AsyncTicket; state: TripState } | null> {
492
+ // 账本优先;无账本的 root 回退旧 json 文件(只读,兼容存量已交付工单)
493
+ const ledger = openLedgerIfExists(stateRoot)
494
+ const run = ledger?.getWorkflowRun(ticketId)
495
+ if (run) {
496
+ return {
497
+ ticket: JSON.parse(run.ticket_json) as AsyncTicket,
498
+ state: JSON.parse(run.state_json) as TripState,
499
+ }
500
+ }
317
501
  try {
318
- const p = await asyncPath(`${ticketId}.json`)
502
+ const p = join(await asyncDir(stateRoot), `${ticketId}.json`)
319
503
  return JSON.parse(await readFile(p, 'utf-8')) as { ticket: AsyncTicket; state: TripState }
320
504
  } catch {
321
505
  return null
322
506
  }
323
507
  }
324
508
 
325
- export async function settleAsyncTicket(ticketId: string, reply: string): Promise<string> {
326
- const p = await asyncPath(`${ticketId}.deliverable.md`)
327
- await writeFile(p, reply, 'utf-8')
509
+ export async function settleAsyncTicket(
510
+ ticketId: string,
511
+ reply: string,
512
+ stateRoot = '.',
513
+ outcome?: AsyncTerminalOutcome,
514
+ ): Promise<string> {
515
+ const ledger = openLedgerIfExists(stateRoot)
516
+ const terminalOutcome = outcome ?? recoverAsyncTerminalOutcome(ledger, ticketId)
517
+ if (ledger && !terminalOutcome) {
518
+ throw new Error(`workflow ${ticketId} 缺少可恢复的 ${ASYNC_TERMINAL_SCHEMA},拒绝误结算`)
519
+ }
520
+ if (terminalOutcome?.status === 'failed') ledger?.failWorkflowRun(ticketId, reply, terminalOutcome)
521
+ else ledger?.settleWorkflowRun(ticketId, reply, terminalOutcome)
522
+ const dir = await asyncDir(stateRoot)
523
+ const p = join(dir, `${ticketId}.deliverable.md`)
524
+ await atomicWrite(p, reply)
328
525
  return p
329
526
  }
330
527
 
528
+ function recoverAsyncTerminalOutcome(ledger: StateLedger | null, ticketId: string): AsyncTerminalOutcome | undefined {
529
+ if (!ledger) return undefined
530
+ const run = ledger.getWorkflowRun(ticketId)
531
+ if (!run) return undefined
532
+
533
+ try {
534
+ const state = JSON.parse(run.state_json) as TripState
535
+ if (!state.spec) return terminalOutcomeFromState(ticketId, state)
536
+
537
+ const solveStep = ledger.getWorkflowStep(ticketId, 'solve')
538
+ if (solveStep?.status !== 'done' || !solveStep.result) return undefined
539
+ state.solve = JSON.parse(solveStep.result) as SolveResult
540
+ state.gates = state.gates.filter(g => !g.id.startsWith('async-'))
541
+ return terminalOutcomeFromState(ticketId, state)
542
+ } catch {
543
+ return undefined
544
+ }
545
+ }
546
+
547
+ /**
548
+ * 可日志化 solve 端口(ADR-15 步骤日志,intent-before-execute):
549
+ * 先记 intent 再执行,done 后结果落账本——任意进程恢复时 done 的步骤直接复用
550
+ * 结果不重执行(exactly-once:LLM/求解调用不重复花钱),intent 悬挂的重试。
551
+ */
552
+ export function makeJournaledSolvePort(ledger: StateLedger, runId: string, solve: SolvePort, opts?: { onRealSolve?: () => void }): SolvePort {
553
+ return async spec => {
554
+ const step = ledger.getWorkflowStep(runId, 'solve')
555
+ if (step?.status === 'done' && step.result) {
556
+ return JSON.parse(step.result) as Awaited<ReturnType<SolvePort>>
557
+ }
558
+ ledger.markStepIntent(runId, 'solve')
559
+ opts?.onRealSolve?.()
560
+ const r = await solve(spec)
561
+ ledger.markStepDone(runId, 'solve', r)
562
+ return r
563
+ }
564
+ }
565
+
331
566
  /**
332
567
  * probePoi:从 user msg 探测"查 POI/酒店" 信号,返回关键词。
333
568
  * 触发的不是关键词,是**结构**;关键词抓取有方向性——
@@ -340,6 +575,9 @@ export async function settleAsyncTicket(ticketId: string, reply: string): Promis
340
575
  * 2026-08-28 巡检收紧:旧版 ≤24 直通把访谈答案整句当关键词,垃圾 hbcli 调用+证据噪音)。
341
576
  * 不是意图(改求解器),只是 datasources 编排层的"提早调 anything"提示。
342
577
  */
578
+ /** 住宿名称段上界:路径 2 已要求含拉丁/数字且被标点截断,旧 24 字会把
579
+ * 「The Title East Wing Rawai」这类真实酒店名截残后回显给用户(2026-09-22)。 */
580
+ const POI_NAME_MAX = 48
343
581
  export function probePoi(msg: string): string | null {
344
582
  const trimmed = msg.trim()
345
583
  if (!trimmed) return null
@@ -355,7 +593,7 @@ export function probePoi(msg: string): string | null {
355
593
  if (/(酒店|民宿|客栈|饭店)/.test(trimmed)) {
356
594
  const parts = trimmed.split(/(酒店|民宿|客栈|饭店)/)
357
595
  const after = cutAtPunct((parts[2] ?? '').replace(/^[的::\s]+/, ''))
358
- if (after.length >= 2 && /[A-Za-z0-9]/.test(after)) return after.slice(0, 24)
596
+ if (after.length >= 2 && /[A-Za-z0-9]/.test(after)) return after.length > POI_NAME_MAX ? `${after.slice(0, POI_NAME_MAX - 1)}…` : after
359
597
  const nounAt = trimmed.search(/(酒店|民宿|客栈|饭店)/)
360
598
  const pre = trimmed.slice(Math.max(0, nounAt - 6), nounAt).match(/[一-龥]{2,6}$/)
361
599
  if (pre && !/(机票|车票|和|做|订|的)$/.test(pre[0])) return pre[0]