@danceiny/gotry 0.0.1-rc.15 → 0.0.1-rc.16

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 (139) hide show
  1. package/README.md +10 -6
  2. package/README.zh-CN.md +10 -6
  3. package/bin/gotry-bootstrap.js +294 -5
  4. package/cordis.gotry-patch.yml +10 -0
  5. package/data/airline-airports.json +39 -0
  6. package/dist/capabilities/agent-reach-deep.js +1 -1
  7. package/dist/capabilities/agent-reach.js +1 -1
  8. package/dist/capabilities/anything.js +1 -1
  9. package/dist/capabilities/artifacts.js +1 -1
  10. package/dist/capabilities/effect.js +293 -0
  11. package/dist/capabilities/fact-log.js +41 -0
  12. package/dist/capabilities/flyai.js +1 -1
  13. package/dist/capabilities/hbcli.js +43 -1
  14. package/dist/capabilities/incident-log.js +1 -1
  15. package/dist/capabilities/opensky.js +1 -1
  16. package/dist/capabilities/resilience.js +90 -0
  17. package/dist/capabilities/session/action-cache.js +1 -1
  18. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  19. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  20. package/dist/capabilities/session/benchmark.js +2 -1
  21. package/dist/capabilities/session/extension-bridge.js +345 -0
  22. package/dist/capabilities/session/extension-channel.js +93 -0
  23. package/dist/capabilities/session/extract.js +1 -1
  24. package/dist/capabilities/session/health-watch.js +216 -0
  25. package/dist/capabilities/session/read-guard.js +1 -1
  26. package/dist/capabilities/session/transport.js +1 -1
  27. package/dist/capabilities/session/wizard.js +377 -0
  28. package/dist/capabilities/session-consent.js +1 -1
  29. package/dist/capabilities/session-login.js +77 -1
  30. package/dist/capabilities/session-search.js +69 -2
  31. package/dist/capabilities/weather.js +1 -1
  32. package/dist/scripts/action-cache-tests.js +1 -1
  33. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  34. package/dist/scripts/agent-reach-tests.js +1 -1
  35. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  36. package/dist/scripts/anything-tests.js +1 -1
  37. package/dist/scripts/async-collect.js +1 -1
  38. package/dist/scripts/booking-saga-tests.js +1 -1
  39. package/dist/scripts/bootstrap-tests.js +39 -3
  40. package/dist/scripts/build-changelog.js +279 -0
  41. package/dist/scripts/changelog-tests.js +165 -0
  42. package/dist/scripts/companion-tests.js +1 -1
  43. package/dist/scripts/diff-test.js +1 -1
  44. package/dist/scripts/effect-tests.js +474 -0
  45. package/dist/scripts/engine-run.js +1 -1
  46. package/dist/scripts/engine-tests.js +1 -1
  47. package/dist/scripts/extension-tests.js +468 -0
  48. package/dist/scripts/fact-gate-tests.js +318 -0
  49. package/dist/scripts/flyai-tests.js +1 -1
  50. package/dist/scripts/hbcli-e2e-tests.js +180 -0
  51. package/dist/scripts/hbcli-tests.js +1 -1
  52. package/dist/scripts/health-watch-cli.js +51 -0
  53. package/dist/scripts/i18n-tests.js +1 -1
  54. package/dist/scripts/incident-tests.js +1 -1
  55. package/dist/scripts/journey-tests.js +1 -1
  56. package/dist/scripts/ledger-tests.js +1 -1
  57. package/dist/scripts/ledger-workflow-crash.js +1 -1
  58. package/dist/scripts/memory-capture-tests.js +1 -1
  59. package/dist/scripts/memory-decay-tests.js +1 -1
  60. package/dist/scripts/memory-metrics.js +1 -1
  61. package/dist/scripts/memory-value-report.js +1 -1
  62. package/dist/scripts/nightly-evidence-tests.js +67 -4
  63. package/dist/scripts/nightly-evidence.js +59 -3
  64. package/dist/scripts/nudge-digest.js +1 -1
  65. package/dist/scripts/onboarding-tests.js +218 -0
  66. package/dist/scripts/opensky-check.js +1 -1
  67. package/dist/scripts/opensky-tests.js +1 -1
  68. package/dist/scripts/price-drift-tests.js +441 -0
  69. package/dist/scripts/price-drift-watch.js +491 -0
  70. package/dist/scripts/probe-poi-tests.js +1 -1
  71. package/dist/scripts/product-metrics.js +1 -1
  72. package/dist/scripts/publish-preverify.js +1 -1
  73. package/dist/scripts/realtime-pricing-tests.js +1 -1
  74. package/dist/scripts/replay-async.js +1 -1
  75. package/dist/scripts/replay-real.js +1 -1
  76. package/dist/scripts/replay.js +1 -1
  77. package/dist/scripts/session-attach-diagnose.js +1 -1
  78. package/dist/scripts/session-attach-poc.js +1 -1
  79. package/dist/scripts/session-benchmark.js +1 -1
  80. package/dist/scripts/session-extract-tests.js +1 -1
  81. package/dist/scripts/session-login.js +1 -1
  82. package/dist/scripts/session-tests.js +1 -1
  83. package/dist/scripts/skeleton-check.js +1 -1
  84. package/dist/scripts/skeleton-integration-test.js +1 -1
  85. package/dist/scripts/skills-contract-tests.js +1 -1
  86. package/dist/scripts/smoke.js +76 -1
  87. package/dist/scripts/state-cli-tests.js +1 -1
  88. package/dist/scripts/state-cli.js +1 -1
  89. package/dist/scripts/time-eval-tests.js +1 -1
  90. package/dist/scripts/travel-timeline-tests.js +1 -1
  91. package/dist/scripts/unified-tests.js +1 -1
  92. package/dist/scripts/weather-tests.js +1 -1
  93. package/dist/scripts/wizard-bootstrap.js +32 -0
  94. package/dist/scripts/z3-race-tests.js +1 -1
  95. package/dist/src/artifact-gate.js +310 -0
  96. package/dist/src/bookable-facts.js +349 -0
  97. package/dist/src/booking-saga.js +1 -1
  98. package/dist/src/bridge.js +1 -1
  99. package/dist/src/companions.js +1 -1
  100. package/dist/src/contracts.js +1 -1
  101. package/dist/src/dsh-llm.js +1 -1
  102. package/dist/src/engine.js +1 -1
  103. package/dist/src/i18n.js +1 -1
  104. package/dist/src/index.js +189 -46
  105. package/dist/src/journey.js +1 -1
  106. package/dist/src/loop.js +1 -1
  107. package/dist/src/memory-capture.js +1 -1
  108. package/dist/src/memory-decay.js +1 -1
  109. package/dist/src/memory-utility.js +1 -1
  110. package/dist/src/mock-llm.js +1 -1
  111. package/dist/src/model.js +1 -1
  112. package/dist/src/realtime-pricing.js +14 -7
  113. package/dist/src/slot-spec.js +1 -1
  114. package/dist/src/state-ledger.js +1 -1
  115. package/dist/src/time-anchor.js +1 -1
  116. package/dist/src/tool-packet.js +1 -1
  117. package/dist/src/travel-slots.js +1 -1
  118. package/dist/src/travel-timeline.js +1 -1
  119. package/dist/src/unified.js +1 -1
  120. package/dist/src/wish-pool.js +1 -1
  121. package/dist/src/z3-shared.js +1 -1
  122. package/extension/README.md +26 -0
  123. package/extension/background.js +160 -0
  124. package/extension/content-bridge.js +41 -0
  125. package/extension/content-main.js +62 -0
  126. package/extension/manifest.json +32 -0
  127. package/package.json +6 -2
  128. package/ts/capabilities/fact-log.ts +54 -0
  129. package/ts/capabilities/hbcli.ts +46 -0
  130. package/ts/capabilities/session/benchmark.ts +4 -1
  131. package/ts/capabilities/session/extension-bridge.ts +354 -0
  132. package/ts/capabilities/session/extension-channel.ts +92 -0
  133. package/ts/capabilities/session/health-watch.ts +257 -0
  134. package/ts/capabilities/session/wizard.ts +282 -0
  135. package/ts/capabilities/session-login.ts +53 -2
  136. package/ts/capabilities/session-search.ts +68 -3
  137. package/ts/src/artifact-gate.ts +365 -0
  138. package/ts/src/bookable-facts.ts +557 -0
  139. package/ts/src/index.ts +102 -8
