@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.28

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 (533) 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 +1788 -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 +161 -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 +553 -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 +676 -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/provider-detail-sanitize.js +64 -0
  41. package/dist/capabilities/resilience.js +191 -0
  42. package/dist/capabilities/route-provider-conformance.js +341 -0
  43. package/dist/capabilities/session/action-cache.js +50 -44
  44. package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
  45. package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
  46. package/dist/capabilities/session/adapters/dida-portal.js +274 -0
  47. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  48. package/dist/capabilities/session/adapters/rail-12306.js +342 -0
  49. package/dist/capabilities/session/benchmark.js +189 -0
  50. package/dist/capabilities/session/bridge-ledger.js +224 -0
  51. package/dist/capabilities/session/extension-bridge.js +636 -0
  52. package/dist/capabilities/session/extension-channel.js +109 -0
  53. package/dist/capabilities/session/extension-distribution.js +230 -0
  54. package/dist/capabilities/session/extract.js +31 -21
  55. package/dist/capabilities/session/golden-score.js +114 -0
  56. package/dist/capabilities/session/health-watch.js +215 -0
  57. package/dist/capabilities/session/read-guard.js +75 -24
  58. package/dist/capabilities/session/static-flight-golden.js +150 -0
  59. package/dist/capabilities/session/transport.js +97 -83
  60. package/dist/capabilities/session/wizard.js +117 -0
  61. package/dist/capabilities/session-consent.js +178 -0
  62. package/dist/capabilities/session-login.js +170 -0
  63. package/dist/capabilities/session-search.js +635 -44
  64. package/dist/capabilities/spawn-bounded.js +203 -0
  65. package/dist/capabilities/sponsor-plugin.js +341 -0
  66. package/dist/capabilities/visa-policy.js +140 -0
  67. package/dist/capabilities/weather.js +243 -138
  68. package/dist/data/session-golden-20.json +25 -0
  69. package/dist/data/sf-golden-manifest.json +103 -0
  70. package/dist/data/sf-static-routes.json +91 -0
  71. package/dist/scripts/action-cache-tests.js +24 -59
  72. package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
  73. package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
  74. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  75. package/dist/scripts/agent-reach-error-tests.js +354 -0
  76. package/dist/scripts/agent-reach-tests.js +26 -18
  77. package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
  78. package/dist/scripts/anything-path-measure-tests.js +133 -0
  79. package/dist/scripts/anything-path-measure.js +150 -0
  80. package/dist/scripts/anything-tests.js +250 -60
  81. package/dist/scripts/artifact-client-contract-tests.js +220 -0
  82. package/dist/scripts/artifacts-capability-tests.js +727 -0
  83. package/dist/scripts/async-collect.js +50 -11
  84. package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
  85. package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
  86. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
  87. package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
  88. package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
  89. package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
  90. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
  91. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
  92. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
  93. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
  94. package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
  95. package/dist/scripts/booking-copilot-dsh-warmer-proof.js +126 -0
  96. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
  97. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
  98. package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
  99. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
  100. package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
  101. package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
  102. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
  103. package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
  104. package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
  105. package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
  106. package/dist/scripts/booking-executor-tests.js +80 -0
  107. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  108. package/dist/scripts/booking-saga-tests.js +125 -0
  109. package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
  110. package/dist/scripts/bootstrap-tests.js +1549 -0
  111. package/dist/scripts/bridge-events-tests.js +239 -0
  112. package/dist/scripts/bridge-ledger-tests.js +428 -0
  113. package/dist/scripts/build-changelog.js +238 -0
  114. package/dist/scripts/build-metrics-report.js +331 -0
  115. package/dist/scripts/changelog-tests.js +118 -0
  116. package/dist/scripts/channel-probe-tests.js +103 -0
  117. package/dist/scripts/channel-probe.js +169 -0
  118. package/dist/scripts/channel-registry-tests.js +208 -0
  119. package/dist/scripts/city-scenario-tier-tests.js +234 -0
  120. package/dist/scripts/companion-tests.js +25 -70
  121. package/dist/scripts/dida-cookie-probe.js +87 -0
  122. package/dist/scripts/dida-runner-stop-tests.js +311 -0
  123. package/dist/scripts/dida-sw-probe.js +114 -0
  124. package/dist/scripts/diff-test.js +17 -12
  125. package/dist/scripts/doctor-tests.js +600 -0
  126. package/dist/scripts/drill-multiuser-ledger-tests.js +1076 -0
  127. package/dist/scripts/drill-sdk-descendant-cleanup-tests.js +606 -0
  128. package/dist/scripts/drill-w2a-sensor-tests.js +788 -0
  129. package/dist/scripts/dsh-artifact-e2e.js +279 -0
  130. package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
  131. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  132. package/dist/scripts/dsh-llm-tests.js +437 -0
  133. package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
  134. package/dist/scripts/dsh-runtime-closure.js +162 -0
  135. package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
  136. package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
  137. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  138. package/dist/scripts/effect-tests.js +539 -0
  139. package/dist/scripts/engine-run.js +1 -3
  140. package/dist/scripts/engine-tests.js +16 -10
  141. package/dist/scripts/evaluation-cadence-tests.js +174 -0
  142. package/dist/scripts/evaluation-contract-tests.js +268 -0
  143. package/dist/scripts/extension-distribution-cli.js +39 -0
  144. package/dist/scripts/extension-distribution-tests.js +443 -0
  145. package/dist/scripts/extension-tests.js +988 -0
  146. package/dist/scripts/external-event-tests.js +350 -0
  147. package/dist/scripts/fact-gate-tests.js +869 -0
  148. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  149. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  150. package/dist/scripts/flight-malformed-tests.js +159 -0
  151. package/dist/scripts/flyai-setup-tests.js +228 -0
  152. package/dist/scripts/flyai-setup-tool-tests.js +54 -0
  153. package/dist/scripts/flyai-tests.js +692 -0
  154. package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
  155. package/dist/scripts/fx-contract-tests.js +201 -0
  156. package/dist/scripts/g5-guard-tests.js +150 -0
  157. package/dist/scripts/g5-guard.js +193 -0
  158. package/dist/scripts/gen-lunar-anchors.js +106 -0
  159. package/dist/scripts/geo-atlas-tests.js +283 -0
  160. package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
  161. package/dist/scripts/gotry-try-demo-tests.js +79 -0
  162. package/dist/scripts/gotry-try-demo.js +49 -0
  163. package/dist/scripts/ground-transfer-tests.js +956 -0
  164. package/dist/scripts/hbcli-e2e-tests.js +159 -0
  165. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  166. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  167. package/dist/scripts/hbcli-tests.js +121 -69
  168. package/dist/scripts/health-watch-cli.js +73 -0
  169. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  170. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  171. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  172. package/dist/scripts/i18n-tests.js +41 -0
  173. package/dist/scripts/incident-tests.js +281 -87
  174. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  175. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  176. package/dist/scripts/issue-271-liveness-tests.js +569 -0
  177. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  178. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  179. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  180. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  181. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  182. package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
  183. package/dist/scripts/itinerary-artifact-tests.js +368 -0
  184. package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
  185. package/dist/scripts/itinerary-deck-export-tests.js +552 -0
  186. package/dist/scripts/itinerary-deck-tests.js +435 -0
  187. package/dist/scripts/itinerary-html-tests.js +426 -0
  188. package/dist/scripts/journey-tests.js +22 -27
  189. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  190. package/dist/scripts/kernel-manifest-gate.js +126 -0
  191. package/dist/scripts/kernel-manifest-tests.js +212 -0
  192. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  193. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  194. package/dist/scripts/kernel-manifest.js +369 -0
  195. package/dist/scripts/lavish-local-tests.js +1226 -0
  196. package/dist/scripts/lavish-product-tools-tests.js +537 -0
  197. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  198. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  199. package/dist/scripts/ledger-tests.js +382 -246
  200. package/dist/scripts/ledger-workflow-crash.js +10 -20
  201. package/dist/scripts/m3-cohort-tests.js +714 -0
  202. package/dist/scripts/m3-cohort.js +310 -0
  203. package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
  204. package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
  205. package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
  206. package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
  207. package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
  208. package/dist/scripts/memory-capture-tests.js +242 -61
  209. package/dist/scripts/memory-decay-tests.js +18 -52
  210. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  211. package/dist/scripts/memory-lifecycle.js +196 -0
  212. package/dist/scripts/memory-metrics.js +24 -12
  213. package/dist/scripts/memory-value-report.js +631 -0
  214. package/dist/scripts/memory-value-tests.js +328 -0
  215. package/dist/scripts/metrics-report-tests.js +151 -0
  216. package/dist/scripts/model-override-e2e.js +137 -0
  217. package/dist/scripts/nightly-evidence-tests.js +203 -0
  218. package/dist/scripts/nightly-evidence.js +303 -0
  219. package/dist/scripts/nudge-digest.js +39 -28
  220. package/dist/scripts/observability-tests.js +381 -0
  221. package/dist/scripts/onboarding-tests.js +161 -0
  222. package/dist/scripts/opensky-check.js +18 -13
  223. package/dist/scripts/opensky-tests.js +28 -28
  224. package/dist/scripts/outcome-projection-tests.js +427 -0
  225. package/dist/scripts/persona-sim-tests.js +603 -0
  226. package/dist/scripts/persona-sim.js +1081 -0
  227. package/dist/scripts/persona-surface-guard-tests.js +49 -0
  228. package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
  229. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  230. package/dist/scripts/price-drift-tests.js +340 -0
  231. package/dist/scripts/price-drift-watch.js +447 -0
  232. package/dist/scripts/probe-poi-tests.js +53 -45
  233. package/dist/scripts/product-metrics.js +483 -0
  234. package/dist/scripts/publish-preverify.js +110 -0
  235. package/dist/scripts/realtime-pricing-tests.js +76 -0
  236. package/dist/scripts/recall-tests.js +513 -0
  237. package/dist/scripts/replay-async.js +22 -18
  238. package/dist/scripts/replay-real.js +18 -18
  239. package/dist/scripts/replay.js +54 -82
  240. package/dist/scripts/route-provider-conformance-tests.js +391 -0
  241. package/dist/scripts/session-attach-diagnose.js +20 -20
  242. package/dist/scripts/session-attach-poc.js +34 -37
  243. package/dist/scripts/session-benchmark.js +180 -0
  244. package/dist/scripts/session-dida-live-e2e.js +199 -0
  245. package/dist/scripts/session-extract-tests.js +14 -35
  246. package/dist/scripts/session-link-tests.js +478 -0
  247. package/dist/scripts/session-login.js +27 -46
  248. package/dist/scripts/session-tests.js +796 -96
  249. package/dist/scripts/session-zone-crash.js +49 -0
  250. package/dist/scripts/session-zone-ledger-tests.js +432 -0
  251. package/dist/scripts/session-zone-observation-tests.js +492 -0
  252. package/dist/scripts/session-zone-observe.js +135 -0
  253. package/dist/scripts/session-zone-wiring-tests.js +566 -0
  254. package/dist/scripts/session-zones-tests.js +374 -0
  255. package/dist/scripts/sf-live-benchmark.js +367 -0
  256. package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
  257. package/dist/scripts/sf-live-cli-tests.js +92 -0
  258. package/dist/scripts/sf-live-options.js +46 -0
  259. package/dist/scripts/sf-manifest-tests.js +30 -0
  260. package/dist/scripts/sf-manifest.js +33 -0
  261. package/dist/scripts/sf-soft-score-tests.js +67 -0
  262. package/dist/scripts/sf-summary-tests.js +324 -0
  263. package/dist/scripts/sf-summary.js +525 -0
  264. package/dist/scripts/share-tests.js +432 -0
  265. package/dist/scripts/skeleton-check.js +16 -14
  266. package/dist/scripts/skeleton-integration-test.js +28 -11
  267. package/dist/scripts/skills-contract-tests.js +51 -46
  268. package/dist/scripts/smoke-session-gate-tests.js +21 -0
  269. package/dist/scripts/smoke.js +690 -246
  270. package/dist/scripts/spawn-bounded-abort-tests.js +244 -0
  271. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  272. package/dist/scripts/state-cli-tests.js +226 -101
  273. package/dist/scripts/state-cli.js +565 -198
  274. package/dist/scripts/state-repair-cli.js +66 -0
  275. package/dist/scripts/state-repair-tests.js +140 -0
  276. package/dist/scripts/static-golden-tests.js +165 -0
  277. package/dist/scripts/time-eval-tests.js +212 -192
  278. package/dist/scripts/travel-timeline-tests.js +29 -93
  279. package/dist/scripts/turn-handoff-collect-tests.js +169 -0
  280. package/dist/scripts/turn-handoff-collect.js +161 -0
  281. package/dist/scripts/turn-policy-tests.js +55 -0
  282. package/dist/scripts/typed-contract-canary.js +176 -0
  283. package/dist/scripts/tz-resolver-sanity.js +50 -0
  284. package/dist/scripts/tz-resolver-tests.js +584 -0
  285. package/dist/scripts/unified-tests.js +60 -20
  286. package/dist/scripts/visa-policy-tests.js +62 -0
  287. package/dist/scripts/weather-tests.js +276 -41
  288. package/dist/scripts/wish-channel-gate-tests.js +54 -0
  289. package/dist/scripts/write-gate-tests.js +522 -0
  290. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  291. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  292. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  293. package/dist/scripts/z3-race-tests.js +46 -0
  294. package/dist/src/artifact-gate.js +829 -0
  295. package/dist/src/backend/kernel.js +91 -0
  296. package/dist/src/backend/modules/booking-copilot.js +92 -0
  297. package/dist/src/backend/modules/booking-executor.js +124 -0
  298. package/dist/src/backend/modules/session-search.js +551 -0
  299. package/dist/src/benchmark-agent-conformance.js +578 -0
  300. package/dist/src/benchmark-environment-bridge.js +515 -0
  301. package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
  302. package/dist/src/benchmark-tool-isolation.js +151 -0
  303. package/dist/src/bookable-facts.js +533 -0
  304. package/dist/src/booking-saga.js +97 -0
  305. package/dist/src/booking-surface/availability-policy.js +697 -0
  306. package/dist/src/booking-surface/booking-intent.js +43 -0
  307. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  308. package/dist/src/booking-surface/canonical-schema.js +116 -0
  309. package/dist/src/booking-surface/contracts.js +75 -0
  310. package/dist/src/booking-surface/dsh-planner.js +1854 -0
  311. package/dist/src/booking-surface/dsh-plugin.js +378 -0
  312. package/dist/src/booking-surface/dsh-worker.js +98 -0
  313. package/dist/src/booking-surface/error-codes.js +231 -0
  314. package/dist/src/booking-surface/index.js +9 -0
  315. package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
  316. package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
  317. package/dist/src/booking-surface/profile.js +24 -0
  318. package/dist/src/booking-surface/recovery-chain.js +367 -0
  319. package/dist/src/booking-surface/runtime.js +1968 -0
  320. package/dist/src/booking-surface/server.js +674 -0
  321. package/dist/src/booking-surface/startup.js +168 -0
  322. package/dist/src/booking-surface/validation.js +261 -0
  323. package/dist/src/bridge.js +20 -13
  324. package/dist/src/city-scenario-tier.js +378 -0
  325. package/dist/src/companions.js +48 -74
  326. package/dist/src/contracts.js +21 -43
  327. package/dist/src/dsh-llm.js +171 -115
  328. package/dist/src/engine.js +276 -193
  329. package/dist/src/evaluation-cadence.js +97 -0
  330. package/dist/src/evaluation-contracts.js +283 -0
  331. package/dist/src/flight-pack-adapter.js +54 -0
  332. package/dist/src/flight-pack-contract.js +2 -0
  333. package/dist/src/flyai-setup-tool.js +70 -0
  334. package/dist/src/gotry-backend.js +125 -0
  335. package/dist/src/hotel-date-gate.js +118 -0
  336. package/dist/src/i18n.js +88 -0
  337. package/dist/src/index.js +2385 -881
  338. package/dist/src/itinerary-deck.js +139 -0
  339. package/dist/src/itinerary-doc-shared.js +661 -0
  340. package/dist/src/itinerary-html.js +131 -0
  341. package/dist/src/journey.js +155 -108
  342. package/dist/src/lavish-tools.js +400 -0
  343. package/dist/src/ledger-repair-apply.js +538 -0
  344. package/dist/src/ledger-repair-plan.js +463 -0
  345. package/dist/src/loop.js +331 -161
  346. package/dist/src/m3-cohort.js +1577 -0
  347. package/dist/src/memory-capture.js +180 -32
  348. package/dist/src/memory-decay.js +30 -14
  349. package/dist/src/memory-lifecycle.js +1310 -0
  350. package/dist/src/memory-utility.js +27 -40
  351. package/dist/src/mock-llm.js +36 -61
  352. package/dist/src/model.js +81 -36
  353. package/dist/src/outcome-projection.js +746 -0
  354. package/dist/src/realtime-pricing.js +103 -0
  355. package/dist/src/recall/card.js +74 -0
  356. package/dist/src/recall/evaluator.js +140 -0
  357. package/dist/src/recall/integration.js +17 -0
  358. package/dist/src/recall/tick.js +166 -0
  359. package/dist/src/session-link/plan-it-action.js +48 -0
  360. package/dist/src/session-link/session-link.js +248 -0
  361. package/dist/src/session-zone-ledger.js +312 -0
  362. package/dist/src/session-zone-observation.js +354 -0
  363. package/dist/src/session-zone-wiring.js +658 -0
  364. package/dist/src/session-zones.js +510 -0
  365. package/dist/src/share/adapters.js +51 -0
  366. package/dist/src/share/share-consent.js +76 -0
  367. package/dist/src/share/share-deck.js +58 -0
  368. package/dist/src/share/share-token.js +146 -0
  369. package/dist/src/slot-spec.js +84 -106
  370. package/dist/src/state-ledger.js +696 -524
  371. package/dist/src/state-repair.js +299 -0
  372. package/dist/src/subagent-job-id-guard.js +58 -0
  373. package/dist/src/time-anchor.js +231 -33
  374. package/dist/src/tool-packet.js +20 -8
  375. package/dist/src/travel-slots.js +58 -35
  376. package/dist/src/travel-timeline.js +44 -63
  377. package/dist/src/turn-deadline.js +287 -0
  378. package/dist/src/turn-policy.js +118 -0
  379. package/dist/src/tz-resolver.js +143 -0
  380. package/dist/src/unified.js +600 -360
  381. package/dist/src/wish-pool.js +42 -12
  382. package/dist/src/write-gate.js +495 -0
  383. package/dist/src/z3-shared.js +284 -0
  384. package/extension/README.md +21 -0
  385. package/extension/README.zh-CN.md +21 -0
  386. package/extension/background.js +463 -0
  387. package/extension/content-bridge.js +270 -0
  388. package/extension/content-main.js +111 -0
  389. package/extension/manifest.json +60 -0
  390. package/package.json +407 -14
  391. package/schemas/booking.intent.schema.json +193 -0
  392. package/schemas/booking.surface.schema.json +2622 -0
  393. package/ts/capabilities/agent-reach-deep.ts +114 -0
  394. package/ts/capabilities/agent-reach.ts +198 -0
  395. package/ts/capabilities/anything.ts +282 -0
  396. package/ts/capabilities/artifacts.ts +428 -0
  397. package/ts/capabilities/fact-log.ts +54 -0
  398. package/ts/capabilities/flyai-config.ts +388 -0
  399. package/ts/capabilities/flyai.ts +1274 -0
  400. package/ts/capabilities/hbcli.ts +253 -71
  401. package/ts/capabilities/incident-log.ts +88 -59
  402. package/ts/capabilities/itinerary-artifact.ts +214 -0
  403. package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
  404. package/ts/capabilities/itinerary-deck-export.ts +378 -0
  405. package/ts/capabilities/opensky.ts +121 -0
  406. package/ts/capabilities/session/action-cache.ts +148 -0
  407. package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
  408. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  409. package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
  410. package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
  411. package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
  412. package/ts/capabilities/session/benchmark.ts +276 -0
  413. package/ts/capabilities/session/bridge-ledger.ts +333 -0
  414. package/ts/capabilities/session/extension-bridge.ts +794 -0
  415. package/ts/capabilities/session/extension-channel.ts +144 -0
  416. package/ts/capabilities/session/extension-distribution.ts +266 -0
  417. package/ts/capabilities/session/extract.ts +54 -0
  418. package/ts/capabilities/session/golden-score.ts +139 -0
  419. package/ts/capabilities/session/health-watch.ts +264 -0
  420. package/ts/capabilities/session/read-guard.ts +141 -0
  421. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  422. package/ts/capabilities/session/transport.ts +146 -0
  423. package/ts/capabilities/session/wizard.ts +140 -0
  424. package/ts/capabilities/session-consent.ts +211 -0
  425. package/ts/capabilities/session-login.ts +208 -0
  426. package/ts/capabilities/session-search.ts +893 -0
  427. package/ts/capabilities/weather.ts +330 -0
  428. package/ts/dsh-runtime/vendor/README.md +68 -0
  429. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  430. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
  431. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
  432. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
  433. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  434. package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
  435. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
  436. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  437. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
  438. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  439. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  440. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
  441. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  442. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
  443. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
  444. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
  445. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
  446. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  447. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  448. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
  449. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
  450. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  451. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  452. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  453. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  454. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
  455. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  456. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
  457. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  458. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  459. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
  460. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  461. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  462. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
  463. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
  464. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  465. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
  466. package/ts/package.json +293 -3
  467. package/ts/scripts/async-collect.ts +91 -0
  468. package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
  469. package/ts/scripts/skeleton-check.ts +1 -1
  470. package/ts/scripts/skeleton-integration-test.ts +23 -1
  471. package/ts/scripts/state-cli.ts +586 -0
  472. package/ts/scripts/turn-handoff-collect.ts +178 -0
  473. package/ts/src/artifact-gate.ts +991 -0
  474. package/ts/src/benchmark-agent-conformance.ts +619 -0
  475. package/ts/src/benchmark-environment-bridge.ts +538 -0
  476. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  477. package/ts/src/benchmark-tool-isolation.ts +166 -0
  478. package/ts/src/bookable-facts.ts +837 -0
  479. package/ts/src/booking-saga.ts +133 -0
  480. package/ts/src/booking-surface/availability-policy.ts +522 -0
  481. package/ts/src/booking-surface/booking-intent.ts +86 -0
  482. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  483. package/ts/src/booking-surface/canonical-schema.js +116 -0
  484. package/ts/src/booking-surface/contracts.ts +475 -0
  485. package/ts/src/booking-surface/dsh-planner.ts +1949 -0
  486. package/ts/src/booking-surface/dsh-plugin.js +378 -0
  487. package/ts/src/booking-surface/dsh-worker.ts +110 -0
  488. package/ts/src/booking-surface/error-codes.ts +241 -0
  489. package/ts/src/booking-surface/index.ts +9 -0
  490. package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
  491. package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
  492. package/ts/src/booking-surface/profile.ts +42 -0
  493. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  494. package/ts/src/booking-surface/runtime.ts +1793 -0
  495. package/ts/src/booking-surface/server.ts +733 -0
  496. package/ts/src/booking-surface/startup.ts +201 -0
  497. package/ts/src/booking-surface/validation.ts +217 -0
  498. package/ts/src/companions.ts +113 -0
  499. package/ts/src/contracts.ts +17 -3
  500. package/ts/src/dsh-llm.ts +152 -17
  501. package/ts/src/flyai-setup-tool.ts +77 -0
  502. package/ts/src/index.ts +2123 -200
  503. package/ts/src/itinerary-deck.ts +189 -0
  504. package/ts/src/itinerary-doc-shared.ts +741 -0
  505. package/ts/src/itinerary-html.ts +171 -0
  506. package/ts/src/loop.ts +234 -26
  507. package/ts/src/memory-capture.ts +283 -0
  508. package/ts/src/memory-decay.ts +55 -0
  509. package/ts/src/memory-utility.ts +70 -0
  510. package/ts/src/mock-llm.ts +4 -8
  511. package/ts/src/model.ts +88 -2
  512. package/ts/src/recall/card.ts +106 -0
  513. package/ts/src/recall/evaluator.ts +170 -0
  514. package/ts/src/recall/integration.ts +35 -0
  515. package/ts/src/recall/tick.ts +213 -0
  516. package/ts/src/session-link/plan-it-action.ts +78 -0
  517. package/ts/src/session-link/session-link.ts +275 -0
  518. package/ts/src/share/adapters.ts +93 -0
  519. package/ts/src/share/share-consent.ts +105 -0
  520. package/ts/src/share/share-deck.ts +85 -0
  521. package/ts/src/share/share-token.ts +166 -0
  522. package/ts/src/slot-spec.ts +172 -0
  523. package/ts/src/state-ledger.ts +1166 -0
  524. package/ts/src/subagent-job-id-guard.ts +72 -0
  525. package/ts/src/time-anchor.ts +338 -0
  526. package/ts/src/tool-packet.ts +32 -0
  527. package/ts/src/travel-slots.ts +219 -0
  528. package/ts/src/travel-timeline.ts +83 -0
  529. package/ts/src/turn-deadline.ts +366 -0
  530. package/ts/src/turn-policy.ts +154 -0
  531. package/ts/src/unified.ts +444 -105
  532. package/ts/src/wish-pool.ts +79 -0
  533. package/dist/scripts/session-attach-wait.js +0 -41
