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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/README.md +172 -217
  2. package/README.zh-CN.md +222 -0
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-booking-copilot.js +53 -0
  5. package/bin/gotry-bootstrap.js +1744 -0
  6. package/bin/gotry-inner.js +733 -83
  7. package/bin/gotry-process-liveness.d.ts +27 -0
  8. package/bin/gotry-process-liveness.js +97 -0
  9. package/bin/gotry-runtime-resolution.d.ts +27 -0
  10. package/bin/gotry-runtime-resolution.js +49 -0
  11. package/bin/gotry.js +1 -1
  12. package/client/client.js +203 -0
  13. package/cordis.gotry-patch.yml +80 -49
  14. package/data/airline-airports.json +39 -0
  15. package/data/stations-12306-verify.json +524 -0
  16. package/dist/capabilities/agent-reach-deep.js +45 -122
  17. package/dist/capabilities/agent-reach.js +101 -146
  18. package/dist/capabilities/anything.js +135 -94
  19. package/dist/capabilities/artifacts.js +370 -0
  20. package/dist/capabilities/channel-health.js +131 -0
  21. package/dist/capabilities/channel-registry.js +256 -0
  22. package/dist/capabilities/doctor.js +546 -0
  23. package/dist/capabilities/effect.js +394 -0
  24. package/dist/capabilities/external-event.js +149 -0
  25. package/dist/capabilities/fact-log.js +55 -0
  26. package/dist/capabilities/flyai-config.js +337 -0
  27. package/dist/capabilities/flyai.js +968 -0
  28. package/dist/capabilities/fx-contract.js +293 -0
  29. package/dist/capabilities/geo-atlas.js +246 -0
  30. package/dist/capabilities/ground-transfer.js +509 -0
  31. package/dist/capabilities/hbcli.js +260 -123
  32. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  33. package/dist/capabilities/incident-log.js +131 -61
  34. package/dist/capabilities/itinerary-artifact.js +190 -0
  35. package/dist/capabilities/itinerary-deck-artifact.js +194 -0
  36. package/dist/capabilities/itinerary-deck-export.js +339 -0
  37. package/dist/capabilities/lavish-local.js +924 -0
  38. package/dist/capabilities/model-override.js +38 -0
  39. package/dist/capabilities/opensky.js +54 -43
  40. package/dist/capabilities/resilience.js +191 -0
  41. package/dist/capabilities/session/action-cache.js +126 -0
  42. package/dist/capabilities/session/adapters/ctrip-flight.js +141 -0
  43. package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
  44. package/dist/capabilities/session/adapters/dida-portal.js +274 -0
  45. package/dist/capabilities/session/adapters/meituan-local.js +54 -0
  46. package/dist/capabilities/session/adapters/rail-12306.js +342 -0
  47. package/dist/capabilities/session/benchmark.js +189 -0
  48. package/dist/capabilities/session/bridge-ledger.js +224 -0
  49. package/dist/capabilities/session/extension-bridge.js +636 -0
  50. package/dist/capabilities/session/extension-channel.js +109 -0
  51. package/dist/capabilities/session/extension-distribution.js +230 -0
  52. package/dist/capabilities/session/extract.js +50 -0
  53. package/dist/capabilities/session/golden-score.js +114 -0
  54. package/dist/capabilities/session/health-watch.js +215 -0
  55. package/dist/capabilities/session/read-guard.js +107 -0
  56. package/dist/capabilities/session/static-flight-golden.js +150 -0
  57. package/dist/capabilities/session/transport.js +109 -0
  58. package/dist/capabilities/session/wizard.js +117 -0
  59. package/dist/capabilities/session-consent.js +178 -0
  60. package/dist/capabilities/session-login.js +170 -0
  61. package/dist/capabilities/session-search.js +686 -0
  62. package/dist/capabilities/spawn-bounded.js +203 -0
  63. package/dist/capabilities/sponsor-plugin.js +341 -0
  64. package/dist/capabilities/visa-policy.js +140 -0
  65. package/dist/capabilities/weather.js +243 -138
  66. package/dist/data/session-golden-20.json +25 -0
  67. package/dist/data/sf-golden-manifest.json +103 -0
  68. package/dist/data/sf-static-routes.json +91 -0
  69. package/dist/scripts/action-cache-tests.js +72 -0
  70. package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
  71. package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
  72. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  73. package/dist/scripts/agent-reach-error-tests.js +354 -0
  74. package/dist/scripts/agent-reach-tests.js +26 -18
  75. package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
  76. package/dist/scripts/anything-tests.js +116 -60
  77. package/dist/scripts/artifact-client-contract-tests.js +220 -0
  78. package/dist/scripts/artifacts-capability-tests.js +727 -0
  79. package/dist/scripts/async-collect.js +68 -10
  80. package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
  81. package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
  82. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
  83. package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
  84. package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
  85. package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
  86. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
  87. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
  88. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
  89. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
  90. package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
  91. package/dist/scripts/booking-copilot-dsh-warmer-proof.js +123 -0
  92. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
  93. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
  94. package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
  95. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
  96. package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
  97. package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
  98. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
  99. package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
  100. package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
  101. package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
  102. package/dist/scripts/booking-executor-tests.js +80 -0
  103. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  104. package/dist/scripts/booking-saga-tests.js +125 -0
  105. package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
  106. package/dist/scripts/bootstrap-tests.js +1479 -0
  107. package/dist/scripts/bridge-events-tests.js +239 -0
  108. package/dist/scripts/bridge-ledger-tests.js +428 -0
  109. package/dist/scripts/build-changelog.js +238 -0
  110. package/dist/scripts/build-metrics-report.js +331 -0
  111. package/dist/scripts/changelog-tests.js +118 -0
  112. package/dist/scripts/channel-probe-tests.js +103 -0
  113. package/dist/scripts/channel-probe.js +169 -0
  114. package/dist/scripts/channel-registry-tests.js +208 -0
  115. package/dist/scripts/companion-tests.js +67 -0
  116. package/dist/scripts/dida-cookie-probe.js +87 -0
  117. package/dist/scripts/dida-runner-stop-tests.js +311 -0
  118. package/dist/scripts/dida-sw-probe.js +114 -0
  119. package/dist/scripts/diff-test.js +17 -12
  120. package/dist/scripts/doctor-tests.js +564 -0
  121. package/dist/scripts/dsh-artifact-e2e.js +279 -0
  122. package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
  123. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  124. package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
  125. package/dist/scripts/dsh-runtime-closure.js +162 -0
  126. package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
  127. package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
  128. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  129. package/dist/scripts/effect-tests.js +539 -0
  130. package/dist/scripts/engine-run.js +1 -3
  131. package/dist/scripts/engine-tests.js +16 -10
  132. package/dist/scripts/evaluation-cadence-tests.js +174 -0
  133. package/dist/scripts/evaluation-contract-tests.js +268 -0
  134. package/dist/scripts/extension-distribution-cli.js +39 -0
  135. package/dist/scripts/extension-distribution-tests.js +443 -0
  136. package/dist/scripts/extension-tests.js +988 -0
  137. package/dist/scripts/external-event-tests.js +350 -0
  138. package/dist/scripts/fact-gate-tests.js +869 -0
  139. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  140. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  141. package/dist/scripts/flight-malformed-tests.js +159 -0
  142. package/dist/scripts/flyai-setup-tests.js +228 -0
  143. package/dist/scripts/flyai-setup-tool-tests.js +54 -0
  144. package/dist/scripts/flyai-tests.js +692 -0
  145. package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
  146. package/dist/scripts/fx-contract-tests.js +201 -0
  147. package/dist/scripts/g5-guard-tests.js +131 -0
  148. package/dist/scripts/g5-guard.js +189 -0
  149. package/dist/scripts/gen-lunar-anchors.js +106 -0
  150. package/dist/scripts/geo-atlas-tests.js +283 -0
  151. package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
  152. package/dist/scripts/gotry-try-demo-tests.js +79 -0
  153. package/dist/scripts/gotry-try-demo.js +49 -0
  154. package/dist/scripts/ground-transfer-tests.js +956 -0
  155. package/dist/scripts/hbcli-e2e-tests.js +159 -0
  156. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  157. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  158. package/dist/scripts/hbcli-tests.js +121 -69
  159. package/dist/scripts/health-watch-cli.js +73 -0
  160. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  161. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  162. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  163. package/dist/scripts/i18n-tests.js +41 -0
  164. package/dist/scripts/incident-tests.js +281 -87
  165. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  166. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  167. package/dist/scripts/issue-271-liveness-tests.js +569 -0
  168. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  169. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  170. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  171. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  172. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  173. package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
  174. package/dist/scripts/itinerary-artifact-tests.js +368 -0
  175. package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
  176. package/dist/scripts/itinerary-deck-export-tests.js +552 -0
  177. package/dist/scripts/itinerary-deck-tests.js +435 -0
  178. package/dist/scripts/itinerary-html-tests.js +426 -0
  179. package/dist/scripts/journey-tests.js +22 -27
  180. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  181. package/dist/scripts/kernel-manifest-gate.js +126 -0
  182. package/dist/scripts/kernel-manifest-tests.js +212 -0
  183. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  184. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  185. package/dist/scripts/kernel-manifest.js +369 -0
  186. package/dist/scripts/lavish-local-tests.js +1226 -0
  187. package/dist/scripts/lavish-product-tools-tests.js +537 -0
  188. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  189. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  190. package/dist/scripts/ledger-tests.js +480 -0
  191. package/dist/scripts/ledger-workflow-crash.js +32 -0
  192. package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
  193. package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
  194. package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
  195. package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
  196. package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
  197. package/dist/scripts/memory-capture-tests.js +242 -61
  198. package/dist/scripts/memory-decay-tests.js +49 -0
  199. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  200. package/dist/scripts/memory-lifecycle.js +196 -0
  201. package/dist/scripts/memory-metrics.js +29 -31
  202. package/dist/scripts/memory-value-report.js +631 -0
  203. package/dist/scripts/memory-value-tests.js +328 -0
  204. package/dist/scripts/metrics-report-tests.js +151 -0
  205. package/dist/scripts/model-override-e2e.js +137 -0
  206. package/dist/scripts/nightly-evidence-tests.js +161 -0
  207. package/dist/scripts/nightly-evidence.js +286 -0
  208. package/dist/scripts/nudge-digest.js +43 -42
  209. package/dist/scripts/observability-tests.js +381 -0
  210. package/dist/scripts/onboarding-tests.js +161 -0
  211. package/dist/scripts/opensky-check.js +18 -13
  212. package/dist/scripts/opensky-tests.js +28 -28
  213. package/dist/scripts/persona-surface-guard-tests.js +49 -0
  214. package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
  215. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  216. package/dist/scripts/price-drift-tests.js +340 -0
  217. package/dist/scripts/price-drift-watch.js +447 -0
  218. package/dist/scripts/probe-poi-tests.js +53 -45
  219. package/dist/scripts/product-metrics.js +483 -0
  220. package/dist/scripts/publish-preverify.js +110 -0
  221. package/dist/scripts/realtime-pricing-tests.js +76 -0
  222. package/dist/scripts/recall-tests.js +513 -0
  223. package/dist/scripts/replay-async.js +20 -18
  224. package/dist/scripts/replay-real.js +18 -18
  225. package/dist/scripts/replay.js +52 -82
  226. package/dist/scripts/session-attach-diagnose.js +31 -0
  227. package/dist/scripts/session-attach-poc.js +62 -0
  228. package/dist/scripts/session-benchmark.js +180 -0
  229. package/dist/scripts/session-dida-live-e2e.js +199 -0
  230. package/dist/scripts/session-extract-tests.js +60 -0
  231. package/dist/scripts/session-link-tests.js +478 -0
  232. package/dist/scripts/session-login.js +29 -0
  233. package/dist/scripts/session-tests.js +862 -0
  234. package/dist/scripts/session-zones-tests.js +358 -0
  235. package/dist/scripts/sf-live-benchmark.js +367 -0
  236. package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
  237. package/dist/scripts/sf-live-cli-tests.js +92 -0
  238. package/dist/scripts/sf-live-options.js +46 -0
  239. package/dist/scripts/sf-manifest-tests.js +30 -0
  240. package/dist/scripts/sf-manifest.js +33 -0
  241. package/dist/scripts/sf-soft-score-tests.js +67 -0
  242. package/dist/scripts/sf-summary-tests.js +324 -0
  243. package/dist/scripts/sf-summary.js +525 -0
  244. package/dist/scripts/share-tests.js +432 -0
  245. package/dist/scripts/skeleton-check.js +16 -14
  246. package/dist/scripts/skeleton-integration-test.js +28 -11
  247. package/dist/scripts/skills-contract-tests.js +51 -46
  248. package/dist/scripts/smoke-session-gate-tests.js +21 -0
  249. package/dist/scripts/smoke.js +703 -219
  250. package/dist/scripts/spawn-bounded-abort-tests.js +241 -0
  251. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  252. package/dist/scripts/state-cli-tests.js +261 -0
  253. package/dist/scripts/state-cli.js +585 -0
  254. package/dist/scripts/state-repair-cli.js +66 -0
  255. package/dist/scripts/state-repair-tests.js +140 -0
  256. package/dist/scripts/static-golden-tests.js +165 -0
  257. package/dist/scripts/time-eval-tests.js +210 -192
  258. package/dist/scripts/travel-timeline-tests.js +59 -0
  259. package/dist/scripts/turn-handoff-collect-tests.js +169 -0
  260. package/dist/scripts/turn-handoff-collect.js +161 -0
  261. package/dist/scripts/turn-policy-tests.js +55 -0
  262. package/dist/scripts/typed-contract-canary.js +176 -0
  263. package/dist/scripts/tz-resolver-sanity.js +50 -0
  264. package/dist/scripts/tz-resolver-tests.js +584 -0
  265. package/dist/scripts/unified-tests.js +23 -19
  266. package/dist/scripts/visa-policy-tests.js +62 -0
  267. package/dist/scripts/weather-tests.js +276 -41
  268. package/dist/scripts/wish-channel-gate-tests.js +54 -0
  269. package/dist/scripts/write-gate-tests.js +522 -0
  270. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  271. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  272. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  273. package/dist/scripts/z3-race-tests.js +41 -0
  274. package/dist/src/artifact-gate.js +829 -0
  275. package/dist/src/backend/kernel.js +91 -0
  276. package/dist/src/backend/modules/booking-copilot.js +92 -0
  277. package/dist/src/backend/modules/booking-executor.js +124 -0
  278. package/dist/src/backend/modules/session-search.js +551 -0
  279. package/dist/src/benchmark-agent-conformance.js +578 -0
  280. package/dist/src/benchmark-environment-bridge.js +515 -0
  281. package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
  282. package/dist/src/benchmark-tool-isolation.js +151 -0
  283. package/dist/src/bookable-facts.js +533 -0
  284. package/dist/src/booking-saga.js +97 -0
  285. package/dist/src/booking-surface/availability-policy.js +697 -0
  286. package/dist/src/booking-surface/booking-intent.js +43 -0
  287. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  288. package/dist/src/booking-surface/canonical-schema.js +116 -0
  289. package/dist/src/booking-surface/contracts.js +75 -0
  290. package/dist/src/booking-surface/dsh-planner.js +1854 -0
  291. package/dist/src/booking-surface/dsh-plugin.js +378 -0
  292. package/dist/src/booking-surface/dsh-worker.js +98 -0
  293. package/dist/src/booking-surface/error-codes.js +231 -0
  294. package/dist/src/booking-surface/index.js +9 -0
  295. package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
  296. package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
  297. package/dist/src/booking-surface/profile.js +24 -0
  298. package/dist/src/booking-surface/recovery-chain.js +367 -0
  299. package/dist/src/booking-surface/runtime.js +1968 -0
  300. package/dist/src/booking-surface/server.js +674 -0
  301. package/dist/src/booking-surface/startup.js +168 -0
  302. package/dist/src/booking-surface/validation.js +261 -0
  303. package/dist/src/bridge.js +20 -13
  304. package/dist/src/companions.js +86 -0
  305. package/dist/src/contracts.js +21 -43
  306. package/dist/src/dsh-llm.js +97 -111
  307. package/dist/src/engine.js +276 -193
  308. package/dist/src/evaluation-cadence.js +97 -0
  309. package/dist/src/evaluation-contracts.js +283 -0
  310. package/dist/src/flight-pack-adapter.js +54 -0
  311. package/dist/src/flight-pack-contract.js +2 -0
  312. package/dist/src/flyai-setup-tool.js +70 -0
  313. package/dist/src/gotry-backend.js +125 -0
  314. package/dist/src/hotel-date-gate.js +118 -0
  315. package/dist/src/i18n.js +88 -0
  316. package/dist/src/index.js +2305 -731
  317. package/dist/src/itinerary-deck.js +139 -0
  318. package/dist/src/itinerary-doc-shared.js +661 -0
  319. package/dist/src/itinerary-html.js +131 -0
  320. package/dist/src/journey.js +155 -108
  321. package/dist/src/lavish-tools.js +400 -0
  322. package/dist/src/ledger-repair-apply.js +538 -0
  323. package/dist/src/ledger-repair-plan.js +463 -0
  324. package/dist/src/loop.js +358 -162
  325. package/dist/src/memory-capture.js +180 -32
  326. package/dist/src/memory-decay.js +47 -0
  327. package/dist/src/memory-lifecycle.js +1310 -0
  328. package/dist/src/memory-utility.js +27 -40
  329. package/dist/src/mock-llm.js +36 -61
  330. package/dist/src/model.js +81 -36
  331. package/dist/src/realtime-pricing.js +103 -0
  332. package/dist/src/recall/card.js +74 -0
  333. package/dist/src/recall/evaluator.js +140 -0
  334. package/dist/src/recall/integration.js +17 -0
  335. package/dist/src/recall/tick.js +166 -0
  336. package/dist/src/session-link/plan-it-action.js +48 -0
  337. package/dist/src/session-link/session-link.js +248 -0
  338. package/dist/src/session-zones.js +507 -0
  339. package/dist/src/share/adapters.js +51 -0
  340. package/dist/src/share/share-consent.js +76 -0
  341. package/dist/src/share/share-deck.js +58 -0
  342. package/dist/src/share/share-token.js +146 -0
  343. package/dist/src/slot-spec.js +84 -106
  344. package/dist/src/state-ledger.js +767 -0
  345. package/dist/src/state-repair.js +299 -0
  346. package/dist/src/subagent-job-id-guard.js +58 -0
  347. package/dist/src/time-anchor.js +231 -33
  348. package/dist/src/tool-packet.js +20 -8
  349. package/dist/src/travel-slots.js +58 -35
  350. package/dist/src/travel-timeline.js +59 -0
  351. package/dist/src/turn-deadline.js +287 -0
  352. package/dist/src/turn-policy.js +118 -0
  353. package/dist/src/tz-resolver.js +143 -0
  354. package/dist/src/unified.js +538 -360
  355. package/dist/src/wish-pool.js +42 -12
  356. package/dist/src/write-gate.js +495 -0
  357. package/dist/src/z3-shared.js +284 -0
  358. package/extension/README.md +21 -0
  359. package/extension/README.zh-CN.md +21 -0
  360. package/extension/background.js +463 -0
  361. package/extension/content-bridge.js +270 -0
  362. package/extension/content-main.js +111 -0
  363. package/extension/manifest.json +60 -0
  364. package/package.json +407 -14
  365. package/schemas/booking.intent.schema.json +193 -0
  366. package/schemas/booking.surface.schema.json +2622 -0
  367. package/ts/capabilities/agent-reach-deep.ts +114 -0
  368. package/ts/capabilities/agent-reach.ts +198 -0
  369. package/ts/capabilities/anything.ts +255 -0
  370. package/ts/capabilities/artifacts.ts +428 -0
  371. package/ts/capabilities/fact-log.ts +54 -0
  372. package/ts/capabilities/flyai-config.ts +388 -0
  373. package/ts/capabilities/flyai.ts +1274 -0
  374. package/ts/capabilities/hbcli.ts +253 -71
  375. package/ts/capabilities/incident-log.ts +88 -59
  376. package/ts/capabilities/itinerary-artifact.ts +214 -0
  377. package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
  378. package/ts/capabilities/itinerary-deck-export.ts +378 -0
  379. package/ts/capabilities/opensky.ts +121 -0
  380. package/ts/capabilities/session/action-cache.ts +148 -0
  381. package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
  382. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  383. package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
  384. package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
  385. package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
  386. package/ts/capabilities/session/benchmark.ts +276 -0
  387. package/ts/capabilities/session/bridge-ledger.ts +333 -0
  388. package/ts/capabilities/session/extension-bridge.ts +794 -0
  389. package/ts/capabilities/session/extension-channel.ts +144 -0
  390. package/ts/capabilities/session/extension-distribution.ts +266 -0
  391. package/ts/capabilities/session/extract.ts +54 -0
  392. package/ts/capabilities/session/golden-score.ts +139 -0
  393. package/ts/capabilities/session/health-watch.ts +264 -0
  394. package/ts/capabilities/session/read-guard.ts +141 -0
  395. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  396. package/ts/capabilities/session/transport.ts +146 -0
  397. package/ts/capabilities/session/wizard.ts +140 -0
  398. package/ts/capabilities/session-consent.ts +211 -0
  399. package/ts/capabilities/session-login.ts +208 -0
  400. package/ts/capabilities/session-search.ts +893 -0
  401. package/ts/capabilities/weather.ts +330 -0
  402. package/ts/dsh-runtime/vendor/README.md +68 -0
  403. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  404. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
  405. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
  406. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
  407. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  408. package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
  409. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
  410. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  411. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
  412. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  413. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  414. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
  415. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  416. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
  417. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
  418. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
  419. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
  420. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  421. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  422. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
  423. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
  424. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  425. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  426. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  427. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  428. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
  429. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  430. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
  431. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  432. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  433. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
  434. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  435. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  436. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
  437. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
  438. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  439. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
  440. package/ts/package.json +296 -3
  441. package/ts/scripts/async-collect.ts +91 -0
  442. package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
  443. package/ts/scripts/skeleton-check.ts +1 -1
  444. package/ts/scripts/skeleton-integration-test.ts +23 -1
  445. package/ts/scripts/state-cli.ts +568 -0
  446. package/ts/scripts/turn-handoff-collect.ts +178 -0
  447. package/ts/src/artifact-gate.ts +991 -0
  448. package/ts/src/benchmark-agent-conformance.ts +619 -0
  449. package/ts/src/benchmark-environment-bridge.ts +538 -0
  450. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  451. package/ts/src/benchmark-tool-isolation.ts +166 -0
  452. package/ts/src/bookable-facts.ts +837 -0
  453. package/ts/src/booking-saga.ts +133 -0
  454. package/ts/src/booking-surface/availability-policy.ts +522 -0
  455. package/ts/src/booking-surface/booking-intent.ts +86 -0
  456. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  457. package/ts/src/booking-surface/canonical-schema.js +116 -0
  458. package/ts/src/booking-surface/contracts.ts +475 -0
  459. package/ts/src/booking-surface/dsh-planner.ts +1949 -0
  460. package/ts/src/booking-surface/dsh-plugin.js +378 -0
  461. package/ts/src/booking-surface/dsh-worker.ts +110 -0
  462. package/ts/src/booking-surface/error-codes.ts +241 -0
  463. package/ts/src/booking-surface/index.ts +9 -0
  464. package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
  465. package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
  466. package/ts/src/booking-surface/profile.ts +42 -0
  467. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  468. package/ts/src/booking-surface/runtime.ts +1793 -0
  469. package/ts/src/booking-surface/server.ts +733 -0
  470. package/ts/src/booking-surface/startup.ts +201 -0
  471. package/ts/src/booking-surface/validation.ts +217 -0
  472. package/ts/src/companions.ts +113 -0
  473. package/ts/src/contracts.ts +13 -3
  474. package/ts/src/dsh-llm.ts +44 -11
  475. package/ts/src/flyai-setup-tool.ts +77 -0
  476. package/ts/src/index.ts +2107 -224
  477. package/ts/src/itinerary-deck.ts +189 -0
  478. package/ts/src/itinerary-doc-shared.ts +741 -0
  479. package/ts/src/itinerary-html.ts +171 -0
  480. package/ts/src/loop.ts +278 -40
  481. package/ts/src/memory-capture.ts +283 -0
  482. package/ts/src/memory-decay.ts +55 -0
  483. package/ts/src/memory-utility.ts +70 -0
  484. package/ts/src/mock-llm.ts +4 -8
  485. package/ts/src/model.ts +88 -2
  486. package/ts/src/recall/card.ts +106 -0
  487. package/ts/src/recall/evaluator.ts +170 -0
  488. package/ts/src/recall/integration.ts +35 -0
  489. package/ts/src/recall/tick.ts +213 -0
  490. package/ts/src/session-link/plan-it-action.ts +78 -0
  491. package/ts/src/session-link/session-link.ts +275 -0
  492. package/ts/src/share/adapters.ts +93 -0
  493. package/ts/src/share/share-consent.ts +105 -0
  494. package/ts/src/share/share-deck.ts +85 -0
  495. package/ts/src/share/share-token.ts +166 -0
  496. package/ts/src/slot-spec.ts +172 -0
  497. package/ts/src/state-ledger.ts +912 -0
  498. package/ts/src/subagent-job-id-guard.ts +72 -0
  499. package/ts/src/time-anchor.ts +338 -0
  500. package/ts/src/tool-packet.ts +32 -0
  501. package/ts/src/travel-slots.ts +219 -0
  502. package/ts/src/travel-timeline.ts +83 -0
  503. package/ts/src/turn-deadline.ts +366 -0
  504. package/ts/src/turn-policy.ts +154 -0
  505. package/ts/src/unified.ts +360 -89
  506. package/ts/src/wish-pool.ts +79 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Recall condition evaluator(issue #577,Phase D;研究决策 §3 Phase D;
3
+ * 自检 review 修复见 PR #578 评论——两轮:广播升级/容器与时钟守卫/单表派生;
4
+ * #577 收口:条件资格门 + 命中证据,见 docs/design/recall-trigger.md §2)。
5
+ *
6
+ * 纯函数:给定 wish-pool 候选条目 + 当前上下文(时间 + 召回窗口 match_context +
7
+ * 各类简化信号),产出一组 RecallTrigger——每个含「为什么是现在」的具体原因
8
+ * (条件名 + 当前值 + 阈值)与实际命中的愿望条件(match_hits)与 source tag
9
+ * (**proactive with provenance**——研究文档红线)。
10
+ *
11
+ * 纪律:
12
+ * - 原因闭集 5 类,**单表派生**(RECALL_REASON_LABEL 为唯一事实源;union / 数组 /
13
+ * label 全部从它推导——新增原因 = 改一张表,编译器强制 card.ts 的 hint 表补全);
14
+ * - **条件资格门**:候选必须先过 scoreWishMatch(wish-pool.ts 原样复用,不复制
15
+ * 判定逻辑)——任一 days/budget/month 命中即有召回资格(既有语义:是召回资格,
16
+ * 不是「全部出行条件已满足」的证明);命名 down 通道否证。信号(含定向)不能
17
+ * 绕过资格门;match_context 缺省/畸形 → fail-closed 空返回,绝不降级为广播;
18
+ * - **职责边界**:信号是 producer 给的人话信号(current_value/threshold 是展示
19
+ * 字符串,不是类型化价格/日期事实),评估器不解析它们;资格事实只来自注入的
20
+ * match_context;
21
+ * - 畸形输入(信号/容器/时钟/条目)**跳过或空返回,不崩**——评估器是消费链一环,
22
+ * 一个坏 tick 不能杀掉调用方的 drain loop;
23
+ * - wish_ids 非数组(如字符串)= 畸形信号,**跳过**(绝不降级为广播匹配——
24
+ * 广播是最激进模式,降级等于扩权);
25
+ * - muted 过滤用 `muted === true` 严格判定(JSON 往返的 'false' 字符串不再误杀)。
26
+ */
27
+
28
+ import { scoreWishMatch, type WishMatch, type WishMatchContext, type WishPoolEntry } from '../wish-pool.ts'
29
+
30
+ /** 唯一事实源:5 类原因 → 中文标签。新增原因 = 在此表加一行(union/数组自动派生;
31
+ * card.ts 的 ACTION_HINT 表由 Record<RecallReason,...> 编译强制补全)。 */
32
+ export const RECALL_REASON_LABEL = {
33
+ holiday_proximity: '假期临近',
34
+ price_drop: '价格降至阈值',
35
+ weather_window: '天气窗口打开',
36
+ route_new: '新航线/班次开通',
37
+ availability_recovered: '通道恢复可用',
38
+ } as const
39
+
40
+ export type RecallReason = keyof typeof RECALL_REASON_LABEL
41
+
42
+ /** 闭集数组从 label 表派生(单一事实源;不再手抄第二份) */
43
+ export const RECALL_REASONS: readonly RecallReason[] = Object.keys(RECALL_REASON_LABEL) as RecallReason[]
44
+
45
+ /** 单条触发信号(外部世界的一次状态变化;由 M4 的 sensor/tick 填充) */
46
+ export interface RecallSignal {
47
+ reason: RecallReason
48
+ /** 源 tag(必填非空;渲染为 `[source:xxx]`) */
49
+ source: string
50
+ /** 当前值(人话,如「2026-10-01(距今 7 天)」;producer 展示串,评估器不解析) */
51
+ current_value: string
52
+ /** 阈值描述(人话,如「距假期 ≤14 天」;同上) */
53
+ threshold: string
54
+ /** 受影响的 wish_id 列表(空/缺省 = 广播;**非数组 = 畸形,整条跳过**) */
55
+ wish_ids?: string[]
56
+ }
57
+
58
+ /** 评估上下文(tick 到来时由调用方组装;纯数据)。
59
+ * match_context 是 scoreWishMatch 的窗口事实(days/budgetCny/month/channelDown),
60
+ * 缺省/畸形 → 整轮 fail-closed(空返回)——没有窗口证据就不宣称「现在可以去」。 */
61
+ export interface RecallContext {
62
+ now: Date
63
+ signals: RecallSignal[]
64
+ match_context: WishMatchContext
65
+ }
66
+
67
+ /** 触发结果:wish × signal 的配对(一个 wish 可命中多个信号,一卡一信号) */
68
+ export interface RecallTrigger {
69
+ wish_id: string
70
+ wish_name: string
71
+ signal: RecallSignal
72
+ /** 评估时刻(ISO) */
73
+ evaluated_at: string
74
+ /** 实际命中的愿望条件(scoreWishMatch 的逐项 hits,如「days≥5」;恒非空数组——
75
+ * 资格门保证;渲染「为什么是它」用,不主张全部出行条件已满足) */
76
+ match_hits: string[]
77
+ }
78
+
79
+ /**
80
+ * 评估:候选 × 信号 → 触发列表。
81
+ * 两道门,先资格后匹配:
82
+ * 1. **资格门**:候选非 muted + 有稳定非空 wish_id + scoreWishMatch(entry,
83
+ * match_context) 非空(任一条件命中,且无命名 down 通道否证)——信号匹配只在
84
+ * 有资格的候选上进行;
85
+ * 2. **匹配门**:signal.wish_ids 是非空字符串数组时按 id 精确匹配(定向);
86
+ * 为空/缺省时匹配全部**有资格**候选(广播)。**wish_ids 为其它真值类型
87
+ * (字符串/对象等)视为畸形信号,整条跳过**——绝不降级为广播。
88
+ * 容器/时钟/上下文守卫:signals/pool 非数组、ctx.now 非法、match_context 缺省或
89
+ * 畸形 → 返回 [](不抛,不崩 drain loop,不广播扩权)。
90
+ */
91
+ export function evaluateRecallTriggers(pool: WishPoolEntry[], ctx: RecallContext): RecallTrigger[] {
92
+ // 容器与时钟守卫:一个坏 tick 不能杀掉调用方(fail-closed 空返回)
93
+ if (!Array.isArray(pool) || !Array.isArray(ctx?.signals)) return []
94
+ const nowMs = ctx.now instanceof Date ? ctx.now.getTime() : Number.NaN
95
+ if (!Number.isFinite(nowMs)) return []
96
+ // 条件上下文守卫:缺省/畸形 → fail-closed(绝不降级为「全部广播」——
97
+ // 没有窗口证据就广播「现在可以去」正是 #577 要堵住的扩权)
98
+ if (!isWishMatchContext(ctx.match_context)) return []
99
+ const evaluatedAt = new Date(nowMs).toISOString()
100
+
101
+ // 资格门:scoreWishMatch 非空(≥1 项 days/budget/month 命中,且无命名通道 down 否证)
102
+ // 才进入候选;保持 pool 原序(广播/定向的输出顺序与既有行为一致)
103
+ const eligible: Array<{ entry: WishPoolEntry; match: WishMatch }> = []
104
+ for (const e of pool) {
105
+ if (e?.muted === true || typeof e?.wish_id !== 'string' || e.wish_id.length === 0) continue
106
+ const match = scoreCandidate(e, ctx.match_context)
107
+ if (match !== null) eligible.push({ entry: e, match })
108
+ }
109
+
110
+ const triggers: RecallTrigger[] = []
111
+ for (const signal of ctx.signals) {
112
+ if (!isRecallSignal(signal)) continue
113
+ const targets = Array.isArray(signal.wish_ids) && signal.wish_ids.length > 0
114
+ ? eligible.filter(({ entry }) => (signal.wish_ids as string[]).includes(entry.wish_id as string))
115
+ : eligible
116
+ for (const { entry, match } of targets) {
117
+ triggers.push({
118
+ wish_id: String(entry.wish_id),
119
+ wish_name: typeof entry.name === 'string' && entry.name.length > 0 ? entry.name : String(entry.wish_id),
120
+ signal,
121
+ evaluated_at: evaluatedAt,
122
+ match_hits: [...match.hits],
123
+ })
124
+ }
125
+ }
126
+ return triggers
127
+ }
128
+
129
+ /**
130
+ * 资格判定:scoreWishMatch 原样复用(唯一判定层,不复制判定/阈值逻辑)。
131
+ * 意外抛错(运行时畸形形态逃过静态与结构检查)→ 该条目无资格,不崩——
132
+ * 单个坏条目不能杀掉整轮评估。
133
+ */
134
+ function scoreCandidate(entry: WishPoolEntry, ctx: WishMatchContext): WishMatch | null {
135
+ try {
136
+ return scoreWishMatch(entry, ctx)
137
+ } catch {
138
+ return null
139
+ }
140
+ }
141
+
142
+ /** match_context 运行时校验(JSON 往返边界强制):非数组对象 + 数值字段(若给出)
143
+ * 必须是有限数字 + channelDown(若给出非 null)必须具备 has 方法(Set 形)。
144
+ * 与 scoreWishMatch 的缺省语义对齐:channelDown 缺省/null = 不启用通道否证。 */
145
+ function isWishMatchContext(value: unknown): value is WishMatchContext {
146
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) return false
147
+ const c = value as Record<string, unknown>
148
+ for (const k of ['days', 'budgetCny', 'month'] as const) {
149
+ const v = c[k]
150
+ if (v !== undefined && (typeof v !== 'number' || !Number.isFinite(v))) return false
151
+ }
152
+ const down = c.channelDown
153
+ if (down !== undefined && down !== null && typeof (down as { has?: unknown }).has !== 'function') return false
154
+ return true
155
+ }
156
+
157
+ /** 信号校验(边界强制,不从输入信任):reason 在闭集 + source 非空字符串 +
158
+ * current_value/threshold 为字符串 + **wish_ids 一旦给出必须是字符串数组**
159
+ * (字符串等真值非数组 = 畸形,拒绝——绝不降级广播)。 */
160
+ function isRecallSignal(value: unknown): value is RecallSignal {
161
+ if (typeof value !== 'object' || value === null) return false
162
+ const s = value as Record<string, unknown>
163
+ if (!RECALL_REASONS.includes(s.reason as RecallReason)) return false
164
+ if (typeof s.source !== 'string' || s.source.length === 0) return false
165
+ if (typeof s.current_value !== 'string' || typeof s.threshold !== 'string') return false
166
+ if (s.wish_ids !== undefined && s.wish_ids !== null) {
167
+ if (!Array.isArray(s.wish_ids) || !s.wish_ids.every(id => typeof id === 'string')) return false
168
+ }
169
+ return true
170
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Recall wish-pool integration(issue #577,Phase D)。
3
+ *
4
+ * 把 wish-pool 候选条目接到 evaluator:从 tick + signals 组装上下文 → 跑评估 →
5
+ * 产触发列表 + why-now 卡。**只读 wish-pool**(不调用 mutation;撤回/通知留 M4)。
6
+ *
7
+ * 本模块不接 IO——数据进出全靠参数(测试全离线;M4 由真实 tick source + sensor 喂数据)。
8
+ */
9
+
10
+ import type { WishMatchContext, WishPoolEntry } from '../wish-pool.ts'
11
+ import { evaluateRecallTriggers, type RecallContext, type RecallSignal, type RecallTrigger } from './evaluator.ts'
12
+ import { buildWhyNowCard, type WhyNowCard } from './card.ts'
13
+ import type { RecallTick } from './tick.ts'
14
+
15
+ export interface PoolRecallInput {
16
+ pool: WishPoolEntry[]
17
+ tick: RecallTick
18
+ signals: RecallSignal[]
19
+ /** 召回窗口事实(days/budgetCny/month/channelDown;原样传给 evaluateRecallTriggers
20
+ * → scoreWishMatch 条件资格门)。缺省/畸形 → 评估整轮 fail-closed(空返回)。 */
21
+ match_context: WishMatchContext
22
+ }
23
+
24
+ export interface PoolRecallResult {
25
+ triggers: RecallTrigger[]
26
+ cards: WhyNowCard[]
27
+ }
28
+
29
+ /** 从 wish-pool + tick + signals + 召回窗口跑一轮评估并产卡(纯函数编排) */
30
+ export function evaluatePoolRecall(input: PoolRecallInput): PoolRecallResult {
31
+ const ctx: RecallContext = { now: input.tick.at, signals: input.signals, match_context: input.match_context }
32
+ const triggers = evaluateRecallTriggers(input.pool, ctx)
33
+ const cards = triggers.map(buildWhyNowCard)
34
+ return { triggers, cards }
35
+ }
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Recall tick scheduler(issue #577,Phase D;研究决策见
3
+ * docs/research/karpo-deck-web-research.md §3 Phase D;
4
+ * seam 约束见 docs/design/external-event-seam.md §3.3/§4——**pull model,M5 前无 push**;
5
+ * 自检 review 两轮修复见 PR #578 评论)。
6
+ *
7
+ * 设计契约:
8
+ * - TickSource:`next()` **运行中阻塞到下一个 tick 可用**;**未运行(未启动或已
9
+ * stop)返回 null = 流终结**——drain-loop 调用方安全退出,永不悬挂;
10
+ * - PeriodicTickSource:**默认 enabled=false**(显式 opt-in);start() 三态
11
+ * ('started'|'disabled'|'already-running');timer.unref()(不拽住事件循环);
12
+ * pending 有界(maxPending 默认 100,溢出丢最旧);**stop() 清空 pending 并把
13
+ * 全部等待者以 null 唤醒**(终结核平——消费协程不泄漏,重启不吐陈旧 tick);
14
+ * - RecallTickScheduler:**永不抛错**——evaluate/toCard/deliver 全部容错:
15
+ * evaluate 抛错 → {delivered:0, failed:0, error};单卡 toCard/deliver 抛错 →
16
+ * 只弃该卡(failed++ 并记 errors),余下继续;返回 {delivered, failed, errors?};
17
+ * - **不推送**:sink 是注入的下游,不是 notification/push 通道(seam §4)。
18
+ */
19
+
20
+ export interface RecallTick {
21
+ /** tick 发生的时刻 */
22
+ at: Date
23
+ /** 来源标记(fail-safe 渲染用;不参与逻辑) */
24
+ source: string
25
+ }
26
+
27
+ export interface TickSource {
28
+ /** 拉下一个 tick:**运行中**阻塞到可用;**未运行**(未启动/已 stop)返回 null = 流终结 */
29
+ next(): Promise<RecallTick | null>
30
+ }
31
+
32
+ /** 测试用 InMemory tick source:预排好的一组 tick,按序返回,耗尽返回 null。
33
+ * 构造与 next() 均防御式深拷贝(调用方改返回的 tick.at 不会污染队列快照)。 */
34
+ export class InMemoryTickSource implements TickSource {
35
+ private queue: RecallTick[]
36
+ constructor(ticks: RecallTick[]) {
37
+ this.queue = ticks.map(t => ({ at: new Date(t.at.getTime()), source: t.source }))
38
+ }
39
+ async next(): Promise<RecallTick | null> {
40
+ const t = this.queue.shift()
41
+ if (t === undefined) return null
42
+ return { at: new Date(t.at.getTime()), source: t.source } // 返回副本,调用方可安全改
43
+ }
44
+ }
45
+
46
+ export type PeriodicTickStartResult = 'started' | 'disabled' | 'already-running'
47
+
48
+ export interface PeriodicTickSourceOptions {
49
+ /** tick 间隔(毫秒;必须为正数) */
50
+ intervalMs: number
51
+ /** **默认 false;显式 opt-in 才产生 tick**(纪律:不悄悄起后台循环) */
52
+ enabled?: boolean
53
+ /** 来源标记 */
54
+ source?: string
55
+ /** pending 队列上界(默认 100;溢出丢最旧——慢消费者不会无限积压过期 tick) */
56
+ maxPending?: number
57
+ }
58
+
59
+ /**
60
+ * 周期 tick source(默认关闭):封装 Node setInterval。
61
+ * - enabled=false 构造默认,显式 opt-in;start() 三态返回(不混同);
62
+ * - next() 语义:**运行中**阻塞到下个 tick;**未运行**(disabled/未 start/已 stop)
63
+ * 立即返回 null(流终结——drain-loop 安全退出,永不悬挂);
64
+ * - stop() 幂等:**清空 pending(重启不吐陈旧 tick)+ 把全部等待者以 null 唤醒**
65
+ * (消费协程不泄漏);
66
+ * - timer.unref():忘了 stop() 也不拽住进程事件循环;
67
+ * - pending 有界(maxPending,溢出丢最旧)。
68
+ */
69
+ export class PeriodicTickSource implements TickSource {
70
+ readonly enabled: boolean
71
+ private intervalMs: number
72
+ private sourceLabel: string
73
+ private maxPending: number
74
+ private timer: ReturnType<typeof setInterval> | null = null
75
+ private pending: RecallTick[] = []
76
+ private waiters: Array<(t: RecallTick | null) => void> = []
77
+
78
+ constructor(opts: PeriodicTickSourceOptions) {
79
+ this.enabled = opts.enabled === true
80
+ this.intervalMs = opts.intervalMs
81
+ this.sourceLabel = opts.source ?? 'periodic'
82
+ this.maxPending = opts.maxPending ?? 100
83
+ if (!Number.isFinite(this.intervalMs) || this.intervalMs <= 0) {
84
+ throw new Error(`PeriodicTickSource intervalMs 必须为正数(实测 ${this.intervalMs})`)
85
+ }
86
+ if (!Number.isInteger(this.maxPending) || this.maxPending < 1) {
87
+ throw new Error(`PeriodicTickSource maxPending 必须为 ≥1 整数(实测 ${this.maxPending})`)
88
+ }
89
+ }
90
+
91
+ /** 启动内部 setInterval;三态返回(见类注释)。已启动时幂等。 */
92
+ start(): PeriodicTickStartResult {
93
+ if (!this.enabled) return 'disabled'
94
+ if (this.timer !== null) return 'already-running'
95
+ this.timer = setInterval(() => {
96
+ const tick: RecallTick = { at: new Date(), source: this.sourceLabel }
97
+ // 优先唤醒等待者;无等待者入有界队列(溢出丢最旧)
98
+ const waiter = this.waiters.shift()
99
+ if (waiter !== undefined) {
100
+ waiter(tick)
101
+ return
102
+ }
103
+ if (this.pending.length >= this.maxPending) this.pending.shift()
104
+ this.pending.push(tick)
105
+ }, this.intervalMs)
106
+ // unref:不拽住事件循环(调用方忘了 stop() 也不会让进程赖着不死;测试进程自然退出)
107
+ ;(this.timer as unknown as { unref?: () => void }).unref?.()
108
+ return 'started'
109
+ }
110
+
111
+ /** 停止(幂等):清 timer + **清空 pending(重启不吐陈旧 tick)+ 等待者全部以 null 唤醒**(终结核平) */
112
+ stop(): void {
113
+ if (this.timer !== null) {
114
+ clearInterval(this.timer)
115
+ this.timer = null
116
+ }
117
+ this.pending = []
118
+ const waiters = this.waiters
119
+ this.waiters = []
120
+ for (const w of waiters) w(null)
121
+ }
122
+
123
+ /** 当前 pending 队列长度(只读;测试与诊断用) */
124
+ get pendingLength(): number {
125
+ return this.pending.length
126
+ }
127
+
128
+ /** **运行中**阻塞到下一个 tick;**未运行**(disabled/未 start/已 stop)返回 null(流终结) */
129
+ async next(): Promise<RecallTick | null> {
130
+ if (this.timer === null) return null
131
+ const queued = this.pending.shift()
132
+ if (queued !== undefined) return queued
133
+ return new Promise<RecallTick | null>(resolve => {
134
+ this.waiters.push(resolve)
135
+ })
136
+ }
137
+ }
138
+
139
+ /** scheduler 的下游 sink:注入(测试用数组收集;M4 接 wish_pool_list / 对话面) */
140
+ export interface RecallSink {
141
+ deliver(card: unknown): Promise<void> | void
142
+ }
143
+
144
+ /** 数组收集 sink(测试用) */
145
+ export class ArrayRecallSink implements RecallSink {
146
+ readonly items: unknown[] = []
147
+ deliver(card: unknown): void {
148
+ this.items.push(card)
149
+ }
150
+ }
151
+
152
+ export interface RecallTickSchedulerDeps<E, C> {
153
+ /** tick 到来时的评估函数(evaluator;契约上纯函数) */
154
+ evaluate: (tick: RecallTick) => E[]
155
+ /** 评估结果到卡的转换(纯函数;签名带 tick——卡的 evaluated_at 应取 tick.at) */
156
+ toCard: (evaluation: E, tick: RecallTick) => C
157
+ /** 下游(注入;测试用 ArrayRecallSink) */
158
+ sink: RecallSink
159
+ }
160
+
161
+ export interface RecallRunResult {
162
+ /** 成功送出的卡数 */
163
+ delivered: number
164
+ /** toCard 或 deliver 抛错被跳过的卡数(不弃余下;不外泄异常) */
165
+ failed: number
166
+ /** 容错捕获的错误消息(前几条;诊断用,绝不外泄为异常) */
167
+ errors?: string[]
168
+ }
169
+
170
+ /**
171
+ * 编排器:拉 tick → 评估 → 转卡 → 送 sink。
172
+ * 每次 run 处理一个 tick(调用方决定循环;scheduler 自己不循环——不悄悄长驻)。
173
+ * **永不抛错**:evaluate 抛错 → {delivered:0, failed:0, error 记入 errors};
174
+ * 单卡 toCard/deliver 抛错 → 只弃该卡(failed++/errors 记一条),余下继续。
175
+ */
176
+ export class RecallTickScheduler<E, C> {
177
+ private deps: RecallTickSchedulerDeps<E, C>
178
+
179
+ constructor(deps: RecallTickSchedulerDeps<E, C>) {
180
+ this.deps = deps
181
+ }
182
+
183
+ async run(tick: RecallTick): Promise<RecallRunResult> {
184
+ const errors: string[] = []
185
+ let evaluations: E[]
186
+ try {
187
+ evaluations = this.deps.evaluate(tick)
188
+ if (!Array.isArray(evaluations)) evaluations = []
189
+ } catch (err) {
190
+ return { delivered: 0, failed: 0, errors: [`evaluate: ${(err as Error)?.message ?? String(err)}`] }
191
+ }
192
+ let delivered = 0
193
+ let failed = 0
194
+ for (const evaluation of evaluations) {
195
+ let card: C
196
+ try {
197
+ card = this.deps.toCard(evaluation, tick)
198
+ } catch (err) {
199
+ failed++
200
+ if (errors.length < 4) errors.push(`toCard: ${(err as Error)?.message ?? String(err)}`)
201
+ continue
202
+ }
203
+ try {
204
+ await this.deps.sink.deliver(card)
205
+ delivered++
206
+ } catch (err) {
207
+ failed++
208
+ if (errors.length < 4) errors.push(`deliver: ${(err as Error)?.message ?? String(err)}`)
209
+ }
210
+ }
211
+ return errors.length > 0 ? { delivered, failed, errors } : { delivered, failed }
212
+ }
213
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Plan-it 行动卡(issue #580,Phase E)——why-now 卡(Phase D)的一键行动
3
+ * 面:「好,规划它」深链回本地 session 继续规划该 wish。**只打开,不写**:
4
+ * 行动词位只有 plan_it(Continue planning),booking/payment 不在链接词位
5
+ * (session-link.ts 的 action 闭集),WriteGate 保持 sealed。
6
+ *
7
+ * 纪律:
8
+ * - 行动卡是数据(不是渲染 HTML);渲染面(deck / 对话 / 通知)各自消费;
9
+ * - `wish_id` 与 token payload **同源构造**(同一个值进两处),不可能漂移——
10
+ * 渲染面显示的 wish 与链接打开的 wish 是构造级同一个;
11
+ * - label 是封闭词汇(「好,规划它」改措辞 = 契约变更,先 review);
12
+ * - why-now 卡的「not_now」是渲染面本地消解(无链接、无状态写),
13
+ * 本契约不覆盖(文档声明)。
14
+ */
15
+
16
+ import type { WhyNowCard } from '../recall/card.ts'
17
+ import { formatSessionLink, signSessionLink, type SessionLinkPayload } from './session-link.ts'
18
+
19
+ export interface PlanItAction {
20
+ /** 恒 'plan_it'(构造保证;不存在其它 kind 的 PlanItAction) */
21
+ kind: 'plan_it'
22
+ /** 行动文案(封闭词汇) */
23
+ label: string
24
+ /** 渲染好的深链(formatSessionLink 默认 base `gotry://session/<token>`) */
25
+ deep_link: string
26
+ /** 与 token payload 同源构造的 wish_id(冗余自洽;渲染面可直接显示) */
27
+ wish_id: string
28
+ }
29
+
30
+ /** plan-it 行动文案(封闭词汇) */
31
+ export const PLAN_IT_LABEL = '好,规划它'
32
+
33
+ export interface BuildPlanItActionDeps {
34
+ /** 不透明 session 引用(session-link.ts 护栏:非路径) */
35
+ session_ref: string
36
+ /** 幂等 id(调用方给;缺省随机) */
37
+ link_id?: string
38
+ /** 链接有效秒数(默认 24h;上限 SESSION_LINK_TTL_MAX_SECONDS) */
39
+ ttl_seconds?: number
40
+ /** 渲染 base(缺省 gotry://session;本地 http 消费端可传 http://127.0.0.1:3080/session) */
41
+ base?: string
42
+ /** HMAC secret(缺省 resolveSessionLinkSecret()) */
43
+ secret?: string
44
+ /** 时钟注入(测试用) */
45
+ now?: () => Date
46
+ }
47
+
48
+ /**
49
+ * why-now 卡 → plan-it 行动卡。wish_id 取卡上的结构化字段(Phase E 起
50
+ * WhyNowCard 必含 wish_id——从 title 字符串反解不可接受)。
51
+ * 非法输入(wish_id 缺失/空、session_ref 路径化)抛错:行动卡构造层
52
+ * 即 fail-closed,不产「点了不知道开什么」的链接。
53
+ */
54
+ export function buildPlanItAction(card: WhyNowCard, deps: BuildPlanItActionDeps): PlanItAction {
55
+ if (typeof card?.wish_id !== 'string' || card.wish_id.length === 0) {
56
+ throw new Error('buildPlanItAction:card.wish_id 必须是非空字符串(WhyNowCard 结构化自指其 wish 是 plan-it 行动的前提)')
57
+ }
58
+ if (typeof deps?.session_ref !== 'string' || deps.session_ref.length === 0) {
59
+ throw new Error(`buildPlanItAction:session_ref 必须是非空字符串(实测 ${String(deps?.session_ref)})`)
60
+ }
61
+ const now = deps.now ?? (() => new Date())
62
+ const payload: SessionLinkPayload = {
63
+ link_id: deps.link_id ?? `plan-${now().getTime().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,
64
+ session_ref: deps.session_ref,
65
+ action: 'plan_it',
66
+ wish_id: card.wish_id,
67
+ created_at: now().toISOString(),
68
+ ttl_seconds: deps.ttl_seconds ?? 86_400,
69
+ }
70
+ // wish_id 同源:payload.wish_id 就是上面 card.wish_id 这一个值
71
+ const token = signSessionLink(payload, deps.secret)
72
+ return {
73
+ kind: 'plan_it',
74
+ label: PLAN_IT_LABEL,
75
+ deep_link: formatSessionLink(token, deps.base),
76
+ wish_id: card.wish_id,
77
+ }
78
+ }