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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (507) hide show
  1. package/README.md +172 -219
  2. package/README.zh-CN.md +222 -0
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-booking-copilot.js +53 -0
  5. package/bin/gotry-bootstrap.js +1744 -0
  6. package/bin/gotry-inner.js +733 -83
  7. package/bin/gotry-process-liveness.d.ts +27 -0
  8. package/bin/gotry-process-liveness.js +97 -0
  9. package/bin/gotry-runtime-resolution.d.ts +27 -0
  10. package/bin/gotry-runtime-resolution.js +49 -0
  11. package/bin/gotry.js +1 -1
  12. package/client/client.js +203 -0
  13. package/cordis.gotry-patch.yml +80 -56
  14. package/data/airline-airports.json +39 -0
  15. package/data/stations-12306-verify.json +524 -0
  16. package/dist/capabilities/agent-reach-deep.js +45 -122
  17. package/dist/capabilities/agent-reach.js +101 -146
  18. package/dist/capabilities/anything.js +135 -94
  19. package/dist/capabilities/artifacts.js +370 -0
  20. package/dist/capabilities/channel-health.js +131 -0
  21. package/dist/capabilities/channel-registry.js +256 -0
  22. package/dist/capabilities/doctor.js +546 -0
  23. package/dist/capabilities/effect.js +394 -0
  24. package/dist/capabilities/external-event.js +149 -0
  25. package/dist/capabilities/fact-log.js +55 -0
  26. package/dist/capabilities/flyai-config.js +337 -0
  27. package/dist/capabilities/flyai.js +947 -110
  28. package/dist/capabilities/fx-contract.js +293 -0
  29. package/dist/capabilities/geo-atlas.js +246 -0
  30. package/dist/capabilities/ground-transfer.js +509 -0
  31. package/dist/capabilities/hbcli.js +260 -123
  32. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  33. package/dist/capabilities/incident-log.js +131 -61
  34. package/dist/capabilities/itinerary-artifact.js +190 -0
  35. package/dist/capabilities/itinerary-deck-artifact.js +194 -0
  36. package/dist/capabilities/itinerary-deck-export.js +339 -0
  37. package/dist/capabilities/lavish-local.js +924 -0
  38. package/dist/capabilities/model-override.js +38 -0
  39. package/dist/capabilities/opensky.js +54 -43
  40. package/dist/capabilities/resilience.js +191 -0
  41. package/dist/capabilities/session/action-cache.js +50 -44
  42. package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
  43. package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
  44. package/dist/capabilities/session/adapters/dida-portal.js +274 -0
  45. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  46. package/dist/capabilities/session/adapters/rail-12306.js +342 -0
  47. package/dist/capabilities/session/benchmark.js +189 -0
  48. package/dist/capabilities/session/bridge-ledger.js +224 -0
  49. package/dist/capabilities/session/extension-bridge.js +636 -0
  50. package/dist/capabilities/session/extension-channel.js +109 -0
  51. package/dist/capabilities/session/extension-distribution.js +230 -0
  52. package/dist/capabilities/session/extract.js +31 -21
  53. package/dist/capabilities/session/golden-score.js +114 -0
  54. package/dist/capabilities/session/health-watch.js +215 -0
  55. package/dist/capabilities/session/read-guard.js +75 -24
  56. package/dist/capabilities/session/static-flight-golden.js +150 -0
  57. package/dist/capabilities/session/transport.js +97 -83
  58. package/dist/capabilities/session/wizard.js +117 -0
  59. package/dist/capabilities/session-consent.js +178 -0
  60. package/dist/capabilities/session-login.js +170 -0
  61. package/dist/capabilities/session-search.js +635 -44
  62. package/dist/capabilities/spawn-bounded.js +203 -0
  63. package/dist/capabilities/sponsor-plugin.js +341 -0
  64. package/dist/capabilities/visa-policy.js +140 -0
  65. package/dist/capabilities/weather.js +243 -138
  66. package/dist/data/session-golden-20.json +25 -0
  67. package/dist/data/sf-golden-manifest.json +103 -0
  68. package/dist/data/sf-static-routes.json +91 -0
  69. package/dist/scripts/action-cache-tests.js +24 -59
  70. package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
  71. package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
  72. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  73. package/dist/scripts/agent-reach-error-tests.js +354 -0
  74. package/dist/scripts/agent-reach-tests.js +26 -18
  75. package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
  76. package/dist/scripts/anything-tests.js +116 -60
  77. package/dist/scripts/artifact-client-contract-tests.js +220 -0
  78. package/dist/scripts/artifacts-capability-tests.js +727 -0
  79. package/dist/scripts/async-collect.js +50 -11
  80. package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
  81. package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
  82. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
  83. package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
  84. package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
  85. package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
  86. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
  87. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
  88. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
  89. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
  90. package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
  91. package/dist/scripts/booking-copilot-dsh-warmer-proof.js +123 -0
  92. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
  93. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
  94. package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
  95. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
  96. package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
  97. package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
  98. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
  99. package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
  100. package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
  101. package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
  102. package/dist/scripts/booking-executor-tests.js +80 -0
  103. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  104. package/dist/scripts/booking-saga-tests.js +125 -0
  105. package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
  106. package/dist/scripts/bootstrap-tests.js +1479 -0
  107. package/dist/scripts/bridge-events-tests.js +239 -0
  108. package/dist/scripts/bridge-ledger-tests.js +428 -0
  109. package/dist/scripts/build-changelog.js +238 -0
  110. package/dist/scripts/build-metrics-report.js +331 -0
  111. package/dist/scripts/changelog-tests.js +118 -0
  112. package/dist/scripts/channel-probe-tests.js +103 -0
  113. package/dist/scripts/channel-probe.js +169 -0
  114. package/dist/scripts/channel-registry-tests.js +208 -0
  115. package/dist/scripts/companion-tests.js +25 -70
  116. package/dist/scripts/dida-cookie-probe.js +87 -0
  117. package/dist/scripts/dida-runner-stop-tests.js +311 -0
  118. package/dist/scripts/dida-sw-probe.js +114 -0
  119. package/dist/scripts/diff-test.js +17 -12
  120. package/dist/scripts/doctor-tests.js +564 -0
  121. package/dist/scripts/dsh-artifact-e2e.js +279 -0
  122. package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
  123. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  124. package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
  125. package/dist/scripts/dsh-runtime-closure.js +162 -0
  126. package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
  127. package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
  128. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  129. package/dist/scripts/effect-tests.js +539 -0
  130. package/dist/scripts/engine-run.js +1 -3
  131. package/dist/scripts/engine-tests.js +16 -10
  132. package/dist/scripts/evaluation-cadence-tests.js +174 -0
  133. package/dist/scripts/evaluation-contract-tests.js +268 -0
  134. package/dist/scripts/extension-distribution-cli.js +39 -0
  135. package/dist/scripts/extension-distribution-tests.js +443 -0
  136. package/dist/scripts/extension-tests.js +988 -0
  137. package/dist/scripts/external-event-tests.js +350 -0
  138. package/dist/scripts/fact-gate-tests.js +869 -0
  139. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  140. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  141. package/dist/scripts/flight-malformed-tests.js +159 -0
  142. package/dist/scripts/flyai-setup-tests.js +228 -0
  143. package/dist/scripts/flyai-setup-tool-tests.js +54 -0
  144. package/dist/scripts/flyai-tests.js +692 -0
  145. package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
  146. package/dist/scripts/fx-contract-tests.js +201 -0
  147. package/dist/scripts/g5-guard-tests.js +131 -0
  148. package/dist/scripts/g5-guard.js +189 -0
  149. package/dist/scripts/gen-lunar-anchors.js +106 -0
  150. package/dist/scripts/geo-atlas-tests.js +283 -0
  151. package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
  152. package/dist/scripts/gotry-try-demo-tests.js +79 -0
  153. package/dist/scripts/gotry-try-demo.js +49 -0
  154. package/dist/scripts/ground-transfer-tests.js +956 -0
  155. package/dist/scripts/hbcli-e2e-tests.js +159 -0
  156. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  157. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  158. package/dist/scripts/hbcli-tests.js +121 -69
  159. package/dist/scripts/health-watch-cli.js +73 -0
  160. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  161. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  162. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  163. package/dist/scripts/i18n-tests.js +41 -0
  164. package/dist/scripts/incident-tests.js +281 -87
  165. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  166. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  167. package/dist/scripts/issue-271-liveness-tests.js +569 -0
  168. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  169. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  170. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  171. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  172. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  173. package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
  174. package/dist/scripts/itinerary-artifact-tests.js +368 -0
  175. package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
  176. package/dist/scripts/itinerary-deck-export-tests.js +552 -0
  177. package/dist/scripts/itinerary-deck-tests.js +435 -0
  178. package/dist/scripts/itinerary-html-tests.js +426 -0
  179. package/dist/scripts/journey-tests.js +22 -27
  180. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  181. package/dist/scripts/kernel-manifest-gate.js +126 -0
  182. package/dist/scripts/kernel-manifest-tests.js +212 -0
  183. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  184. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  185. package/dist/scripts/kernel-manifest.js +369 -0
  186. package/dist/scripts/lavish-local-tests.js +1226 -0
  187. package/dist/scripts/lavish-product-tools-tests.js +537 -0
  188. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  189. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  190. package/dist/scripts/ledger-tests.js +382 -246
  191. package/dist/scripts/ledger-workflow-crash.js +10 -20
  192. package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
  193. package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
  194. package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
  195. package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
  196. package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
  197. package/dist/scripts/memory-capture-tests.js +242 -61
  198. package/dist/scripts/memory-decay-tests.js +18 -52
  199. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  200. package/dist/scripts/memory-lifecycle.js +196 -0
  201. package/dist/scripts/memory-metrics.js +24 -12
  202. package/dist/scripts/memory-value-report.js +631 -0
  203. package/dist/scripts/memory-value-tests.js +328 -0
  204. package/dist/scripts/metrics-report-tests.js +151 -0
  205. package/dist/scripts/model-override-e2e.js +137 -0
  206. package/dist/scripts/nightly-evidence-tests.js +161 -0
  207. package/dist/scripts/nightly-evidence.js +286 -0
  208. package/dist/scripts/nudge-digest.js +39 -28
  209. package/dist/scripts/observability-tests.js +381 -0
  210. package/dist/scripts/onboarding-tests.js +161 -0
  211. package/dist/scripts/opensky-check.js +18 -13
  212. package/dist/scripts/opensky-tests.js +28 -28
  213. package/dist/scripts/persona-surface-guard-tests.js +49 -0
  214. package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
  215. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  216. package/dist/scripts/price-drift-tests.js +340 -0
  217. package/dist/scripts/price-drift-watch.js +447 -0
  218. package/dist/scripts/probe-poi-tests.js +53 -45
  219. package/dist/scripts/product-metrics.js +483 -0
  220. package/dist/scripts/publish-preverify.js +110 -0
  221. package/dist/scripts/realtime-pricing-tests.js +76 -0
  222. package/dist/scripts/recall-tests.js +513 -0
  223. package/dist/scripts/replay-async.js +20 -18
  224. package/dist/scripts/replay-real.js +18 -18
  225. package/dist/scripts/replay.js +52 -82
  226. package/dist/scripts/session-attach-diagnose.js +20 -20
  227. package/dist/scripts/session-attach-poc.js +34 -37
  228. package/dist/scripts/session-benchmark.js +180 -0
  229. package/dist/scripts/session-dida-live-e2e.js +199 -0
  230. package/dist/scripts/session-extract-tests.js +14 -35
  231. package/dist/scripts/session-link-tests.js +478 -0
  232. package/dist/scripts/session-login.js +27 -46
  233. package/dist/scripts/session-tests.js +796 -96
  234. package/dist/scripts/session-zones-tests.js +358 -0
  235. package/dist/scripts/sf-live-benchmark.js +367 -0
  236. package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
  237. package/dist/scripts/sf-live-cli-tests.js +92 -0
  238. package/dist/scripts/sf-live-options.js +46 -0
  239. package/dist/scripts/sf-manifest-tests.js +30 -0
  240. package/dist/scripts/sf-manifest.js +33 -0
  241. package/dist/scripts/sf-soft-score-tests.js +67 -0
  242. package/dist/scripts/sf-summary-tests.js +324 -0
  243. package/dist/scripts/sf-summary.js +525 -0
  244. package/dist/scripts/share-tests.js +432 -0
  245. package/dist/scripts/skeleton-check.js +16 -14
  246. package/dist/scripts/skeleton-integration-test.js +28 -11
  247. package/dist/scripts/skills-contract-tests.js +51 -46
  248. package/dist/scripts/smoke-session-gate-tests.js +21 -0
  249. package/dist/scripts/smoke.js +690 -246
  250. package/dist/scripts/spawn-bounded-abort-tests.js +241 -0
  251. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  252. package/dist/scripts/state-cli-tests.js +226 -101
  253. package/dist/scripts/state-cli.js +550 -199
  254. package/dist/scripts/state-repair-cli.js +66 -0
  255. package/dist/scripts/state-repair-tests.js +140 -0
  256. package/dist/scripts/static-golden-tests.js +165 -0
  257. package/dist/scripts/time-eval-tests.js +210 -192
  258. package/dist/scripts/travel-timeline-tests.js +29 -93
  259. package/dist/scripts/turn-handoff-collect-tests.js +169 -0
  260. package/dist/scripts/turn-handoff-collect.js +161 -0
  261. package/dist/scripts/turn-policy-tests.js +55 -0
  262. package/dist/scripts/typed-contract-canary.js +176 -0
  263. package/dist/scripts/tz-resolver-sanity.js +50 -0
  264. package/dist/scripts/tz-resolver-tests.js +584 -0
  265. package/dist/scripts/unified-tests.js +23 -19
  266. package/dist/scripts/visa-policy-tests.js +62 -0
  267. package/dist/scripts/weather-tests.js +276 -41
  268. package/dist/scripts/wish-channel-gate-tests.js +54 -0
  269. package/dist/scripts/write-gate-tests.js +522 -0
  270. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  271. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  272. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  273. package/dist/scripts/z3-race-tests.js +41 -0
  274. package/dist/src/artifact-gate.js +829 -0
  275. package/dist/src/backend/kernel.js +91 -0
  276. package/dist/src/backend/modules/booking-copilot.js +92 -0
  277. package/dist/src/backend/modules/booking-executor.js +124 -0
  278. package/dist/src/backend/modules/session-search.js +551 -0
  279. package/dist/src/benchmark-agent-conformance.js +578 -0
  280. package/dist/src/benchmark-environment-bridge.js +515 -0
  281. package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
  282. package/dist/src/benchmark-tool-isolation.js +151 -0
  283. package/dist/src/bookable-facts.js +533 -0
  284. package/dist/src/booking-saga.js +97 -0
  285. package/dist/src/booking-surface/availability-policy.js +697 -0
  286. package/dist/src/booking-surface/booking-intent.js +43 -0
  287. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  288. package/dist/src/booking-surface/canonical-schema.js +116 -0
  289. package/dist/src/booking-surface/contracts.js +75 -0
  290. package/dist/src/booking-surface/dsh-planner.js +1854 -0
  291. package/dist/src/booking-surface/dsh-plugin.js +378 -0
  292. package/dist/src/booking-surface/dsh-worker.js +98 -0
  293. package/dist/src/booking-surface/error-codes.js +231 -0
  294. package/dist/src/booking-surface/index.js +9 -0
  295. package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
  296. package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
  297. package/dist/src/booking-surface/profile.js +24 -0
  298. package/dist/src/booking-surface/recovery-chain.js +367 -0
  299. package/dist/src/booking-surface/runtime.js +1968 -0
  300. package/dist/src/booking-surface/server.js +674 -0
  301. package/dist/src/booking-surface/startup.js +168 -0
  302. package/dist/src/booking-surface/validation.js +261 -0
  303. package/dist/src/bridge.js +20 -13
  304. package/dist/src/companions.js +48 -74
  305. package/dist/src/contracts.js +21 -43
  306. package/dist/src/dsh-llm.js +97 -111
  307. package/dist/src/engine.js +276 -193
  308. package/dist/src/evaluation-cadence.js +97 -0
  309. package/dist/src/evaluation-contracts.js +283 -0
  310. package/dist/src/flight-pack-adapter.js +54 -0
  311. package/dist/src/flight-pack-contract.js +2 -0
  312. package/dist/src/flyai-setup-tool.js +70 -0
  313. package/dist/src/gotry-backend.js +125 -0
  314. package/dist/src/hotel-date-gate.js +118 -0
  315. package/dist/src/i18n.js +88 -0
  316. package/dist/src/index.js +2227 -882
  317. package/dist/src/itinerary-deck.js +139 -0
  318. package/dist/src/itinerary-doc-shared.js +661 -0
  319. package/dist/src/itinerary-html.js +131 -0
  320. package/dist/src/journey.js +155 -108
  321. package/dist/src/lavish-tools.js +400 -0
  322. package/dist/src/ledger-repair-apply.js +538 -0
  323. package/dist/src/ledger-repair-plan.js +463 -0
  324. package/dist/src/loop.js +319 -160
  325. package/dist/src/memory-capture.js +180 -32
  326. package/dist/src/memory-decay.js +30 -14
  327. package/dist/src/memory-lifecycle.js +1310 -0
  328. package/dist/src/memory-utility.js +27 -40
  329. package/dist/src/mock-llm.js +36 -61
  330. package/dist/src/model.js +81 -36
  331. package/dist/src/realtime-pricing.js +103 -0
  332. package/dist/src/recall/card.js +74 -0
  333. package/dist/src/recall/evaluator.js +140 -0
  334. package/dist/src/recall/integration.js +17 -0
  335. package/dist/src/recall/tick.js +166 -0
  336. package/dist/src/session-link/plan-it-action.js +48 -0
  337. package/dist/src/session-link/session-link.js +248 -0
  338. package/dist/src/session-zones.js +507 -0
  339. package/dist/src/share/adapters.js +51 -0
  340. package/dist/src/share/share-consent.js +76 -0
  341. package/dist/src/share/share-deck.js +58 -0
  342. package/dist/src/share/share-token.js +146 -0
  343. package/dist/src/slot-spec.js +84 -106
  344. package/dist/src/state-ledger.js +435 -516
  345. package/dist/src/state-repair.js +299 -0
  346. package/dist/src/subagent-job-id-guard.js +58 -0
  347. package/dist/src/time-anchor.js +231 -33
  348. package/dist/src/tool-packet.js +20 -8
  349. package/dist/src/travel-slots.js +58 -35
  350. package/dist/src/travel-timeline.js +44 -63
  351. package/dist/src/turn-deadline.js +287 -0
  352. package/dist/src/turn-policy.js +118 -0
  353. package/dist/src/tz-resolver.js +143 -0
  354. package/dist/src/unified.js +535 -359
  355. package/dist/src/wish-pool.js +42 -12
  356. package/dist/src/write-gate.js +495 -0
  357. package/dist/src/z3-shared.js +284 -0
  358. package/extension/README.md +21 -0
  359. package/extension/README.zh-CN.md +21 -0
  360. package/extension/background.js +463 -0
  361. package/extension/content-bridge.js +270 -0
  362. package/extension/content-main.js +111 -0
  363. package/extension/manifest.json +60 -0
  364. package/package.json +407 -14
  365. package/schemas/booking.intent.schema.json +193 -0
  366. package/schemas/booking.surface.schema.json +2622 -0
  367. package/ts/capabilities/agent-reach-deep.ts +114 -0
  368. package/ts/capabilities/agent-reach.ts +198 -0
  369. package/ts/capabilities/anything.ts +255 -0
  370. package/ts/capabilities/artifacts.ts +428 -0
  371. package/ts/capabilities/fact-log.ts +54 -0
  372. package/ts/capabilities/flyai-config.ts +388 -0
  373. package/ts/capabilities/flyai.ts +1274 -0
  374. package/ts/capabilities/hbcli.ts +253 -71
  375. package/ts/capabilities/incident-log.ts +88 -59
  376. package/ts/capabilities/itinerary-artifact.ts +214 -0
  377. package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
  378. package/ts/capabilities/itinerary-deck-export.ts +378 -0
  379. package/ts/capabilities/opensky.ts +121 -0
  380. package/ts/capabilities/session/action-cache.ts +148 -0
  381. package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
  382. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  383. package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
  384. package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
  385. package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
  386. package/ts/capabilities/session/benchmark.ts +276 -0
  387. package/ts/capabilities/session/bridge-ledger.ts +333 -0
  388. package/ts/capabilities/session/extension-bridge.ts +794 -0
  389. package/ts/capabilities/session/extension-channel.ts +144 -0
  390. package/ts/capabilities/session/extension-distribution.ts +266 -0
  391. package/ts/capabilities/session/extract.ts +54 -0
  392. package/ts/capabilities/session/golden-score.ts +139 -0
  393. package/ts/capabilities/session/health-watch.ts +264 -0
  394. package/ts/capabilities/session/read-guard.ts +141 -0
  395. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  396. package/ts/capabilities/session/transport.ts +146 -0
  397. package/ts/capabilities/session/wizard.ts +140 -0
  398. package/ts/capabilities/session-consent.ts +211 -0
  399. package/ts/capabilities/session-login.ts +208 -0
  400. package/ts/capabilities/session-search.ts +893 -0
  401. package/ts/capabilities/weather.ts +330 -0
  402. package/ts/dsh-runtime/vendor/README.md +68 -0
  403. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  404. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
  405. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
  406. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
  407. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  408. package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
  409. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
  410. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  411. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
  412. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  413. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  414. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
  415. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  416. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
  417. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
  418. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
  419. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
  420. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  421. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  422. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
  423. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
  424. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  425. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  426. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  427. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  428. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
  429. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  430. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
  431. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  432. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  433. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
  434. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  435. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  436. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
  437. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
  438. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  439. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
  440. package/ts/package.json +293 -3
  441. package/ts/scripts/async-collect.ts +91 -0
  442. package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
  443. package/ts/scripts/skeleton-check.ts +1 -1
  444. package/ts/scripts/skeleton-integration-test.ts +23 -1
  445. package/ts/scripts/state-cli.ts +568 -0
  446. package/ts/scripts/turn-handoff-collect.ts +178 -0
  447. package/ts/src/artifact-gate.ts +991 -0
  448. package/ts/src/benchmark-agent-conformance.ts +619 -0
  449. package/ts/src/benchmark-environment-bridge.ts +538 -0
  450. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  451. package/ts/src/benchmark-tool-isolation.ts +166 -0
  452. package/ts/src/bookable-facts.ts +837 -0
  453. package/ts/src/booking-saga.ts +133 -0
  454. package/ts/src/booking-surface/availability-policy.ts +522 -0
  455. package/ts/src/booking-surface/booking-intent.ts +86 -0
  456. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  457. package/ts/src/booking-surface/canonical-schema.js +116 -0
  458. package/ts/src/booking-surface/contracts.ts +475 -0
  459. package/ts/src/booking-surface/dsh-planner.ts +1949 -0
  460. package/ts/src/booking-surface/dsh-plugin.js +378 -0
  461. package/ts/src/booking-surface/dsh-worker.ts +110 -0
  462. package/ts/src/booking-surface/error-codes.ts +241 -0
  463. package/ts/src/booking-surface/index.ts +9 -0
  464. package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
  465. package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
  466. package/ts/src/booking-surface/profile.ts +42 -0
  467. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  468. package/ts/src/booking-surface/runtime.ts +1793 -0
  469. package/ts/src/booking-surface/server.ts +733 -0
  470. package/ts/src/booking-surface/startup.ts +201 -0
  471. package/ts/src/booking-surface/validation.ts +217 -0
  472. package/ts/src/companions.ts +113 -0
  473. package/ts/src/contracts.ts +13 -3
  474. package/ts/src/dsh-llm.ts +44 -11
  475. package/ts/src/flyai-setup-tool.ts +77 -0
  476. package/ts/src/index.ts +1948 -198
  477. package/ts/src/itinerary-deck.ts +189 -0
  478. package/ts/src/itinerary-doc-shared.ts +741 -0
  479. package/ts/src/itinerary-html.ts +171 -0
  480. package/ts/src/loop.ts +223 -25
  481. package/ts/src/memory-capture.ts +283 -0
  482. package/ts/src/memory-decay.ts +55 -0
  483. package/ts/src/memory-utility.ts +70 -0
  484. package/ts/src/mock-llm.ts +4 -8
  485. package/ts/src/model.ts +88 -2
  486. package/ts/src/recall/card.ts +106 -0
  487. package/ts/src/recall/evaluator.ts +170 -0
  488. package/ts/src/recall/integration.ts +35 -0
  489. package/ts/src/recall/tick.ts +213 -0
  490. package/ts/src/session-link/plan-it-action.ts +78 -0
  491. package/ts/src/session-link/session-link.ts +275 -0
  492. package/ts/src/share/adapters.ts +93 -0
  493. package/ts/src/share/share-consent.ts +105 -0
  494. package/ts/src/share/share-deck.ts +85 -0
  495. package/ts/src/share/share-token.ts +166 -0
  496. package/ts/src/slot-spec.ts +172 -0
  497. package/ts/src/state-ledger.ts +912 -0
  498. package/ts/src/subagent-job-id-guard.ts +72 -0
  499. package/ts/src/time-anchor.ts +338 -0
  500. package/ts/src/tool-packet.ts +32 -0
  501. package/ts/src/travel-slots.ts +219 -0
  502. package/ts/src/travel-timeline.ts +83 -0
  503. package/ts/src/turn-deadline.ts +366 -0
  504. package/ts/src/turn-policy.ts +154 -0
  505. package/ts/src/unified.ts +354 -88
  506. package/ts/src/wish-pool.ts +79 -0
  507. package/dist/scripts/session-attach-wait.js +0 -41