package/README.md CHANGED
@@ -26,6 +26,8 @@ npx @danceiny/gotry web
26
26
  # → open http://127.0.0.1:3080 and chat: "I want three relaxing days in Dali"
27
27
  ```
28
28
 
29
+ > **Switching models / providers?** The price table (`ts/data/llm-price-table.json`, schema `gotry_llm_price_table_v2`) is the single source of truth for `gotry_m3_nightly_run_v1.cost_usd`. Adding a new model or switching relay = update this file via PR (ADR-11, peak-conservative upper bound only). Unknown models **fail-closed** — no guessed prices. Drift monitor: `npx tsx ts/scripts/price-drift-watch.ts` (offline baseline diff; `--fetch` for live official pages). Never auto-applies changes.
30
+
29
31
  | You want | Command |
30
32
  |---|---|
31
33
  | 🖥️ Conversational planner (recommended) | `npx @danceiny/gotry web` → chat UI on :3080 |
@@ -78,7 +80,7 @@ cp .env.example .env # ② set LLM_API_KEY
78
80
 
79
81
  ---
80
82
 
81
- ## 🧰 20 tools
83
+ ## 🧰 21 tools
82
84
 
83
85
  | Group | Tool | What it does |
84
86
  |---|---|---|
@@ -95,6 +97,7 @@ cp .env.example .env # ② set LLM_API_KEY
95
97
  | | `gotry_wish_pool_add` / `gotry_wish_pool_list` | "next departure" wish pool + 0..1 conditional recall |
96
98
  | | `gotry_companion_save` · `gotry_trip_log` | companion profile / travel timeline |
97
99
  | **Artifacts** | `gotry_artifacts_list` / `gotry_artifacts_read` | Discover & view generated artifacts (async deliverables + working-dir markdown) as a line-numbered file view (read-only) |
100
+ | **Factuality gate** | `gotry_fact_gate` | Pre-delivery gate for itinerary artifacts: every bookable claim (flight no./time/airport/price/policy) must trace to an exact-date tool result (hit AND miss recorded); unverifiable ⇒ blocked — never present as a verified plan |
98
101
  | **General external** | `gotry_web_search` · `gotry_video_subtitle` · `gotry_github_search` · `gotry_agent_reach` | web / subtitles / GitHub / all-channel external info (via Agent-Reach) |
99
102
 
100
103
  ---
@@ -108,7 +111,7 @@ The account session channel reads realtime hotel/flight data from **the user's o
108
111
  3. **Physically read-only.** A ReadGuard aborts all write requests at the network layer (ordering/payment is unreachable in transport); the agent never touches credentials or captchas — on a captcha it stops and hands control back.
109
112
  4. **Never hijacks your browser.** Retrieval/login always open their own dedicated tab; the login page is brought to front and stays with you; routine test runs never open browser windows.
110
113
 
111
- > One-time prerequisite: in your daily Chrome, open `chrome://inspect/#remote-debugging` and enable the switch (Chrome 144+). Without it the tools return `needs-attach` with instructions and spend nothing.
114
+ > One-time prerequisite: install the bundled **GoTry Session Bridge** browser extension (MV3, ~30 seconds): run `npx gotry setup` to place it at `~/.gotry/extension`, then in Chrome open `chrome://extensions`, enable Developer mode and "Load unpacked" that folder. Zero Chrome system dialogs afterwards — the extension passively forwards the site's own search responses (read-only by construction; cookies are read by NAME only, values never leave the browser). Until installed, tools return `needs-extension` with instructions and spend nothing. (A `cdp` fallback via `chrome://inspect` remote debugging still exists for diagnostics: `GOTRY_SESSION_TRANSPORT=cdp` — note Chrome 144+ shows a permission box on every connection.)
112
115
 
113
116
  ---
114
117
 
@@ -141,7 +144,7 @@ Engine verdict:
141
144
  ```
142
145
  ┌──────────────────────────────────────────────────────────────┐
143
146
  │ L1 chat-as-interface; gates are in-message choice cards │
144
- │ L2 orchestration dsh runtime + GoTry plugin (ReAct); 20 tools│
147
+ │ L2 orchestration dsh runtime + GoTry plugin (ReAct); 21 tools│
145
148
  │ L3 domain unified itinerary model + Z3 feasibility engine │
146
149
  │ L4 data static packs + hotelbyte-cli bridge + OpenFlights │
147
150
  │ L5 governance LoopX (objective / gates / evidence / quota) │
@@ -150,7 +153,7 @@ Engine verdict:
150
153
 
151
154
  | Layer | Module | Role |
152
155
  |---|---|---|
153
- | L2 | `ts/src/index.ts` (dsh plugin) | 20 tools, time-anchor & memory-brief variables; execute isolation + consent gate + process guards |
156
+ | L2 | `ts/src/index.ts` (dsh plugin) | 21 tools, time-anchor & memory-brief variables; execute isolation + consent gate + process guards |
154
157
  | L3 | `ts/src/unified.ts` · `py/gotry_feasibility/` | single solving entry (candidate enumeration + flight-chain Z3) |
155
158
  | L4 | `ts/capabilities/effect.ts` · `hbcli.ts` · `skeleton-check.ts` | effect interpreter (backoff retry / circuit breaker / mock interpreter, issue #16) + realtime inventory bridge + OpenFlights skeleton (three-valued semantics) |
156
159
  | L5 | loopx governance | objective / gates / evidence / quota |
@@ -166,6 +169,7 @@ Engine verdict:
166
169
  - **Z3 solving engine** — feasibility verdicts + door-to-door whole-cost; the historical concurrency race is fixed (§30 regression gate)
167
170
  - **Realtime retrieval** — flight/train/hotel (Fliggy official channel), destination/hotel catalogs, weather, live flight observation, route connectivity; realtime prices can overwrite solver prices (`GOTRY_REALTIME_PRICING=1`)
168
171
  - **Account session search** — Ctrip flights on your own logged-in Chrome; consent & privacy rules above (see 🔐 **Account session: consent & privacy**)
172
+ - **One-time browser extension setup** — `npx gotry setup wizard` walks you through a 30-second install of GoTry Session Bridge (MV3, fixed extension ID, zero system dialogs per session). Background health-watch auto-plays your query once the extension is connected — no manual retry needed.
169
173
  - **Memory & reachability** — motivation profile / wish pool / companions / travel timeline; English output via `GOTRY_LOCALE=en`
170
174
 
171
175
  **Open limitations** (as of 2026-08-29, honest list):
@@ -189,7 +193,7 @@ The authoritative state lives in the docs, not this README: transactional state
189
193
  ./scripts/run-all-tests.sh
190
194
  ```
