@danceiny/gotry 0.0.1-rc.9 → 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 (507) hide show
  1. package/README.md +172 -219
  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 -56
  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 +947 -110
  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 +50 -44
  42. package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
  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 +35 -54
  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 +31 -21
  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 +75 -24
  56. package/dist/capabilities/session/static-flight-golden.js +150 -0
  57. package/dist/capabilities/session/transport.js +97 -83
  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 +635 -44
  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 +24 -59
  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 +50 -11
  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 +25 -70
  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 +382 -246
  191. package/dist/scripts/ledger-workflow-crash.js +10 -20
  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 +18 -52
  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 +24 -12
  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 +39 -28
  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 +20 -20
  227. package/dist/scripts/session-attach-poc.js +34 -37
  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 +14 -35
  231. package/dist/scripts/session-link-tests.js +478 -0
  232. package/dist/scripts/session-login.js +27 -46
  233. package/dist/scripts/session-tests.js +796 -96
  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 +690 -246
  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 +226 -101
  253. package/dist/scripts/state-cli.js +550 -199
  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 +29 -93
  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 +48 -74
  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 +2227 -882
  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 +319 -160
  325. package/dist/src/memory-capture.js +180 -32
  326. package/dist/src/memory-decay.js +30 -14
  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 +435 -516
  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 +44 -63
  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 +535 -359
  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 +293 -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 +1948 -198
  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 +223 -25
  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 +354 -88
  506. package/ts/src/wish-pool.ts +79 -0
  507. package/dist/scripts/session-attach-wait.js +0 -41