@@ -1,100 +1,170 @@
1
- import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
1
+ /**
2
+ * 进程级事故日志(D-NEW):同步、durable 的 JSONL 诊断记录。
3
+ *
4
+ * `uncaughtExceptionMonitor` 只观察 fatal 事故并留下证据;宿主仍决定
5
+ * 是否退出。GoTry 不吞掉 fatal 事件、不调用 process.exit、也不隐式重启。
6
+ * 工具 execute 的业务失败仍由 guardToolExecute 转成结构化失败返回。
7
+ *
8
+ * §11 状态面同步:事故日志是 gotry-state/incidents.jsonl,
9
+ * 用户可见、可清理。
10
+ */
11
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, writeFileSync } from 'node:fs';
2
12
  import { dirname, isAbsolute, join } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
13
+ import { pathToFileURL } from 'node:url';
14
+ /** 解析 gotry-state 绝对路径(stateRoot 可相对也可绝对) */
4
15
  export function resolveIncidentsPath(stateRoot, filename = 'incidents.jsonl') {
5
- const root = isAbsolute(stateRoot) ? stateRoot : join(process.cwd(), stateRoot);
16
+ const root = isAbsolute(stateRoot)
17
+ ? stateRoot
18
+ : join(process.cwd(), stateRoot);
6
19
  return join(root, 'gotry-state', filename);
7
20
  }