191
195
 
192
- One-shot full-stack green (pure TS, no Python needed): golden engines · dialogue replay · cross-process async work-orders · plugin smoke · hbcli · process guards · weather · flights · Anything · probePoi · agent-reach · dual-path stability · time-awareness eval · memory domain · **Z3 race (§30) · realtime pricing (§31) · i18n catalog (§32) · M3 cohort evidence contract (§33) · M4 value evidence contract (§34) · M3 nightly evidence producer contract (§35)**.
196
+ One-shot full-stack green (pure TS, no Python needed): golden engines · dialogue replay · cross-process async work-orders · plugin smoke · hbcli · process guards · weather · flights · Anything · probePoi · agent-reach · dual-path stability · time-awareness eval · memory domain · **Z3 race (§30) · realtime pricing (§31) · i18n catalog (§32) · M3 cohort evidence contract (§33) · M4 value evidence contract (§34) · M3 nightly evidence producer contract (§35) · session transport extension bridge (§38) · onboarding UX wizard (§40) · bookable-fact gate (§39)**.
193
197
 
194
198
  ---
195
199
 
@@ -209,4 +213,4 @@ Standard open-source flow: branch off latest `main` (`feat/ · fix/ · docs/ ·
209
213
 
210
214
  **Built with**: DeepSeek Harness 0.1.2-alpha.1 (vendored) · Cordis · Z3 (WASM) · loopx (pipx) · hotelbyte-cli · Agent-Reach v1.5.0 (`.venv/`) · OpenFlights · TypeScript
211
215
 
212
- **Last verified against `v0.0.1-rc.15` (2026-08-29)** — full-stack regression green §1-§36 (release flow: `scripts/publish-npm.sh`).
216
+ **Last verified against `v0.0.1-rc.16` (2026-08-30)** — full-stack regression green §1-§42 (release flow: `scripts/publish-npm.sh`).
package/README.zh-CN.md CHANGED
@@ -21,6 +21,8 @@ npx @danceiny/gotry web
21
21
  # → 浏览器打开 http://127.0.0.1:3080,像聊天一样说「我想去大理三天」
22
22
  ```
23
23
 
24
+ > **换模型 / 换中转?** 价表(`ts/data/llm-price-table.json`,schema `gotry_llm_price_table_v2`)是 `gotry_m3_nightly_run_v1.cost_usd` 唯一事实源(ADR-11)。新增模型或换中转=改本文件走 PR(peak 保守上界只高不低),未知模型 → **fail-closed 不猜价**。漂移监测:`npx tsx ts/scripts/price-drift-watch.ts`(默认离线对照 baseline 比对输出 PR-就绪 Markdown diff;`--fetch` 拉取官方页 + 首次写 fixture)。**永不自动 apply 价格**。
25
+
24
26
  | 你想要的 | 命令 |
25
27
  |---|---|
26
28
  | 🖥️ 对话规划(推荐) | `npx @danceiny/gotry web` → :3080 对话界面 |
@@ -98,7 +100,7 @@ GoTry: 收到。先把约束记下来——
98
100
 
99
101
  ---
100
102
 
101
- ## 🧰 20 个工具 — Tools
103
+ ## 🧰 21 个工具 — Tools
102
104
 
103
105
  | 组 | 工具 | 干什么 |
104
106
  |---|---|---|
@@ -115,6 +117,7 @@ GoTry: 收到。先把约束记下来——
115
117
  | | `gotry_wish_pool_add` / `gotry_wish_pool_list` | 「下一次出发」愿望池 + 0..1 条件召回 |
116
118
  | | `gotry_companion_save` · `gotry_trip_log` | 同行人档案 / 旅行时间线 |
117
119
  | **产物** | `gotry_artifacts_list` / `gotry_artifacts_read` | 发现与查看已生成的产物(异步交付 + 工作目录 markdown),行号文件视图,只读 |
120
+ | **事实闸** | `gotry_fact_gate` | 行程产物交付前闸:每条可下单 claim(航班号/时刻/机场/价格/政策)必须回溯到 exact-date 工具结果(hit/miss 均落账);无法回溯 ⇒ blocked——不得宣称「已验证方案」 |
118
121
  | **通用外部** | `gotry_web_search` · `gotry_video_subtitle` · `gotry_github_search` · `gotry_agent_reach` | 网页/字幕/GitHub/全渠道外部信息(经 Agent-Reach) |
119
122
 
120
123
  ---
@@ -128,7 +131,7 @@ GoTry: 收到。先把约束记下来——
128
131
  3. **物理只读** —— ReadGuard 在网络层中止一切写请求(下单/支付在传输层不可达),agent 永不接触凭证与验证码;遇到验证码立即停,交还给你。
129
132
  4. **绝不劫持你的浏览器** —— 检索/登录只开自己的独立标签页,登录页置前台、留在你那;例行动测试永不自动开浏览器窗。
130
133
 
131
- > 前置(一次性):日常 Chrome 打开 `chrome://inspect/#remote-debugging` 打开开关(Chrome 144+),并保持 Chrome 在线。未开启时工具返回 `needs-attach` 并给出指引,不消耗执行配额。
134
+ > 前置(一次性):安装随包分发的 **GoTry Session Bridge** 浏览器扩展(MV3,约 30 秒):跑 `npx gotry setup` 落位到 `~/.gotry/extension`,再到 Chrome `chrome://extensions` 开启「开发者模式」→「加载已解压的扩展程序」选该目录。装完**零系统弹窗**——扩展只被动转发站点自己发出的检索响应(构造上只读;cookie 只读名字,值永不离开浏览器)。未安装时工具返回 `needs-extension` 并给出指引,不消耗执行配额。(诊断后备:cdp 车道经 `chrome://inspect` 远程调试,`GOTRY_SESSION_TRANSPORT=cdp` 显式开启——注意 Chrome 144+ 每次连接都会弹权限框。)
132
135
 
133
136
  ---
134
137
 
@@ -137,7 +140,7 @@ GoTry: 收到。先把约束记下来——
137
140
  ```
138
141
  ┌──────────────────────────────────────────────────────────────┐
139
142
  │ L1 对话即界面 chat-as-UI; gates 是消息内选择题 │
140
- │ L2 编排 dsh 运行时 + GoTry 插件(ReAct);20 个工具 │
143
+ │ L2 编排 dsh 运行时 + GoTry 插件(ReAct);21 个工具 │
141
144
  │ L3 领域 统一行程模型 + Z3 可行性引擎(枚举/Z3 双形态) │
142
145
  │ L4 数据 静态数据包 + hotelbyte-cli 实时桥 + OpenFlights 骨架 │
143
146
  │ L5 治理 LoopX(objective / gates / evidence / quota) │
@@ -146,7 +149,7 @@ GoTry: 收到。先把约束记下来——
146
149
 
147
150
  | 层 | 模块 | 角色 |
148
151
  |---|---|---|
149
- | L2 | `ts/src/index.ts`(dsh 插件) | 注册 20 工具,挂时间锚点/记忆 brief 变量;execute 异常隔离 + 授权闸 + 进程护栏 |
152
+ | L2 | `ts/src/index.ts`(dsh 插件) | 注册 21 工具,挂时间锚点/记忆 brief 变量;execute 异常隔离 + 授权闸 + 进程护栏 |
150
153
  | L3 | `ts/src/unified.ts` · `py/gotry_feasibility/` | 唯一求解入口(候选枚举 + 航班链 Z3) |
151
154
  | L4 | `ts/capabilities/effect.ts` · `hbcli.ts` · `skeleton-check.ts` | 效应解译层(退避重试/断路器/mock 解译器,issue #16)+ 实时库存桥 + OpenFlights 骨架(三值语义) |
152
155
  | L5 | loopx 治理面 | objective / gates / evidence / quota |
@@ -162,6 +165,7 @@ GoTry: 收到。先把约束记下来——
162
165
  - **Z3 求解引擎** —— 可行性判定 + 门到门全成本;历史并发竞态已根治(§30 并发回归闸)
163
166
  - **实时检索**:机票/火车/酒店(飞猪官方通道)、目的地/酒店目录、天气、航班观测、通航性校验;可选让实时票价覆写进求解(`GOTRY_REALTIME_PRICING=1`)
164
167
  - **账号会话检索**:你本人登录态查携程机票,授权与隐私规则见上方 **🔐 账号会话:授权与隐私** 小节
168
+ - **一次性浏览器扩展安装**:`npx gotry setup wizard` 引导你 30 秒装好 GoTry Session Bridge(MV3,扩展 ID 跨机器稳定,会话内零系统弹窗);后台 health-watch 探活,扩展一就位自动重放你的检索,**无需你手动重跑命令**
165
169
  - **记忆与触达**:动机画像 / 愿望池 / 同行人 / 旅行时间线;英文输出一键切换(`GOTRY_LOCALE=en`)
166
170
 
167
171
  **已知限制**(截至 2026-08-29,诚实清单):
@@ -200,7 +204,7 @@ GoTry: 收到。先把约束记下来——
200
204
  ./scripts/run-all-tests.sh
201
205
  ```
202
206
 
203
- 全栈一次性绿(纯 TS,无 Python 依赖):engine/journey/unified 金标准 · 对话重放 · 异步工单跨进程 · 插件 smoke · hbcli · 进程护栏(含工具异常隔离)· 天气 · 航班 · Anything · probePoi · agent-reach(web/deep/wrapper)· 双路径稳定性 · 时间感评测(锚点卡/槽位过期校验/评分器/mock 回放;真模型巡检 `time-eval-tests.ts --real`)· 记忆域(动机合并守门/效用 sidecar/只读指标投影)· **Z3 并发竞态(§30)· 实时票价桥(§31)· i18n 目录(§32)· M3 cohort 证据合同(§33)· M4 价值证据合同(§34)**。
207
+ 全栈一次性绿(纯 TS,无 Python 依赖):engine/journey/unified 金标准 · 对话重放 · 异步工单跨进程 · 插件 smoke · hbcli · 进程护栏(含工具异常隔离)· 天气 · 航班 · Anything · probePoi · agent-reach(web/deep/wrapper)· 双路径稳定性 · 时间感评测(锚点卡/槽位过期校验/评分器/mock 回放;真模型巡检 `time-eval-tests.ts --real`)· 记忆域(动机合并守门/效用 sidecar/只读指标投影)· **Z3 并发竞态(§30)· 实时票价桥(§31)· i18n 目录(§32)· M3 cohort 证据合同(§33)· M4 价值证据合同(§34)· 会话传输扩展桥(§38)· onboarding UX wizard(§40)· 可下单事实闸(§39)**。
204
208
 
205
209
  ---
206
210
 
@@ -232,4 +236,4 @@ GoTry: 收到。先把约束记下来——
232
236
 
233
237
  **Built with**: DeepSeek Harness 0.1.2-alpha.1 (vendored) · Cordis · Z3 (WASM) · loopx (pipx) · hotelbyte-cli · Agent-Reach v1.5.0 (`.venv/`) · OpenFlights · TypeScript
234
238
 
235
- **Last verified against `v0.0.1-rc.15`(2026-08-29)** — 全栈回归全绿 §1-§36(发布流程见 `scripts/publish-npm.sh`)。
239
+ **Last verified against `v0.0.1-rc.16`(2026-08-30)** — 全栈回归全绿 §1-§42(发布流程见 `scripts/publish-npm.sh`)。
@@ -8,17 +8,26 @@
8
8
  * dsh web 侧栏工作台(文件浏览/Markdown/Mermaid/PDF 预览)——
9
9
  * gotry 产物(工单交付 md/行程 md)的成熟查看面(issue #25)。
10
10
  * 宿主层安装(dsh plugin → ~/.dsh/profiles/web),不进 gotry 依赖。
11
+ * gotry-session-bridge → 会话检索浏览器扩展(issue #21 传输层方案 C,2026-08-29 定案):
12
+ * 一次性安装替代逐连接 CDP 弹窗;幂等拷贝到 ~/.gotry/extension,
13
+ * 最后一步是用户在 chrome://extensions 「加载已解压的扩展程序」(约 30 秒,零系统弹窗)。
11
14
  *
12
15
  * 用法:
13
16
  * node bin/gotry-bootstrap.js # 显式安装(缺啥装啥;失败 exit 1)
14
17
  * node bin/gotry-bootstrap.js --auto # postinstall 模式:CI/跳过开关检测,任何失败不挡安装(exit 0)
15
18
  * node bin/gotry-bootstrap.js --check-only # 只探测报告,不安装(测试钩子)
19
+ * node bin/gotry-bootstrap.js wizard # 会话扩展 onboarding 闭环(issue #21 onboarding UX,§3.3):
20
+ * # 5 步编排 + 后台 health-watch 等扩展心跳,
21
+ * # 扩展一就位 stdout 翻绿并自动重放同 query。
22
+ * # 详见 docs/user-session-data-rfc.md §3.3 / RFC P3.6。
16
23
  *
17
24
  * 环境开关:
18
25
  * GOTRY_SETUP_SKIP=1 全部跳过
19
26
  * GOTRY_SETUP_HBCLI=0 跳过 hbcli
20
27
  * GOTRY_SETUP_REACH=0 跳过 agent-reach
21
28
  * GOTRY_SETUP_SIDEBAR=0 跳过 dsh-better-sidebar
29
+ * GOTRY_SETUP_EXTENSION=0 跳过会话检索扩展落位
30
+ * GOTRY_ONBOARDING_HEADLESS=1 wizard 子命令强制走终端面板(SSH/CI 默认探测)
22
31
  *
23
32
  * 契约:安装外部依赖永远不挡 gotry 本体——能力层各有降级路径(静态包/not-installed
24
33
  * verdict),自举失败只降级体验,不产生故障。凭证(hbcli auth / agent-reach 渠道
@@ -26,7 +35,7 @@
26
35
  */
27
36
 
28
37
  import { spawn } from 'node:child_process'
29
- import { existsSync } from 'node:fs'
38
+ import { copyFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs'
30
39
  import { homedir } from 'node:os'
31
40
  import { join, dirname } from 'node:path'
32
41
  import { fileURLToPath } from 'node:url'
@@ -34,6 +43,8 @@ import { fileURLToPath } from 'node:url'
34
43
  const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
35
44
  const AUTO = process.argv.includes('--auto')
36
45
  const CHECK_ONLY = process.argv.includes('--check-only')
46
+ const WIZARD = process.argv.includes('wizard') || process.argv.includes('--wizard')
47
+ const WIZARD_DRY_RUN = process.argv.includes('--dry-run')
37
48
 
38
49
  const HBCLI_INSTALL_CMD = 'curl -fsSL https://github.com/hotelbyte-com/docs/releases/latest/download/install.sh | bash'
39
50
  const REACH_INSTALL_URL = 'git+https://github.com/Panniantong/Agent-Reach.git'
@@ -68,7 +79,14 @@ async function setupHbcli() {
68
79
  say('[gotry-setup] hbcli(hotelbyte-cli,可选酒店实时源)')
69
80
  const candidates = ['hbcli', join(homedir(), '.local/bin/hbcli'), join(homedir(), '.staicli/current/hbcli')]
70
81
  const present = candidates.some((p) => existsSync(p)) && (await probe(candidates[0], ['version']) || await probe(candidates[1], ['version']) || await probe(candidates[2], ['version']))
71
- if (present) { say(' 已安装'); return { ok: true } }
82
+ const credFile = join(homedir(), '.staicli', 'credentials.json')
83
+ if (present) {
84
+ say(' ✓ 已安装')
85
+ if (existsSync(credFile)) say(' ✓ 凭证已配置(自检: hbcli auth whoami)')
86
+ else say(' ✗ 凭证未配置——酒店检索将用内置静态包(非实时),账号配置见下方指引')
87
+ if (CHECK_ONLY) say(' (--check-only 只报告,不安装)')
88
+ return { ok: true }
89
+ }
72
90
  if (CHECK_ONLY) { say(' ✗ 未安装(--check-only 只报告)'); return { ok: true } }
73
91
  say(` 安装中(官方脚本): ${HBCLI_INSTALL_CMD}`)
74
92
  const r = await run('bash', ['-c', HBCLI_INSTALL_CMD], { timeoutMs: 120_000 })
@@ -77,7 +95,11 @@ async function setupHbcli() {
77
95
  if (!process.env.PATH.split(':').includes(binDir)) {
78
96
  say(` ⚠ ${binDir} 不在当前 PATH —— gotry 工具已内建候选路径回退,无需手动处理;其他程序可用: export PATH="${binDir}:$PATH"`)
79
97
  }
80
- say(' ✓ 安装完成(凭证选配: hbcli auth set-credentials --app-key ... --app-secret ...,或 HOTELBYTE_TOKEN;未配时酒店检索自动用静态包)')
98
+ say(' ✓ 安装完成——账号配置(未配时酒店检索自动用静态包,非实时):')
99
+ say(' · 快速试用(官方沙箱演示账号): hbcli auth set-credentials --app-key hotelbyte_api_demo --app-secret hotelbyte_api_demo')
100
+ say(' · 正式接入: 向 HotelByte 申请专属 appKey/appSecret 后用同一命令替换')
101
+ say(' · 门户账号(权限更大,酒店搜索无必要): hbcli auth login --username <email>')
102
+ say(' 自检: hbcli auth whoami(api_key.configured=true 即就位)')
81
103
  return { ok: true }
82
104
  }
83
105
 
@@ -98,8 +120,7 @@ async function setupReach() {
98
120
  return { ok: true }
99
121
  }
100
122
 
101
- async function setupSidebar() {
102
- say('[gotry-setup] dsh-better-sidebar(dsh web 侧栏工作台,产物查看面 issue #25)')
123
+ async function setupSidebar() { say('[gotry-setup] dsh-better-sidebar(dsh web 侧栏工作台,产物查看面 issue #25)')
103
124
  const installed = existsSync(join(homedir(), '.dsh/profiles/web/node_modules/dsh-better-sidebar/package.json'))
104
125
  if (installed) { say(' ✓ 已安装(~/.dsh/profiles/web)'); return { ok: true } }
105
126
  if (CHECK_ONLY) { say(' ✗ 未安装(--check-only 只报告)'); return { ok: true } }
@@ -129,11 +150,277 @@ async function setupSidebar() {
129
150
  return { ok: true }
130
151
  }
131
152
 
153
+ const EXTENSION_FILES = ['manifest.json', 'background.js', 'content-main.js', 'content-bridge.js', 'README.md']
154
+
155
+ function readManifestVersion(manifestPath) {
156
+ try { return JSON.parse(readFileSync(manifestPath, 'utf8')).version ?? null } catch { return null }
157
+ }
158
+
159
+ function copyExtensionDir(srcDir, dstDir) {
160
+ mkdirSync(dstDir, { recursive: true })
161
+ for (const f of EXTENSION_FILES) {
162
+ const s = join(srcDir, f)
163
+ if (!existsSync(s)) continue
164
+ copyFileSync(s, join(dstDir, f))
165
+ }
166
+ }
167
+
168
+ /** 会话检索扩展(issue #21 方案 C):包内 extension/ → ~/.gotry/extension 幂等落位 + 一次性加载指引 */
169
+ async function setupExtension() {
170
+ say('[gotry-setup] GoTry Session Bridge 扩展(会话检索数据面 issue #21;一次性安装,替代逐连接弹窗)')
171
+ const srcDir = join(repoRoot, 'extension')
172
+ const srcManifest = join(srcDir, 'manifest.json')
173
+ if (!existsSync(srcManifest)) {
174
+ say(` ✗ 包内未找到扩展文件(${srcDir})——不影响 gotry:会话检索降级,重装 npx gotry 可恢复`)
175
+ return { ok: false }
176
+ }
177
+ const srcVersion = readManifestVersion(srcManifest)
178
+ const dstDir = join(homedir(), '.gotry', 'extension')
179
+ const dstVersion = readManifestVersion(join(dstDir, 'manifest.json'))
180
+ if (dstVersion != null && dstVersion === srcVersion) {
181
+ say(` ✓ 已就位(~/.gotry/extension,v${dstVersion};若浏览器里尚未加载:chrome://extensions → 开发者模式 → 加载已解压的扩展程序 → 选 ~/.gotry/extension)`)
182
+ return { ok: true }
183
+ }
184
+ if (CHECK_ONLY) { say(` ✗ ${dstVersion == null ? `未安装(落位 ${dstDir})` : `待更新 v${dstVersion} → v${srcVersion},落位 ${dstDir}`}(--check-only 只报告)`); return { ok: true } }
185
+ try {
186
+ copyExtensionDir(srcDir, dstDir)
187
+ } catch (e) {
188
+ say(` ✗ 落位失败(${e.message})——不影响 gotry:会话检索降级;可手动拷贝 ${srcDir} → ${dstDir}`)
189
+ return { ok: false }
190
+ }
191
+ say(` ✓ 已落位 ${dstDir}(v${srcVersion};manifest 带固定 key,扩展 ID 恒为 olpgkofjhhiiiahdkkbcninhjmegghfe)`)
192
+ say(' 最后一步(每台浏览器一次,约 30 秒):Chrome 打开 chrome://extensions → 右上角开启「开发者模式」→「加载已解压的扩展程序」→ 选择 ~/.gotry/extension')
193
+ say(' 装好即生效,零系统弹窗;扩展卡片开关=总闸(与 gotry 授权闸 sessionAccess 双重控制)')
194
+ return { ok: true }
195
+ }
196
+
197
+ /**
198
+ * 调 wizard-bootstrap.ts(wizard.ts 的 CLI 入口):拿 5 步 OnboardingResult 打印到 stdout。
199
+ * tsx 子进程跑 wizard 全套(扩展落位 + 开 Chrome + pbcopy 真实覆写剪贴板 + osascript GUI 面板);
200
+ * bootstrap 不再 inline 任何 spawn——所有 UX 都走 wizard.ts,与 onboarding-tests §40 同一份代码。
201
+ * npm 安装态(无 ts/目录):降级 inline 跑 5 步的最小子集——只调 setupExtension + 打印路径,UI 面板/剪贴板由用户手动。
202
+ */
203
+ async function runWizardBootstrap() {
204
+ const wizardScript = join(repoRoot, 'ts', 'scripts', 'wizard-bootstrap.ts')
205
+ const extensionDir = join(homedir(), '.gotry', 'extension')
206
+ if (!existsSync(wizardScript)) {
207
+ // npm 安装态降级:只确保扩展落位,文案照常打
208
+ say('[gotry-wizard] 内置 wizard 脚本缺失(已装 npm 包态)——降级手动模式:')
209
+ await setupExtension()
210
+ return { ok: true }
211
+ }
212
+ return new Promise((resolve) => {
213
+ const child = spawn('npx', ['--yes', 'tsx', wizardScript, '--extension-dir', extensionDir, '--source-dir', join(repoRoot, 'extension')], {
214
+ cwd: join(repoRoot, 'ts'),
215
+ stdio: ['ignore', 'pipe', 'inherit'],
216
+ })
217
+ let stdoutBuf = ''
218
+ let resolved = false
219
+ const finish = (r) => {
220
+ if (resolved) return
221
+ resolved = true
222
+ resolve(r)
223
+ }
224
+ child.stdout.on('data', (c) => {
225
+ stdoutBuf += c.toString('utf8')
226
+ let nl = stdoutBuf.indexOf('\n')
227
+ while (nl >= 0) {
228
+ const line = stdoutBuf.slice(0, nl).trim()
229
+ stdoutBuf = stdoutBuf.slice(nl + 1)
230
+ if (line.startsWith('{')) {
231
+ try {
232
+ const o = JSON.parse(line)
233
+ // 打印 5 步 status(wizard 自己的 print 已在子进程跑了;此处父进程再 human-friendly 一次)
234
+ if (Array.isArray(o.steps)) {
235
+ for (const s of o.steps) {
236
+ const tag = s.status === 'ok' ? '✅' : s.status === 'skip' ? '⏭ ' : '❌'
237
+ say(`[gotry-wizard] ${tag} ${s.step}: ${s.summary}`)
238
+ }
239
+ }
240
+ say(`[gotry-wizard] platform=${o.platform}; extensionDir=${o.extensionDir}; ok=${o.ok}`)
241
+ finish({ ok: o.ok === true })
242
+ } catch {
243
+ say(`[gotry-wizard] ✗ wizard JSON 解析失败`)
244
+ finish({ ok: false })
245
+ }
246
+ try { child.kill('SIGTERM') } catch { /* ignore */ }
247
+ return
248
+ }
249
+ nl = stdoutBuf.indexOf('\n')
250
+ }
251
+ })
252
+ child.on('exit', (code) => {
253
+ if (!resolved) {
254
+ say(`[gotry-wizard] ✗ wizard 子进程 exit ${code}(stdout 无 JSON)`)
255
+ finish({ ok: code === 0 })
256
+ }
257
+ })
258
+ child.on('error', (e) => {
259
+ say(`[gotry-wizard] ✗ wizard 子进程启动失败: ${e.message}`)
260
+ finish({ ok: false })
261
+ })
262
+ })
263
+ }
264
+
265
+ /**
266
+ * 后台探活扩展心跳(wizard 闭环「装完零重跑」,§3.3):
267
+ * spawn tsx 跑 health-watch.ts(纯 TS 探活,run-all §40 同一份代码),接 stdout JSON 结果行。
268
+ * 探活期间每 5s stdout 一行 `.`,用户看见 stdout 持续推进,不卡死假象。
269
+ * 默认 120s 超时;若用户提前 Ctrl+C,timeoutMs 走 0 立即返回 cancelled。
270
+ */
271
+ async function runHealthWatch() {
272
+ // 跨语言:bootstrap 是纯 JS,health-watch 是 TS;走 spawn npx tsx 子进程,stdout 解析。
273
+ // tsx 子进程路径 = <repoRoot>/ts;不在 npm 包里跑(发布面只走 src/bin,不走 wizard 自动化)
274
+ const watcherScript = join(repoRoot, 'ts', 'scripts', 'health-watch-cli.ts')
275
+ // 默认 120s / 5s,§3.3 设计值;env 可覆盖(bootstrap-tests 跑真实路径用)
276
+ const timeoutMs = parseInt(process.env.GOTRY_ONBOARDING_TIMEOUT_MS ?? '120000', 10) || 120_000
277
+ const probeIntervalMs = parseInt(process.env.GOTRY_ONBOARDING_INTERVAL_MS ?? '5000', 10) || 5_000
278
+ return new Promise((resolve) => {
279
+ const args = [watcherScript, '--timeout', String(timeoutMs), '--interval', String(probeIntervalMs), '--json']
280
+ // 先确认 watcher 脚本存在(npm 安装态可能未带 ts/目录——降级:走内联 node:http 短探活,避免挡用户)
281
+ if (!existsSync(watcherScript)) {
282
+ say(`[gotry-wizard] 内置探活脚本缺失(${watcherScript})——改用内置 5s 轮询`)
283
+ resolve(runInlineHealthWatch(timeoutMs))
284
+ return
285
+ }
286
+ const child = spawn('npx', ['--yes', 'tsx', ...args], {
287
+ cwd: join(repoRoot, 'ts'),
288
+ stdio: ['ignore', 'pipe', 'inherit'],
289
+ env: { ...process.env, GOTRY_ONBOARDING_HEADLESS: process.env.GOTRY_ONBOARDING_HEADLESS ?? '1' },
290
+ })
291
+ let stdoutBuf = ''
292
+ let heartbeatTicker = 0
293
+ const hbInterval = setInterval(() => {
294
+ heartbeatTicker += 1
295
+ process.stdout.write('.')
296
+ if (heartbeatTicker % 12 === 0) process.stdout.write(`(${Math.round(heartbeatTicker * probeIntervalMs / 1000)}s)\n`)
297
+ }, probeIntervalMs)
298
+ child.stdout.on('data', (c) => {
299
+ const s = c.toString('utf8')
300
+ stdoutBuf += s
301
+ // JSON 结果行以 \n 分隔;watcher 一次性 stdout 一行 JSON 退出
302
+ let nl = stdoutBuf.indexOf('\n')
303
+ while (nl >= 0) {
304
+ const line = stdoutBuf.slice(0, nl).trim()
305
+ stdoutBuf = stdoutBuf.slice(nl + 1)
306
+ if (line.startsWith('{')) {
307
+ clearInterval(hbInterval)
308
+ try {
309
+ const o = JSON.parse(line)
310
+ resolve({ ready: o.ready === true, attempts: o.attempts ?? 0, waitedMs: o.waitedMs ?? 0, reason: o.reason ?? 'timeout', timeoutMs })
311
+ } catch {
312
+ resolve({ ready: false, attempts: 0, waitedMs: 0, reason: 'parse-error', timeoutMs })
313
+ }
314
+ try { child.kill('SIGTERM') } catch { /* ignore */ }
315
+ return
316
+ }
317
+ nl = stdoutBuf.indexOf('\n')
318
+ }
319
+ })
320
+ child.on('exit', (code) => {
321
+ clearInterval(hbInterval)
322
+ process.stdout.write('\n')
323
+ if (code !== 0 && stdoutBuf.trim() === '') {
324
+ resolve({ ready: false, attempts: 0, waitedMs: 0, reason: `watcher-exit-${code}`, timeoutMs })
325
+ }
326
+ })
327
+ child.on('error', () => {
328
+ clearInterval(hbInterval)
329
+ resolve({ ready: false, attempts: 0, waitedMs: 0, reason: 'watcher-spawn-error', timeoutMs })
330
+ })
331
+ })
332
+ }
333
+
334
+ /** npm 安装态(无 ts/目录)降级:内置 5s 轮询 node:http,与 health-watch 同节奏 */
335
+ async function runInlineHealthWatch(timeoutMs) {
336
+ const intervalMs = parseInt(process.env.GOTRY_ONBOARDING_INTERVAL_MS ?? '5000', 10) || 5_000
337
+ const ports = [8791, 8792, 8793, 8794, 8795]
338
+ const http = await import('node:http')
339
+ const startedAt = Date.now()
340
+ let attempts = 0
341
+ while (Date.now() - startedAt < timeoutMs) {
342
+ attempts += 1
343
+ for (const port of ports) {
344
+ const ok = await new Promise((resolve) => {
345
+ const req = http.request({ host: '127.0.0.1', port, path: '/status', method: 'GET', timeout: 2_000 }, (res) => {
346
+ if (!res.statusCode || res.statusCode >= 400) { resolve(false); return }
347
+ let buf = ''
348
+ res.setEncoding('utf8')
349
+ res.on('data', (c) => { buf += c })
350
+ res.on('end', () => {
351
+ try { resolve(JSON.parse(buf).extensionConnected === true) } catch { resolve(false) }
352
+ })
353
+ res.on('error', () => resolve(false))
354
+ })
355
+ req.on('error', () => resolve(false))
356
+ req.on('timeout', () => { req.destroy(); resolve(false) })
357
+ req.end()
358
+ })
359
+ if (ok) {
360
+ process.stdout.write('\n')
361
+ return { ready: true, attempts, waitedMs: Date.now() - startedAt, reason: 'ready', timeoutMs }
362
+ }
363
+ }
364
+ process.stdout.write('.')
365
+ await new Promise((r) => setTimeout(r, intervalMs))
366
+ }
367
+ process.stdout.write('\n')
368
+ return { ready: false, attempts, waitedMs: Date.now() - startedAt, reason: 'timeout', timeoutMs }
369
+ }
370
+
132
371
  async function main() {
372
+ // wizard 子命令(issue #21 onboarding UX,§3.3 / P3.6):扩展安装闭环,5 步 + 后台 health-watch。
373
+ // **纯 onboarding**,不调 hbcli/agent-reach/sidebar 等其他节——只跑扩展落位 + 健康探活;
374
+ // 想要全量安装仍走 `npx gotry setup`(无 wizard)。
375
+ if (WIZARD) {
376
+ if (WIZARD_DRY_RUN) {
377
+ say('[gotry-wizard] dry-run 模式(零网络零浏览器,只校验命令编排与输出形态;run-all §40 走这条)')
378
+ say(' 步骤: ensure-extension-files → open-chrome-extensions → clipboard-extension-path → panel-guide → watch-extension-ready')
379
+ say(' 平台: darwin(zh-CN stdout);headless/Linux/Windows 同理降级')
380
+ say(' ✅ exit 0')
381
+ process.exit(0)
382
+ }
383
+ // 真实路径:spawn tsx 子进程跑 wizard-bootstrap.ts(同一份 wizard.ts 代码),
384
+ // 拿 5 步 OnboardingResult 打印 status(ok/✅/⏭ /❌)+ 剪贴板/GUI 面板/落位实跑。
385
+ const wizardResult = await runWizardBootstrap()
386
+ if (!wizardResult.ok) {
387
+ say('[gotry-wizard] ✗ wizard 编排有失败步——见上方;扩展未引导到位;可手动 chrome://extensions 加载 ~/.gotry/extension')
388
+ process.exit(1)
389
+ }
390
+ // wizard 闭环:装完扩展就位 stdout 翻「✅ 就绪」(由 health-watch 推动)
391
+ say('')
392
+ say('[gotry-wizard] ────────────────────────────────')
393
+ say('[gotry-wizard] 共 3 步,约 30 秒,装完零弹窗:')
394
+ say('[gotry-wizard] ① Chrome 右上角开启「开发者模式」')
395
+ say(`[gotry-wizard] ② 点「加载已解压的扩展程序」,选这个目录(已复制到剪贴板):`)
396
+ say(`[gotry-wizard] ${join(homedir(), '.gotry', 'extension')}`)
397
+ say('[gotry-wizard] ③ 弹窗「添加扩展」点「添加」')
398
+ say('[gotry-wizard] ────────────────────────────────')
399
+ say('[gotry-wizard] 装好后**无需重跑任何命令**——下面会自动探活,扩展一就位 stdout 翻绿。')
400
+ say('')
401
+ say('[gotry-wizard] 正在后台探活扩展心跳(最长 120s;Ctrl+C 取消)...')
402
+ // 后台 health-watch:spawn tsx 子进程跑 health-watch.ts(纯 TS 模块,browser-only),
403
+ // 探活逻辑与 onboarding-tests §40 同一份代码;无 GUI 依赖。
404
+ const watchResult = await runHealthWatch()
405
+ if (watchResult.ready) {
406
+ say(`[gotry-wizard] ✅ 扩展就绪(等待 ${watchResult.waitedMs}ms,${watchResult.attempts} 次探活)`)
407
+ say('[gotry-wizard] 现在调用 gotry_session_search 即可拿到会话检索结果(零后续手工动作)。')
408
+ say('[gotry-wizard] 入口示例:')
409
+ say(' ./gotry # 启动 dsh → 用 gotry_session_search 工具')
410
+ say(' ./gotry session-check # 跑 sf-01..08 八条 query 双源 scorer(goal 2)')
411
+ process.exit(0)
412
+ } else {
413
+ say(`[gotry-wizard] ✗ ${watchResult.reason}(${watchResult.attempts} 次探活)——扩展未在 ${watchResult.timeoutMs}ms 内就绪`)
414
+ say('[gotry-wizard] 重新打开 chrome://extensions 确认「GoTry Session Bridge · 已启用」,再跑 `npx gotry setup wizard` 重试。')
415
+ process.exit(1)
416
+ }
417
+ }
418
+
133
419
  if (process.platform === 'win32') {
134
420
  say('[gotry-setup] Windows 暂不支持自动安装(hbcli 上游仅 darwin/linux)。手动指引:')
135
421
  say(` hbcli: ${HBCLI_INSTALL_CMD}(WSL);agent-reach: python -m venv .venv && .venv/Scripts/pip install ${REACH_INSTALL_URL}`)
136
422
  say(' dsh-better-sidebar: npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar@latest')
423
+ say(' GoTry Session Bridge 扩展:手动把包内 extension/ 目录拷到 %USERPROFILE%\\.gotry\\extension,再在 chrome://extensions 开发者模式「加载已解压的扩展程序」')
137
424
  process.exit(AUTO ? 0 : 1)
138
425
  }
139
426
  if (AUTO && (process.env.CI || process.env.GOTRY_SETUP_SKIP === '1')) {
@@ -149,6 +436,8 @@ async function main() {
149
436
  else say('[gotry-setup] agent-reach:GOTRY_SETUP_REACH=0 跳过')
150
437
  if (process.env.GOTRY_SETUP_SIDEBAR !== '0') results.push(await setupSidebar())
151
438
  else say('[gotry-setup] dsh-better-sidebar:GOTRY_SETUP_SIDEBAR=0 跳过')
439
+ if (process.env.GOTRY_SETUP_EXTENSION !== '0') results.push(await setupExtension())
440
+ else say('[gotry-setup] GoTry Session Bridge 扩展:GOTRY_SETUP_EXTENSION=0 跳过')
152
441
  say('[gotry-setup] flyai:无需安装(npx 每次自拉 @fly-ai/flyai-cli,免 key)')
153
442
  const failed = results.filter((r) => !r.ok).length
154
443
  if (failed > 0) {
@@ -91,3 +91,13 @@
91
91
  开页探测;返回 challenged→立即停手如实告知,绝不重试
92
92
  绝不绕过。跨源结论各带来源标注([实时API:flyai@ts]/[会话:ctrip-flight@ts]/
93
93
  [静态包:估算]),分桶比对直达/中转,不比裸最低价;证据链随结论必达用户。
94
+ (20)事实闸(可下单事实 fail-closed,issue #46):航班号/时刻/机场/价格/签证政策
95
+ 只能来自本轮 exact-date 工具结果;exact-date 返回 miss = 「未确认/当前不可售,
96
+ 到 D-xx 复核」,**不得**用航线页当日班期、历史航季、相邻日期或记忆填充;
97
+ 「只验证航线存在」永不升级为「该日期有该航班」;「联程」仅用于同票保护
98
+ (protected_connection),分票/自助转机必须显式标红(无行李直挂/误机无保护);
99
+ 政策只能表述为「截至 YYYY-MM-DD 的现行政策」并附复核日期,不用无条件 ✓;
100
+ 双机场城市的航司落点不互换(FD 落 DMK、VZ 落 BKK)。
101
+ **交付任何含可下单事实的行程产物(markdown 文件或长方案)前,必须调
102
+ gotry_fact_gate**;闸 blocked 时逐条修正为可回溯事实或降级措辞,
103
+ 不得宣称「已验证方案」。
@@ -0,0 +1,39 @@
1
+ {
2
+ "meta": {
3
+ "schema": "gotry_airline_airports.v1",
4
+ "as_of": "2026-08-29",
5
+ "review_by": "2026-09-28",
6
+ "source": "issue #46 exact-date audit snapshot (trip.com 2027 日期可售快照)",
7
+ "note": "两张表职责不同:city_alias 是稳定词汇(解析航线上下文);carrier_airport 是 dated 快照,只作冲突检测——可下单事实的唯一来源永远是 exact-date 工具结果,本表不产事实。双机场城市的航司→机场映射过期风险高,过 review_by 后只降级使用(仍作冲突检测,结论标注快照日)。"
8
+ },
9
+ "city_alias": {
10
+ "香港": "HKG",
11
+ "普吉": "HKT",
12
+ "甲米": "KBV",
13
+ "奥南": "KBV",
14
+ "曼谷": "BKK",
15
+ "深圳": "SZX",
16
+ "广州": "CAN",
17
+ "迪拜": "DXB",
18
+ "昆明": "KMG",
19
+ "大理": "DLU",
20
+ "丽江": "LJG",
21
+ "南京": "NKG",
22
+ "上海": "PVG",
23
+ "北京": "PEK",
24
+ "成都": "CTU",
25
+ "西安": "XIY",
26
+ "杭州": "HGH",
27
+ "多哈": "DOH"
28
+ },
29
+ "carrier_airport": {
30
+ "BKK": {
31
+ "FD": "DMK",
32
+ "VZ": "BKK",
33
+ "SL": "DMK",
34
+ "DD": "DMK",
35
+ "PG": "BKK",
36
+ "TG": "BKK"
37
+ }
38
+ }
39
+ }
@@ -169,4 +169,4 @@ export async function githubSearch(q) {
169
169
  }
170
170
 
171
171
 
172
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/agent-reach-deep.ts
172
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/agent-reach-deep.ts
@@ -201,4 +201,4 @@ export async function reachStatus(timeoutMs = 90_000) {
201
201
  }
202
202
 
203
203
 
204
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/agent-reach.ts
204
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/agent-reach.ts
@@ -136,4 +136,4 @@ export const _ = {
136
136
  };
137
137
 
138
138
 
139
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/anything.ts
139
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/anything.ts
@@ -214,4 +214,4 @@ export async function readArtifact(opts) {
214
214
  }
215
215
 
216
216
 
217
- //# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/artifacts.ts
217
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/capabilities/artifacts.ts