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

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 (137) hide show
  1. package/README.md +42 -16
  2. package/README.zh-CN.md +37 -15
  3. package/bin/gotry-bootstrap.js +236 -3
  4. package/bin/gotry-inner.js +66 -20
  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 +15 -5
  8. package/data/stations-12306-verify.json +524 -0
  9. package/dist/capabilities/agent-reach.js +4 -4
  10. package/dist/capabilities/channel-health.js +103 -0
  11. package/dist/capabilities/channel-registry.js +213 -0
  12. package/dist/capabilities/doctor.js +285 -0
  13. package/dist/capabilities/effect.js +178 -5
  14. package/dist/capabilities/flyai.js +13 -0
  15. package/dist/capabilities/session/adapters/ctrip-hotel.js +207 -0
  16. package/dist/capabilities/session/adapters/rail-12306.js +285 -0
  17. package/dist/capabilities/session/extension-bridge.js +14 -1
  18. package/dist/capabilities/session/extension-channel.js +4 -2
  19. package/dist/capabilities/session/extension-distribution.js +1 -1
  20. package/dist/capabilities/session-search.js +311 -0
  21. package/dist/capabilities/visa-policy.js +126 -0
  22. package/dist/scripts/agent-planning-turn-deadline-e2e.js +304 -0
  23. package/dist/scripts/agent-planning-turn-deadline-tests.js +281 -0
  24. package/dist/scripts/benchmark-environment-bridge-e2e.js +5 -10
  25. package/dist/scripts/benchmark-environment-bridge-tests.js +148 -142
  26. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +26 -26
  27. package/dist/scripts/{booking-copilot-availability-policy-v2-tests.js → booking-copilot-availability-policy-tests.js} +115 -115
  28. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +41 -1
  29. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +75 -120
  30. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +370 -61
  31. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +2 -2
  32. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +7 -3
  33. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +15 -9
  34. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +19 -11
  35. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +16 -52
  36. package/dist/scripts/booking-copilot-runtime-proof-tests.js +3953 -155
  37. package/dist/scripts/booking-copilot-server-proof-tests.js +58 -23
  38. package/dist/scripts/booking-copilot-startup-proof-tests.js +10 -119
  39. package/dist/scripts/booking-surface-contract-proof-tests.js +1154 -326
  40. package/dist/scripts/bootstrap-tests.js +87 -2
  41. package/dist/scripts/build-metrics-report.js +339 -0
  42. package/dist/scripts/channel-probe-tests.js +148 -0
  43. package/dist/scripts/channel-probe.js +252 -0
  44. package/dist/scripts/channel-registry-tests.js +262 -0
  45. package/dist/scripts/doctor-tests.js +121 -0
  46. package/dist/scripts/effect-tests.js +274 -1
  47. package/dist/scripts/extension-tests.js +129 -11
  48. package/dist/scripts/fact-gate-tests.js +91 -2
  49. package/dist/scripts/flyai-tests.js +17 -1
  50. package/dist/scripts/hbcli-e2e-tests.js +1 -3
  51. package/dist/scripts/metrics-report-tests.js +288 -0
  52. package/dist/scripts/persona-surface-guard-tests.js +19 -0
  53. package/dist/scripts/session-tests.js +401 -1
  54. package/dist/scripts/smoke.js +100 -83
  55. package/dist/scripts/turn-handoff-collect-tests.js +216 -0
  56. package/dist/scripts/turn-handoff-collect.js +189 -0
  57. package/dist/scripts/turn-policy-tests.js +56 -0
  58. package/dist/scripts/typed-contract-canary.js +261 -0
  59. package/dist/scripts/visa-policy-tests.js +75 -0
  60. package/dist/scripts/wish-channel-gate-tests.js +111 -0
  61. package/dist/src/artifact-gate.js +91 -3
  62. package/dist/src/benchmark-agent-conformance.js +7 -4
  63. package/dist/src/benchmark-environment-bridge.js +68 -7
  64. package/dist/src/bookable-facts.js +62 -3
  65. package/dist/src/booking-surface/{availability-policy-v2.js → availability-policy.js} +89 -89
  66. package/dist/src/booking-surface/canonical-schema.js +8 -22
  67. package/dist/src/booking-surface/contracts.js +70 -19
  68. package/dist/src/booking-surface/dsh-planner.js +310 -177
  69. package/dist/src/booking-surface/dsh-plugin.js +3 -3
  70. package/dist/src/booking-surface/index.js +1 -5
  71. package/dist/src/booking-surface/profile.js +1 -1
  72. package/dist/src/booking-surface/runtime.js +1682 -247
  73. package/dist/src/booking-surface/server.js +371 -185
  74. package/dist/src/booking-surface/startup.js +17 -27
  75. package/dist/src/booking-surface/validation.js +270 -760
  76. package/dist/src/index.js +735 -149
  77. package/dist/src/turn-deadline.js +292 -0
  78. package/dist/src/turn-policy.js +146 -0
  79. package/dist/src/wish-pool.js +5 -0
  80. package/extension/background.js +23 -4
  81. package/extension/content-main.js +47 -9
  82. package/extension/manifest.json +27 -9
  83. package/package.json +6 -26
  84. package/schemas/booking.surface.schema.json +2593 -0
  85. package/ts/capabilities/agent-reach.ts +4 -4
  86. package/ts/capabilities/flyai.ts +21 -3
  87. package/ts/capabilities/session/action-cache.ts +1 -1
  88. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  89. package/ts/capabilities/session/adapters/rail-12306.ts +293 -0
  90. package/ts/capabilities/session/extension-bridge.ts +32 -2
  91. package/ts/capabilities/session/extension-channel.ts +9 -8
  92. package/ts/capabilities/session/extension-distribution.ts +3 -1
  93. package/ts/capabilities/session/health-watch.ts +1 -1
  94. package/ts/capabilities/session/read-guard.ts +1 -1
  95. package/ts/capabilities/session/wizard.ts +1 -1
  96. package/ts/capabilities/session-search.ts +323 -1
  97. package/ts/package.json +1 -0
  98. package/ts/scripts/turn-handoff-collect.ts +178 -0
  99. package/ts/src/artifact-gate.ts +82 -2
  100. package/ts/src/benchmark-agent-conformance.ts +14 -6
  101. package/ts/src/benchmark-environment-bridge.ts +29 -10
  102. package/ts/src/bookable-facts.ts +109 -5
  103. package/ts/src/booking-saga.ts +1 -1
  104. package/ts/src/booking-surface/{availability-policy-v2.ts → availability-policy.ts} +124 -125
  105. package/ts/src/booking-surface/canonical-schema.js +8 -22
  106. package/ts/src/booking-surface/contracts.ts +278 -239
  107. package/ts/src/booking-surface/dsh-planner.ts +333 -194
  108. package/ts/src/booking-surface/dsh-plugin.js +3 -3
  109. package/ts/src/booking-surface/index.ts +1 -5
  110. package/ts/src/booking-surface/profile.ts +11 -11
  111. package/ts/src/booking-surface/runtime.ts +1351 -351
  112. package/ts/src/booking-surface/server.ts +282 -175
  113. package/ts/src/booking-surface/startup.ts +36 -48
  114. package/ts/src/booking-surface/validation.ts +194 -442
  115. package/ts/src/contracts.ts +1 -1
  116. package/ts/src/index.ts +489 -171
  117. package/ts/src/loop.ts +1 -1
  118. package/ts/src/state-ledger.ts +1 -1
  119. package/ts/src/turn-deadline.ts +361 -0
  120. package/ts/src/turn-policy.ts +154 -0
  121. package/ts/src/wish-pool.ts +17 -4
  122. package/dist/scripts/agent-planning-budget-e2e.js +0 -227
  123. package/dist/scripts/agent-planning-budget-tests.js +0 -173
  124. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +0 -3935
  125. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +0 -1174
  126. package/dist/src/booking-surface/contracts-v2.js +0 -89
  127. package/dist/src/booking-surface/runtime-v2.js +0 -1771
  128. package/dist/src/booking-surface/server-v2.js +0 -334
  129. package/dist/src/booking-surface/validation-v2.js +0 -319
  130. package/dist/src/tool-budget.js +0 -136
  131. package/schemas/booking.surface.v1.schema.json +0 -927
  132. package/schemas/booking.surface.v2.schema.json +0 -61
  133. package/ts/src/booking-surface/contracts-v2.ts +0 -118
  134. package/ts/src/booking-surface/runtime-v2.ts +0 -1466
  135. package/ts/src/booking-surface/server-v2.ts +0 -247
  136. package/ts/src/booking-surface/validation-v2.ts +0 -205
  137. package/ts/src/tool-budget.ts +0 -165
