@danceiny/gotry 0.0.1-rc.18 → 0.0.1-rc.20

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 (176) hide show
  1. package/README.md +42 -16
  2. package/README.zh-CN.md +37 -15
  3. package/bin/gotry-bootstrap.js +309 -30
  4. package/bin/gotry-inner.js +74 -23
  5. package/bin/gotry-runtime-resolution.d.ts +1 -1
  6. package/bin/gotry-runtime-resolution.js +7 -8
  7. package/cordis.gotry-patch.yml +22 -8
  8. package/data/stations-12306-verify.json +524 -0
  9. package/dist/capabilities/agent-reach.js +4 -4
  10. package/dist/capabilities/anything.js +62 -29
  11. package/dist/capabilities/channel-health.js +103 -0
  12. package/dist/capabilities/channel-registry.js +213 -0
  13. package/dist/capabilities/doctor.js +307 -0
  14. package/dist/capabilities/effect.js +178 -5
  15. package/dist/capabilities/flyai.js +13 -0
  16. package/dist/capabilities/hbcli.js +28 -2
  17. package/dist/capabilities/session/adapters/ctrip-hotel.js +207 -0
  18. package/dist/capabilities/session/adapters/rail-12306.js +285 -0
  19. package/dist/capabilities/session/extension-bridge.js +14 -1
  20. package/dist/capabilities/session/extension-channel.js +4 -2
  21. package/dist/capabilities/session/extension-distribution.js +1 -1
  22. package/dist/capabilities/session-search.js +311 -0
  23. package/dist/capabilities/visa-policy.js +126 -0
  24. package/dist/scripts/agent-planning-turn-deadline-e2e.js +305 -0
  25. package/dist/scripts/agent-planning-turn-deadline-tests.js +281 -0
  26. package/dist/scripts/anything-tests.js +70 -21
  27. package/dist/scripts/benchmark-environment-bridge-e2e.js +139 -37
  28. package/dist/scripts/benchmark-environment-bridge-tests.js +1099 -306
  29. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +26 -26
  30. package/dist/scripts/{booking-copilot-availability-policy-v2-tests.js → booking-copilot-availability-policy-tests.js} +115 -115
  31. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +41 -1
  32. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +75 -120
  33. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +370 -61
  34. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +2 -2
  35. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +7 -3
  36. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +15 -9
  37. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +19 -11
  38. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +16 -52
  39. package/dist/scripts/booking-copilot-runtime-proof-tests.js +3953 -155
  40. package/dist/scripts/booking-copilot-server-proof-tests.js +58 -23
  41. package/dist/scripts/booking-copilot-startup-proof-tests.js +10 -119
  42. package/dist/scripts/booking-surface-contract-proof-tests.js +1154 -326
  43. package/dist/scripts/bootstrap-tests.js +113 -2
  44. package/dist/scripts/build-metrics-report.js +339 -0
  45. package/dist/scripts/channel-probe-tests.js +148 -0
  46. package/dist/scripts/channel-probe.js +252 -0
  47. package/dist/scripts/channel-registry-tests.js +262 -0
  48. package/dist/scripts/doctor-tests.js +175 -0
  49. package/dist/scripts/effect-tests.js +274 -1
  50. package/dist/scripts/extension-tests.js +129 -11
  51. package/dist/scripts/fact-gate-tests.js +91 -2
  52. package/dist/scripts/flyai-tests.js +17 -1
  53. package/dist/scripts/hbcli-e2e-tests.js +1 -3
  54. package/dist/scripts/hbcli-tests.js +38 -1
  55. package/dist/scripts/metrics-report-tests.js +288 -0
  56. package/dist/scripts/persona-surface-guard-tests.js +19 -0
  57. package/dist/scripts/session-tests.js +401 -1
  58. package/dist/scripts/smoke.js +100 -83
  59. package/dist/scripts/turn-handoff-collect-tests.js +216 -0
  60. package/dist/scripts/turn-handoff-collect.js +189 -0
  61. package/dist/scripts/turn-policy-tests.js +56 -0
  62. package/dist/scripts/typed-contract-canary.js +261 -0
  63. package/dist/scripts/visa-policy-tests.js +75 -0
  64. package/dist/scripts/wish-channel-gate-tests.js +111 -0
  65. package/dist/src/artifact-gate.js +91 -3
  66. package/dist/src/benchmark-agent-conformance.js +39 -16
  67. package/dist/src/benchmark-environment-bridge.js +316 -52
  68. package/dist/src/bookable-facts.js +62 -3
  69. package/dist/src/booking-surface/{availability-policy-v2.js → availability-policy.js} +89 -89
  70. package/dist/src/booking-surface/canonical-schema.js +8 -22
  71. package/dist/src/booking-surface/contracts.js +70 -19
  72. package/dist/src/booking-surface/dsh-planner.js +310 -177
  73. package/dist/src/booking-surface/dsh-plugin.js +3 -3
  74. package/dist/src/booking-surface/index.js +1 -5
  75. package/dist/src/booking-surface/profile.js +1 -1
  76. package/dist/src/booking-surface/runtime.js +1682 -247
  77. package/dist/src/booking-surface/server.js +371 -185
  78. package/dist/src/booking-surface/startup.js +17 -27
  79. package/dist/src/booking-surface/validation.js +270 -760
  80. package/dist/src/index.js +739 -150
  81. package/dist/src/turn-deadline.js +292 -0
  82. package/dist/src/turn-policy.js +146 -0
  83. package/dist/src/wish-pool.js +5 -0
  84. package/extension/background.js +23 -4
  85. package/extension/content-main.js +47 -9
  86. package/extension/manifest.json +27 -9
  87. package/package.json +7 -26
  88. package/schemas/booking.surface.schema.json +2593 -0
  89. package/ts/capabilities/agent-reach.ts +4 -4
  90. package/ts/capabilities/anything.ts +89 -38
  91. package/ts/capabilities/flyai.ts +21 -3
  92. package/ts/capabilities/hbcli.ts +53 -4
  93. package/ts/capabilities/session/action-cache.ts +1 -1
  94. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  95. package/ts/capabilities/session/adapters/rail-12306.ts +293 -0
  96. package/ts/capabilities/session/extension-bridge.ts +32 -2
  97. package/ts/capabilities/session/extension-channel.ts +9 -8
  98. package/ts/capabilities/session/extension-distribution.ts +3 -1
  99. package/ts/capabilities/session/health-watch.ts +1 -1
  100. package/ts/capabilities/session/read-guard.ts +1 -1
  101. package/ts/capabilities/session/wizard.ts +1 -1
  102. package/ts/capabilities/session-search.ts +323 -1
  103. package/ts/dsh-runtime/vendor/README.md +60 -0
  104. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  105. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +200 -0
  106. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +202 -0
  107. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +216 -0
  108. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  109. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +371 -0
  110. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  111. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +56 -0
  112. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  113. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  114. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +126 -0
  115. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  116. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +109 -0
  117. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +27 -0
  118. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  119. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  120. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +170 -0
  121. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +53 -0
  122. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  123. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  124. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  125. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  126. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +20 -0
  127. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  128. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  129. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  130. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  131. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  132. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +16 -0
  133. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +55 -0
  134. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  135. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +91 -0
  136. package/ts/package.json +9 -0
  137. package/ts/scripts/turn-handoff-collect.ts +178 -0
  138. package/ts/src/artifact-gate.ts +82 -2
  139. package/ts/src/benchmark-agent-conformance.ts +54 -15
  140. package/ts/src/benchmark-environment-bridge.ts +234 -46
  141. package/ts/src/bookable-facts.ts +109 -5
  142. package/ts/src/booking-saga.ts +1 -1
  143. package/ts/src/booking-surface/{availability-policy-v2.ts → availability-policy.ts} +124 -125
  144. package/ts/src/booking-surface/canonical-schema.js +8 -22
  145. package/ts/src/booking-surface/contracts.ts +278 -239
  146. package/ts/src/booking-surface/dsh-planner.ts +333 -194
  147. package/ts/src/booking-surface/dsh-plugin.js +3 -3
  148. package/ts/src/booking-surface/index.ts +1 -5
  149. package/ts/src/booking-surface/profile.ts +11 -11
  150. package/ts/src/booking-surface/runtime.ts +1351 -351
  151. package/ts/src/booking-surface/server.ts +282 -175
  152. package/ts/src/booking-surface/startup.ts +36 -48
  153. package/ts/src/booking-surface/validation.ts +194 -442
  154. package/ts/src/contracts.ts +1 -1
  155. package/ts/src/index.ts +492 -172
  156. package/ts/src/loop.ts +1 -1
  157. package/ts/src/state-ledger.ts +1 -1
  158. package/ts/src/turn-deadline.ts +365 -0
  159. package/ts/src/turn-policy.ts +154 -0
  160. package/ts/src/wish-pool.ts +17 -4
  161. package/dist/scripts/agent-planning-budget-e2e.js +0 -227
  162. package/dist/scripts/agent-planning-budget-tests.js +0 -173
  163. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +0 -3935
  164. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +0 -1174
  165. package/dist/src/booking-surface/contracts-v2.js +0 -89
  166. package/dist/src/booking-surface/runtime-v2.js +0 -1771
  167. package/dist/src/booking-surface/server-v2.js +0 -334
  168. package/dist/src/booking-surface/validation-v2.js +0 -319
  169. package/dist/src/tool-budget.js +0 -136
  170. package/schemas/booking.surface.v1.schema.json +0 -927
  171. package/schemas/booking.surface.v2.schema.json +0 -61
  172. package/ts/src/booking-surface/contracts-v2.ts +0 -118
  173. package/ts/src/booking-surface/runtime-v2.ts +0 -1466
  174. package/ts/src/booking-surface/server-v2.ts +0 -247
  175. package/ts/src/booking-surface/validation-v2.ts +0 -205
  176. package/ts/src/tool-budget.ts +0 -165
