@danceiny/gotry 0.0.1-rc.19 → 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.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/gotry-bootstrap.js +83 -37
- package/bin/gotry-inner.js +11 -6
- package/cordis.gotry-patch.yml +7 -3
- package/dist/capabilities/anything.js +62 -29
- package/dist/capabilities/doctor.js +25 -3
- package/dist/capabilities/hbcli.js +28 -2
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +2 -1
- package/dist/scripts/anything-tests.js +70 -21
- package/dist/scripts/benchmark-environment-bridge-e2e.js +136 -29
- package/dist/scripts/benchmark-environment-bridge-tests.js +1056 -269
- package/dist/scripts/bootstrap-tests.js +28 -2
- package/dist/scripts/doctor-tests.js +55 -1
- package/dist/scripts/hbcli-tests.js +38 -1
- package/dist/src/benchmark-agent-conformance.js +32 -12
- package/dist/src/benchmark-environment-bridge.js +317 -114
- package/dist/src/index.js +5 -2
- package/extension/manifest.json +2 -2
- package/package.json +2 -1
- package/ts/capabilities/anything.ts +89 -38
- package/ts/capabilities/hbcli.ts +53 -4
- package/ts/dsh-runtime/vendor/README.md +60 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +200 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +202 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +216 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +371 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +56 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +126 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +109 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +27 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +170 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +53 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +20 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +91 -0
- package/ts/package.json +9 -1
- package/ts/src/benchmark-agent-conformance.ts +40 -9
- package/ts/src/benchmark-environment-bridge.ts +235 -66
- package/ts/src/index.ts +4 -2
- package/ts/src/turn-deadline.ts +4 -0
package/README.md
CHANGED
|
@@ -205,7 +205,7 @@ Current release: **v0.0.1-rc.18** (npm `latest` and `rc` both point here; regist
|
|
|
205
205
|
- **M3 Exit not closed** — engineering & distribution are ready, but real seed-user evidence (50–200 person cohort) has not been accumulated; automated tests prove contracts and formulas, not business pass
|
|
206
206
|
- **Hotel session adapters** — Ctrip-hotel / Meituan logged-in surfaces await real login-state backfill; flights are done
|
|
207
207
|
- **Interface language** — English covers the deterministic solve-output layer; the dsh host UI and dialogue surface belong to the host / calibration samples
|
|
208
|
-
- **External benchmark generalization** — every frozen external run to date remains diagnostic-only (no score, no uplift claim); the round-by-round engineering ledger lives in [`docs/evaluation/benchmark-environment-bridge.md`](docs/evaluation/benchmark-environment-bridge.md)
|
|
208
|
+
- **External benchmark generalization** — every frozen external run to date remains diagnostic-only (no score, no uplift claim). The bridge now combines the generic `tools/call/errors` recovery surface with per-tool typed descriptors, exact result/domain/failure conformance, and a latest-response terminal fence; the round-by-round engineering ledger lives in [`docs/evaluation/benchmark-environment-bridge.md`](docs/evaluation/benchmark-environment-bridge.md)
|
|
209
209
|
- **Booking** — nothing bookable ships today; M5 opens only through WriteGate and the booking-saga FSM
|
|
210
210
|
|
|
211
211
|
<details>
|
package/README.zh-CN.md
CHANGED
|
@@ -201,7 +201,7 @@ node scripts/build-dist.mjs # 构建 JS runtime
|
|
|
201
201
|
- **M3 Exit 未关闭** —— 工程与分发面就绪,但真实种子用户证据(50–200 人 cohort)尚未积累;自动化测试证明的是合同与公式,不是 business pass
|
|
202
202
|
- **酒店会话适配** —— 携程酒店/美团登录态面等实测回填;机票已通
|
|
203
203
|
- **界面语言** —— 英文仅覆盖求解确定性输出层;dsh 宿主界面与对话面属宿主/校准件
|
|
204
|
-
- **外部 benchmark 泛化** —— 迄今所有冻结外部运行均仅 diagnostic
|
|
204
|
+
- **外部 benchmark 泛化** —— 迄今所有冻结外部运行均仅 diagnostic(无分数、无 uplift 声明)。bridge 现已合并通用 `tools/call/errors` 恢复面、逐工具 typed descriptor、严格 result/domain/failure 分流与 latest-response terminal fence;逐轮工程台账见 [`docs/evaluation/benchmark-environment-bridge.md`](docs/evaluation/benchmark-environment-bridge.md)
|
|
205
205
|
- **预订** —— 今天没有任何可下单路径;M5 只经 WriteGate 与 booking-saga 状态机启封
|
|
206
206
|
|
|
207
207
|
<details>
|
package/bin/gotry-bootstrap.js
CHANGED
|
@@ -58,6 +58,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSyn
|
|
|
58
58
|
import { homedir } from 'node:os'
|
|
59
59
|
import { join, dirname } from 'node:path'
|
|
60
60
|
import { fileURLToPath } from 'node:url'
|
|
61
|
+
import { createRequire } from 'node:module'
|
|
61
62
|
|
|
62
63
|
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
63
64
|
const AUTO = process.argv.includes('--auto')
|
|
@@ -73,9 +74,9 @@ const HBCLI_INSTALL_CMD = 'curl -fsSL https://github.com/hotelbyte-com/docs/rele
|
|
|
73
74
|
const REACH_INSTALL_URL = 'git+https://github.com/Panniantong/Agent-Reach.git'
|
|
74
75
|
|
|
75
76
|
/** 带超时的子进程(inherit stdio 让用户看见上游安装进度) */
|
|
76
|
-
function run(cmd, args, { timeoutMs, cwd } = {}) {
|
|
77
|
+
function run(cmd, args, { timeoutMs, cwd, env } = {}) {
|
|
77
78
|
return new Promise((resolve) => {
|
|
78
|
-
const child = spawn(cmd, args, { stdio: 'inherit', cwd, env: process.env })
|
|
79
|
+
const child = spawn(cmd, args, { stdio: 'inherit', cwd, env: env ?? process.env })
|
|
79
80
|
let done = false
|
|
80
81
|
const timer = setTimeout(() => {
|
|
81
82
|
if (!done) { done = true; try { child.kill('SIGKILL') } catch { /* ignore */ } }
|
|
@@ -143,34 +144,56 @@ async function setupReach() {
|
|
|
143
144
|
return { ok: true }
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
/** sidebar 落盘状态 = 与 doctor 检查同口径的唯一事实面(profile 里的 package.json) */
|
|
148
|
+
function sidebarInstalled() {
|
|
149
|
+
return existsSync(join(homedir(), '.dsh/profiles/web/node_modules/dsh-better-sidebar/package.json'))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** dsh plugin 在 profile 目录里跑 pnpm;pnpm ≥10.5 默认不执行依赖构建脚本,严格态
|
|
153
|
+
* (pnpm 11 默认 / strict-dep-builds)下 ERR_PNPM_IGNORED_BUILDS 直接 exit 1——
|
|
154
|
+
* 但包本身已完整落盘(2026-09-08 实测:exit 1 而 167 包全部就位,doctor 复检即绿)。
|
|
155
|
+
* 对该调用降回警告态(env 只作用于这一次 spawn,不改用户全局 pnpm 配置);
|
|
156
|
+
* 老版本 pnpm 不认识此配置,零影响。 */
|
|
157
|
+
const SIDEBAR_PNPM_ENV = { ...process.env, npm_config_strict_dep_builds: 'false' }
|
|
158
|
+
|
|
159
|
+
async function setupSidebar(attemptInstall) { say('[gotry-setup] dsh-better-sidebar(dsh web 侧栏工作台,产物查看面 issue #25)')
|
|
160
|
+
if (sidebarInstalled()) { say(' ✓ 已安装(~/.dsh/profiles/web)'); return { ok: true } }
|
|
149
161
|
if (CHECK_ONLY) { say(' ✗ 未安装(--check-only 只报告)'); return { ok: true } }
|
|
150
162
|
const SIDEBAR_PKG = 'dsh-better-sidebar@latest'
|
|
151
|
-
// dsh CLI:本包依赖里的 @deepseek-ai/dsh 优先;解析不到走 npx 自拉(官方安装途径同款)
|
|
152
|
-
|
|
163
|
+
// dsh CLI:本包依赖里的 @deepseek-ai/dsh 优先;解析不到走 npx 自拉(官方安装途径同款)。
|
|
164
|
+
// attemptInstall 注入位供测试模拟「安装器失败但状态落盘」(pnpm 忽略构建脚本策略)。
|
|
153
165
|
const require_ = createRequire(join(repoRoot, 'package.json'))
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (r.ok) {
|
|
161
|
-
say(' ✓ 安装完成(gotry web 刷新浏览器即见右侧工作台;工作区里的产物 md 可直接预览)')
|
|
162
|
-
return { ok: true }
|
|
163
|
-
}
|
|
164
|
-
say(` ✗ dsh plugin 安装失败(${r.error})——尝试 npx 途径`)
|
|
165
|
-
} catch { /* 本包未携带 @deepseek-ai/dsh */ }
|
|
166
|
-
if (!launched) say(' 本包未携带 @deepseek-ai/dsh,走 npx 途径安装')
|
|
167
|
-
const r2 = await run('npx', ['-y', '--package', '@deepseek-ai/dsh', 'dsh', 'plugin', '--profile', 'web', 'add', SIDEBAR_PKG], { timeoutMs: 300_000 })
|
|
168
|
-
if (!r2.ok) {
|
|
169
|
-
say(' ✗ 安装失败——不影响 gotry:产物仍可在对话里说「看看我生成的行程」经 gotry_artifacts_list/read 查看;可稍后重试: npx gotry doctor --fix')
|
|
170
|
-
return { ok: false }
|
|
166
|
+
const defaultAttempt = async () => {
|
|
167
|
+
try {
|
|
168
|
+
const dshBin = require_.resolve('@deepseek-ai/dsh/lib/bin.js')
|
|
169
|
+
say(` 安装中(dsh plugin → web profile): ${SIDEBAR_PKG}`)
|
|
170
|
+
return await run(process.execPath, [dshBin, 'plugin', '--profile', 'web', 'add', SIDEBAR_PKG], { timeoutMs: 300_000, env: SIDEBAR_PNPM_ENV })
|
|
171
|
+
} catch { return { ok: false, noDsh: true } }
|
|
171
172
|
}
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
const r = await (attemptInstall ?? defaultAttempt)()
|
|
174
|
+
if (r.ok) {
|
|
175
|
+
say(' ✓ 安装完成(gotry web 刷新浏览器即见右侧工作台;工作区里的产物 md 可直接预览)')
|
|
176
|
+
return { ok: true }
|
|
177
|
+
}
|
|
178
|
+
// exit 非 0 ≠ 没装上(pnpm 忽略构建脚本策略):先按落盘状态复核,再决定是否回退 npx 途径
|
|
179
|
+
if (sidebarInstalled()) {
|
|
180
|
+
say(' ✓ 安装完成(安装器 exit 非 0,但按落盘状态复核已就位;gotry web 刷新浏览器即见右侧工作台)')
|
|
181
|
+
say(' ⚠ pnpm 跳过了 node-pty(侧栏内嵌终端)的构建脚本——预览功能不受影响;如需内嵌终端:cd ~/.dsh/profiles/web && pnpm approve-builds 勾选 node-pty')
|
|
182
|
+
return { ok: true }
|
|
183
|
+
}
|
|
184
|
+
if (r.noDsh) say(' 本包未携带 @deepseek-ai/dsh,走 npx 途径安装')
|
|
185
|
+
else say(' ✗ dsh plugin 安装失败且未落盘——尝试 npx 途径')
|
|
186
|
+
const r2 = await run('npx', ['-y', '--package', '@deepseek-ai/dsh', 'dsh', 'plugin', '--profile', 'web', 'add', SIDEBAR_PKG], { timeoutMs: 300_000, env: SIDEBAR_PNPM_ENV })
|
|
187
|
+
if (r2.ok) {
|
|
188
|
+
say(' ✓ 安装完成(gotry web 刷新浏览器即见右侧工作台)')
|
|
189
|
+
return { ok: true }
|
|
190
|
+
}
|
|
191
|
+
if (sidebarInstalled()) {
|
|
192
|
+
say(' ✓ 安装完成(npx 途径 exit 非 0,但按落盘状态复核已就位)')
|
|
193
|
+
return { ok: true }
|
|
194
|
+
}
|
|
195
|
+
say(' ✗ 安装失败——不影响 gotry:产物仍可在对话里说「看看我生成的行程」经 gotry_artifacts_list/read 查看;可稍后重试: npx gotry doctor --fix')
|
|
196
|
+
return { ok: false }
|
|
174
197
|
}
|
|
175
198
|
|
|
176
199
|
// ---------------------------------------------------------------------------
|
|
@@ -269,9 +292,8 @@ async function doctorChecks() {
|
|
|
269
292
|
// flyai key(匿名试用额度共享易达限;正式 key 即免)
|
|
270
293
|
const flyaiKey = (process.env.FLYAI_API_KEY ?? '').trim()
|
|
271
294
|
items.push({ label: 'FlyAI(飞猪官方检索)', ok: Boolean(flyaiKey), level: flyaiKey ? 'ok' : 'degraded', detail: flyaiKey ? 'FLYAI_API_KEY 已配(正式 key,无试用额度限制)' : '未配 FLYAI_API_KEY——走匿名试用额度(共享,易达限;达限报 "Trial limit reached")', fix: flyaiKey ? undefined : '到 flyai.open.fliggy.com 控制台申请正式 key,配进环境变量 FLYAI_API_KEY' })
|
|
272
|
-
// sidebar
|
|
273
|
-
const
|
|
274
|
-
const sbOk = existsSync(sidebarPkg)
|
|
295
|
+
// sidebar(状态面与 setupSidebar 的落盘复核同一口径)
|
|
296
|
+
const sbOk = sidebarInstalled()
|
|
275
297
|
items.push({ label: 'dsh-better-sidebar(侧栏工作台)', ok: sbOk, level: sbOk ? 'ok' : 'missing', detail: sbOk ? '已安装——web UI 右侧工作台可预览产物与 doctor 报告(gotry-state/doctor-report.md)' : '未安装——dsh web 无右侧工作台,产物与 doctor 报告只能在对话里看(gotry_artifacts_list)', fix: sbOk ? undefined : 'npx gotry doctor --fix' })
|
|
276
298
|
// dsh-calendar(setup 状态面;默认不挂载=ok 是合法态,opt-in 未配置才 degraded)
|
|
277
299
|
const calState = readCalendarState()
|
|
@@ -279,24 +301,42 @@ async function doctorChecks() {
|
|
|
279
301
|
const calConfigured = calOn && calendarProfileConfigured()
|
|
280
302
|
items.push({ label: 'dsh-calendar(日历工作窗口)', ok: !calOn || calConfigured, level: !calOn ? 'ok' : calConfigured ? 'ok' : 'degraded', detail: calendarDetail(calState), fix: !calOn ? undefined : calConfigured ? undefined : '在 ~/.dsh/profiles/web/cordis.patch.yml 覆盖 calendar 行 config 填 username(或 npx gotry setup calendar --off 恢复默认不挂载)' })
|
|
281
303
|
// dsh-map-tools(patch 分发面宿主插件,issue #139):解析失败启动时整块静默剔除,
|
|
282
|
-
// doctor 把两态照亮。候选清单与 bin/gotry-inner.js 解析链、ts/capabilities/doctor.ts
|
|
304
|
+
// doctor 把两态照亮。候选清单与 bin/gotry-inner.js 解析链、ts/capabilities/doctor.ts 同口径:
|
|
305
|
+
// tarball vendor 副本优先(随 files[] 分发,不进 npm 依赖——其 peerDependencies 要求
|
|
306
|
+
// dsh-settings/dsh-tools >=0.1.2-rc.1,与锁定的 0.1.2-alpha.3 家族在 npm 严格 peer
|
|
307
|
+
// 解析下 ERESOLVE,依赖形态会弄坏 npx 安装),其余覆盖 source/提升/profile 布局。
|
|
308
|
+
const rootRequire = createRequire(join(repoRoot, 'package.json'))
|
|
283
309
|
const mapCandidates = [
|
|
310
|
+
join(repoRoot, 'ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js'),
|
|
284
311
|
join(repoRoot, 'ts/dsh-runtime/node_modules/dsh-map-tools/lib/index.js'),
|
|
285
312
|
join(repoRoot, 'node_modules/dsh-map-tools/package.json'),
|
|
286
313
|
join(repoRoot, 'ts/node_modules/dsh-map-tools/package.json'),
|
|
287
314
|
join(homedir(), '.dsh/profiles/web/node_modules/dsh-map-tools/package.json'),
|
|
288
315
|
]
|
|
289
|
-
|
|
316
|
+
let mapHit = mapCandidates.find((p) => existsSync(p))
|
|
317
|
+
// npm 提升布局:gotry 的依赖不在包目录 node_modules 里,而在同级提升位——
|
|
318
|
+
// 与 inner 的 require_.resolve 同机制(createRequire 从包位置向上走查)
|
|
319
|
+
if (!mapHit) { try { mapHit = rootRequire.resolve('dsh-map-tools') } catch { mapHit = undefined } }
|
|
290
320
|
items.push(mapHit
|
|
291
321
|
? { label: 'dsh-map-tools(地图/路线/POI)', ok: true, level: 'ok', detail: `已就位(${mapHit})——地图工具可用(零 key,走 OSM/OSRM)`, fix: undefined }
|
|
292
|
-
: { label: 'dsh-map-tools(地图/路线/POI)', ok: false, level: 'missing', detail: '未随包解析——启动时该 patch 条目被静默剔除,地图/路线/POI 工具不会出现在模型工具箱(缺地图不挡旅行规划,只少能力)', fix: '
|
|
322
|
+
: { label: 'dsh-map-tools(地图/路线/POI)', ok: false, level: 'missing', detail: '未随包解析——启动时该 patch 条目被静默剔除,地图/路线/POI 工具不会出现在模型工具箱(缺地图不挡旅行规划,只少能力)', fix: '重装 @danceiny/gotry(地图插件随包 vendor 分发,缺失多为安装不完整)' })
|
|
293
323
|
// dsh-tool-ask-user(结构化澄清卡,人格契约 (5) 的卡片形态载体)
|
|
294
324
|
const askCandidates = [
|
|
295
325
|
join(repoRoot, 'ts/dsh-runtime/vendor/deepseek-ai-dsh-tool-ask-user/package.json'),
|
|
296
326
|
join(repoRoot, 'node_modules/@deepseek-ai/dsh-tool-ask-user/package.json'),
|
|
297
327
|
join(homedir(), '.dsh/profiles/web/node_modules/@deepseek-ai/dsh-tool-ask-user/package.json'),
|
|
298
328
|
]
|
|
299
|
-
|
|
329
|
+
let askHit = askCandidates.find((p) => existsSync(p))
|
|
330
|
+
// 与 inner 同口径:优先从 dsh 包上下文解析(pnpm 嵌套布局只有 dsh 看得见),再从包根上下文
|
|
331
|
+
// (npm/npx 提升布局),最后静态候选(source vendored / profile)
|
|
332
|
+
if (!askHit) {
|
|
333
|
+
try {
|
|
334
|
+
const reqFromDsh = createRequire(rootRequire.resolve('@deepseek-ai/dsh/lib/bin.js'))
|
|
335
|
+
askHit = reqFromDsh.resolve('@deepseek-ai/dsh-tool-ask-user')
|
|
336
|
+
} catch {
|
|
337
|
+
try { askHit = rootRequire.resolve('@deepseek-ai/dsh-tool-ask-user') } catch { askHit = undefined }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
300
340
|
items.push(askHit
|
|
301
341
|
? { label: 'dsh-tool-ask-user(结构化澄清卡)', ok: true, level: 'ok', detail: `已就位(${askHit})——ask_user_question 澄清卡可用(web 原生卡片;headless+TTY 用 stdio 提供方)`, fix: undefined }
|
|
302
342
|
: { label: 'dsh-tool-ask-user(结构化澄清卡)', ok: false, level: 'missing', detail: '未解析——启动时澄清卡注入被静默剔除,模型只能散文追问(人格契约 (5) 退化文本形态)', fix: '重装 @danceiny/gotry——该依赖随 dsh 闭包自带,缺失多为安装不完整' })
|
|
@@ -728,7 +768,13 @@ if (WIZARD) {
|
|
|
728
768
|
process.exit(0)
|
|
729
769
|
}
|
|
730
770
|
|
|
731
|
-
main(
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
771
|
+
// 直接执行才跑 main(bootstrap-tests import 本模块做 setupSidebar 单测时不触发安装面;
|
|
772
|
+
// bin/gotry-inner.js 始终以 `node …/gotry-bootstrap.js` 直接 spawn,不受影响)
|
|
773
|
+
if (process.argv[1] && process.argv[1].endsWith('gotry-bootstrap.js')) {
|
|
774
|
+
main().catch((e) => {
|
|
775
|
+
say(`[gotry-setup] 异常:${e.message}(不影响 gotry 本体;可重试 npx gotry setup)`)
|
|
776
|
+
process.exit(AUTO ? 0 : 1)
|
|
777
|
+
})
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
export { setupSidebar, sidebarInstalled }
|
package/bin/gotry-inner.js
CHANGED
|
@@ -82,10 +82,7 @@ if (help) {
|
|
|
82
82
|
Usage:
|
|
83
83
|
gotry web # dsh Web UI on http://127.0.0.1:3080
|
|
84
84
|
gotry setup # 扩展就位检查/指引(商店一键装)
|
|
85
|
-
<<<<<<< HEAD
|
|
86
85
|
gotry setup calendar # 可选日历(CalDAV 工作窗口)挂载开关:默认关;--off 关闭;--status 查看
|
|
87
|
-
=======
|
|
88
|
-
>>>>>>> origin/main
|
|
89
86
|
gotry doctor # 可选依赖体检:扩展/agent-reach/hbcli/flyai/sidebar 状态 + 补装指引
|
|
90
87
|
gotry doctor --fix # 体检 + 按报告补装(hbcli 官方脚本 / agent-reach pip / sidebar 插件)
|
|
91
88
|
gotry "一段完整任务..." # headless 一问一答
|
|
@@ -368,13 +365,21 @@ if (benchmarkEnvironmentConfig) {
|
|
|
368
365
|
process.exit(1)
|
|
369
366
|
}
|
|
370
367
|
}
|
|
371
|
-
// dsh-map-tools 宿主插件(地图/路线/POI,零 key 走 OSM/OSRM):
|
|
372
|
-
//
|
|
368
|
+
// dsh-map-tools 宿主插件(地图/路线/POI,零 key 走 OSM/OSRM):tarball 随包 vendor,
|
|
369
|
+
// repo 工作副本走 runtime workspace 链接/依赖解析;都找不到就整块剔除 patch 条目
|
|
370
|
+
// (缺地图不挡旅行规划)。不能改成 npm 依赖:其 peerDependencies 要求
|
|
371
|
+
// dsh-settings/dsh-tools >=0.1.2-rc.1,与本包锁定的 0.1.2-alpha.3 家族在 npm
|
|
372
|
+
// 严格 peer 解析下 ERESOLVE,会直接弄坏 npx 安装。
|
|
373
373
|
let mapEntry = ''
|
|
374
374
|
if (!benchmarkEnvironmentConfig) {
|
|
375
|
-
const vendoredMap = join(repoRoot, 'ts/dsh-runtime/
|
|
375
|
+
const vendoredMap = join(repoRoot, 'ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js')
|
|
376
|
+
// (随 tarball 分发;source 布局同一份文件)
|
|
377
|
+
const runtimeMap = join(repoRoot, 'ts/dsh-runtime/node_modules/dsh-map-tools/lib/index.js')
|
|
378
|
+
// (ts/dsh-runtime pnpm workspace vendor/* 链接形态)
|
|
376
379
|
if (existsSync(vendoredMap)) {
|
|
377
380
|
mapEntry = vendoredMap
|
|
381
|
+
} else if (existsSync(runtimeMap)) {
|
|
382
|
+
mapEntry = runtimeMap
|
|
378
383
|
} else {
|
|
379
384
|
// npm 布局:子路径可能被 exports 挡(resolve 抛错不能留下旧值),裸包名返回真实入口
|
|
380
385
|
try { mapEntry = require_.resolve('dsh-map-tools/lib/index.js') } catch { mapEntry = '' }
|
package/cordis.gotry-patch.yml
CHANGED
|
@@ -109,9 +109,13 @@
|
|
|
109
109
|
**交付任何含可下单事实的行程产物(markdown 文件或长方案)前,必须调
|
|
110
110
|
gotry_fact_gate**;闸 blocked 时逐条修正为可回溯事实或降级措辞,
|
|
111
111
|
不得宣称「已验证方案」。
|
|
112
|
-
(21)
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
(21)工具循环收敛(wall-clock,ADR-24 v2):工具按需取用,没有「次数预算」,
|
|
113
|
+
也**永远不要**向用户宣称「工具预算已耗尽」——每轮唯一的边界是运行时时间闸,
|
|
114
|
+
只认三种信号:收到 TURN_DEADLINE_SOFT = 时间过半,立即停止发起新工具,开始
|
|
115
|
+
整理已有结果;收到 TURN_DEADLINE_EXHAUSTED = 本轮时间到,只能基于已有结果
|
|
116
|
+
输出最终答复,不得重试或改调其他工具,更不得用记忆/常识填充工具没查到的
|
|
117
|
+
实时事实(如实说明哪部分未能核实);收到 TURN_DEADLINE_HANDOFF = 深规划
|
|
118
|
+
转入后台,按提示告知用户预计时间与回访方式(附工单号),本轮给简短确认。
|
|
115
119
|
(22)到达账必达:红眼/凌晨起飞、或落地当天有硬安排(上班/办事)的航段,解释方案时必须
|
|
116
120
|
显式给出到达账——当地到达时刻(含日期偏移)、时差、到达精力与落地后可用时长,并主动给
|
|
117
121
|
前一晚落脚建议;时刻表正确不等于行程可行。
|
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { hbcliBinCandidates } from './hbcli.js';
|
|
3
|
+
function i18nText(v) {
|
|
4
|
+
if (!v) return '';
|
|
5
|
+
if (typeof v === 'string') return v;
|
|
6
|
+
return v.zh || v.en || v.ar || '';
|
|
7
|
+
}
|
|
8
|
+
function latlngOf(coord) {
|
|
9
|
+
for (const src of [
|
|
10
|
+
coord?.google,
|
|
11
|
+
coord?.gaode
|
|
12
|
+
]){
|
|
13
|
+
const lat = typeof src?.lat === 'number' ? src.lat : undefined;
|
|
14
|
+
const lng = typeof src?.lng === 'number' ? src.lng : undefined;
|
|
15
|
+
if (lat !== undefined && lng !== undefined && (lat !== 0 || lng !== 0)) return {
|
|
16
|
+
latitude: lat,
|
|
17
|
+
longitude: lng
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
2
22
|
function sh(cmd, args, opts) {
|
|
3
23
|
const ctrl = new AbortController();
|
|
4
24
|
const timer = setTimeout(()=>{
|
|
@@ -48,8 +68,20 @@ function sh(cmd, args, opts) {
|
|
|
48
68
|
});
|
|
49
69
|
});
|
|
50
70
|
}
|
|
51
|
-
|
|
52
|
-
const
|
|
71
|
+
function buildArgs(keyword, q) {
|
|
72
|
+
const args = [
|
|
73
|
+
'--json',
|
|
74
|
+
'search',
|
|
75
|
+
'anything',
|
|
76
|
+
keyword
|
|
77
|
+
];
|
|
78
|
+
if (q.contentType) args.push('--content-type', q.contentType);
|
|
79
|
+
if (q.parentDestinationId !== undefined) args.push('--destination-id', String(q.parentDestinationId));
|
|
80
|
+
return args;
|
|
81
|
+
}
|
|
82
|
+
function upgradeHint(stderr) {
|
|
83
|
+
return /unknown command/i.test(stderr) ? 'hbcli 版本过旧(无 search anything 子命令)——请升级:hbcli update,或重跑 npx gotry setup' : null;
|
|
84
|
+
}
|
|
53
85
|
export async function anythingSearch(q) {
|
|
54
86
|
const started = Date.now();
|
|
55
87
|
const ts = new Date().toISOString();
|
|
@@ -64,30 +96,30 @@ export async function anythingSearch(q) {
|
|
|
64
96
|
error: 'keyword is required'
|
|
65
97
|
};
|
|
66
98
|
}
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
const args = buildArgs(kw, q);
|
|
100
|
+
const candidates = hbcliBinCandidates(q.hbcliBin ?? 'hbcli');
|
|
101
|
+
let r = {
|
|
102
|
+
code: -1,
|
|
103
|
+
stdout: '',
|
|
104
|
+
stderr: ''
|
|
105
|
+
};
|
|
106
|
+
for(let i = 0; i < candidates.length; i++){
|
|
107
|
+
r = await sh(candidates[i], args, {
|
|
108
|
+
timeoutMs: q.timeoutMs ?? 12_000,
|
|
109
|
+
env: process.env
|
|
110
|
+
});
|
|
111
|
+
if (!(r.error && i < candidates.length - 1)) break;
|
|
77
112
|
}
|
|
78
|
-
const r = await sh(bin, args.slice(1), {
|
|
79
|
-
timeoutMs: q.timeoutMs ?? 12_000,
|
|
80
|
-
env: process.env
|
|
81
|
-
});
|
|
82
113
|
const latencyMs = Date.now() - started;
|
|
83
114
|
if (r.error || r.code !== 0) {
|
|
115
|
+
const raw = r.error ?? `${r.stderr.slice(0, 200)} (exit ${r.code})`;
|
|
84
116
|
return {
|
|
85
117
|
ok: false,
|
|
86
118
|
via: 'hbcli-anything-error',
|
|
87
|
-
evidence: `[实时API:hbcli-anything@error@${ts}] ${
|
|
119
|
+
evidence: `[实时API:hbcli-anything@error@${ts}] ${raw}`,
|
|
88
120
|
latencyMs,
|
|
89
121
|
verdict: 'error',
|
|
90
|
-
error: r.
|
|
122
|
+
error: upgradeHint(r.stderr) ?? raw
|
|
91
123
|
};
|
|
92
124
|
}
|
|
93
125
|
let data;
|
|
@@ -109,15 +141,19 @@ export async function anythingSearch(q) {
|
|
|
109
141
|
const hits = rawItems.map((it)=>{
|
|
110
142
|
const region = it.region ?? {};
|
|
111
143
|
const hotel = it.hotel ?? {};
|
|
112
|
-
const isHotel = it.type === 'hotel' ||
|
|
144
|
+
const isHotel = it.type === 'hotel' || Boolean(hotel.id);
|
|
145
|
+
const hotelCoords = latlngOf(hotel.latlngCoordinator);
|
|
146
|
+
const placeCoords = latlngOf(it.place?.latlngCoordinator);
|
|
147
|
+
const center = region.coordinates ?? {};
|
|
113
148
|
return {
|
|
114
|
-
type: isHotel ? 'hotel' : it.type === 'place'
|
|
115
|
-
name:
|
|
116
|
-
score: it.
|
|
117
|
-
latitude:
|
|
118
|
-
longitude:
|
|
119
|
-
destinationId: region.id,
|
|
120
|
-
hotelId: hotel.id
|
|
149
|
+
type: isHotel ? 'hotel' : it.type === 'place' ? 'place' : 'city',
|
|
150
|
+
name: i18nText(hotel.name) || i18nText(region.name) || '?',
|
|
151
|
+
score: it.matchScore,
|
|
152
|
+
latitude: hotelCoords.latitude ?? placeCoords.latitude ?? (typeof center.centerLat === 'number' ? center.centerLat : undefined),
|
|
153
|
+
longitude: hotelCoords.longitude ?? placeCoords.longitude ?? (typeof center.centerLng === 'number' ? center.centerLng : undefined),
|
|
154
|
+
destinationId: hotel.destinationId !== undefined ? String(hotel.destinationId) : region.id !== undefined ? String(region.id) : undefined,
|
|
155
|
+
hotelId: hotel.id !== undefined ? String(hotel.id) : undefined,
|
|
156
|
+
star: typeof hotel.star === 'number' && hotel.star > 0 ? hotel.star : undefined
|
|
121
157
|
};
|
|
122
158
|
});
|
|
123
159
|
const verdict = hits.length > 0 ? 'hit' : 'miss';
|
|
@@ -131,9 +167,6 @@ export async function anythingSearch(q) {
|
|
|
131
167
|
totalCandidates: rawItems.length
|
|
132
168
|
};
|
|
133
169
|
}
|
|
134
|
-
export const _ = {
|
|
135
|
-
upperFirst
|
|
136
|
-
};
|
|
137
170
|
|
|
138
171
|
|
|
139
172
|
//# sourceURL=ts/capabilities/anything.ts
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import { homedir } from 'node:os';
|
|
4
5
|
import { join, resolve } from 'node:path';
|
|
5
6
|
import { readLatestChannelEvents } from './channel-health.js';
|
|
@@ -206,13 +207,22 @@ export async function runDoctorChecks(opts = {}) {
|
|
|
206
207
|
detail: '已挂载但 calendar 未配置 username——日历工具会话中会报「未配置」',
|
|
207
208
|
fix: `npx gotry setup calendar --off(恢复默认不挂载),或在 ${calProfilePatch} 覆盖 calendar 行 config 填 username(指引: npx gotry setup calendar --status)`
|
|
208
209
|
});
|
|
210
|
+
const rootRequire = createRequire(join(repoRoot, 'package.json'));
|
|
209
211
|
const mapCandidates = [
|
|
212
|
+
join(repoRoot, 'ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js'),
|
|
210
213
|
join(repoRoot, 'ts/dsh-runtime/node_modules/dsh-map-tools/lib/index.js'),
|
|
211
214
|
join(repoRoot, 'node_modules/dsh-map-tools/package.json'),
|
|
212
215
|
join(repoRoot, 'ts/node_modules/dsh-map-tools/package.json'),
|
|
213
216
|
join(home, '.dsh/profiles/web/node_modules/dsh-map-tools/package.json')
|
|
214
217
|
];
|
|
215
|
-
|
|
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
|
+
}
|
|
216
226
|
items.push(mapHit ? {
|
|
217
227
|
id: 'map-tools',
|
|
218
228
|
label: 'dsh-map-tools(地图/路线/POI)',
|
|
@@ -223,14 +233,26 @@ export async function runDoctorChecks(opts = {}) {
|
|
|
223
233
|
label: 'dsh-map-tools(地图/路线/POI)',
|
|
224
234
|
status: 'missing',
|
|
225
235
|
detail: '未随包解析——启动时该 patch 条目被静默剔除,地图/路线/POI 工具不会出现在模型工具箱(缺地图不挡旅行规划,只少能力)',
|
|
226
|
-
fix: '
|
|
236
|
+
fix: '重装 @danceiny/gotry(地图插件随包 vendor 分发,缺失多为安装不完整)'
|
|
227
237
|
});
|
|
228
238
|
const askCandidates = [
|
|
229
239
|
join(repoRoot, 'ts/dsh-runtime/vendor/deepseek-ai-dsh-tool-ask-user/package.json'),
|
|
230
240
|
join(repoRoot, 'node_modules/@deepseek-ai/dsh-tool-ask-user/package.json'),
|
|
231
241
|
join(home, '.dsh/profiles/web/node_modules/@deepseek-ai/dsh-tool-ask-user/package.json')
|
|
232
242
|
];
|
|
233
|
-
|
|
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
|
+
}
|
|
234
256
|
items.push(askHit ? {
|
|
235
257
|
id: 'ask-user',
|
|
236
258
|
label: 'dsh-tool-ask-user(结构化澄清卡)',
|
|
@@ -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
|
|
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 {
|
|
@@ -117,7 +117,8 @@ async function startRelay() {
|
|
|
117
117
|
if (toolNames(body).includes(TOOL)) {
|
|
118
118
|
plannerBodies.push(body);
|
|
119
119
|
const plannerIndex = plannerBodies.length;
|
|
120
|
-
const
|
|
120
|
+
const realToolMessages = (body.messages ?? []).filter((message)=>message.role === 'tool');
|
|
121
|
+
const alreadyHandedOff = realToolMessages.some((message)=>JSON.stringify(message).includes(HANDOFF));
|
|
121
122
|
const stall = !alreadyHandedOff && plannerIndex === 1 ? RELAY_STALL_MS : 0;
|
|
122
123
|
setTimeout(()=>{
|
|
123
124
|
if (!alreadyHandedOff && plannerIndex === 1) {
|