@@ -0,0 +1,478 @@
1
+ /**
2
+ * Session-link 契约验收套件(issue #580,Phase E):
3
+ * 签名 token 签发/校验(HMAC / 恰一个点 / timingSafeEqual / ttl 上界 /
4
+ * schema 闭集未知键拒绝 / plan_it⇔wish_id 成对 / session_ref 路径护栏 /
5
+ * 写动词词位不存在 / production fail-closed)
6
+ * + format/parse 互逆与 query/hash 拒绝
7
+ * + 校验面运行期边界(失效时钟不得放行;非字符串 secret 键拒绝;
8
+ * production 缺 secret 的 verify 收敛,签发面刻意抛错保留——配置面用
9
+ * 隔离子进程验证)
10
+ * + plan-it 行动卡(wish_id 与 token payload 同源 / label 封闭词汇)
11
+ * + WhyNowCard.wish_id 结构化自指(Phase E 合同增补)。
12
+ *
13
+ * 全离线合成数据,无网络、无 IO、无 stateRoot 写、无 scheme handler 注册
14
+ * (消费端激活是 M4;本套件只验契约)。
15
+ *
16
+ * 运行(在 ts/ 下):npx tsx scripts/session-link-tests.ts
17
+ */
18
+ import { spawnSync } from 'node:child_process';
19
+ import { createHmac } from 'node:crypto';
20
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
21
+ import { createRequire } from 'node:module';
22
+ import { tmpdir } from 'node:os';
23
+ import { dirname, join } from 'node:path';
24
+ import { pathToFileURL } from 'node:url';
25
+ import { DEFAULT_DEV_SHARE_SECRET } from '../src/share/share-token.js';
26
+ import { DEFAULT_DEV_SESSION_LINK_SECRET, DEFAULT_SESSION_LINK_BASE, SESSION_LINK_ACTIONS, SESSION_LINK_TTL_MAX_SECONDS, formatSessionLink, parseSessionLink, resolveSessionLinkSecret, signSessionLink, verifySessionLink, } from '../src/session-link/session-link.js';
27
+ import { PLAN_IT_LABEL, buildPlanItAction } from '../src/session-link/plan-it-action.js';
28
+ import { buildWhyNowCard } from '../src/recall/card.js';
29
+ let pass = 0;
30
+ let fail = 0;
31
+ const failures = [];
32
+ function ok(cond, msg) {
33
+ if (cond) {
34
+ pass++;
35
+ return;
36
+ }
37
+ fail++;
38
+ failures.push(msg);
39
+ }
40
+ function section(name) {
41
+ console.log(`\n== ${name} ==`);
42
+ }
43
+ const NOW = new Date('2026-09-25T08:00:00.000Z');
44
+ const SECRET = 'session-link-test-secret-not-for-prod';
45
+ const REF = 'sess-8f3a2c'; // 不透明引用(无路径分隔符)
46
+ function basePayload(overrides = {}) {
47
+ return {
48
+ link_id: 'link-1',
49
+ session_ref: REF,
50
+ action: 'plan_it',
51
+ wish_id: 'w-dali-erhai',
52
+ created_at: NOW.toISOString(),
53
+ ttl_seconds: 3600,
54
+ ...overrides,
55
+ };
56
+ }
57
+ // ---------------------------------------------------------------- §1 往返
58
+ section('§1 round-trip 与格式契约');
59
+ {
60
+ const token = signSessionLink(basePayload(), SECRET);
61
+ ok(typeof token === 'string' && token.length > 0, '§1a sign 产非空 token');
62
+ ok(token.indexOf('.') > 0 && token.indexOf('.') === token.lastIndexOf('.'), '§1b token 恰一个点');
63
+ const result = verifySessionLink(token, SECRET, () => NOW);
64
+ ok(result.ok === true, '§1c verify 通过');
65
+ if (result.ok) {
66
+ ok(result.payload.link_id === 'link-1'
67
+ && result.payload.session_ref === REF
68
+ && result.payload.action === 'plan_it'
69
+ && result.payload.wish_id === 'w-dali-erhai'
70
+ && result.payload.ttl_seconds === 3600, '§1d payload 逐字段回读一致');
71
+ }
72
+ // open 行动(wish_id 缺省)往返——序列化剥 undefined,verify 补不回 wish_id
73
+ const openToken = signSessionLink(basePayload({ action: 'open', wish_id: undefined }), SECRET);
74
+ const openResult = verifySessionLink(openToken, SECRET, () => NOW);
75
+ ok(openResult.ok === true && openResult.payload.action === 'open' && openResult.payload.wish_id === undefined, '§1e open 行动往返(wish_id 缺省保持缺省)');
76
+ }
77
+ // ---------------------------------------------------------------- §2 篡改/过期
78
+ section('§2 篡改、过期与失败闭集');
79
+ {
80
+ const token = signSessionLink(basePayload(), SECRET);
81
+ const dot = token.indexOf('.');
82
+ ok(verifySessionLink(token, 'wrong-secret', () => NOW).ok === false, '§2a 错 secret → 失败');
83
+ const wrongSig = verifySessionLink(token, 'wrong-secret', () => NOW);
84
+ ok(!wrongSig.ok && wrongSig.reason === 'link_invalid', '§2b 错 secret → 闭集 link_invalid');
85
+ // 改载荷(换 wish_id)但保留原签名 → HMAC 不匹配
86
+ const body = token.slice(0, dot);
87
+ const sig = token.slice(dot + 1);
88
+ const forged = JSON.parse(Buffer.from(body, 'base64url').toString('utf-8'));
89
+ forged.wish_id = 'w-qiandao';
90
+ const forgedBody = Buffer.from(JSON.stringify(forged), 'utf-8').toString('base64url');
91
+ const forgedResult = verifySessionLink(`${forgedBody}.${sig}`, SECRET, () => NOW);
92
+ ok(!forgedResult.ok && forgedResult.reason === 'link_invalid', '§2c 篡改 wish_id 保留原签名 → link_invalid(绑定防重放)');
93
+ // 截断/多点/无点
94
+ ok(!verifySessionLink('', SECRET, () => NOW).ok, '§2d 空 token → 失败');
95
+ ok(!verifySessionLink('no-dot-at-all', SECRET, () => NOW).ok, '§2e 无点 → link_invalid');
96
+ ok(!verifySessionLink(`${body}.`, SECRET, () => NOW).ok, '§2f 尾随空签名 → link_invalid');
97
+ ok(!verifySessionLink(`.${sig}`, SECRET, () => NOW).ok, '§2g 空 body → link_invalid');
98
+ ok(!verifySessionLink(`${body}.${sig}.extra`, SECRET, () => NOW).ok, '§2h 多点 → link_invalid');
99
+ // 过期(now 超过 created_at + ttl)
100
+ const expiring = signSessionLink(basePayload({ ttl_seconds: 60 }), SECRET);
101
+ const before = verifySessionLink(expiring, SECRET, () => new Date(NOW.getTime() + 59_000));
102
+ const after = verifySessionLink(expiring, SECRET, () => new Date(NOW.getTime() + 61_000));
103
+ ok(before.ok === true, '§2i ttl 内 verify 通过');
104
+ ok(!after.ok && after.reason === 'link_expired', '§2j 过期 → 闭集 link_expired');
105
+ // ttl=0:created_at 即过期(> 判定,等值时刻仍有效;下一毫秒失效)
106
+ const zero = signSessionLink(basePayload({ ttl_seconds: 0 }), SECRET);
107
+ ok(verifySessionLink(zero, SECRET, () => NOW).ok === true, '§2k ttl=0 签发时刻本身仍有效');
108
+ ok(!verifySessionLink(zero, SECRET, () => new Date(NOW.getTime() + 1)).ok, '§2l ttl=0 下一毫秒过期');
109
+ // 非字符串 token 永不抛错
110
+ for (const garbage of [null, undefined, 42, {}, ['a.b']]) {
111
+ const r = verifySessionLink(garbage, SECRET, () => NOW);
112
+ ok(!r.ok && r.reason === 'link_invalid', `§2m 非字符串 token(${JSON.stringify(garbage)})→ link_invalid 不抛错`);
113
+ }
114
+ }
115
+ // ---------------------------------------------------------------- §3 构造层护栏
116
+ section('§3 构造层护栏(sign 抛错 = 坏链接不出仓库)');
117
+ {
118
+ // **写动词词位不存在**:WriteGate 在链接层的结构性封死
119
+ for (const verb of ['book', 'pay', 'confirm', 'purchase', 'reserve']) {
120
+ let threw = false;
121
+ try {
122
+ signSessionLink(basePayload({ action: verb }), SECRET);
123
+ }
124
+ catch {
125
+ threw = true;
126
+ }
127
+ ok(threw, `§3a 写动词 action=${verb} → sign 抛错(词位里没有,连构造都不可能)`);
128
+ }
129
+ ok(SESSION_LINK_ACTIONS.length === 2 && SESSION_LINK_ACTIONS.join(',') === 'plan_it,open', '§3b action 闭集恰 plan_it/open 两个词位');
130
+ // plan_it ⇔ wish_id 成对(双向)
131
+ let threw1 = false;
132
+ try {
133
+ signSessionLink(basePayload({ wish_id: undefined }), SECRET);
134
+ }
135
+ catch {
136
+ threw1 = true;
137
+ }
138
+ ok(threw1, '§3c plan_it 缺 wish_id → sign 抛错');
139
+ let threw2 = false;
140
+ try {
141
+ signSessionLink(basePayload({ wish_id: '' }), SECRET);
142
+ }
143
+ catch {
144
+ threw2 = true;
145
+ }
146
+ ok(threw2, '§3d plan_it 空 wish_id → sign 抛错');
147
+ let threw3 = false;
148
+ try {
149
+ signSessionLink(basePayload({ action: 'open', wish_id: 'w-x' }), SECRET);
150
+ }
151
+ catch {
152
+ threw3 = true;
153
+ }
154
+ ok(threw3, '§3e open 夹带 wish_id → sign 抛错(语义混淆拒绝)');
155
+ // session_ref 路径护栏(链接永远无法命名 state 路径;含 NUL——源码里该
156
+ // 守卫字面量曾存真实 NUL 字节,用 \0 转义后此断言钉住行为)
157
+ for (const bad of ['../gotry-state', 'a/b', 'a\\b', 'a..b', 'x/y/z', '/abs', 'a\0b']) {
158
+ let threw = false;
159
+ try {
160
+ signSessionLink(basePayload({ session_ref: bad }), SECRET);
161
+ }
162
+ catch {
163
+ threw = true;
164
+ }
165
+ ok(threw, `§3f session_ref=${JSON.stringify(bad)} → sign 抛错(路径化引用拒绝)`);
166
+ }
167
+ let threwLong = false;
168
+ try {
169
+ signSessionLink(basePayload({ session_ref: 'x'.repeat(129) }), SECRET);
170
+ }
171
+ catch {
172
+ threwLong = true;
173
+ }
174
+ ok(threwLong, '§3g session_ref 超 128 字符 → sign 抛错');
175
+ // ttl 越界
176
+ for (const badTtl of [-1, 1.5, Number.NaN, SESSION_LINK_TTL_MAX_SECONDS + 1, Infinity]) {
177
+ let threw = false;
178
+ try {
179
+ signSessionLink(basePayload({ ttl_seconds: badTtl }), SECRET);
180
+ }
181
+ catch {
182
+ threw = true;
183
+ }
184
+ ok(threw, `§3h ttl=${String(badTtl)} → sign 抛错(非负整数且 ≤2^31-1)`);
185
+ }
186
+ let threwOk = false;
187
+ try {
188
+ signSessionLink(basePayload({ ttl_seconds: SESSION_LINK_TTL_MAX_SECONDS }), SECRET);
189
+ threwOk = true;
190
+ }
191
+ catch { /* noop */ }
192
+ ok(threwOk, `§3i ttl=${SESSION_LINK_TTL_MAX_SECONDS}(上界本身)→ sign 接受`);
193
+ // created_at 不可解析
194
+ let threwDate = false;
195
+ try {
196
+ signSessionLink(basePayload({ created_at: 'not-a-date' }), SECRET);
197
+ }
198
+ catch {
199
+ threwDate = true;
200
+ }
201
+ ok(threwDate, '§3j created_at 不可解析 → sign 抛错');
202
+ // link_id 非空
203
+ let threwId = false;
204
+ try {
205
+ signSessionLink(basePayload({ link_id: '' }), SECRET);
206
+ }
207
+ catch {
208
+ threwId = true;
209
+ }
210
+ ok(threwId, '§3k link_id 空 → sign 抛错');
211
+ }
212
+ // ---------------------------------------------------------------- §4 schema 闭集
213
+ section('§4 schema 闭集(de-identified by construction 的可测试字面真)');
214
+ {
215
+ // 手工构造合法签名但带未知键的 token → verify 拒绝
216
+ const payloadWithExtra = { ...basePayload(), note: 'user-memory-content', score: 99 };
217
+ const body = Buffer.from(JSON.stringify(payloadWithExtra), 'utf-8').toString('base64url');
218
+ const sig = createSig(body);
219
+ const r = verifySessionLink(`${body}.${sig}`, SECRET, () => NOW);
220
+ ok(!r.ok && r.reason === 'link_invalid', '§4a 未知键(note/score)→ verify 拒绝(链接不携带事实/记忆)');
221
+ // wish_id 类型化(数字)→ 拒绝
222
+ const badWish = { ...basePayload(), wish_id: 12345 };
223
+ const badWishBody = Buffer.from(JSON.stringify(badWish), 'utf-8').toString('base64url');
224
+ const r2 = verifySessionLink(`${badWishBody}.${createSig(badWishBody)}`, SECRET, () => NOW);
225
+ ok(!r2.ok && r2.reason === 'link_invalid', '§4b wish_id 非字符串 → verify 拒绝');
226
+ // plan_it 缺 wish_id 的签名 token → verify 拒绝(与 sign 同规则)
227
+ const noWish = { link_id: 'x', session_ref: REF, action: 'plan_it', created_at: NOW.toISOString(), ttl_seconds: 60 };
228
+ const noWishBody = Buffer.from(JSON.stringify(noWish), 'utf-8').toString('base64url');
229
+ const r3 = verifySessionLink(`${noWishBody}.${createSig(noWishBody)}`, SECRET, () => NOW);
230
+ ok(!r3.ok && r3.reason === 'link_invalid', '§4c plan_it 无 wish_id → verify 拒绝(手工绕过 sign 也过不了)');
231
+ // session_ref 路径化(手工签名)→ verify 拒绝
232
+ const badRef = { ...basePayload(), session_ref: '../../etc/passwd' };
233
+ const badRefBody = Buffer.from(JSON.stringify(badRef), 'utf-8').toString('base64url');
234
+ const r4 = verifySessionLink(`${badRefBody}.${createSig(badRefBody)}`, SECRET, () => NOW);
235
+ ok(!r4.ok && r4.reason === 'link_invalid', '§4d session_ref 路径化(手工签名)→ verify 拒绝');
236
+ // open 带 wish_id(手工签名)→ verify 拒绝
237
+ const openWithWish = { link_id: 'x', session_ref: REF, action: 'open', wish_id: 'w-x', created_at: NOW.toISOString(), ttl_seconds: 60 };
238
+ const owwBody = Buffer.from(JSON.stringify(openWithWish), 'utf-8').toString('base64url');
239
+ const r5 = verifySessionLink(`${owwBody}.${createSig(owwBody)}`, SECRET, () => NOW);
240
+ ok(!r5.ok && r5.reason === 'link_invalid', '§4e open 夹带 wish_id(手工签名)→ verify 拒绝');
241
+ // JSON 数组体(合法 HMAC,形态非对象)→ verify 拒绝
242
+ const arrBody = Buffer.from(JSON.stringify(['a']), 'utf-8').toString('base64url');
243
+ const r6 = verifySessionLink(`${arrBody}.${createSig(arrBody)}`, SECRET, () => NOW);
244
+ ok(!r6.ok && r6.reason === 'link_invalid', '§4f JSON 数组体(手工签名)→ verify 拒绝(形态守卫)');
245
+ }
246
+ function createSig(body) {
247
+ return createHmac('sha256', SECRET).update(body).digest('base64url');
248
+ }
249
+ // ---------------------------------------------------------------- §5 secret 纪律
250
+ section('§5 secret 解析与 production fail-closed');
251
+ {
252
+ ok(resolveSessionLinkSecret({ SESSION_LINK_HMAC_SECRET: 'from-env' }) === 'from-env', '§5a env 优先');
253
+ ok(resolveSessionLinkSecret({}) === DEFAULT_DEV_SESSION_LINK_SECRET, '§5b 缺 env 非生产回落 dev 常量');
254
+ let threw = false;
255
+ try {
256
+ resolveSessionLinkSecret({ NODE_ENV: 'production' });
257
+ }
258
+ catch {
259
+ threw = true;
260
+ }
261
+ ok(threw, '§5c production 缺 env → fail-closed 抛错(不用公开常量签生产链接)');
262
+ // dev 默认常量与 share 的常量不同源(独立 secret 面)
263
+ ok(DEFAULT_DEV_SESSION_LINK_SECRET !== DEFAULT_DEV_SHARE_SECRET, '§5d session-link 与 share 的 dev 常量独立');
264
+ }
265
+ // ---------------------------------------------------------------- §6 format/parse
266
+ section('§6 format/parse 互逆与严格性');
267
+ {
268
+ const token = signSessionLink(basePayload(), SECRET);
269
+ const url = formatSessionLink(token);
270
+ ok(url === `${DEFAULT_SESSION_LINK_BASE}/${token}`, '§6a 默认 base 渲染 gotry://session/<token>');
271
+ ok(parseSessionLink(url) === token, '§6b parse(format(t)) === t(互逆)');
272
+ const custom = formatSessionLink(token, 'http://127.0.0.1:3080/session');
273
+ ok(custom === `http://127.0.0.1:3080/session/${token}`, '§6c 自定义 base(本地 http 消费端)渲染');
274
+ ok(parseSessionLink(custom) === token, '§6d 自定义 base 反解互逆');
275
+ ok(parseSessionLink(`${url}?utm=x`) === null, '§6e 带 query → null(拼接歧义拒绝)');
276
+ ok(parseSessionLink(`${url}#frag`) === null, '§6f 带 hash → null');
277
+ ok(parseSessionLink('gotry://session') === null, '§6g 无 token 段 → null');
278
+ ok(parseSessionLink('') === null, '§6h 空串 → null');
279
+ ok(parseSessionLink('justtoken') === null, '§6i 单段无 base → null(形态不符)');
280
+ let threw = false;
281
+ try {
282
+ formatSessionLink('', DEFAULT_SESSION_LINK_BASE);
283
+ }
284
+ catch {
285
+ threw = true;
286
+ }
287
+ ok(threw, '§6j format 空 token 抛错');
288
+ let threw2 = false;
289
+ try {
290
+ formatSessionLink(token, 'gotry://session/');
291
+ }
292
+ catch {
293
+ threw2 = true;
294
+ }
295
+ ok(threw2, '§6k base 以 / 结尾抛错(防双斜杠漂移)');
296
+ let threw3 = false;
297
+ try {
298
+ formatSessionLink(token, 'gotry://session?x=1');
299
+ }
300
+ catch {
301
+ threw3 = true;
302
+ }
303
+ ok(threw3, '§6l base 含 query 抛错(产出即 parse 拒绝的形态)');
304
+ let threw4 = false;
305
+ try {
306
+ formatSessionLink(token, 'gotry://session#f');
307
+ }
308
+ catch {
309
+ threw4 = true;
310
+ }
311
+ ok(threw4, '§6m base 含 hash 抛错');
312
+ }
313
+ // ---------------------------------------------------------------- §7 plan-it 行动卡
314
+ section('§7 plan-it 行动卡(wish_id 同源构造)');
315
+ function makeCard(wishId) {
316
+ return buildWhyNowCard({
317
+ wish_id: wishId,
318
+ wish_name: '大理 · 洱海恢复之旅',
319
+ signal: { reason: 'price_drop', source: 'fare-probe', current_value: '¥1,880(降 12%)', threshold: '≤ ¥2,000' },
320
+ match_hits: [], // 手工构造的 fixture:不附条件命中证据(buildWhyNowCard 不发明证据)
321
+ evaluated_at: NOW.toISOString(),
322
+ });
323
+ }
324
+ {
325
+ const card = makeCard('w-dali-erhai');
326
+ ok(card.wish_id === 'w-dali-erhai', '§7a WhyNowCard.wish_id 结构化自指(Phase E 合同增补)');
327
+ const action = buildPlanItAction(card, {
328
+ session_ref: REF,
329
+ link_id: 'link-action-1',
330
+ secret: SECRET,
331
+ now: () => NOW,
332
+ ttl_seconds: 3600,
333
+ });
334
+ ok(action.kind === 'plan_it', '§7b kind 恒 plan_it');
335
+ ok(action.label === PLAN_IT_LABEL && action.label === '好,规划它', '§7c label 封闭词汇');
336
+ ok(action.wish_id === 'w-dali-erhai', '§7d action.wish_id 取自卡的结构化字段');
337
+ // 同源构造:deep_link 里解出的 token,payload.wish_id 与 action.wish_id 是同一个值
338
+ const token = parseSessionLink(action.deep_link);
339
+ ok(token !== null, '§7e deep_link 可反解出 token');
340
+ if (token !== null) {
341
+ const r = verifySessionLink(token, SECRET, () => NOW);
342
+ ok(r.ok === true, '§7f deep_link token verify 通过');
343
+ if (r.ok) {
344
+ ok(r.payload.wish_id === action.wish_id, '§7g token payload.wish_id 与 action.wish_id 同源相等(构造级,不可能漂移)');
345
+ ok(r.payload.action === 'plan_it', '§7h token action === plan_it');
346
+ ok(r.payload.session_ref === REF, '§7i token session_ref 绑定正确');
347
+ ok(r.payload.link_id === 'link-action-1', '§7j link_id 注入生效');
348
+ }
349
+ }
350
+ // 无 wish_id 的卡(手工构造,绕过 buildWhyNowCard)→ 构造层抛错
351
+ const brokenCard = { ...card, wish_id: '' };
352
+ let threw = false;
353
+ try {
354
+ buildPlanItAction(brokenCard, { session_ref: REF, secret: SECRET, now: () => NOW });
355
+ }
356
+ catch {
357
+ threw = true;
358
+ }
359
+ ok(threw, '§7k 空 wish_id 卡 → buildPlanItAction 抛错(fail-closed 不产空目标链接)');
360
+ // 路径化 session_ref → 构造层抛错(即使 base 合法)
361
+ let threw2 = false;
362
+ try {
363
+ buildPlanItAction(card, { session_ref: '../state/x', secret: SECRET, now: () => NOW });
364
+ }
365
+ catch {
366
+ threw2 = true;
367
+ }
368
+ ok(threw2, '§7l 路径化 session_ref → buildPlanItAction 抛错');
369
+ // 默认 deps:link_id 自动生成,ttl 默认 24h
370
+ const auto = buildPlanItAction(card, { session_ref: REF, secret: SECRET, now: () => NOW });
371
+ const autoToken = parseSessionLink(auto.deep_link);
372
+ ok(autoToken !== null, '§7m 默认 deps 产出 deep_link');
373
+ if (autoToken !== null) {
374
+ const r = verifySessionLink(autoToken, SECRET, () => NOW);
375
+ ok(r.ok === true && r.payload.link_id.length > 0 && r.payload.ttl_seconds === 86_400, '§7n 默认 link_id 非空 + ttl 24h');
376
+ }
377
+ }
378
+ // ---------------------------------------------------------------- §8 校验面运行期边界
379
+ section('§8 校验面运行期边界(失效时钟不得放行;production 缺 secret 的 verify 收敛)');
380
+ const TS_ROOT = join(import.meta.dirname, '..');
381
+ /** tsx CLI 文件(直接以 process.execPath 启动,不经 npx/shell 孙进程)。 */
382
+ function resolveTsxCli() {
383
+ const req = createRequire(join(TS_ROOT, 'package.json'));
384
+ const pkgPath = req.resolve('tsx/package.json');
385
+ const binField = JSON.parse(readFileSync(pkgPath, 'utf-8')).bin;
386
+ const bin = typeof binField === 'string' ? binField : binField?.tsx;
387
+ if (!bin)
388
+ throw new Error('tsx package.json 必须有 bin 入口');
389
+ return join(dirname(pkgPath), bin);
390
+ }
391
+ /** production 缺 secret 的配置面必须放隔离子进程验证:env 是进程级全局,
392
+ * 进程内改写会污染本套件其余默认 secret 断言(§5a/§5b 依赖真实 process.env)。 */
393
+ function runProdEnvChild(fixtureBody, token) {
394
+ const home = mkdtempSync(join(tmpdir(), 'gotry-session-link-prod-env-'));
395
+ const fixture = join(home, 'prod-env-probe.mts');
396
+ writeFileSync(fixture, fixtureBody, 'utf8');
397
+ const env = { ...process.env, NODE_ENV: 'production' };
398
+ delete env.SESSION_LINK_HMAC_SECRET;
399
+ const res = spawnSync(process.execPath, [resolveTsxCli(), fixture, token], {
400
+ cwd: TS_ROOT, encoding: 'utf8', timeout: 30_000, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024, env,
401
+ });
402
+ rmSync(home, { recursive: true, force: true });
403
+ return { status: res.status, stdout: res.stdout, stderr: res.stderr };
404
+ }
405
+ {
406
+ const nanClock = () => new Date(Number.NaN);
407
+ // 过期 token + NaN 时钟:`NaN > expiresMs` 恒 false,曾把过期链接静默判成有效(#580 缺口)
408
+ const expired = signSessionLink(basePayload({ ttl_seconds: 1 }), SECRET);
409
+ const r1 = verifySessionLink(expired, SECRET, nanClock);
410
+ ok(!r1.ok && r1.reason === 'link_invalid', '§8a 过期 token + NaN 时钟 → link_invalid(失效时钟不得把过期判成有效)');
411
+ // 未过期 token + NaN 时钟同样拒绝:任何接受都不得建立在无效时间上
412
+ const live = signSessionLink(basePayload(), SECRET);
413
+ const r2 = verifySessionLink(live, SECRET, nanClock);
414
+ ok(!r2.ok && r2.reason === 'link_invalid', '§8b 未过期 token + NaN 时钟 → link_invalid');
415
+ // 时钟回调抛错:异常不外泄,收敛为 link_invalid(「verify 永不抛错」的字面真)
416
+ const throwingClock = () => { throw new Error('clock unavailable'); };
417
+ let threw = false;
418
+ let r3;
419
+ try {
420
+ r3 = verifySessionLink(live, SECRET, throwingClock);
421
+ }
422
+ catch {
423
+ threw = true;
424
+ }
425
+ ok(!threw && r3 !== undefined && !r3.ok && r3.reason === 'link_invalid', '§8c 时钟回调抛错 → 不外泄,收敛 link_invalid');
426
+ // production 缺 secret(NODE_ENV=production 且无 SESSION_LINK_HMAC_SECRET),全在子进程:
427
+ // token 用公开 dev 常量签——若 verify 静默回落 dev 常量,它将 verify 通过(锋利形式)
428
+ const devSigned = signSessionLink(basePayload({ link_id: 'prod-env-probe', ttl_seconds: 3600 }), DEFAULT_DEV_SESSION_LINK_SECRET);
429
+ const sessionLinkUrl = pathToFileURL(join(TS_ROOT, 'src', 'session-link', 'session-link.ts')).href;
430
+ const child = runProdEnvChild([
431
+ `import { DEFAULT_DEV_SESSION_LINK_SECRET, resolveSessionLinkSecret, signSessionLink, verifySessionLink } from ${JSON.stringify(sessionLinkUrl)}`,
432
+ `const token = process.argv[2] ?? ''`,
433
+ `const now = () => new Date('2026-09-25T08:00:00.000Z')`,
434
+ `const payload = { link_id: 'x', session_ref: 'sess-x', action: 'open', created_at: '2026-09-25T08:00:00.000Z', ttl_seconds: 60 }`,
435
+ `const out = {}`,
436
+ `try { out.defaultSecretVerify = verifySessionLink(token, undefined, now) } catch (e) { out.defaultSecretVerifyThrew = String(e) }`,
437
+ `try { out.explicitSecretVerify = verifySessionLink(token, DEFAULT_DEV_SESSION_LINK_SECRET, now) } catch (e) { out.explicitSecretVerifyThrew = String(e) }`,
438
+ `try { resolveSessionLinkSecret(); out.resolveThrew = false } catch { out.resolveThrew = true }`,
439
+ `try { signSessionLink(payload); out.signThrew = false } catch { out.signThrew = true }`,
440
+ `console.log(JSON.stringify(out))`,
441
+ ].join('\n'), devSigned);
442
+ ok(child.status === 0, `§8d 子进程探针正常退出(status=${child.status};stderr=${child.stderr.slice(0, 300)})`);
443
+ let report = {};
444
+ try {
445
+ report = JSON.parse(child.stdout.trim());
446
+ }
447
+ catch { /* 失败落进下面的断言 */ }
448
+ const v = report.defaultSecretVerify;
449
+ ok(v?.ok === false && v?.reason === 'link_invalid' && report.defaultSecretVerifyThrew === undefined, '§8e production 缺 secret:verify 缺省 secret 解析 → 不抛错,收敛 link_invalid(绝不静默回落公开 dev 常量)');
450
+ const explicit = report.explicitSecretVerify;
451
+ ok(explicit?.ok === true && report.explicitSecretVerifyThrew === undefined, '§8f 显式传入 secret 的 verify 在 production 照常工作(守卫只针对缺省解析,不做全量否决)');
452
+ ok(report.resolveThrew === true, '§8g production 缺 secret:resolveSessionLinkSecret 仍抛错(签发面刻意的配置异常保留)');
453
+ ok(report.signThrew === true, '§8h production 缺 secret:signSessionLink 缺省 secret 仍抛错(签发面 fail-closed)');
454
+ // 非字符串运行期 secret 键(畸形 JS 调用方输入:数字/对象/Symbol)——
455
+ // createHmac 曾在守卫体外对它们抛 TypeError;校验面必须收敛 link_invalid
456
+ const badKeys = [['123(数字)', 123], ['{}(对象)', {}], ['Symbol(bad)', Symbol('bad')]];
457
+ for (const [label, badKey] of badKeys) {
458
+ let keyThrew = false;
459
+ let keyResult;
460
+ try {
461
+ keyResult = verifySessionLink(live, badKey);
462
+ }
463
+ catch {
464
+ keyThrew = true;
465
+ }
466
+ ok(!keyThrew && keyResult !== undefined && !keyResult.ok && keyResult.reason === 'link_invalid', `§8i 非字符串 secret(${label})→ 不抛错,收敛 link_invalid`);
467
+ }
468
+ // 合法字符串键保留:空字符串键签/验往返照常(守卫只拒非字符串,不做全量收紧)
469
+ const emptyKeyToken = signSessionLink(basePayload({ ttl_seconds: 60 }), '');
470
+ ok(verifySessionLink(emptyKeyToken, '', () => NOW).ok === true, '§8j 空字符串 secret(合法字符串键)签/验往返照常');
471
+ }
472
+ console.log(`\nSESSION-LINK TESTS: ${pass} pass, ${fail} fail`);
473
+ if (fail > 0) {
474
+ console.log('\nFAILURES:');
475
+ for (const f of failures)
476
+ console.log(' - ' + f);
477
+ process.exit(1);
478
+ }
@@ -1,48 +1,29 @@
1
- import { openSession } from '../capabilities/session/transport.js';
2
- import { LOGIN_COOKIE_NAMES, SITE_DOMAIN } from '../capabilities/session/adapters/ctrip-flight.js';
3
- const t = await openSession({
4
- headless: false
5
- });
6
- if (!t.ok) {
7
- console.error('打开会话失败:', t.summary);
8
- process.exit(1);
9
- }
10
- try {
11
- await t.page.goto('https://flights.ctrip.com/', {
12
- waitUntil: 'domcontentloaded',
13
- timeout: 30_000
14
- });
15
- const loggedIn = async ()=>{
16
- const cookies = await t.context.cookies([
17
- `https://flights${SITE_DOMAIN.replace(/^\./, '')}/`
18
- ]).catch(()=>[]);
19
- return cookies.some((c)=>LOGIN_COOKIE_NAMES.includes(c.name));
20
- };
21
- if (await loggedIn()) {
22
- console.log('已是登录态(~/.gotry/session-profile 持久保存),无需操作。');
23
- } else {
24
- console.log('请在打开的窗口中登录携程(手机号+验证码/账密均可)。agent 不会读取或输入任何凭证;登录完成自动检测,最长等 5 分钟...');
25
- let windowClosed = false;
26
- t.page.on('close', ()=>{
27
- windowClosed = true;
28
- });
29
- const deadline = Date.now() + 300_000;
30
- while(Date.now() < deadline && !windowClosed && !await loggedIn()){
31
- await new Promise((r)=>setTimeout(r, 3_000));
32
- }
33
- if (windowClosed) {
34
- console.log('窗口被提前关闭——登录态未建立。重跑本脚本,等打印「登录态已建立」后再关窗。');
35
- process.exitCode = 1;
36
- } else if (await loggedIn()) {
37
- console.log('登录态已建立并持久化(profile 落在 ~/.gotry/session-profile;后续会话检索直接复用)。');
38
- } else {
39
- console.log('超时未检测到登录票据(cookie 名单待校准,或未完成登录)——重跑本脚本即可。');
40
- process.exitCode = 1;
41
- }
1
+ /**
2
+ * 会话登录 CLD 薄壳(scripts/session-login.ts):产品形态是对话内工具
3
+ * `gotry_session_login`(web 会话里 agent 直调,用户在弹出的标签页正常登录,
4
+ * 全程无需终端);本脚本仅保留给脚本派/CI 探活:`npx tsx scripts/session-login.ts
5
+ * [--site ctrip-flight] [--wait=90]`。
6
+ *
7
+ * 语义红线:登录发生在外部网站(携程官网)+ 用户自己的浏览器;gotry 永不
8
+ * 收集/存储/回传任何密码、验证码或 cookie 值——只读票据 cookie 的名字,
9
+ * 判断"是否已登录"这一个布尔(名称级存在性检查)。
10
+ */
11
+ import { sessionLogin } from '../capabilities/session-login.js';
12
+ async function main() {
13
+ const site = process.argv.find((a) => a === 'ctrip-flight' || a === 'meituan-hotel') ?? 'ctrip-flight';
14
+ const waitArg = process.argv.find((a) => a.startsWith('--timeout') || a.startsWith('--wait'));
15
+ const waitMs = Number(waitArg?.split('=')[1] ?? 180) * 1000;
16
+ const r = await sessionLogin({ site, waitMs });
17
+ if (r.verdict === 'logged-in') {
18
+ console.log(`[login] OK——${r.site} 登录票据已检出 [${(r.tickets ?? []).join(', ')}](只读名字)。`);
19
+ console.log('[login] 说明:登录是在携程官网、用你自己的浏览器完成的;gotry 永不经手密码/验证码/cookie 值');
20
+ return;
21
+ }
22
+ if (r.verdict === 'pending') {
23
+ console.log(`[login] 登录入口已在你的 Chrome 打开(${site});在标签页里完成登录即可,无需再跑本脚本——工具面按 cookie 名自动感知`);
24
+ return;
42
25
  }
43
- } finally{
44
- await t.close();
26
+ console.error(`[login] ${r.verdict}:${r.error ?? ''}\n${r.evidence}`);
27
+ process.exit(2);
45
28
  }
46
-
47
-
48
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/session-login.ts
29
+ main().catch((e) => { console.error(e); process.exit(1); });