8
21
  function ensureDir(path) {
9
22
  const dir = dirname(path);
10
- if (!existsSync(dir)) mkdirSync(dir, {
11
- recursive: true
12
- });
23
+ if (!existsSync(dir))
24
+ mkdirSync(dir, { recursive: true });
13
25
  }
14
26
  let installed = false;
15
27
  let logPath = null;
28
+ let activeMonitor = null;
29
+ function safeErrorMessage(value) {
30
+ try {
31
+ if (value instanceof Error)
32
+ return typeof value.message === 'string' ? value.message : '[error-without-message]';
33
+ if (typeof value === 'string')
34
+ return value;
35
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint' || typeof value === 'symbol')
36
+ return String(value);
37
+ const json = JSON.stringify(value);
38
+ return typeof json === 'string' ? json : '[unprintable-error]';
39
+ }
40
+ catch {
41
+ return '[unprintable-error]';
42
+ }
43
+ }
44
+ function safeErrorStack(value) {
45
+ try {
46
+ const stack = value?.stack;
47
+ return typeof stack === 'string' ? stack : '';
48
+ }
49
+ catch {
50
+ return '';
51
+ }
52
+ }
53
+ /**
54
+ * 写入一条事故并 fsync。整个 append 使用同一个 fd,并保证所有失败路径
55
+ * 都尝试 close;返回 true 只表示写入、fsync、close 全部成功。
56
+ */
16
57
  export function recordIncident(inc, stateRoot) {
17
- const target = stateRoot ? resolveIncidentsPath(stateRoot) : logPath ?? resolveIncidentsPath('.');
58
+ const target = stateRoot ? resolveIncidentsPath(stateRoot) : (logPath ?? resolveIncidentsPath('.'));
59
+ let fd;
60
+ let closeAttempted = false;
18
61
  try {
19
62
  ensureDir(target);
20
- appendFileSync(target, JSON.stringify(inc) + '\n', {
21
- flag: 'a'
22
- });
23
- const fd = require('node:fs').openSync(target, 'a');
24
- require('node:fs').fsyncSync(fd);
25
- require('node:fs').closeSync(fd);
63
+ fd = openSync(target, 'a');
64
+ writeFileSync(fd, JSON.stringify(inc) + '\n', { encoding: 'utf8' });
65
+ fsyncSync(fd);
66
+ closeAttempted = true;
67
+ closeSync(fd);
68
+ fd = undefined;
26
69
  return true;
27
- } catch {
70
+ }
71
+ catch {
72
+ // 事故处理中的诊断失败绝不能再次抛出。
28
73
  return false;
29
74
  }
75
+ finally {
76
+ if (fd !== undefined && !closeAttempted) {
77
+ closeAttempted = true;
78
+ try {
79
+ closeSync(fd);
80
+ }
81
+ catch { /* best effort close on a failed write */ }
82
+ }
83
+ }
30
84
  }