@@ -0,0 +1,307 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { homedir } from 'node:os';
5
+ import { join, resolve } from 'node:path';
6
+ import { readLatestChannelEvents } from './channel-health.js';
7
+ function hbcliCandidates(homeDir) {
8
+ return [
9
+ 'hbcli',
10
+ join(homeDir, '.local/bin/hbcli'),
11
+ join(homeDir, '.staicli/current/hbcli')
12
+ ];
13
+ }
14
+ function probe(bin, args, timeoutMs = 8_000) {
15
+ return new Promise((resolveProbe)=>{
16
+ const child = spawn(bin, args, {
17
+ stdio: 'ignore',
18
+ env: process.env
19
+ });
20
+ let done = false;
21
+ const timer = setTimeout(()=>{
22
+ if (!done) {
23
+ done = true;
24
+ try {
25
+ child.kill('SIGKILL');
26
+ } catch {}
27
+ resolveProbe(false);
28
+ }
29
+ }, timeoutMs);
30
+ child.on('error', ()=>{
31
+ if (!done) {
32
+ done = true;
33
+ clearTimeout(timer);
34
+ resolveProbe(false);
35
+ }
36
+ });
37
+ child.on('exit', (code)=>{
38
+ if (!done) {
39
+ done = true;
40
+ clearTimeout(timer);
41
+ resolveProbe(code === 0);
42
+ }
43
+ });
44
+ });
45
+ }
46
+ export function nodeOk(version) {
47
+ const [maj = '0', min = '0'] = version.split('.');
48
+ return Number(maj) > 22 || Number(maj) === 22 && Number(min) >= 15;
49
+ }
50
+ export async function runDoctorChecks(opts = {}) {
51
+ const repoRoot = opts.repoRoot ? resolve(opts.repoRoot) : resolve(import.meta.dirname, '..', '..');
52
+ const home = opts.homeDir ?? homedir();
53
+ const env = opts.env ?? process.env;
54
+ const items = [];
55
+ const nodeVersion = process.versions.node;
56
+ items.push(nodeOk(nodeVersion) ? {
57
+ id: 'node',
58
+ label: 'Node 运行时',
59
+ status: 'ok',
60
+ detail: `Node ${nodeVersion}(≥22.15)`
61
+ } : {
62
+ id: 'node',
63
+ label: 'Node 运行时',
64
+ status: 'missing',
65
+ detail: `Node ${nodeVersion} 过旧(gotry 需 ≥22.15)`,
66
+ fix: '升级 Node.js 至 22.15+(https://nodejs.org)'
67
+ });
68
+ const extManifest = join(home, '.gotry', 'extension', 'manifest.json');
69
+ items.push(existsSync(extManifest) ? {
70
+ id: 'extension',
71
+ label: 'GoTry Session Bridge 扩展',
72
+ status: 'ok',
73
+ detail: `已就位(本地通道:${extManifest})`
74
+ } : {
75
+ id: 'extension',
76
+ label: 'GoTry Session Bridge 扩展',
77
+ status: 'missing',
78
+ detail: '本地通道未落位(~/.gotry/extension/manifest.json 不存在)。若你已从 Chrome 商店安装(自动更新,商店版 ID oeajpicc…),本项可忽略——会话检索可用性以运行时为准(gotry_session_search 的 verdict)。若未安装:应用商店一键装即可',
79
+ fix: '在 Chrome 应用商店一键安装(自动更新): https://chromewebstore.google.com/detail/gotry-session-bridge/oeajpiccmonococjcegddlooeeohlbgd'
80
+ });
81
+ const venvPython = join(repoRoot, '.venv/bin/python');
82
+ const reachBin = join(repoRoot, '.venv/bin/agent-reach');
83
+ if (existsSync(reachBin)) {
84
+ items.push({
85
+ id: 'agent-reach',
86
+ label: 'Agent Reach(网页/社媒读取)',
87
+ status: 'ok',
88
+ detail: `已安装(${reachBin});渠道凭证选配体检:在对话里调 gotry_agent_reach {action:"status"}`
89
+ });
90
+ } else if (existsSync(venvPython)) {
91
+ items.push({
92
+ id: 'agent-reach',
93
+ label: 'Agent Reach(网页/社媒读取)',
94
+ status: 'degraded',
95
+ detail: '.venv 存在但缺 agent-reach 包——gotry_agent_reach / gotry_web_search 读页会失败',
96
+ fix: `npx gotry doctor --fix(或在包根执行: ${venvPython} -m pip install git+https://github.com/Panniantong/Agent-Reach.git)`
97
+ });
98
+ } else {
99
+ items.push({
100
+ id: 'agent-reach',
101
+ label: 'Agent Reach(网页/社媒读取)',
102
+ status: 'missing',
103
+ detail: '未安装——gotry_agent_reach / gotry_web_search(读网页)/ gotry_video_subtitle / gotry_github_search 全部不可用',
104
+ fix: 'npx gotry doctor --fix'
105
+ });
106
+ }
107
+ let hbPresent = '';
108
+ for (const p of hbcliCandidates(home)){
109
+ if (p !== 'hbcli' && existsSync(p)) {
110
+ hbPresent = p;
111
+ break;
112
+ }
113
+ if (p === 'hbcli' && await probe(p, [
114
+ 'version'
115
+ ])) {
116
+ hbPresent = 'hbcli(PATH)';
117
+ break;
118
+ }
119
+ }
120
+ if (hbPresent) {
121
+ const whoami = await probe(hbPresent === 'hbcli(PATH)' ? 'hbcli' : hbPresent, [
122
+ 'auth',
123
+ 'whoami'
124
+ ]);
125
+ items.push(whoami ? {
126
+ id: 'hbcli',
127
+ label: 'hbcli(酒店实时源)',
128
+ status: 'ok',
129
+ detail: `已安装且凭证有效(${hbPresent})`
130
+ } : {
131
+ id: 'hbcli',
132
+ label: 'hbcli(酒店实时源)',
133
+ status: 'degraded',
134
+ detail: '二进制在,但凭证未配置/失效——酒店检索将降级静态包(非实时)',
135
+ fix: 'hbcli auth set-credentials --app-key hotelbyte_api_demo --app-secret hotelbyte_api_demo(快速试用沙箱;正式 key 向 HotelByte 申请)'
136
+ });
137
+ } else {
138
+ items.push({
139
+ id: 'hbcli',
140
+ label: 'hbcli(酒店实时源)',
141
+ status: 'missing',
142
+ detail: '未安装——酒店检索降级静态包(公开渠道估算,非实时,仅覆盖内置场景)',
143
+ fix: 'npx gotry doctor --fix'
144
+ });
145
+ }
146
+ const flyaiKey = env.FLYAI_API_KEY?.trim();
147
+ const flyaiQuotaNote = !flyaiKey && opts.stateRoot ? await (async ()=>{
148
+ const ev = (await readLatestChannelEvents(opts.stateRoot)).get('flyai');
149
+ return ev?.state === 'down' ? `;最近一次试用达限: ${ev.at}(匿名共享池,正式 key 可解除)` : '';
150
+ })() : '';
151
+ items.push(flyaiKey ? {
152
+ id: 'flyai',
153
+ label: 'FlyAI(飞猪官方检索)',
154
+ status: 'ok',
155
+ detail: 'FLYAI_API_KEY 已配(正式 key,无试用额度限制)'
156
+ } : {
157
+ id: 'flyai',
158
+ label: 'FlyAI(飞猪官方检索)',
159
+ status: 'degraded',
160
+ detail: `未配 FLYAI_API_KEY——走匿名试用额度(共享,易达限;达限报 "Trial limit reached")${flyaiQuotaNote}`,
161
+ fix: '到 flyai.open.fliggy.com 控制台申请正式 key,配进环境变量 FLYAI_API_KEY;无 key 期间机/火/酒检索请以 gotry_session_search(账号会话)为主'
162
+ });
163
+ const sidebarPkg = join(home, '.dsh/profiles/web/node_modules/dsh-better-sidebar/package.json');
164
+ items.push(existsSync(sidebarPkg) ? {
165
+ id: 'sidebar',
166
+ label: 'dsh-better-sidebar(侧栏工作台)',
167
+ status: 'ok',
168
+ detail: '已安装——web UI 右侧工作台可预览产物与 doctor 报告(gotry-state/doctor-report.md)'
169
+ } : {
170
+ id: 'sidebar',
171
+ label: 'dsh-better-sidebar(侧栏工作台)',
172
+ status: 'missing',
173
+ detail: '未安装——dsh web 无右侧工作台,产物与 doctor 报告只能在对话里看(gotry_artifacts_list)',
174
+ fix: 'npx gotry doctor --fix'
175
+ });
176
+ const calStatePath = join(home, '.gotry', 'calendar.json');
177
+ const calEnabled = (()=>{
178
+ try {
179
+ return JSON.parse(readFileSync(calStatePath, 'utf-8'))?.enabled === true;
180
+ } catch {
181
+ return false;
182
+ }
183
+ })();
184
+ const calProfilePatch = join(home, '.dsh/profiles/web/cordis.patch.yml');
185
+ const calConfigured = calEnabled && existsSync(calProfilePatch) && (()=>{
186
+ try {
187
+ const content = readFileSync(calProfilePatch, 'utf-8');
188
+ return /calendar/.test(content) && /username\s*:/.test(content);
189
+ } catch {
190
+ return false;
191
+ }
192
+ })();
193
+ items.push(!calEnabled ? {
194
+ id: 'calendar',
195
+ label: 'dsh-calendar(日历工作窗口)',
196
+ status: 'ok',
197
+ detail: '默认未挂载(D-9:未配置的日历工具不进工具箱;工作窗口由访谈覆盖,不影响任何检索)'
198
+ } : calConfigured ? {
199
+ id: 'calendar',
200
+ label: 'dsh-calendar(日历工作窗口)',
201
+ status: 'ok',
202
+ detail: `已挂载且已配置(${calStatePath})`
203
+ } : {
204
+ id: 'calendar',
205
+ label: 'dsh-calendar(日历工作窗口)',
206
+ status: 'degraded',
207
+ detail: '已挂载但 calendar 未配置 username——日历工具会话中会报「未配置」',
208
+ fix: `npx gotry setup calendar --off(恢复默认不挂载),或在 ${calProfilePatch} 覆盖 calendar 行 config 填 username(指引: npx gotry setup calendar --status)`
209
+ });
210
+ const rootRequire = createRequire(join(repoRoot, 'package.json'));
211
+ const mapCandidates = [
212
+ join(repoRoot, 'ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js'),
213
+ join(repoRoot, 'ts/dsh-runtime/node_modules/dsh-map-tools/lib/index.js'),
214
+ join(repoRoot, 'node_modules/dsh-map-tools/package.json'),
215
+ join(repoRoot, 'ts/node_modules/dsh-map-tools/package.json'),
216
+ join(home, '.dsh/profiles/web/node_modules/dsh-map-tools/package.json')
217
+ ];
218
+ let mapHit = mapCandidates.find((p)=>existsSync(p));
219
+ if (!mapHit) {
220
+ try {
221
+ mapHit = rootRequire.resolve('dsh-map-tools');
222
+ } catch {
223
+ mapHit = undefined;
224
+ }
225
+ }
226
+ items.push(mapHit ? {
227
+ id: 'map-tools',
228
+ label: 'dsh-map-tools(地图/路线/POI)',
229
+ status: 'ok',
230
+ detail: `已就位(${mapHit})——地图工具可用(零 key,走 OSM/OSRM)`
231
+ } : {
232
+ id: 'map-tools',
233
+ label: 'dsh-map-tools(地图/路线/POI)',
234
+ status: 'missing',
235
+ detail: '未随包解析——启动时该 patch 条目被静默剔除,地图/路线/POI 工具不会出现在模型工具箱(缺地图不挡旅行规划,只少能力)',
236
+ fix: '重装 @danceiny/gotry(地图插件随包 vendor 分发,缺失多为安装不完整)'
237
+ });
238
+ const askCandidates = [
239
+ join(repoRoot, 'ts/dsh-runtime/vendor/deepseek-ai-dsh-tool-ask-user/package.json'),
240
+ join(repoRoot, 'node_modules/@deepseek-ai/dsh-tool-ask-user/package.json'),
241
+ join(home, '.dsh/profiles/web/node_modules/@deepseek-ai/dsh-tool-ask-user/package.json')
242
+ ];
243
+ let askHit = askCandidates.find((p)=>existsSync(p));
244
+ if (!askHit) {
245
+ try {
246
+ const reqFromDsh = createRequire(rootRequire.resolve('@deepseek-ai/dsh/lib/bin.js'));
247
+ askHit = reqFromDsh.resolve('@deepseek-ai/dsh-tool-ask-user');
248
+ } catch {
249
+ try {
250
+ askHit = rootRequire.resolve('@deepseek-ai/dsh-tool-ask-user');
251
+ } catch {
252
+ askHit = undefined;
253
+ }
254
+ }
255
+ }
256
+ items.push(askHit ? {
257
+ id: 'ask-user',
258
+ label: 'dsh-tool-ask-user(结构化澄清卡)',
259
+ status: 'ok',
260
+ detail: `已就位(${askHit})——ask_user_question 澄清卡可用(web 原生卡片;headless+TTY 用 stdio 提供方)`
261
+ } : {
262
+ id: 'ask-user',
263
+ label: 'dsh-tool-ask-user(结构化澄清卡)',
264
+ status: 'missing',
265
+ detail: '未解析——启动时澄清卡注入被静默剔除,模型只能散文追问(人格契约 (5) 退化文本形态)',
266
+ fix: '重装 @danceiny/gotry——该依赖随 dsh 闭包自带,缺失多为安装不完整'
267
+ });
268
+ items.push({
269
+ id: 'llm-key',
270
+ label: 'LLM key',
271
+ status: 'ok',
272
+ detail: '由 dsh 宿主 UI 管理——不在 doctor 体检范围(gotry 不接触、不回显凭证)'
273
+ });
274
+ const broken = items.filter((i)=>i.status !== 'ok' && i.id !== 'llm-key');
275
+ const summary = broken.length === 0 ? `体检通过:${items.length} 项全部就绪(可选依赖齐,LLM key 归 dsh 宿主管)。` : `体检发现 ${broken.length} 项待处理:${broken.map((i)=>`${i.label}(${i.status === 'missing' ? '未装' : '半可用'})`).join('、')}。补装:终端跑 npx gotry doctor --fix,或按各项 fix 指引逐项处理。`;
276
+ return {
277
+ ok: broken.length === 0,
278
+ items,
279
+ summary
280
+ };
281
+ }
282
+ const fixCell = (fix)=>!fix ? '—' : /^(npx|hbcli|curl|pip|python|\$)/.test(fix) ? `\`${fix}\`` : fix;
283
+ export function renderDoctorReportMd(report, now = new Date()) {
284
+ const icon = (s)=>s === 'ok' ? '✅' : s === 'degraded' ? '⚠️' : '❌';
285
+ const lines = [
286
+ '# GoTry 依赖体检报告(doctor)',
287
+ '',
288
+ `> 生成于 ${now.toISOString()};重新生成:终端 \`npx gotry doctor\`,或在对话里让助手调 gotry_doctor。`,
289
+ '',
290
+ '| 状态 | 依赖 | 现状 | 修复指引 |',
291
+ '|---|---|---|---|',
292
+ ...report.items.map((i)=>`| ${icon(i.status)} | ${i.label} | ${i.detail} | ${fixCell(i.fix)} |`),
293
+ '',
294
+ `**结论**:${report.summary}`,
295
+ '',
296
+ '---',
297
+ '',
298
+ '- `npx gotry doctor` 随时可重跑(只读,不改任何东西);',
299
+ '- `npx gotry doctor --fix` 按上表补装(hbcli 官方脚本 / agent-reach pip / dsh-better-sidebar 插件);',
300
+ '- LLM key 由 dsh 宿主 UI 管理,gotry 永不体检、不回显。',
301
+ ''
302
+ ];
303
+ return lines.join('\n');
304
+ }
305
+
306
+
307
+ //# sourceURL=ts/capabilities/doctor.ts
@@ -1,9 +1,76 @@
1
1
  import { CircuitBreaker, withRetry } from './resilience.js';