@@ -0,0 +1,213 @@
1
+ import { channelState } from './channel-health.js';
2
+ export const CHANNELS = [
3
+ {
4
+ id: 'flyai',
5
+ label: '飞猪官方 API',
6
+ tool: 'gotry_flyai_search',
7
+ intents: [
8
+ 'search-flight',
9
+ 'search-train',
10
+ 'search-hotel'
11
+ ],
12
+ quotaClass: 'anonymous-trial',
13
+ evidenceTag: '[实时API:flyai@ts]',
14
+ tier: 'realtime-api',
15
+ efficiencyRank: 0,
16
+ setup: '零 setup(匿名试用共享池,易达限;正式 key 配 FLYAI_API_KEY 解除)'
17
+ },
18
+ {
19
+ id: 'hbcli-hotel',
20
+ label: 'hbcli 酒店实时源',
21
+ tool: 'gotry_hotel_search',
22
+ intents: [
23
+ 'search-hotel'
24
+ ],
25
+ quotaClass: 'user-key',
26
+ evidenceTag: '[实时API:hbcli@ts]',
27
+ tier: 'realtime-api',
28
+ efficiencyRank: 1,
29
+ setup: 'hbcli 凭证(demo 沙箱一键);未装自动降静态包'
30
+ },
31
+ {
32
+ id: 'session:ctrip-flight',
33
+ label: '携程·用户登录态',
34
+ tool: 'gotry_session_search',
35
+ intents: [
36
+ 'search-flight'
37
+ ],
38
+ quotaClass: 'user-session',
39
+ evidenceTag: '[会话:ctrip-flight@ts]',
40
+ tier: 'session',
41
+ efficiencyRank: 2,
42
+ setup: '一次性装 Session Bridge 扩展 + 授权卡 + (如需)官网登录',
43
+ hint: 'kind=flight'
44
+ },
45
+ {
46
+ id: 'session:ctrip-hotel',
47
+ label: '携程·用户登录态',
48
+ tool: 'gotry_session_search',
49
+ intents: [
50
+ 'search-hotel'
51
+ ],
52
+ quotaClass: 'user-session',
53
+ evidenceTag: '[会话:ctrip-hotel@ts]',
54
+ tier: 'session',
55
+ efficiencyRank: 2,
56
+ setup: '一次性装 Session Bridge 扩展 + 授权卡 + (如需)官网登录',
57
+ hint: 'kind=hotel'
58
+ },
59
+ {
60
+ id: 'session:12306-train',
61
+ label: '12306 公开查询面',
62
+ tool: 'gotry_session_search',
63
+ intents: [
64
+ 'search-train'
65
+ ],
66
+ quotaClass: 'user-session',
67
+ evidenceTag: '[会话:train-12306@ts]',
68
+ tier: 'session',
69
+ efficiencyRank: 2,
70
+ setup: '一次性装 Session Bridge 扩展(公开查询面,无需登录)',
71
+ hint: 'kind=train'
72
+ },
73
+ {
74
+ id: 'anything-geo',
75
+ label: '目的地/酒店候选(hotel-be)',
76
+ tool: 'gotry_anything_search',
77
+ intents: [
78
+ 'search-geo'
79
+ ],
80
+ quotaClass: 'user-key',
81
+ evidenceTag: '[实时API:hbcli-anything@ts]',
82
+ tier: 'realtime-api',
83
+ efficiencyRank: 1,
84
+ setup: 'hbcli 可达即可(随 hbcli 安装)'
85
+ },
86
+ {
87
+ id: 'open-meteo',
88
+ label: '天气(open-meteo)',
89
+ tool: 'gotry_weather_check',
90
+ intents: [
91
+ 'weather'
92
+ ],
93
+ quotaClass: 'free-public',
94
+ evidenceTag: '[实时API:open-meteo@ts]',
95
+ tier: 'realtime-api',
96
+ efficiencyRank: 0
97
+ },
98
+ {
99
+ id: 'opensky',
100
+ label: '航班实时印证(OpenSky)',
101
+ tool: 'gotry_flight_verify',
102
+ intents: [
103
+ 'verify-flight'
104
+ ],
105
+ quotaClass: 'free-public',
106
+ evidenceTag: '[实时API:opensky@ts]',
107
+ tier: 'realtime-api',
108
+ efficiencyRank: 0
109
+ },
110
+ {
111
+ id: 'web-read',
112
+ label: '网页读取兜底(agent-reach)',
113
+ tool: 'gotry_web_search',
114
+ intents: [
115
+ 'read-web',
116
+ 'search-flight',
117
+ 'search-train',
118
+ 'search-hotel'
119
+ ],
120
+ quotaClass: 'user-key',
121
+ evidenceTag: '[agent-reach:web.read@ts]',
122
+ tier: 'best-effort',
123
+ efficiencyRank: 3,
124
+ setup: 'gotry .venv 装 agent-reach(npx gotry doctor --fix)'
125
+ },
126
+ {
127
+ id: 'static-hotel',
128
+ label: '内置静态数据包',
129
+ tool: 'gotry_hotel_search',
130
+ intents: [
131
+ 'search-hotel'
132
+ ],
133
+ quotaClass: 'static',
134
+ evidenceTag: '[静态包:估算]',
135
+ tier: 'static',
136
+ efficiencyRank: 9,
137
+ routable: false
138
+ }
139
+ ];
140
+ const TIER_RANK = {
141
+ 'realtime-api': 0,
142
+ session: 1,
143
+ 'best-effort': 2,
144
+ static: 3
145
+ };
146
+ const TIER_WHY = {
147
+ 'realtime-api': '官方结构化数据,证据链最强',
148
+ 'session': '用户本人登录态的真实价/库存',
149
+ 'best-effort': '非结构化兜底(读页汇总),证据弱于 API',
150
+ 'static': '内置估算,仅覆盖内置场景'
151
+ };
152
+ export const INTENT_LABELS = {
153
+ 'search-flight': '机票',
154
+ 'search-train': '火车',
155
+ 'search-hotel': '酒店',
156
+ 'search-geo': '目的地候选',
157
+ 'read-web': '网页/社媒',
158
+ 'weather': '天气',
159
+ 'verify-flight': '航班印证'
160
+ };
161
+ export function channelsForIntent(intent) {
162
+ return CHANNELS.filter((c)=>c.routable !== false && c.intents.includes(intent)).slice().sort((a, b)=>TIER_RANK[a.tier] - TIER_RANK[b.tier] || a.efficiencyRank - b.efficiencyRank);
163
+ }
164
+ export function routingAdvice(intent, opts = {}) {
165
+ const alternatives = channelsForIntent(intent).filter((c)=>c.id !== opts.excludeChannel).filter((c)=>channelState(c.id, opts.now)?.state !== 'down').slice(0, opts.limit ?? 3).map((c)=>({
166
+ tool: c.tool,
167
+ channel: c.id,
168
+ label: c.label,
169
+ why: TIER_WHY[c.tier],
170
+ ...c.setup ? {
171
+ setup: c.setup
172
+ } : {},
173
+ ...c.hint ? {
174
+ hint: c.hint
175
+ } : {}
176
+ }));
177
+ return {
178
+ intent,
179
+ alternatives
180
+ };
181
+ }
182
+ export function toolRoutingHeadline(tool) {
183
+ const intents = new Set();
184
+ for (const c of CHANNELS){
185
+ if (c.routable === false || c.tool !== tool) continue;
186
+ for (const i of c.intents)intents.add(i);
187
+ }
188
+ if (intents.size === 0) return '';
189
+ const lines = [
190
+ ...intents
191
+ ].map((intent)=>`- ${INTENT_LABELS[intent]}: ${channelsForIntent(intent).map((c)=>`${c.label}(${c.tool}${c.hint ? ` ${c.hint}` : ''})`).join(' → ')}`);
192
+ return [
193
+ '本工具服务的检索意图与通道顺位(证据级降序):',
194
+ ...lines,
195
+ 'verdict≠hit 时按结果内 routing 字段改道下一通道,勿盲试同通道。'
196
+ ].join('\n');
197
+ }
198
+ export function renderRoutingCard(now = new Date()) {
199
+ const multiChannel = Object.keys(INTENT_LABELS).map((intent)=>({
200
+ intent,
201
+ channels: channelsForIntent(intent)
202
+ })).filter((x)=>x.channels.length >= 2);
203
+ const lines = multiChannel.map(({ intent, channels })=>`- ${INTENT_LABELS[intent]}: ${channels.map((c)=>`${c.label}(${c.tool}${c.hint ? ` ${c.hint}` : ''})`).join(' → ')}`);
204
+ return [
205
+ '检索通道顺位(官方API > 用户会话 > 网页兜底;某次检索 verdict≠hit 时,按该结果内 routing 字段改道下一通道,同通道本会话不盲试第二次):',
206
+ ...lines,
207
+ '额度口径:匿名试用=首次体验导流(达限即改道并提示配 key);正式 key(FLYAI_API_KEY/hbcli 凭证)与账号会话=正式用量;静态包=估算必标注。',
208
+ `卡生成于 ${now.toISOString()}(通道状态变化不改变顺位表本身,只影响 routing 建议的可用性过滤)。`
209
+ ].join('\n');
210
+ }
211
+
212
+
213
+ //# sourceURL=ts/capabilities/channel-registry.ts
@@ -0,0 +1,285 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join, resolve } from 'node:path';
5
+ import { readLatestChannelEvents } from './channel-health.js';
6
+ function hbcliCandidates(homeDir) {
7
+ return [
8
+ 'hbcli',
9
+ join(homeDir, '.local/bin/hbcli'),
10
+ join(homeDir, '.staicli/current/hbcli')
11
+ ];
12
+ }
13
+ function probe(bin, args, timeoutMs = 8_000) {
14
+ return new Promise((resolveProbe)=>{
15
+ const child = spawn(bin, args, {
16
+ stdio: 'ignore',
17
+ env: process.env
18
+ });
19
+ let done = false;
20
+ const timer = setTimeout(()=>{
21
+ if (!done) {
22
+ done = true;
23
+ try {
24
+ child.kill('SIGKILL');
25
+ } catch {}
26
+ resolveProbe(false);
27
+ }
28
+ }, timeoutMs);
29
+ child.on('error', ()=>{
30
+ if (!done) {
31
+ done = true;
32
+ clearTimeout(timer);
33
+ resolveProbe(false);
34
+ }
35
+ });
36
+ child.on('exit', (code)=>{
37
+ if (!done) {
38
+ done = true;
39
+ clearTimeout(timer);
40
+ resolveProbe(code === 0);
41
+ }
42
+ });
43
+ });
44
+ }
45
+ export function nodeOk(version) {
46
+ const [maj = '0', min = '0'] = version.split('.');
47
+ return Number(maj) > 22 || Number(maj) === 22 && Number(min) >= 15;
48
+ }
49
+ export async function runDoctorChecks(opts = {}) {
50
+ const repoRoot = opts.repoRoot ? resolve(opts.repoRoot) : resolve(import.meta.dirname, '..', '..');
51
+ const home = opts.homeDir ?? homedir();
52
+ const env = opts.env ?? process.env;
53
+ const items = [];
54
+ const nodeVersion = process.versions.node;
55
+ items.push(nodeOk(nodeVersion) ? {
56
+ id: 'node',
57
+ label: 'Node 运行时',
58
+ status: 'ok',
59
+ detail: `Node ${nodeVersion}(≥22.15)`
60
+ } : {
61
+ id: 'node',
62
+ label: 'Node 运行时',
63
+ status: 'missing',
64
+ detail: `Node ${nodeVersion} 过旧(gotry 需 ≥22.15)`,
65
+ fix: '升级 Node.js 至 22.15+(https://nodejs.org)'
66
+ });
67
+ const extManifest = join(home, '.gotry', 'extension', 'manifest.json');
68
+ items.push(existsSync(extManifest) ? {
69
+ id: 'extension',
70
+ label: 'GoTry Session Bridge 扩展',
71
+ status: 'ok',
72
+ detail: `已就位(本地通道:${extManifest})`
73
+ } : {
74
+ id: 'extension',
75
+ label: 'GoTry Session Bridge 扩展',
76
+ status: 'missing',
77
+ detail: '本地通道未落位(~/.gotry/extension/manifest.json 不存在)。若你已从 Chrome 商店安装(自动更新,商店版 ID oeajpicc…),本项可忽略——会话检索可用性以运行时为准(gotry_session_search 的 verdict)。若未安装:应用商店一键装即可',
78
+ fix: '在 Chrome 应用商店一键安装(自动更新): https://chromewebstore.google.com/detail/gotry-session-bridge/oeajpiccmonococjcegddlooeeohlbgd'
79
+ });
80
+ const venvPython = join(repoRoot, '.venv/bin/python');
81
+ const reachBin = join(repoRoot, '.venv/bin/agent-reach');
82
+ if (existsSync(reachBin)) {
83
+ items.push({
84
+ id: 'agent-reach',
85
+ label: 'Agent Reach(网页/社媒读取)',
86
+ status: 'ok',
87
+ detail: `已安装(${reachBin});渠道凭证选配体检:在对话里调 gotry_agent_reach {action:"status"}`
88
+ });
89
+ } else if (existsSync(venvPython)) {
90
+ items.push({
91
+ id: 'agent-reach',
92
+ label: 'Agent Reach(网页/社媒读取)',
93
+ status: 'degraded',
94
+ detail: '.venv 存在但缺 agent-reach 包——gotry_agent_reach / gotry_web_search 读页会失败',
95
+ fix: `npx gotry doctor --fix(或在包根执行: ${venvPython} -m pip install git+https://github.com/Panniantong/Agent-Reach.git)`
96
+ });
97
+ } else {
98
+ items.push({
99
+ id: 'agent-reach',
100
+ label: 'Agent Reach(网页/社媒读取)',
101
+ status: 'missing',
102
+ detail: '未安装——gotry_agent_reach / gotry_web_search(读网页)/ gotry_video_subtitle / gotry_github_search 全部不可用',
103
+ fix: 'npx gotry doctor --fix'
104
+ });
105
+ }
106
+ let hbPresent = '';
107
+ for (const p of hbcliCandidates(home)){
108
+ if (p !== 'hbcli' && existsSync(p)) {
109
+ hbPresent = p;
110
+ break;
111
+ }
112
+ if (p === 'hbcli' && await probe(p, [
113
+ 'version'
114
+ ])) {
115
+ hbPresent = 'hbcli(PATH)';
116
+ break;
117
+ }
118
+ }
119
+ if (hbPresent) {
120
+ const whoami = await probe(hbPresent === 'hbcli(PATH)' ? 'hbcli' : hbPresent, [
121
+ 'auth',
122
+ 'whoami'
123
+ ]);
124
+ items.push(whoami ? {
125
+ id: 'hbcli',
126
+ label: 'hbcli(酒店实时源)',
127
+ status: 'ok',
128
+ detail: `已安装且凭证有效(${hbPresent})`
129
+ } : {
130
+ id: 'hbcli',
131
+ label: 'hbcli(酒店实时源)',
132
+ status: 'degraded',
133
+ detail: '二进制在,但凭证未配置/失效——酒店检索将降级静态包(非实时)',
134
+ fix: 'hbcli auth set-credentials --app-key hotelbyte_api_demo --app-secret hotelbyte_api_demo(快速试用沙箱;正式 key 向 HotelByte 申请)'
135
+ });
136
+ } else {
137
+ items.push({
138
+ id: 'hbcli',
139
+ label: 'hbcli(酒店实时源)',
140
+ status: 'missing',
141
+ detail: '未安装——酒店检索降级静态包(公开渠道估算,非实时,仅覆盖内置场景)',
142
+ fix: 'npx gotry doctor --fix'
143
+ });
144
+ }
145
+ const flyaiKey = env.FLYAI_API_KEY?.trim();
146
+ const flyaiQuotaNote = !flyaiKey && opts.stateRoot ? await (async ()=>{
147
+ const ev = (await readLatestChannelEvents(opts.stateRoot)).get('flyai');
148
+ return ev?.state === 'down' ? `;最近一次试用达限: ${ev.at}(匿名共享池,正式 key 可解除)` : '';
149
+ })() : '';
150
+ items.push(flyaiKey ? {
151
+ id: 'flyai',
152
+ label: 'FlyAI(飞猪官方检索)',
153
+ status: 'ok',
154
+ detail: 'FLYAI_API_KEY 已配(正式 key,无试用额度限制)'
155
+ } : {
156
+ id: 'flyai',
157
+ label: 'FlyAI(飞猪官方检索)',
158
+ status: 'degraded',
159
+ detail: `未配 FLYAI_API_KEY——走匿名试用额度(共享,易达限;达限报 "Trial limit reached")${flyaiQuotaNote}`,
160
+ fix: '到 flyai.open.fliggy.com 控制台申请正式 key,配进环境变量 FLYAI_API_KEY;无 key 期间机/火/酒检索请以 gotry_session_search(账号会话)为主'
161
+ });
162
+ const sidebarPkg = join(home, '.dsh/profiles/web/node_modules/dsh-better-sidebar/package.json');
163
+ items.push(existsSync(sidebarPkg) ? {
164
+ id: 'sidebar',
165
+ label: 'dsh-better-sidebar(侧栏工作台)',
166
+ status: 'ok',
167
+ detail: '已安装——web UI 右侧工作台可预览产物与 doctor 报告(gotry-state/doctor-report.md)'
168
+ } : {
169
+ id: 'sidebar',
170
+ label: 'dsh-better-sidebar(侧栏工作台)',
171
+ status: 'missing',
172
+ detail: '未安装——dsh web 无右侧工作台,产物与 doctor 报告只能在对话里看(gotry_artifacts_list)',
173
+ fix: 'npx gotry doctor --fix'
174
+ });
175
+ const calStatePath = join(home, '.gotry', 'calendar.json');
176
+ const calEnabled = (()=>{
177
+ try {
178
+ return JSON.parse(readFileSync(calStatePath, 'utf-8'))?.enabled === true;
179
+ } catch {
180
+ return false;
181
+ }
182
+ })();
183
+ const calProfilePatch = join(home, '.dsh/profiles/web/cordis.patch.yml');
184
+ const calConfigured = calEnabled && existsSync(calProfilePatch) && (()=>{
185
+ try {
186
+ const content = readFileSync(calProfilePatch, 'utf-8');
187
+ return /calendar/.test(content) && /username\s*:/.test(content);
188
+ } catch {
189
+ return false;
190
+ }
191
+ })();
192
+ items.push(!calEnabled ? {
193
+ id: 'calendar',
194
+ label: 'dsh-calendar(日历工作窗口)',
195
+ status: 'ok',
196
+ detail: '默认未挂载(D-9:未配置的日历工具不进工具箱;工作窗口由访谈覆盖,不影响任何检索)'
197
+ } : calConfigured ? {
198
+ id: 'calendar',
199
+ label: 'dsh-calendar(日历工作窗口)',
200
+ status: 'ok',
201
+ detail: `已挂载且已配置(${calStatePath})`
202
+ } : {
203
+ id: 'calendar',
204
+ label: 'dsh-calendar(日历工作窗口)',
205
+ status: 'degraded',
206
+ detail: '已挂载但 calendar 未配置 username——日历工具会话中会报「未配置」',
207
+ fix: `npx gotry setup calendar --off(恢复默认不挂载),或在 ${calProfilePatch} 覆盖 calendar 行 config 填 username(指引: npx gotry setup calendar --status)`
208
+ });
209
+ const mapCandidates = [
210
+ join(repoRoot, 'ts/dsh-runtime/node_modules/dsh-map-tools/lib/index.js'),
211
+ join(repoRoot, 'node_modules/dsh-map-tools/package.json'),
212
+ join(repoRoot, 'ts/node_modules/dsh-map-tools/package.json'),
213
+ join(home, '.dsh/profiles/web/node_modules/dsh-map-tools/package.json')
214
+ ];
215
+ const mapHit = mapCandidates.find((p)=>existsSync(p));
216
+ items.push(mapHit ? {
217
+ id: 'map-tools',
218
+ label: 'dsh-map-tools(地图/路线/POI)',
219
+ status: 'ok',
220
+ detail: `已就位(${mapHit})——地图工具可用(零 key,走 OSM/OSRM)`
221
+ } : {
222
+ id: 'map-tools',
223
+ label: 'dsh-map-tools(地图/路线/POI)',
224
+ status: 'missing',
225
+ detail: '未随包解析——启动时该 patch 条目被静默剔除,地图/路线/POI 工具不会出现在模型工具箱(缺地图不挡旅行规划,只少能力)',
226
+ fix: 'npx gotry doctor --fix 不覆盖此项:source 布局把 dsh-map-tools 放进 ts/dsh-runtime/node_modules(或 ts/node_modules);npm 布局重装 @danceiny/gotry(随发行版依赖提供)'
227
+ });
228
+ const askCandidates = [
229
+ join(repoRoot, 'ts/dsh-runtime/vendor/deepseek-ai-dsh-tool-ask-user/package.json'),
230
+ join(repoRoot, 'node_modules/@deepseek-ai/dsh-tool-ask-user/package.json'),
231
+ join(home, '.dsh/profiles/web/node_modules/@deepseek-ai/dsh-tool-ask-user/package.json')
232
+ ];
233
+ const askHit = askCandidates.find((p)=>existsSync(p));
234
+ items.push(askHit ? {
235
+ id: 'ask-user',
236
+ label: 'dsh-tool-ask-user(结构化澄清卡)',
237
+ status: 'ok',
238
+ detail: `已就位(${askHit})——ask_user_question 澄清卡可用(web 原生卡片;headless+TTY 用 stdio 提供方)`
239
+ } : {
240
+ id: 'ask-user',
241
+ label: 'dsh-tool-ask-user(结构化澄清卡)',
242
+ status: 'missing',
243
+ detail: '未解析——启动时澄清卡注入被静默剔除,模型只能散文追问(人格契约 (5) 退化文本形态)',
244
+ fix: '重装 @danceiny/gotry——该依赖随 dsh 闭包自带,缺失多为安装不完整'
245
+ });
246
+ items.push({
247
+ id: 'llm-key',
248
+ label: 'LLM key',
249
+ status: 'ok',
250
+ detail: '由 dsh 宿主 UI 管理——不在 doctor 体检范围(gotry 不接触、不回显凭证)'
251
+ });
252
+ const broken = items.filter((i)=>i.status !== 'ok' && i.id !== 'llm-key');
253
+ 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 指引逐项处理。`;
254
+ return {
255
+ ok: broken.length === 0,
256
+ items,
257
+ summary
258
+ };
259
+ }
260
+ const fixCell = (fix)=>!fix ? '—' : /^(npx|hbcli|curl|pip|python|\$)/.test(fix) ? `\`${fix}\`` : fix;
261
+ export function renderDoctorReportMd(report, now = new Date()) {
262
+ const icon = (s)=>s === 'ok' ? '✅' : s === 'degraded' ? '⚠️' : '❌';
263
+ const lines = [
264
+ '# GoTry 依赖体检报告(doctor)',
265
+ '',
266
+ `> 生成于 ${now.toISOString()};重新生成:终端 \`npx gotry doctor\`,或在对话里让助手调 gotry_doctor。`,
267
+ '',
268
+ '| 状态 | 依赖 | 现状 | 修复指引 |',
269
+ '|---|---|---|---|',
270
+ ...report.items.map((i)=>`| ${icon(i.status)} | ${i.label} | ${i.detail} | ${fixCell(i.fix)} |`),
271
+ '',
272
+ `**结论**:${report.summary}`,
273
+ '',
274
+ '---',
275
+ '',
276
+ '- `npx gotry doctor` 随时可重跑(只读,不改任何东西);',
277
+ '- `npx gotry doctor --fix` 按上表补装(hbcli 官方脚本 / agent-reach pip / dsh-better-sidebar 插件);',
278
+ '- LLM key 由 dsh 宿主 UI 管理,gotry 永不体检、不回显。',
279
+ ''
280
+ ];
281
+ return lines.join('\n');
282
+ }
283
+
284
+
285
+ //# sourceURL=ts/capabilities/doctor.ts