85
+ /**
86
+ * 安装进程级事故观察器。只挂 monitor,不改变 Node/dsh 的 fatal 行为。
87
+ * 进程整个生命周期仅生效一次;disposer 供宿主卸载和测试使用。
88
+ */
31
89
  export function installProcessGuards(stateRoot, labels) {
32
- if (installed) return ()=>{};
90
+ if (installed)
91
+ return () => { };
33
92
  installed = true;
34
93
  logPath = resolveIncidentsPath(stateRoot);
35
- const uncaughtHandler = (err, origin)=>{
36
- recordIncident({
37
- ts: new Date().toISOString(),
38
- kind: 'uncaughtException',
39
- message: (err?.message ?? String(err)).slice(0, 2000),
40
- stack: (err?.stack ?? '').slice(0, 4000),
41
- source: labels?.uncaughtException ?? String(origin)
42
- }, stateRoot);
43
- };
44
- const rejectionHandler = (reason, promise)=>{
45
- const msg = reason instanceof Error ? reason.message : String(reason);
46
- const stack = reason instanceof Error ? reason.stack ?? '' : '';
47
- recordIncident({
48
- ts: new Date().toISOString(),
49
- kind: 'unhandledRejection',
50
- message: msg.slice(0, 2000),
51
- stack: stack.slice(0, 4000),
52
- source: labels?.unhandledRejection ?? 'promise'
53
- }, stateRoot);
94
+ const monitor = (err, origin) => {
95
+ try {
96
+ const kind = origin === 'unhandledRejection' ? 'unhandledRejection' : 'uncaughtException';
97
+ const source = kind === 'unhandledRejection'
98
+ ? (labels?.unhandledRejection ?? 'promise')
99
+ : (labels?.uncaughtException ?? origin);
100
+ recordIncident({
101
+ ts: new Date().toISOString(),
102
+ kind,
103
+ message: safeErrorMessage(err).slice(0, 2000),
104
+ stack: safeErrorStack(err).slice(0, 4000),
105
+ source,
106
+ }, stateRoot);
107
+ }
108
+ catch {
109
+ // Formatting must never replace the original fatal event. Keep a final
110
+ // plain fallback attempt for diagnostics, while the host retains control.
111
+ try {
112
+ recordIncident({
113
+ ts: new Date().toISOString(),
114
+ kind: origin === 'unhandledRejection' ? 'unhandledRejection' : 'uncaughtException',
115
+ message: '[incident-observer-fallback]',
116
+ source: 'incident-observer',
117
+ }, stateRoot);
118
+ }
119
+ catch { /* observer is strictly best effort */ }
120
+ }
54
121
  };
55
- process.on('uncaughtException', uncaughtHandler);
56
- process.on('unhandledRejection', rejectionHandler);
57
- return ()=>{
58
- process.off('uncaughtException', uncaughtHandler);
59
- process.off('unhandledRejection', rejectionHandler);
122
+ process.on('uncaughtExceptionMonitor', monitor);
123
+ activeMonitor = monitor;
124
+ let active = true;
125
+ return () => {
126
+ if (!active)
127
+ return;
128
+ active = false;
129
+ if (activeMonitor !== monitor)
130
+ return;
131
+ process.off('uncaughtExceptionMonitor', monitor);
60
132
  installed = false;
133
+ logPath = null;
134
+ activeMonitor = null;
61
135
  };
62
136
  }
137
+ /**
138
+ * 工具执行面异常隔离(D-NEW gotry 侧收尾):dsh 的一个工具 execute 抛错/拒绝
139
+ * 会沿 cordis 传到主循环,拖垮整个会话。包装后:降级为结构化错误返回给 LLM、
140
+ * 事故落盘,永不向上抛。落盘失败也不抛(双保险,仍返回结构化错误)。
141
+ */
63
142
  export function guardToolExecute(name, stateRoot, execute) {
64
- return async (args, exec)=>{
143
+ return async (args, exec) => {
65
144
  try {
66
145
  return await execute(args, exec);
67
- } catch (e) {
68
- const err = e instanceof Error ? e : new Error(String(e));
146
+ }
147
+ catch (e) {
148
+ const message = safeErrorMessage(e);
69
149
  recordIncident({
70
150
  ts: new Date().toISOString(),
71
151
  kind: 'tool_execute_error',
72
- message: `${name}: ${err.message}`.slice(0, 2000),
73
- stack: (err.stack ?? '').slice(0, 4000),
74
- source: name
152
+ message: `${name}: ${message}`.slice(0, 2000),
153
+ stack: safeErrorStack(e).slice(0, 4000),
154
+ source: name,
75
155
  }, stateRoot);
76
156
  const failure = {
77
157
  ok: false,
78
- summary: `gotry_${name} 内部错误(已隔离,会话继续): ${err.message.slice(0, 300)}`,
79
- evidence: `[incident:tool_execute_error@${new Date().toISOString()}]`
158
+ summary: `gotry_${name} 内部错误(已隔离,会话继续): ${message.slice(0, 300)}`,
159
+ evidence: `[incident:tool_execute_error@${new Date().toISOString()}]`,
80
160
  };
81
161
  return failure;
82
162
  }
83
163
  };
84
164
  }
85
- if (import.meta.url === `file://${fileURLToPath(import.meta.url)}` && process.argv[2] === '--smoke') {
165
+ /** CLI 调试:最小 fatal 子进程会留下事故证据并按 Node 原生语义非零退出。 */
166
+ if (process.argv[1] !== undefined && pathToFileURL(process.argv[1]).href === import.meta.url && process.argv[2] === '--smoke') {
86
167
  const stateRoot = process.argv[3] ?? '.';
87
- installProcessGuards(stateRoot, {
88
- uncaughtException: 'smoke',
89
- unhandledRejection: 'smoke'
90
- });
91
- writeFileSync(resolveIncidentsPath(stateRoot) + '.smoke', 'ok');
92
- setTimeout(()=>Promise.reject(new Error('intentional smoke-rejection')), 50);
93
- setTimeout(()=>{
94
- console.log('SMOKE OK');
95
- process.exit(0);
96
- }, 500);
168
+ installProcessGuards(stateRoot, { uncaughtException: 'smoke', unhandledRejection: 'smoke' });
169
+ setTimeout(() => { throw new Error('intentional smoke-uncaught'); }, 50);
97
170
  }
98
-
99
-
100
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/incident-log.ts
@@ -0,0 +1,190 @@
1
+ /**
2
+ * 行程 HTML 产物生成入口(issue #442,父 #438)。
3
+ *
4
+ * 与纯渲染器(ts/src/itinerary-html.ts)的分工:渲染器只把「显式行程 + 已选事实」
5
+ * 投影成单文件 HTML;本模块是**唯一的产品生成路径**——从当前配置状态根的
6
+ * 事实注册表按 id 选事实、跑渲染器、把产物独占落盘到会话工作目录。
7
+ *
8
+ * 三条硬纪律:
9
+ * 1. 事实只从 <stateRoot>/gotry-state/bookable-facts.jsonl 注册表取:入参只收
10
+ * fact_ids,调用方自带的事实对象一律不接收;未知/重复/超量 id 显式拒绝,
11
+ * 登记行畸形则由渲染器的运行时校验拒绝(不静默丢弃后仍宣称成功);
12
+ * 2. 只新建文件:basename 限定 gotry-itinerary-<ASCII 安全 token>.html,父目录必须是宿主
13
+ * 显式给出的**绝对**会话工作目录(缺失/空白/相对一律拒绝,绝不回落 `.` 或进程 cwd),
14
+ * realpath 化并排除 .git/node_modules 后,再以 O_CREAT|O_EXCL('wx')独占创建,
15
+ * 绝不覆盖既有文件、绝不跟随符号链接改写其目标;
16
+ * 3. 失败即拒绝、零写入:输入非法/事实未注册/渲染被拒/文件名被占用都返回结构化
17
+ * 错误,不落任何字节(校验全部发生在打开文件之前)。
18
+ */
19
+ import { randomBytes } from 'node:crypto';
20
+ import { open, realpath, stat, unlink } from 'node:fs/promises';
21
+ import { isAbsolute, join, sep } from 'node:path';
22
+ import { loadFactRegistry } from './fact-log.js';
23
+ import { ITINERARY_HTML_LIMITS, renderItineraryHtml } from '../src/itinerary-html.js';
24
+ /** 产物文件名契约:固定前缀 + ASCII 安全 token + .html(无路径分隔符、无前导点) */
25
+ export const ITINERARY_ARTIFACT_BASENAME_RE = /^gotry-itinerary-[A-Za-z0-9][A-Za-z0-9._-]{0,63}\.html$/;
26
+ /** 随机默认名(仅 crypto 十六进制;显式 basename 优先) */
27
+ export const ITINERARY_ARTIFACT_RANDOM_BYTES = 8;
28
+ const RANDOM_ATTEMPTS = 3;
29
+ const DIR_DENY = ['.git', 'node_modules'];
30
+ const ID_SAMPLE = 3;
31
+ function isRecord(value) {
32
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
33
+ }
34
+ function hasDeniedSegment(p) {
35
+ return p.split(sep).some(seg => DIR_DENY.includes(seg));
36
+ }
37
+ function fsErrorText(err) {
38
+ const code = err?.code;
39
+ switch (code) {
40
+ case 'EACCES':
41
+ case 'EPERM': return '无写入权限';
42
+ case 'ENOSPC': return '磁盘空间不足';
43
+ case 'EROFS': return '只读文件系统';
44
+ case 'ENOTDIR': return '路径中某段不是目录';
45
+ case 'ENOENT': return '目录不存在';
46
+ case 'ENAMETOOLONG': return '文件名过长';
47
+ default: return typeof code === 'string' ? `文件系统错误 ${code}` : '文件系统错误';
48
+ }
49
+ }
50
+ /** fact_ids 输入收窄:必填数组、逐条非空短字符串、无重复、不超渲染器容量 */
51
+ function takeFactIds(value) {
52
+ if (value === undefined || value === null) {
53
+ return { error: 'fact_ids 必填(字符串数组,可为空数组)', hint: 'id 来自本次会话 exact-date 工具结果记录的注册表;不接受调用方自带事实' };
54
+ }
55
+ if (!Array.isArray(value))
56
+ return { error: 'fact_ids 必须是字符串数组' };
57
+ if (value.length > ITINERARY_HTML_LIMITS.facts) {
58
+ return { error: `fact_ids 超出容量上限(${ITINERARY_HTML_LIMITS.facts} 条,实测 ${value.length} 条)` };
59
+ }
60
+ const ids = [];
61
+ const seen = new Set();
62
+ const duplicates = [];
63
+ for (const [i, raw] of value.entries()) {
64
+ if (typeof raw !== 'string' || raw.length === 0)
65
+ return { error: `fact_ids[${i}] 必须是非空字符串` };
66
+ if (raw.length > ITINERARY_HTML_LIMITS.factIdChars) {
67
+ return { error: `fact_ids[${i}] 超出 id 长度上限(${ITINERARY_HTML_LIMITS.factIdChars} 字符)` };
68
+ }
69
+ if (seen.has(raw)) {
70
+ duplicates.push(raw);
71
+ continue;
72
+ }
73
+ seen.add(raw);
74
+ ids.push(raw);
75
+ }
76
+ if (duplicates.length > 0) {
77
+ return { error: `fact_ids 有重复 id:${duplicates.slice(0, ID_SAMPLE).join('、')}${duplicates.length > ID_SAMPLE ? ` 等 ${duplicates.length} 条` : ''}`, hint: '同一条事实只能选一次;重复选择说明调用方对证据面不确定' };
78
+ }
79
+ return { ids };
80
+ }
81
+ function takeBasename(value) {
82
+ if (value === undefined || value === null)
83
+ return { basename: `gotry-itinerary-${randomBytes(ITINERARY_ARTIFACT_RANDOM_BYTES).toString('hex')}.html` };
84
+ if (typeof value !== 'string' || value.length === 0)
85
+ return { error: 'basename 必须是字符串' };
86
+ if (value.includes('/') || value.includes('\\') || value.includes('..')) {
87
+ return { error: 'basename 只能是文件名,不能含路径分隔符或 ..', hint: '形如 gotry-itinerary-2027-thailand.html' };
88
+ }
89
+ if (!ITINERARY_ARTIFACT_BASENAME_RE.test(value)) {
90
+ return { error: 'basename 不符合命名契约', hint: '必须是 gotry-itinerary-<ASCII 字母/数字/._->.html(前缀后 1-64 字符)' };
91
+ }
92
+ return { basename: value };
93
+ }
94
+ /** 会话工作目录:必须显式且为绝对路径(缺失/空白/相对一律拒绝,绝不回落 '.' 或 process.cwd()),
95
+ * 按宿主原值解析(trim 只判全空白、绝不归一化路径),已存在、可 realpath、是目录,且不含 .git/node_modules 段 */
96
+ async function resolveWritableDir(cwd) {
97
+ if (typeof cwd !== 'string' || cwd.trim() === '') {
98
+ return { error: '会话工作目录缺失:拒绝在未知目录写产物', hint: '写产物必须有宿主显式给出的绝对会话工作目录,不做任何猜测性回落' };
99
+ }
100
+ // trim 只用于「是否全空白」判定:真实路径一律按宿主原值使用,绝不归一化——
101
+ // 尾随空格可能是真实目录名的一部分,归一化会写到同名兄弟目录
102
+ if (!isAbsolute(cwd)) {
103
+ return { error: `会话工作目录必须是绝对路径:${cwd}`, hint: '相对路径会随进程工作目录漂移,拒绝写入' };
104
+ }
105
+ const canonical = await realpath(cwd).catch(() => null);
106
+ if (!canonical)
107
+ return { error: `会话工作目录不存在或无法解析:${cwd}`, hint: '本工具只在真实存在的会话工作目录顶层新建文件' };
108
+ if (hasDeniedSegment(canonical)) {
109
+ return { error: '会话工作目录位于受限段(.git/node_modules)内,拒绝写入', hint: `解析后:${canonical}` };
110
+ }
111
+ const st = await stat(canonical).catch(() => null);
112
+ if (!st?.isDirectory())
113
+ return { error: `会话工作目录不是目录:${canonical}` };
114
+ return { dir: canonical };
115
+ }
116
+ /** 独占创建:wx(O_CREAT|O_EXCL)既拒绝覆盖既有文件,也拒绝落在指向别处的符号链接上 */
117
+ async function writeExclusive(dir, basename, html) {
118
+ const target = join(dir, basename);
119
+ const bytes = Buffer.byteLength(html, 'utf8');
120
+ let handle;
121
+ try {
122
+ handle = await open(target, 'wx', 0o644);
123
+ }
124
+ catch (err) {
125
+ if (err?.code === 'EEXIST') {
126
+ return { error: `目标文件已存在,拒绝覆盖:${basename}`, hint: '换一个 basename(不传则自动生成随机名);本工具永不覆盖既有产物' };
127
+ }
128
+ return { error: `无法在会话工作目录新建文件:${fsErrorText(err)}`, hint: `目标目录:${dir}` };
129
+ }
130
+ try {
131
+ await handle.writeFile(html, 'utf-8');
132
+ }
133
+ catch (err) {
134
+ await handle.close().catch(() => { });
135
+ await unlink(target).catch(() => { });
136
+ return { error: `写入产物失败:${fsErrorText(err)}`, hint: `目标文件已清理:${target}` };
137
+ }
138
+ await handle.close();
139
+ const finalPath = await realpath(target).catch(() => target);
140
+ return { path: finalPath, bytes };
141
+ }
142
+ /**
143
+ * 生成一份行程 HTML 产物。写入唯一目标 = 会话工作目录顶层的新文件;
144
+ * 事实唯一来源 = deps.stateRoot 的事实注册表。
145
+ */
146
+ export async function generateItineraryArtifact(input, deps) {
147
+ if (!isRecord(input))
148
+ return { ok: false, error: 'input 必须是对象 {title, itinerary, fact_ids, basename?}' };
149
+ const ids = takeFactIds(input.fact_ids);
150
+ if ('error' in ids)
151
+ return { ok: false, error: ids.error, hint: ids.hint };
152
+ const named = takeBasename(input.basename);
153
+ if ('error' in named)
154
+ return { ok: false, error: named.error, hint: named.hint };
155
+ const registry = await loadFactRegistry(deps.stateRoot);
156
+ const byId = new Map(registry.map(f => [f.fact_id, f]));
157
+ const missing = ids.ids.filter(id => !byId.has(id));
158
+ if (missing.length > 0) {
159
+ return {
160
+ ok: false,
161
+ error: `事实 id 未在当前注册表:${missing.slice(0, ID_SAMPLE).join('、')}${missing.length > ID_SAMPLE ? ` 等 ${missing.length} 条` : ''}`,
162
+ hint: 'fact_ids 只能取自本会话 exact-date 工具结果的注册表;不接受调用方自带事实对象,也不从 Markdown 猜事实',
163
+ };
164
+ }
165
+ // 顺序保留调用方选择;登记行畸形不在选择阶段放宽——交给渲染器运行时校验显式拒绝
166
+ const selected = ids.ids.map(id => byId.get(id));
167
+ const rendered = renderItineraryHtml({ title: input.title, itinerary: input.itinerary, facts: selected });
168
+ if (!rendered.ok) {
169
+ return {
170
+ ok: false,
171
+ error: '渲染被拒:行程或事实不满足渲染契约,未写入任何文件',
172
+ errors: rendered.errors,
173
+ hint: '按 errors 修正显式行程结构或换用合法登记的事实;本工具不会静默截断核心行程',
174
+ };
175
+ }
176
+ const dir = await resolveWritableDir(deps.cwd);
177
+ if ('error' in dir)
178
+ return { ok: false, error: dir.error, hint: dir.hint };
179
+ for (let attempt = 0; attempt < (input.basename === undefined || input.basename === null ? RANDOM_ATTEMPTS : 1); attempt++) {
180
+ const basename = attempt === 0 ? named.basename : `gotry-itinerary-${randomBytes(ITINERARY_ARTIFACT_RANDOM_BYTES).toString('hex')}.html`;
181
+ const written = await writeExclusive(dir.dir, basename, rendered.html);
182
+ if ('error' in written) {
183
+ if (attempt + 1 < RANDOM_ATTEMPTS && input.basename === undefined)
184
+ continue; // 随机名撞车:换名重试
185
+ return { ok: false, error: written.error, hint: written.hint };
186
+ }
187
+ return { ok: true, path: written.path, basename, bytes: written.bytes, fact_ids: ids.ids, overall_verified: false };
188
+ }
189
+ return { ok: false, error: '随机文件名多次撞车,未写入文件' };
190
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * 行程 deck 产物生成入口(issue #566,Phase B 切片 2;研究决策见
3
+ * docs/research/karpo-deck-web-research.md 决策 #1;deck 渲染契约见
4
+ * docs/design/itinerary-deck-renderer.md)。
5
+ *
6
+ * 与单页入口 itinerary-artifact.ts 完全对称的产品路径:渲染器只把「显式行程
7
+ * + 已选事实」投影成单文件 deck HTML;本模块是 deck 形态的**唯一产品生成
8
+ * 路径**——从当前配置状态根的事实注册表按 id 选事实、跑 deck 渲染器、把
9
+ * 产物独占落盘到会话工作目录。
10
+ *
11
+ * 三条硬纪律(继承自单页入口,verbatim):
12
+ * 1. 事实只从 <stateRoot>/gotry-state/bookable-facts.jsonl 注册表取:入参只收
13
+ * fact_ids,调用方自带的事实对象一律不接收;未知/重复/超量 id 显式拒绝,
14
+ * 登记行畸形则由 deck 渲染器的运行时校验拒绝(不静默丢弃后仍宣称成功);
15
+ * 2. 只新建文件:basename 限定 gotry-deck-<ASCII 安全 token>.html,父目录必须是宿主
16
+ * 显式给出的**绝对**会话工作目录(缺失/空白/相对一律拒绝,绝不回落 `.` 或进程 cwd),
17
+ * realpath 化并排除 .git/node_modules 后,再以 O_CREAT|O_EXCL('wx')独占创建,
18
+ * 绝不覆盖既有文件、绝不跟随符号链接改写其目标;
19
+ * 3. 失败即拒绝、零写入:输入非法/事实未注册/渲染被拒/文件名被占用都返回结构化
20
+ * 错误,不落任何字节(校验全部发生在打开文件之前)。
21
+ */
22
+ import { randomBytes } from 'node:crypto';
23
+ import { open, realpath, stat, unlink } from 'node:fs/promises';
24
+ import { isAbsolute, join, sep } from 'node:path';
25
+ import { loadFactRegistry } from './fact-log.js';
26
+ import { ITINERARY_DOC_LIMITS } from '../src/itinerary-doc-shared.js';
27
+ import { renderItineraryDeck } from '../src/itinerary-deck.js';
28
+ /** 产物文件名契约:固定前缀 + ASCII 安全 token + .html(无路径分隔符、无前导点) */
29
+ export const ITINERARY_DECK_ARTIFACT_BASENAME_RE = /^gotry-deck-[A-Za-z0-9][A-Za-z0-9._-]{0,63}\.html$/;
30
+ /** 随机默认名(仅 crypto 十六进制;显式 basename 优先) */
31
+ export const ITINERARY_DECK_ARTIFACT_RANDOM_BYTES = 8;
32
+ const RANDOM_ATTEMPTS = 3;
33
+ const DIR_DENY = ['.git', 'node_modules'];
34
+ const ID_SAMPLE = 3;
35
+ function isRecord(value) {
36
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
37
+ }
38
+ function hasDeniedSegment(p) {
39
+ return p.split(sep).some(seg => DIR_DENY.includes(seg));
40
+ }
41
+ function fsErrorText(err) {
42
+ const code = err?.code;
43
+ switch (code) {
44
+ case 'EACCES':
45
+ case 'EPERM': return '无写入权限';
46
+ case 'ENOSPC': return '磁盘空间不足';
47
+ case 'EROFS': return '只读文件系统';
48
+ case 'ENOTDIR': return '路径中某段不是目录';
49
+ case 'ENOENT': return '目录不存在';
50
+ case 'ENAMETOOLONG': return '文件名过长';
51
+ default: return typeof code === 'string' ? `文件系统错误 ${code}` : '文件系统错误';
52
+ }
53
+ }
54
+ /** fact_ids 输入收窄:必填数组、逐条非空短字符串、无重复、不超渲染器容量 */
55
+ function takeFactIds(value) {
56
+ if (value === undefined || value === null) {
57
+ return { error: 'fact_ids 必填(字符串数组,可为空数组)', hint: 'id 来自本次会话 exact-date 工具结果记录的注册表;不接受调用方自带事实' };
58
+ }
59
+ if (!Array.isArray(value))
60
+ return { error: 'fact_ids 必须是字符串数组' };
61
+ if (value.length > ITINERARY_DOC_LIMITS.facts) {
62
+ return { error: `fact_ids 超出容量上限(${ITINERARY_DOC_LIMITS.facts} 条,实测 ${value.length} 条)` };
63
+ }
64
+ const ids = [];
65
+ const seen = new Set();
66
+ const duplicates = [];
67
+ for (const [i, raw] of value.entries()) {
68
+ if (typeof raw !== 'string' || raw.length === 0)
69
+ return { error: `fact_ids[${i}] 必须是非空字符串` };
70
+ if (raw.length > ITINERARY_DOC_LIMITS.factIdChars) {
71
+ return { error: `fact_ids[${i}] 超出 id 长度上限(${ITINERARY_DOC_LIMITS.factIdChars} 字符)` };
72
+ }
73
+ if (seen.has(raw)) {
74
+ duplicates.push(raw);
75
+ continue;
76
+ }
77
+ seen.add(raw);
78
+ ids.push(raw);
79
+ }
80
+ if (duplicates.length > 0) {
81
+ return { error: `fact_ids 有重复 id:${duplicates.slice(0, ID_SAMPLE).join('、')}${duplicates.length > ID_SAMPLE ? ` 等 ${duplicates.length} 条` : ''}`, hint: '同一条事实只能选一次;重复选择说明调用方对证据面不确定' };
82
+ }
83
+ return { ids };
84
+ }
85
+ function takeBasename(value) {
86
+ if (value === undefined || value === null)
87
+ return { basename: `gotry-deck-${randomBytes(ITINERARY_DECK_ARTIFACT_RANDOM_BYTES).toString('hex')}.html` };
88
+ if (typeof value !== 'string' || value.length === 0)
89
+ return { error: 'basename 必须是字符串' };
90
+ if (value.includes('/') || value.includes('\\') || value.includes('..')) {
91
+ return { error: 'basename 只能是文件名,不能含路径分隔符或 ..', hint: '形如 gotry-deck-2027-thailand.html' };
92
+ }
93
+ if (!ITINERARY_DECK_ARTIFACT_BASENAME_RE.test(value)) {
94
+ return { error: 'basename 不符合命名契约', hint: '必须是 gotry-deck-<ASCII 字母/数字/._->.html(前缀后 1-64 字符)' };
95
+ }
96
+ return { basename: value };
97
+ }
98
+ /** 会话工作目录:必须显式且为绝对路径(缺失/空白/相对一律拒绝,绝不回落 '.' 或 process.cwd()),
99
+ * 按宿主原值解析(trim 只判全空白、绝不归一化路径),已存在、可 realpath、是目录,且不含 .git/node_modules 段 */
100
+ async function resolveWritableDir(cwd) {
101
+ if (typeof cwd !== 'string' || cwd.trim() === '') {
102
+ return { error: '会话工作目录缺失:拒绝在未知目录写产物', hint: '写产物必须有宿主显式给出的绝对会话工作目录,不做任何猜测性回落' };
103
+ }
104
+ // trim 只用于「是否全空白」判定:真实路径一律按宿主原值使用,绝不归一化——
105
+ // 尾随空格可能是真实目录名的一部分,归一化会写到同名兄弟目录
106
+ if (!isAbsolute(cwd)) {
107
+ return { error: `会话工作目录必须是绝对路径:${cwd}`, hint: '相对路径会随进程工作目录漂移,拒绝写入' };
108
+ }
109
+ const canonical = await realpath(cwd).catch(() => null);
110
+ if (!canonical)
111
+ return { error: `会话工作目录不存在或无法解析:${cwd}`, hint: '本工具只在真实存在的会话工作目录顶层新建文件' };
112
+ if (hasDeniedSegment(canonical)) {
113
+ return { error: '会话工作目录位于受限段(.git/node_modules)内,拒绝写入', hint: `解析后:${canonical}` };
114
+ }
115
+ const st = await stat(canonical).catch(() => null);
116
+ if (!st?.isDirectory())
117
+ return { error: `会话工作目录不是目录:${canonical}` };
118
+ return { dir: canonical };
119
+ }
120
+ /** 独占创建:wx(O_CREAT|O_EXCL)既拒绝覆盖既有文件,也拒绝落在指向别处的符号链接上 */
121
+ async function writeExclusive(dir, basename, html) {
122
+ const target = join(dir, basename);
123
+ const bytes = Buffer.byteLength(html, 'utf8');
124
+ let handle;
125
+ try {
126
+ handle = await open(target, 'wx', 0o644);
127
+ }
128
+ catch (err) {
129
+ if (err?.code === 'EEXIST') {
130
+ return { error: `目标文件已存在,拒绝覆盖:${basename}`, hint: '换一个 basename(不传则自动生成随机名);本工具永不覆盖既有产物' };
131
+ }
132
+ return { error: `无法在会话工作目录新建文件:${fsErrorText(err)}`, hint: `目标目录:${dir}` };
133
+ }
134
+ try {
135
+ await handle.writeFile(html, 'utf-8');
136
+ }
137
+ catch (err) {
138
+ await handle.close().catch(() => { });
139
+ await unlink(target).catch(() => { });
140
+ return { error: `写入产物失败:${fsErrorText(err)}`, hint: `目标文件已清理:${target}` };
141
+ }
142
+ await handle.close();
143
+ const finalPath = await realpath(target).catch(() => target);
144
+ return { path: finalPath, bytes };
145
+ }
146
+ /**
147
+ * 生成一份行程 deck HTML 产物。写入唯一目标 = 会话工作目录顶层的新文件;
148
+ * 事实唯一来源 = deps.stateRoot 的事实注册表。
149
+ */
150
+ export async function generateItineraryDeckArtifact(input, deps) {
151
+ if (!isRecord(input))
152
+ return { ok: false, error: 'input 必须是对象 {title, itinerary, fact_ids, basename?}' };
153
+ const ids = takeFactIds(input.fact_ids);
154
+ if ('error' in ids)
155
+ return { ok: false, error: ids.error, hint: ids.hint };
156
+ const named = takeBasename(input.basename);
157
+ if ('error' in named)
158
+ return { ok: false, error: named.error, hint: named.hint };
159
+ const registry = await loadFactRegistry(deps.stateRoot);
160
+ const byId = new Map(registry.map(f => [f.fact_id, f]));
161
+ const missing = ids.ids.filter(id => !byId.has(id));
162
+ if (missing.length > 0) {
163
+ return {
164
+ ok: false,
165
+ error: `事实 id 未在当前注册表:${missing.slice(0, ID_SAMPLE).join('、')}${missing.length > ID_SAMPLE ? ` 等 ${missing.length} 条` : ''}`,
166
+ hint: 'fact_ids 只能取自本会话 exact-date 工具结果的注册表;不接受调用方自带事实对象,也不从 Markdown 猜事实',
167
+ };
168
+ }
169
+ // 顺序保留调用方选择;登记行畸形不在选择阶段放宽——交给 deck 渲染器运行时校验显式拒绝
170
+ const selected = ids.ids.map(id => byId.get(id));
171
+ const rendered = renderItineraryDeck({ title: input.title, itinerary: input.itinerary, facts: selected });
172
+ if (!rendered.ok) {
173
+ return {
174
+ ok: false,
175
+ error: '渲染被拒:行程或事实不满足渲染契约,未写入任何文件',
176
+ errors: rendered.errors,
177
+ hint: '按 errors 修正显式行程结构或换用合法登记的事实;本工具不会静默截断核心行程',
178
+ };
179
+ }
180
+ const dir = await resolveWritableDir(deps.cwd);
181
+ if ('error' in dir)
182
+ return { ok: false, error: dir.error, hint: dir.hint };
183
+ for (let attempt = 0; attempt < (input.basename === undefined || input.basename === null ? RANDOM_ATTEMPTS : 1); attempt++) {
184
+ const basename = attempt === 0 ? named.basename : `gotry-deck-${randomBytes(ITINERARY_DECK_ARTIFACT_RANDOM_BYTES).toString('hex')}.html`;
185
+ const written = await writeExclusive(dir.dir, basename, rendered.html);
186
+ if ('error' in written) {
187
+ if (attempt + 1 < RANDOM_ATTEMPTS && input.basename === undefined)
188
+ continue; // 随机名撞车:换名重试
189
+ return { ok: false, error: written.error, hint: written.hint };
190
+ }
191
+ return { ok: true, path: written.path, basename, bytes: written.bytes, fact_ids: ids.ids, overall_verified: false };
192
+ }
193
+ return { ok: false, error: '随机文件名多次撞车,未写入文件' };
194
+ }