@@ -0,0 +1,537 @@
1
+ /** Registered product lifecycle tests for issue #443. */
2
+ import assert from 'node:assert/strict';
3
+ import { createRequire } from 'node:module';
4
+ import { mkdir, mkdtemp, readFile, realpath, rm, symlink, writeFile } from 'node:fs/promises';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { LAVISH_AXI_VERSION, LavishLocalSession } from '../capabilities/lavish-local.js';
8
+ import { apply } from '../src/index.js';
9
+ const TOON_ENTRY = createRequire(import.meta.url).resolve('@toon-format/toon');
10
+ const root = await mkdtemp(join(tmpdir(), 'gotry-lavish-product-'));
11
+ const packageRoot = join(root, 'lavish-axi');
12
+ const markerRoot = join(packageRoot, 'markers');
13
+ const stateRoot = join(root, 'gotry-state');
14
+ const workspace = join(root, 'workspace');
15
+ const spacedWorkspace = join(root, 'workspace ');
16
+ const outside = join(root, 'outside');
17
+ const workspaceLink = join(root, 'workspace-link');
18
+ const CLI = `
19
+ import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
20
+ import { spawn } from 'node:child_process'
21
+ import { createServer } from 'node:http'
22
+ import { dirname, join } from 'node:path'
23
+ import { encode } from ${JSON.stringify(TOON_ENTRY)}
24
+
25
+ const markerRoot = join(dirname(import.meta.filename), '..', 'markers')
26
+ mkdirSync(markerRoot, { recursive: true })
27
+ const stateDir = process.env.LAVISH_AXI_STATE_DIR
28
+ const port = Number(process.env.LAVISH_AXI_PORT)
29
+ const argv = process.argv.slice(2)
30
+ const command = argv[0]
31
+ const now = () => Date.now()
32
+ const append = (name, value) => appendFileSync(join(markerRoot, name), JSON.stringify(value) + '\\n')
33
+ const control = () => { try { return JSON.parse(readFileSync(join(markerRoot, 'control.json'), 'utf8')) } catch { return {} } }
34
+ const emit = value => process.stdout.write(encode(value) + '\\n')
35
+ const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
36
+ append('commands.jsonl', { phase: 'start', command, argv, stateDir, port, pid: process.pid, at: now() })
37
+
38
+ if (command === 'server') {
39
+ append('servers.jsonl', { pid: process.pid, port, stateDir })
40
+ const cfg = control()
41
+ if (cfg.serverDelayMs) await sleep(cfg.serverDelayMs)
42
+ if (cfg.orphan) {
43
+ const child = spawn(process.execPath, [join(dirname(import.meta.filename), 'orphan.mjs'), markerRoot, String(port)], { detached: true, stdio: 'ignore' })
44
+ child.unref()
45
+ append('orphans.jsonl', { phase: 'spawned', parentPid: process.pid, pid: child.pid, port })
46
+ process.on('SIGTERM', () => process.exit(0))
47
+ setInterval(() => {}, 1000)
48
+ } else {
49
+ const server = createServer((req, res) => {
50
+ if (req.url?.startsWith('/health')) {
51
+ res.setHeader('content-type', 'application/json')
52
+ res.end(JSON.stringify({ ok: true, app: 'lavish-axi', version: '${LAVISH_AXI_VERSION}' }))
53
+ } else { res.statusCode = 404; res.end('{}') }
54
+ })
55
+ server.listen(port, '127.0.0.1')
56
+ process.on('SIGTERM', () => { server.close(() => process.exit(0)); setTimeout(() => process.exit(0), 100) })
57
+ }
58
+ } else {
59
+ const cfg = control()
60
+ const delay = Number(cfg[command + 'DelayMs'] || 0)
61
+ if (delay) await sleep(delay)
62
+ if (command === 'open') {
63
+ emit({ session: { file: argv[1], url: 'http://127.0.0.1:' + port + '/session/' + 'a'.repeat(16), status: cfg.openStatus || 'opened' } })
64
+ } else if (command === 'poll') {
65
+ if (cfg.pollRaw !== undefined) process.stdout.write(String(cfg.pollRaw))
66
+ else {
67
+ const status = cfg.pollStatus || 'waiting'
68
+ emit({
69
+ session: { file: argv[1], status, ...(cfg.pollSessionEnded ? { session_ended: true, ended_by: cfg.pollEndedBy || 'user' } : {}) },
70
+ ...(status === 'feedback' ? { prompts: cfg.prompts || [] } : {}),
71
+ })
72
+ }
73
+ } else if (command === 'end') {
74
+ emit({ session: { file: argv[1], status: 'ended' } })
75
+ }
76
+ append('commands.jsonl', { phase: 'end', command, argv, stateDir, port, pid: process.pid, at: now() })
77
+ }
78
+ `;
79
+ const ORPHAN = `
80
+ import { appendFileSync } from 'node:fs'
81
+ import { createServer } from 'node:http'
82
+ import { join } from 'node:path'
83
+ const markerRoot = process.argv[2]
84
+ const port = Number(process.argv[3])
85
+ const server = createServer((req, res) => {
86
+ if (req.url?.startsWith('/health')) {
87
+ res.setHeader('content-type', 'application/json')
88
+ res.end(JSON.stringify({ ok: true, app: 'lavish-axi', version: '${LAVISH_AXI_VERSION}' }))
89
+ } else { res.statusCode = 404; res.end('{}') }
90
+ })
91
+ server.listen(port, '127.0.0.1', () => appendFileSync(join(markerRoot, 'orphans.jsonl'), JSON.stringify({ phase: 'listening', pid: process.pid, port }) + '\\n'))
92
+ process.on('SIGTERM', () => { server.close(() => process.exit(0)); setTimeout(() => process.exit(0), 100) })
93
+ `;
94
+ function config(extra = {}) {
95
+ return {
96
+ stateRoot,
97
+ timeoutMs: 30_000,
98
+ hbcliBin: 'hbcli-not-on-path',
99
+ sessionAccess: 'ask',
100
+ lavishAxiPackageRoot: packageRoot,
101
+ ...extra,
102
+ };
103
+ }
104
+ function hostExec(id, cwd, agentId = 'must-not-fallback') {
105
+ return { agent: { id: agentId, session: { ...(id === undefined ? {} : { id }), header: { cwd } } } };
106
+ }
107
+ async function readJsonl(name) {
108
+ try {
109
+ return (await readFile(join(markerRoot, name), 'utf8')).split('\n').filter(Boolean).map(line => JSON.parse(line));
110
+ }
111
+ catch {
112
+ return [];
113
+ }
114
+ }
115
+ async function setControl(value) {
116
+ await writeFile(join(markerRoot, 'control.json'), JSON.stringify(value));
117
+ }
118
+ function alive(pid) {
119
+ try {
120
+ process.kill(pid, 0);
121
+ return true;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ async function eventually(check, detail) {
128
+ const deadline = Date.now() + 10_000;
129
+ while (Date.now() < deadline) {
130
+ if (await check())
131
+ return;
132
+ await new Promise(resolve => setTimeout(resolve, 40));
133
+ }
134
+ assert.fail(detail);
135
+ }
136
+ async function portClosed(port) {
137
+ try {
138
+ await fetch(`http://127.0.0.1:${port}/health`);
139
+ return false;
140
+ }
141
+ catch {
142
+ return true;
143
+ }
144
+ }
145
+ async function main() {
146
+ for (const dir of [join(packageRoot, 'dist'), markerRoot, stateRoot, workspace, spacedWorkspace, outside, join(workspace, '.git'), join(workspace, 'node_modules')]) {
147
+ await mkdir(dir, { recursive: true });
148
+ }
149
+ await writeFile(join(packageRoot, 'package.json'), JSON.stringify({ name: 'lavish-axi', version: LAVISH_AXI_VERSION, bin: { 'lavish-axi': 'dist/cli.mjs' } }));
150
+ await writeFile(join(packageRoot, 'dist', 'cli.mjs'), CLI);
151
+ await writeFile(join(packageRoot, 'dist', 'orphan.mjs'), ORPHAN);
152
+ for (const path of [join(workspace, 'card.html'), join(spacedWorkspace, 'space.html'), join(outside, 'outside.html'), join(workspace, '.git', 'hidden.html'), join(workspace, 'node_modules', 'dep.html')]) {
153
+ await writeFile(path, '<!doctype html><title>fixture</title>');
154
+ }
155
+ await symlink(join(outside, 'outside.html'), join(workspace, 'escape.html'));
156
+ await symlink(workspace, workspaceLink);
157
+ await setControl({ serverDelayMs: 180 });
158
+ const directState = join(root, 'direct-adapter-state');
159
+ await mkdir(directState, { recursive: true });
160
+ const direct = await LavishLocalSession.create({
161
+ cliPackageRoot: packageRoot,
162
+ cwd: workspace,
163
+ allowedRoot: workspace,
164
+ stateDir: directState,
165
+ commandTimeoutMs: 3_000,
166
+ });
167
+ const [directOpenA, directOpenB] = await Promise.all([
168
+ direct.open(join(workspace, 'card.html')),
169
+ direct.open(join(workspace, 'card.html')),
170
+ ]);
171
+ assert.equal(directOpenA.ok, true);
172
+ assert.equal(directOpenB.ok, true);
173
+ const canonicalDirectState = await realpath(directState);
174
+ assert.equal((await readJsonl('servers.jsonl')).filter(row => row.stateDir === canonicalDirectState).length, 1, 'one adapter must serialize concurrent opens around server creation');
175
+ await direct.stop();
176
+ await setControl({});
177
+ const disabled = [];
178
+ apply({
179
+ tools: { register: (tool) => disabled.push(tool) },
180
+ systemPrompt: { variable() { } },
181
+ on() { return () => { }; },
182
+ }, config({ lavishAxiPackageRoot: '' }));
183
+ assert.equal(disabled.filter(tool => tool.name.startsWith('gotry_lavish_')).length, 0, 'default config must register no Lavish tools');
184
+ apply({
185
+ tools: { register: (tool) => disabled.push(tool) },
186
+ systemPrompt: { variable() { } },
187
+ on() { return () => { }; },
188
+ }, config({ lavishAxiPackageRoot: 'relative/untrusted-cli' }));
189
+ assert.equal(disabled.filter(tool => tool.name.startsWith('gotry_lavish_')).length, 0, 'relative host config must register no Lavish tools');
190
+ const tools = [];
191
+ const listeners = new Map();
192
+ const pluginDisposers = new Map();
193
+ const ctx = {
194
+ tools: { register: (tool) => tools.push(tool) },
195
+ systemPrompt: { variable() { } },
196
+ on(event, listener) {
197
+ const list = listeners.get(event) ?? [];
198
+ list.push(listener);
199
+ listeners.set(event, list);
200
+ return () => { };
201
+ },
202
+ effect(register, label) {
203
+ const disposer = register();
204
+ if (label)
205
+ pluginDisposers.set(label, disposer);
206
+ return async () => { await disposer(); };
207
+ },
208
+ };
209
+ apply(ctx, config());
210
+ const lavish = tools.filter(tool => tool.name.startsWith('gotry_lavish_'));
211
+ assert.deepEqual(lavish.map(tool => tool.name).sort(), [
212
+ 'gotry_lavish_end', 'gotry_lavish_open', 'gotry_lavish_poll', 'gotry_lavish_reply', 'gotry_lavish_stop',
213
+ ]);
214
+ const byName = new Map(lavish.map(tool => [tool.name, tool]));
215
+ const run = (name, args, exec) => {
216
+ const tool = byName.get(name);
217
+ assert.ok(tool, `${name} must be registered`);
218
+ return tool.execute(args, exec);
219
+ };
220
+ const dispose = listeners.get('session/disposed')?.at(-1);
221
+ assert.ok(dispose, 'Lavish registrar must attach exact session disposal cleanup');
222
+ const beforeAuthority = (await readJsonl('servers.jsonl')).length;
223
+ const noId = await run('gotry_lavish_open', { path: 'card.html' }, hostExec(undefined, workspace));
224
+ assert.equal(noId.ok, false, 'missing session.id must fail even when agent.id exists');
225
+ for (const invalid of [undefined, 42, '', ' ', 'relative/path']) {
226
+ const result = await run('gotry_lavish_open', { path: 'card.html' }, hostExec(`bad-cwd-${String(invalid)}`, invalid));
227
+ assert.equal(result.ok, false, `invalid cwd ${String(invalid)} must fail closed`);
228
+ }
229
+ assert.equal((await readJsonl('servers.jsonl')).length, beforeAuthority, 'authority failures must not spawn');
230
+ const spaced = hostExec('space-session', spacedWorkspace);
231
+ const spacedOpen = await run('gotry_lavish_open', { path: 'space.html', cliPackageRoot: outside, cwd: workspace, allowedRoot: root, port: 1, env: { PATH: '/hostile' } }, spaced);
232
+ assert.equal(spacedOpen.ok, true, `raw trailing-space cwd must be preserved: ${JSON.stringify(spacedOpen)}`);
233
+ const openTool = byName.get('gotry_lavish_open');
234
+ assert.match(openTool.output.render({}, spacedOpen)[0].text, /http:\/\/127\.0\.0\.1:\d+\/session\/a{16}/, 'model-visible output must retain the usable local URL');
235
+ assert.match(openTool.presentResult({ path: 'space.html' }, spacedOpen).content[0].text, /\/session\/a{16}/, 'presented result must retain the usable local URL');
236
+ const spacedServer = (await readJsonl('servers.jsonl')).at(-1);
237
+ assert.equal(spacedServer.stateDir !== undefined, true);
238
+ await run('gotry_lavish_stop', {}, spaced);
239
+ const boundary = hostExec('boundary', workspace);
240
+ for (const path of [join(outside, 'outside.html'), 'escape.html', 'notes.txt', '.git/hidden.html', 'node_modules/dep.html']) {
241
+ const result = await run('gotry_lavish_open', { path }, boundary);
242
+ assert.equal(result.ok, false, `${path} must remain inside adapter boundary`);
243
+ }
244
+ const boundaryOpen = await run('gotry_lavish_open', { path: 'card.html' }, boundary);
245
+ assert.equal(boundaryOpen.ok, true);
246
+ const drift = await run('gotry_lavish_poll', { path: 'card.html' }, hostExec('boundary', spacedWorkspace));
247
+ assert.equal(drift.ok, false, 'same id cannot drift raw or canonical cwd');
248
+ await run('gotry_lavish_stop', {}, boundary);
249
+ const retargetExec = hostExec('retarget', workspaceLink);
250
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, retargetExec)).ok, true);
251
+ await rm(workspaceLink);
252
+ await symlink(outside, workspaceLink);
253
+ assert.equal((await run('gotry_lavish_poll', { path: 'outside.html' }, retargetExec)).ok, false, 'symlink retarget must not expand a bound cwd authority');
254
+ await run('gotry_lavish_stop', {}, retargetExec);
255
+ await setControl({ openDelayMs: 180 });
256
+ const concurrentExec = hostExec('concurrent-open', workspace);
257
+ const serversBeforeConcurrentOpen = (await readJsonl('servers.jsonl')).length;
258
+ const [open1, open2] = await Promise.all([
259
+ run('gotry_lavish_open', { path: 'card.html' }, concurrentExec),
260
+ run('gotry_lavish_open', { path: 'card.html' }, concurrentExec),
261
+ ]);
262
+ assert.equal(open1.ok, true);
263
+ assert.equal(open2.ok, true);
264
+ const allServers = await readJsonl('servers.jsonl');
265
+ assert.equal(allServers.length - serversBeforeConcurrentOpen, 1, 'concurrent opens must spawn one physical server');
266
+ await setControl({ pollDelayMs: 180 });
267
+ const commandStart = (await readJsonl('commands.jsonl')).length;
268
+ const [poll, reply] = await Promise.all([
269
+ run('gotry_lavish_poll', { path: 'card.html', timeoutMs: 1_000 }, concurrentExec),
270
+ run('gotry_lavish_reply', { path: 'card.html', reply: 'source updated', timeoutMs: 1_000 }, concurrentExec),
271
+ ]);
272
+ assert.equal(poll.ok, true);
273
+ assert.equal(reply.ok, true);
274
+ const commandEvents = (await readJsonl('commands.jsonl')).slice(commandStart).filter(row => row.command === 'poll');
275
+ let depth = 0;
276
+ let maxDepth = 0;
277
+ for (const event of commandEvents.sort((a, b) => Number(a.at) - Number(b.at))) {
278
+ depth += event.phase === 'start' ? 1 : -1;
279
+ maxDepth = Math.max(maxDepth, depth);
280
+ }
281
+ assert.equal(maxDepth, 1, 'poll and reply commands must be serialized per host record');
282
+ await run('gotry_lavish_stop', {}, concurrentExec);
283
+ await setControl({ openDelayMs: 350 });
284
+ const stopRaceExec = hostExec('stop-race', workspace);
285
+ const stopRaceCommandCount = (await readJsonl('commands.jsonl')).filter(row => row.command === 'open' && row.phase === 'start').length;
286
+ const opening = run('gotry_lavish_open', { path: 'card.html' }, stopRaceExec);
287
+ await eventually(async () => (await readJsonl('commands.jsonl')).filter(row => row.command === 'open' && row.phase === 'start').length > stopRaceCommandCount, 'open command did not start');
288
+ const stopping = run('gotry_lavish_stop', {}, stopRaceExec);
289
+ const [racedOpen, racedStop] = await Promise.all([opening, stopping]);
290
+ assert.equal(racedOpen.ok, false, 'stop must preempt the visible success of an in-flight open');
291
+ assert.equal(racedStop.ok, true);
292
+ const stoppedServer = (await readJsonl('servers.jsonl')).at(-1);
293
+ await eventually(() => !alive(Number(stoppedServer.pid)), 'stop race leaked its owned server pid');
294
+ await eventually(() => portClosed(Number(stoppedServer.port)), 'stop race port must disappear');
295
+ const reopenAfterStop = await run('gotry_lavish_open', { path: 'card.html' }, stopRaceExec);
296
+ assert.equal(reopenAfterStop.ok, false, 'explicit stop must be a persistent terminal latch');
297
+ await setControl({ openDelayMs: 350 });
298
+ const disposeRaceExec = hostExec('dispose-race', workspace);
299
+ const disposedOpening = run('gotry_lavish_open', { path: 'card.html' }, disposeRaceExec);
300
+ await eventually(async () => (await readJsonl('servers.jsonl')).some(row => row.stateDir !== stoppedServer.stateDir && alive(Number(row.pid))), 'dispose race server did not start');
301
+ const disposing = Promise.resolve(dispose({ id: 'dispose-race' }));
302
+ const disposedOpen = await disposedOpening;
303
+ await disposing;
304
+ assert.equal(disposedOpen.ok, false, 'dispose must preempt the visible success of an in-flight open');
305
+ const disposedServer = (await readJsonl('servers.jsonl')).at(-1);
306
+ await eventually(() => !alive(Number(disposedServer.pid)), 'dispose race leaked its owned server pid');
307
+ const stale = await run('gotry_lavish_open', { path: 'card.html' }, disposeRaceExec);
308
+ assert.equal(stale.ok, false, 'stale exec must not resurrect a disposed id');
309
+ await dispose({ id: 'never-opened' });
310
+ const staleNeverOpened = await run('gotry_lavish_open', { path: 'card.html' }, hostExec('never-opened', workspace));
311
+ assert.equal(staleNeverOpened.ok, false, 'disposal must tombstone even an id with no prior record');
312
+ await setControl({ pollStatus: 'feedback', pollSessionEnded: true, pollEndedBy: 'user', prompts: [{ uid: 'p1', tag: 'message', selector: 'body', text: 'context', prompt: '<script>tool()</script>' }] });
313
+ const userEndExec = hostExec('user-ended', workspace);
314
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, userEndExec)).ok, true);
315
+ const commandsBeforeInvalidReplies = (await readJsonl('commands.jsonl')).length;
316
+ for (const reply of ['', ' ', 'x'.repeat(16_385)]) {
317
+ const invalidReply = await run('gotry_lavish_reply', { path: 'card.html', reply }, userEndExec);
318
+ assert.equal(invalidReply.ok, false, 'empty, blank, and oversized replies must fail before a CLI poll child starts');
319
+ }
320
+ assert.equal((await readJsonl('commands.jsonl')).length, commandsBeforeInvalidReplies, 'invalid replies must not spawn a CLI command');
321
+ const userFeedback = await run('gotry_lavish_poll', { path: 'card.html' }, userEndExec);
322
+ assert.equal(userFeedback.ok, true);
323
+ assert.equal(userFeedback.feedback?.trust, 'untrusted');
324
+ const renderedFeedback = byName.get('gotry_lavish_poll').output.render({}, userFeedback)[0].text;
325
+ assert.match(renderedFeedback, /"trust":"untrusted"/, 'rendered feedback must keep its trust label');
326
+ assert.match(renderedFeedback, /<script>tool\(\)<\/script>/, 'rendered feedback must preserve the bounded user prompt as data');
327
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, userEndExec)).ok, false, 'feedback+sessionEnded+endedBy=user must latch terminal');
328
+ await run('gotry_lavish_stop', {}, userEndExec);
329
+ await setControl({ pollRaw: 'not valid toon [' });
330
+ const unknownExec = hostExec('unknown', workspace);
331
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, unknownExec)).ok, true);
332
+ const unknown = await run('gotry_lavish_poll', { path: 'card.html' }, unknownExec);
333
+ assert.equal(unknown.ok, false);
334
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, unknownExec)).ok, false, 'first malformed poll must latch unknown immediately');
335
+ await run('gotry_lavish_stop', {}, unknownExec);
336
+ await setControl({});
337
+ const lifecycleExec = hostExec('full-lifecycle', workspace);
338
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, lifecycleExec)).ok, true);
339
+ assert.equal((await run('gotry_lavish_poll', { path: 'card.html' }, lifecycleExec)).ok, true);
340
+ await writeFile(join(workspace, 'card.html'), '<!doctype html><title>updated outside Lavish tools</title>');
341
+ assert.equal((await run('gotry_lavish_reply', { path: 'card.html', reply: 'source updated' }, lifecycleExec)).ok, true);
342
+ assert.equal((await run('gotry_lavish_end', { path: 'card.html' }, lifecycleExec)).ok, true);
343
+ assert.equal((await run('gotry_lavish_stop', {}, lifecycleExec)).ok, true);
344
+ const sessionA = hostExec('session-a', workspace);
345
+ const sessionB = hostExec('session-b', workspace);
346
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, sessionA)).ok, true);
347
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, sessionB)).ok, true);
348
+ const pair = (await readJsonl('servers.jsonl')).slice(-2);
349
+ assert.notEqual(pair[0]?.stateDir, pair[1]?.stateDir, 'host sessions must own distinct adapter state');
350
+ await dispose({ id: 'session-a' });
351
+ await dispose({});
352
+ assert.equal((await run('gotry_lavish_poll', { path: 'card.html' }, sessionB)).ok, true, 'disposing A or malformed subject must not stop B');
353
+ await run('gotry_lavish_stop', {}, sessionB);
354
+ await setControl({ orphan: true });
355
+ const failureExec = hostExec('cleanup-failure', workspace);
356
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, failureExec)).ok, true);
357
+ const failureServer = (await readJsonl('servers.jsonl')).at(-1);
358
+ await eventually(async () => (await readJsonl('orphans.jsonl')).some(row => row.phase === 'listening' && row.port === failureServer.port), 'foreign listener fixture did not start');
359
+ const orphan = (await readJsonl('orphans.jsonl')).find(row => row.phase === 'listening' && row.port === failureServer.port);
360
+ try {
361
+ await dispose({ id: 'cleanup-failure' });
362
+ const visibleFailure = await run('gotry_lavish_stop', {}, failureExec);
363
+ assert.equal(visibleFailure.ok, false, 'explicit stop must surface a cleanup failure recorded during disposal');
364
+ assert.equal(visibleFailure.code, 'port-occupied');
365
+ const repeatedFailure = await run('gotry_lavish_stop', {}, failureExec);
366
+ assert.equal(repeatedFailure.ok, false, 'a cached cleanup failure must not be replaced by a false not-running result');
367
+ assert.equal(alive(Number(orphan.pid)), true, 'adapter must not kill the detached foreign listener');
368
+ }
369
+ finally {
370
+ try {
371
+ process.kill(-Number(orphan.pid), 'SIGTERM');
372
+ }
373
+ catch { /* already gone */ }
374
+ await eventually(() => !alive(Number(orphan.pid)), 'foreign listener fixture teardown failed');
375
+ }
376
+ const directFailureState = join(root, 'direct-failure-state');
377
+ await mkdir(directFailureState, { recursive: true });
378
+ const directFailure = await LavishLocalSession.create({
379
+ cliPackageRoot: packageRoot,
380
+ cwd: workspace,
381
+ allowedRoot: workspace,
382
+ stateDir: directFailureState,
383
+ commandTimeoutMs: 3_000,
384
+ });
385
+ assert.equal((await directFailure.open(join(workspace, 'card.html'))).ok, true);
386
+ const directFailurePort = directFailure.state().port;
387
+ await eventually(async () => (await readJsonl('orphans.jsonl')).some(row => row.phase === 'listening' && row.port === directFailurePort), 'direct foreign listener fixture did not start');
388
+ const directOrphan = (await readJsonl('orphans.jsonl')).find(row => row.phase === 'listening' && row.port === directFailurePort);
389
+ try {
390
+ const firstFailure = await directFailure.stop();
391
+ const secondFailure = await directFailure.stop();
392
+ assert.equal(firstFailure.ok, false);
393
+ assert.equal(secondFailure.ok, false, 'adapter retry must retain port-occupied evidence');
394
+ assert.equal(secondFailure.ok ? '' : secondFailure.code, 'port-occupied');
395
+ }
396
+ finally {
397
+ try {
398
+ process.kill(-Number(directOrphan.pid), 'SIGTERM');
399
+ }
400
+ catch { /* already gone */ }
401
+ await eventually(() => !alive(Number(directOrphan.pid)), 'direct foreign listener fixture teardown failed');
402
+ }
403
+ await setControl({});
404
+ const endExec = hostExec('end-stop', workspace);
405
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, endExec)).ok, true);
406
+ const endServer = (await readJsonl('servers.jsonl')).at(-1);
407
+ assert.equal((await run('gotry_lavish_end', { path: 'card.html' }, endExec)).ok, true);
408
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, endExec)).ok, false, 'explicit end must remain terminal');
409
+ assert.equal((await run('gotry_lavish_stop', {}, endExec)).ok, true, 'end must retain the owned handle for stop');
410
+ await eventually(() => !alive(Number(endServer.pid)), 'end then stop leaked its owned server');
411
+ // === Regression: in-flight poll returning feedback+sessionEnded+endedBy=user
412
+ // must not publish success after stop / dispose (host authority preserved) ===
413
+ await setControl({ pollDelayMs: 250, pollStatus: 'feedback', pollSessionEnded: true, pollEndedBy: 'user', prompts: [{ uid: 'u', tag: 'message', selector: 'body', text: 'context', prompt: 'feedback during stop' }] });
414
+ const stopFeedbackExec = hostExec('stop-during-feedback-poll', workspace);
415
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, stopFeedbackExec)).ok, true);
416
+ const stopFeedbackPollStart = (await readJsonl('commands.jsonl')).filter(row => row.command === 'poll' && row.phase === 'start').length;
417
+ const inFlightFeedbackPoll = run('gotry_lavish_poll', { path: 'card.html', timeoutMs: 1_000 }, stopFeedbackExec);
418
+ await eventually(async () => (await readJsonl('commands.jsonl')).filter(row => row.command === 'poll' && row.phase === 'start').length > stopFeedbackPollStart, 'feedback poll did not start');
419
+ const stopFeedbackStop = run('gotry_lavish_stop', {}, stopFeedbackExec);
420
+ const pollAfterStop = await inFlightFeedbackPoll;
421
+ const stopAfterFeedPoll = await stopFeedbackStop;
422
+ assert.equal(pollAfterStop.ok, false, 'in-flight poll returning terminal feedback must not publish success after stop');
423
+ assert.equal(pollAfterStop.code, 'lavish-session-terminal');
424
+ assert.match(String(pollAfterStop.error ?? ''), /stopped/);
425
+ assert.equal(stopAfterFeedPoll.ok, true);
426
+ const stopFeedbackServer = (await readJsonl('servers.jsonl')).at(-1);
427
+ await eventually(() => !alive(Number(stopFeedbackServer.pid)), 'stop-during-feedback-poll leaked its owned server pid');
428
+ const disposeFeedbackExec = hostExec('dispose-during-feedback-poll', workspace);
429
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, disposeFeedbackExec)).ok, true);
430
+ const disposeFeedbackPollStart = (await readJsonl('commands.jsonl')).filter(row => row.command === 'poll' && row.phase === 'start').length;
431
+ const inFlightFeedbackPoll2 = run('gotry_lavish_poll', { path: 'card.html', timeoutMs: 1_000 }, disposeFeedbackExec);
432
+ await eventually(async () => (await readJsonl('commands.jsonl')).filter(row => row.command === 'poll' && row.phase === 'start').length > disposeFeedbackPollStart, 'second feedback poll did not start');
433
+ const disposingFeedPoll = Promise.resolve(dispose({ id: 'dispose-during-feedback-poll' }));
434
+ const pollAfterDispose = await inFlightFeedbackPoll2;
435
+ await disposingFeedPoll;
436
+ assert.equal(pollAfterDispose.ok, false, 'in-flight poll returning terminal feedback must not publish success after dispose');
437
+ assert.equal(pollAfterDispose.code, 'lavish-session-terminal');
438
+ assert.match(String(pollAfterDispose.error ?? ''), /disposed/);
439
+ const disposeFeedbackServer = (await readJsonl('servers.jsonl')).at(-1);
440
+ await eventually(() => !alive(Number(disposeFeedbackServer.pid)), 'dispose-during-feedback-poll leaked its owned server pid');
441
+ // === Regression: in-flight end must not publish success after stop (end authority) ===
442
+ await setControl({ endDelayMs: 250 });
443
+ const stopDuringEndExec = hostExec('stop-during-end', workspace);
444
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, stopDuringEndExec)).ok, true);
445
+ const endStartCount = (await readJsonl('commands.jsonl')).filter(row => row.command === 'end' && row.phase === 'start').length;
446
+ const inFlightEnd = run('gotry_lavish_end', { path: 'card.html' }, stopDuringEndExec);
447
+ await eventually(async () => (await readJsonl('commands.jsonl')).filter(row => row.command === 'end' && row.phase === 'start').length > endStartCount, 'end did not start');
448
+ const stopDuringEndStop = run('gotry_lavish_stop', {}, stopDuringEndExec);
449
+ const endAfterStop = await inFlightEnd;
450
+ const stopAfterEnd = await stopDuringEndStop;
451
+ assert.equal(endAfterStop.ok, false, 'in-flight end must not publish success after stop');
452
+ assert.equal(endAfterStop.code, 'lavish-session-terminal');
453
+ assert.match(String(endAfterStop.error ?? ''), /stopped/);
454
+ assert.equal(stopAfterEnd.ok, true);
455
+ const endStopServer = (await readJsonl('servers.jsonl')).at(-1);
456
+ await eventually(() => !alive(Number(endStopServer.pid)), 'stop-during-end leaked its owned server pid');
457
+ await setControl({});
458
+ // === Regression: a fresh bind awaiting realpath must observe synchronous unload
459
+ // closure before record creation (no resurrection, no server spawn) ===
460
+ // Uses a second harness so the registrar is still open at the start of the race.
461
+ const bindRaceTools = [];
462
+ const bindRaceListeners = new Map();
463
+ const bindRaceDisposers = new Map();
464
+ const bindRaceCtx = {
465
+ tools: { register: (tool) => bindRaceTools.push(tool) },
466
+ systemPrompt: { variable() { } },
467
+ on(event, listener) {
468
+ const list = bindRaceListeners.get(event) ?? [];
469
+ list.push(listener);
470
+ bindRaceListeners.set(event, list);
471
+ return () => { };
472
+ },
473
+ effect(register, label) {
474
+ const disposer = register();
475
+ if (label)
476
+ bindRaceDisposers.set(label, disposer);
477
+ return async () => { await disposer(); };
478
+ },
479
+ };
480
+ apply(bindRaceCtx, config());
481
+ const bindRaceLavish = bindRaceTools.filter(tool => tool.name.startsWith('gotry_lavish_'));
482
+ const bindRaceByName = new Map(bindRaceLavish.map(tool => [tool.name, tool]));
483
+ const bindRaceRun = (name, args, exec) => {
484
+ const tool = bindRaceByName.get(name);
485
+ assert.ok(tool, `${name} must be registered on the second harness`);
486
+ return tool.execute(args, exec);
487
+ };
488
+ const bindRaceUnloadDisposer = bindRaceDisposers.get('gotry-lavish-lifecycle');
489
+ assert.ok(bindRaceUnloadDisposer, 'second harness must also attach the Cordis lifecycle disposer');
490
+ const commandsBeforeBindRace = (await readJsonl('commands.jsonl')).length;
491
+ const bindRaceOpenPromise = bindRaceRun('gotry_lavish_open', { path: 'card.html' }, hostExec('new-during-unload-bind', workspace));
492
+ // unload's synchronous prefix (closed = true) must run before bind's realpath resolves
493
+ const bindRaceUnloadPromise = bindRaceUnloadDisposer();
494
+ const bindRaceOpen = await bindRaceOpenPromise;
495
+ await bindRaceUnloadPromise;
496
+ const commandsAfterBindRace = (await readJsonl('commands.jsonl')).length;
497
+ assert.equal(bindRaceOpen.ok, false, 'bind awaiting realpath must observe unload closure before record creation');
498
+ assert.equal(bindRaceOpen.code, 'lavish-plugin-closed');
499
+ assert.equal(commandsAfterBindRace - commandsBeforeBindRace, 0, 'bind/unload race must not spawn any CLI commands');
500
+ const unloadExec = hostExec('plugin-unload', workspace);
501
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, unloadExec)).ok, true);
502
+ const unloadServer = (await readJsonl('servers.jsonl')).at(-1);
503
+ const unload = pluginDisposers.get('gotry-lavish-lifecycle');
504
+ assert.ok(unload, 'registrar must attach cleanup to the verified Cordis fiber effect');
505
+ await assert.rejects(unload, /port-occupied/, 'plugin unload must expose any retained cleanup failure after reaping the other records');
506
+ await eventually(() => !alive(Number(unloadServer.pid)), 'plugin unload leaked a live host session server');
507
+ assert.equal((await run('gotry_lavish_open', { path: 'card.html' }, unloadExec)).ok, false, 'a stale execution cannot reopen after plugin unload');
508
+ // === Regression: plugin unload then a brand-new host session id must fail closed
509
+ // without spawning a server or any CLI command ===
510
+ const postUnloadNewIdExec = hostExec('new-id-after-plugin-unload', workspace);
511
+ const commandsBeforePostUnload = (await readJsonl('commands.jsonl')).length;
512
+ const postUnloadOpen = await run('gotry_lavish_open', { path: 'card.html' }, postUnloadNewIdExec);
513
+ const commandsAfterPostUnload = (await readJsonl('commands.jsonl')).length;
514
+ assert.equal(postUnloadOpen.ok, false, 'new host session id after plugin unload must fail closed');
515
+ assert.equal(postUnloadOpen.code, 'lavish-plugin-closed');
516
+ assert.equal(commandsAfterPostUnload - commandsBeforePostUnload, 0, 'post-unload open for new id must not spawn any CLI command');
517
+ const serverRows = await readJsonl('servers.jsonl');
518
+ for (const [index, row] of serverRows.entries()) {
519
+ await eventually(() => !alive(Number(row.pid)), `owned server #${index + 1} ${String(row.pid)} on ${String(row.port)} state=${String(row.stateDir)} remained alive`);
520
+ }
521
+ console.log(`LAVISH PRODUCT TOOLS TESTS: ${serverRows.length} owned servers reaped; registered lifecycle passed`);
522
+ }
523
+ try {
524
+ await main();
525
+ }
526
+ finally {
527
+ for (const row of await readJsonl('servers.jsonl')) {
528
+ const pid = Number(row.pid);
529
+ if (!Number.isInteger(pid) || !alive(pid))
530
+ continue;
531
+ try {
532
+ process.kill(-pid, 'SIGTERM');
533
+ }
534
+ catch { /* already gone */ }
535
+ }
536
+ await rm(root, { recursive: true, force: true });
537
+ }