2
2
  import { flyaiSearch } from './flyai.js';
3
3
  import { checkAvail, hotelRates, searchHotels } from './hbcli.js';
4
- import { sessionFlightSearch } from './session-search.js';
4
+ import { sessionFlightSearch, sessionHotelSearch, sessionTrainSearch } from './session-search.js';
5
5
  import { geocodePlace, getClimate, getForecast } from './weather.js';
6
6
  import { verifyFlight } from './opensky.js';
7
+ import { anythingSearch } from './anything.js';
8
+ import { continentListUrl, countryPageUrl, extractVisaSection, fetchVisaPolicyPage, listCountryPaths, policyFactsFromMfa } from './visa-policy.js';
9
+ import { readUrl, reach, reachStatus } from './agent-reach.js';
10
+ import { githubSearch, videoSubtitle } from './agent-reach-deep.js';
11
+ import { sessionLogin } from './session-login.js';
12
+ async function fetchVisaPolicyEntries(p) {
13
+ const started = Date.now();
14
+ const fetchedAt = new Date().toISOString();
15
+ const countries = [];
16
+ const facts = [];
17
+ const targets = [];
18
+ if (p.country) {
19
+ targets.push({
20
+ continent: p.continent ?? 'yz_645708',
21
+ country: p.country
22
+ });
23
+ } else if (p.continent) {
24
+ const listUrl = continentListUrl(p.continent);
25
+ if (!listUrl) return {
26
+ ok: false,
27
+ via: 'visa-policy-error',
28
+ evidence: `[visa-policy@${fetchedAt}] 非法洲路径`,
29
+ latencyMs: 0,
30
+ facts: [],
31
+ countries: [],
32
+ error: 'invalid_continent'
33
+ };
34
+ const page = await fetchVisaPolicyPage(listUrl, p.timeoutMs);
35
+ if (!page.ok || !page.html) return {
36
+ ok: false,
37
+ via: 'visa-policy-error',
38
+ evidence: `[visa-policy@${fetchedAt}] 洲列表抓取失败: ${page.error ?? '?'}`,
39
+ latencyMs: 0,
40
+ facts: [],
41
+ countries: [],
42
+ error: page.error
43
+ };
44
+ for (const c of listCountryPaths(page.html, p.continent).slice(0, Math.max(1, Math.min(10, p.limit ?? 10))))targets.push({
45
+ continent: p.continent,
46
+ country: c
47
+ });
48
+ }
49
+ for (const t of targets){
50
+ const url = countryPageUrl(t.continent, t.country);
51
+ if (!url) continue;
52
+ const page = await fetchVisaPolicyPage(url, p.timeoutMs);
53
+ countries.push(t.country);
54
+ if (!page.ok || !page.html) continue;
55
+ const section = extractVisaSection(page.html);
56
+ if (!section) continue;
57
+ facts.push(...policyFactsFromMfa({
58
+ countryLabel: t.country,
59
+ countryPath: t.country,
60
+ section,
61
+ fetchedAt
62
+ }));
63
+ }
64
+ const evidence = facts.length > 0 ? `[visa-policy:cs-mfa@${fetchedAt}] ${facts.length} 条政策事实(抓取 ${countries.length} 国)` : `[visa-policy@${fetchedAt}] 0 条(页面缺「签证入境」章节或抓取失败;不落负事实)`;
65
+ return {
66
+ ok: true,
67
+ via: 'cs-mfa',
68
+ evidence,
69
+ latencyMs: Date.now() - started,
70
+ facts,
71
+ countries
72
+ };
73
+ }
7
74
  const DEFAULT_HANDLERS = {
8
75
  FLYAI_SEARCH: (p)=>flyaiSearch(p),
9
76
  HBCLI_HOTEL_SEARCH: (p)=>searchHotels({
@@ -35,6 +102,8 @@ const DEFAULT_HANDLERS = {
35
102
  timeoutMs: p.timeoutMs
36
103
  }),
37
104
  SESSION_FLIGHT_SEARCH: (p)=>sessionFlightSearch(p),
105
+ SESSION_HOTEL_SEARCH: (p)=>sessionHotelSearch(p),
106
+ SESSION_TRAIN_SEARCH: (p)=>sessionTrainSearch(p),
38
107
  WEATHER_GEOCODE: (p)=>geocodePlace(p.name, {
39
108
  count: p.count,
40
109
  timeoutMs: p.timeoutMs
@@ -52,7 +121,24 @@ const DEFAULT_HANDLERS = {
52
121
  }, p.month, {
53
122
  timeoutMs: p.timeoutMs
54
123
  }),
55
- OPENSKY_FLIGHT_VERIFY: (p)=>verifyFlight(p)
124
+ OPENSKY_FLIGHT_VERIFY: (p)=>verifyFlight(p),
125
+ ANYTHING_SEARCH: (p)=>anythingSearch(p),
126
+ WEB_READ: (p)=>readUrl({
127
+ url: p.url,
128
+ timeoutMs: p.timeoutMs
129
+ }),
130
+ GITHUB_SEARCH: (p)=>githubSearch(p),
131
+ VIDEO_SUBTITLE: (p)=>videoSubtitle(p),
132
+ AGENT_REACH: (p)=>p.action === 'status' || !p.action && !p.channel ? reachStatus(p.timeoutMs) : reach({
133
+ channel: p.channel ?? '',
134
+ method: p.method ?? '',
135
+ args: p.args,
136
+ timeoutMs: p.timeoutMs
137
+ }),
138
+ SESSION_LOGIN: (p)=>sessionLogin({
139
+ waitMs: typeof p.waitSeconds === 'number' ? p.waitSeconds * 1000 : undefined
140
+ }),
141
+ VISA_POLICY_FETCH: (p)=>fetchVisaPolicyEntries(p)
56
142
  };
57
143
  const defaultIsFailure = (r)=>r?.ok === false;
58
144
  const flyaiTransient = (msg)=>msg.length > 0 && !/Sentinel/i.test(msg) && /timeout|timed out|ECONNRESET|ECONNREFUSED|ETIMEDOUT|socket|HTTP 5/i.test(msg);
@@ -65,6 +151,15 @@ const API_BREAKER = {
65
151
  failureThreshold: 3,
66
152
  openMs: 30_000
67
153
  };
154
+ const hbcliTimeout = (r)=>{
155
+ const msg = String((r ?? {}).error ?? '');
156
+ return msg.length > 0 && /timeout/i.test(msg);
157
+ };
158
+ const HBCLI_RETRY = {
159
+ maxAttempts: 2,
160
+ baseDelayMs: 300,
161
+ maxDelayMs: 1_000
162
+ };
68
163
  const SPECS = {
69
164
  FLYAI_SEARCH: {
70
165
  channel: 'cli',
@@ -85,7 +180,8 @@ const SPECS = {
85
180
  },
86
181
  HBCLI_HOTEL_SEARCH: {
87
182
  channel: 'cli',
88
- retry: null,
183
+ retry: HBCLI_RETRY,
184
+ isRetryable: (r)=>hbcliTimeout(r),
89
185
  breaker: {
90
186
  failureThreshold: 3,
91
187
  openMs: 60_000
@@ -94,7 +190,8 @@ const SPECS = {
94
190
  },
95
191
  HBCLI_HOTEL_RATES: {
96
192
  channel: 'cli',
97
- retry: null,
193
+ retry: HBCLI_RETRY,
194
+ isRetryable: (r)=>hbcliTimeout(r),
98
195
  breaker: {
99
196
  failureThreshold: 3,
100
197
  openMs: 60_000
@@ -103,7 +200,8 @@ const SPECS = {
103
200
  },
104
201
  HBCLI_CHECK_AVAIL: {
105
202
  channel: 'cli',
106
- retry: null,
203
+ retry: HBCLI_RETRY,
204
+ isRetryable: (r)=>hbcliTimeout(r),
107
205
  breaker: {
108
206
  failureThreshold: 3,
109
207
  openMs: 60_000
@@ -116,6 +214,18 @@ const SPECS = {
116
214
  breaker: null,
117
215
  isFailure: defaultIsFailure
118
216
  },
217
+ SESSION_HOTEL_SEARCH: {
218
+ channel: 'browser',
219
+ retry: null,
220
+ breaker: null,
221
+ isFailure: defaultIsFailure
222
+ },
223
+ SESSION_TRAIN_SEARCH: {
224
+ channel: 'browser',
225
+ retry: null,
226
+ breaker: null,
227
+ isFailure: defaultIsFailure
228
+ },
119
229
  WEATHER_GEOCODE: {
120
230
  channel: 'api',
121
231
  retry: API_RETRY,
@@ -140,6 +250,69 @@ const SPECS = {
140
250
  breaker: API_BREAKER,
141
251
  isRetryable: (_r, e)=>e != null || (_r ?? {}).verdict === 'unavailable',
142
252
  isFailure: (r)=>r.via === 'opensky-error'
253
+ },
254
+ ANYTHING_SEARCH: {
255
+ channel: 'cli',
256
+ retry: HBCLI_RETRY,
257
+ isRetryable: (r)=>hbcliTimeout(r),
258
+ breaker: {
259
+ failureThreshold: 3,
260
+ openMs: 60_000
261
+ },
262
+ isFailure: (r)=>r.via === 'hbcli-anything-error'
263
+ },
264
+ WEB_READ: {
265
+ channel: 'api',
266
+ retry: API_RETRY,
267
+ breaker: API_BREAKER,
268
+ isFailure: (r)=>r.via === 'r.jina.ai-error'
269
+ },
270
+ GITHUB_SEARCH: {
271
+ channel: 'cli',
272
+ retry: HBCLI_RETRY,
273
+ isRetryable: (r)=>r.verdict === 'timeout',
274
+ breaker: {
275
+ failureThreshold: 3,
276
+ openMs: 60_000
277
+ },
278
+ isFailure: (r)=>{
279
+ const v = r.verdict;
280
+ return v === 'error' || v === 'timeout';
281
+ }
282
+ },
283
+ VIDEO_SUBTITLE: {
284
+ channel: 'cli',
285
+ retry: HBCLI_RETRY,
286
+ isRetryable: (r)=>r.verdict === 'timeout',
287
+ breaker: {
288
+ failureThreshold: 3,
289
+ openMs: 60_000
290
+ },
291
+ isFailure: (r)=>{
292
+ const v = r.verdict;
293
+ return v === 'error' || v === 'timeout';
294
+ }
295
+ },
296
+ AGENT_REACH: {
297
+ channel: 'cli',
298
+ retry: null,
299
+ breaker: null,
300
+ isFailure: (r)=>r.verdict === 'error'
301
+ },
302
+ SESSION_LOGIN: {
303
+ channel: 'browser',
304
+ retry: null,
305
+ breaker: null,
306
+ isFailure: defaultIsFailure
307
+ },
308
+ VISA_POLICY_FETCH: {
309
+ channel: 'api',
310
+ retry: null,
311
+ breaker: {
312
+ failureThreshold: 2,
313
+ openMs: 300_000
314
+ },
315
+ isFailure: defaultIsFailure
143
316
  }
144
317
  };
145
318
  export function declinedObservation(effect, trace) {
@@ -150,6 +150,19 @@ export async function flyaiSearch(q) {
150
150
  });
151
151
  const latencyMs = Date.now() - started;
152
152
  if (r.error || r.code !== 0) {
153
+ const upstream = `${r.stderr}\n${r.stdout}`;
154
+ if (/Trial limit reached|HTTP\s*429|\b429\b/.test(upstream)) {
155
+ return {
156
+ ...base,
157
+ latencyMs,
158
+ ok: false,
159
+ via: 'flyai-error',
160
+ verdict: 'needs-setup',
161
+ evidence: `[实时API:flyai@error@${ts}] trial quota exhausted`,
162
+ error: `FlyAI 匿名试用额度已用尽(上游 429):${upstream.replace(/\s+/g, ' ').slice(0, 160)}`,
163
+ setup: 'FlyAI 试用额度达限——到 flyai.open.fliggy.com 控制台申请正式 API Key,配入环境变量 FLYAI_API_KEY 即恢复;本会话请勿重试 gotry_flyai_search,机/火/酒改走 gotry_session_search(账号会话)或 web 检索推进。'
164
+ };
165
+ }
153
166
  return {
154
167
  ...base,
155
168
  latencyMs,
@@ -112,6 +112,25 @@ export async function callHbcliJson(args, opts = {}) {
112
112
  }
113
113
  return last;
114
114
  }
115
+ function i18nName(v) {
116
+ if (!v) return '';
117
+ if (typeof v === 'string') return v;
118
+ return v.zh || v.en || v.ar || '';
119
+ }
120
+ function hotelLines(result, max = 8) {
121
+ const list = result?.list;
122
+ if (!Array.isArray(list) || list.length === 0) return '';
123
+ const lines = [];
124
+ for (const item of list.slice(0, max)){
125
+ const h = item;
126
+ const name = i18nName(h.name) || `hotel ${h.id ?? '?'}`;
127
+ const star = typeof h.star === 'number' && h.star > 0 ? ` ${h.star}★` : '';
128
+ const price = h.minPrice && typeof h.minPrice.amount === 'number' ? ` 低至 ${h.minPrice.amount}${h.minPrice.currency ? ` ${h.minPrice.currency}` : ''}` : '';
129
+ lines.push(`- id=${h.id ?? '?'} ${name}${star}${price}`);
130
+ }
131
+ if (list.length > max) lines.push(`…(共 ${list.length} 家,仅列前 ${max})`);
132
+ return lines.join('\n');
133
+ }
115
134
  export async function searchHotels(query, opts = {}) {
116
135
  const hbArgs = [
117
136
  'search',
@@ -121,6 +140,8 @@ export async function searchHotels(query, opts = {}) {
121
140
  '10'
122
141
  ];
123
142
  if (query.destination) hbArgs.push('--destination-name', query.destination);
143
+ if (query.checkIn) hbArgs.push('--check-in', query.checkIn);
144
+ if (query.checkOut) hbArgs.push('--check-out', query.checkOut);
124
145
  if (query.adults) hbArgs.push('--room-occupancies', JSON.stringify([
125
146
  {
126
147
  adultCount: query.adults,
@@ -129,10 +150,15 @@ export async function searchHotels(query, opts = {}) {
129
150
  ]));
130
151
  const live = await callHbcliJson(hbArgs, opts);
131
152
  if (live.via === 'hbcli-realtime') {
153
+ const list = live.result?.list;
154
+ const n = Array.isArray(list) ? list.length : 0;
155
+ const dateTag = query.checkIn ? `入住 ${query.checkIn}${query.checkOut ? ` → 退房 ${query.checkOut}` : ''}` : '未传日期(上游按当前窗口价)';
156
+ const lines = hotelLines(live.result);
157
+ const summary = `${query.destination}:hbcli 实时 ${n} 家(${dateTag})${lines ? `\n${lines}` : ''}\n${live.evidence}`;
132
158
  return {
133
159
  ...live,
134
160
  hotels: live.result,
135
- summary: `${query.destination}:hbcli 实时返回${query.checkIn || query.checkOut ? '(日期不传上游 list,以当前窗口房价返回)' : ''}`
161
+ summary
136
162
  };
137
163
  }
138
164
  const rawReason = live.error ?? live.via;
@@ -151,7 +177,7 @@ export async function searchHotels(query, opts = {}) {
151
177
  hotels: {
152
178
  stays: matched
153
179
  },
154
- summary: `${query.destination}:hbcli 实时源不可用(${reason}),已降级到静态包(公开渠道估算,非实时),命中 ${matched.length} 个住宿块`
180
+ summary: `${query.destination}:hbcli 实时源不可用(${reason}),已降级到静态包(公开渠道估算,非实时),命中 ${matched.length} 个住宿块:\n${JSON.stringify(matched).slice(0, 1600)}`
155
181
  };
156
182
  }
157
183
  return {