@danceiny/gotry 0.0.1-rc.19 → 0.0.1-rc.21
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 +14 -3
- package/README.zh-CN.md +15 -4
- package/bin/gotry-bootstrap.js +136 -45
- package/bin/gotry-inner.js +11 -6
- package/cordis.gotry-patch.yml +19 -8
- package/dist/capabilities/anything.js +62 -29
- package/dist/capabilities/doctor.js +113 -19
- package/dist/capabilities/hbcli.js +28 -2
- package/dist/capabilities/session/health-watch.js +10 -3
- 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 +178 -33
- package/dist/scripts/benchmark-environment-bridge-tests.js +1480 -271
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +211 -2
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +132 -2
- package/dist/scripts/bootstrap-tests.js +30 -3
- package/dist/scripts/doctor-tests.js +55 -1
- package/dist/scripts/hbcli-e2e-tests.js +1 -1
- package/dist/scripts/hbcli-tests.js +38 -1
- package/dist/scripts/health-watch-cli.js +9 -1
- package/dist/scripts/map-tools-vendor-package-proof.js +276 -0
- package/dist/scripts/persona-surface-guard-tests.js +8 -3
- package/dist/src/benchmark-agent-conformance.js +187 -17
- package/dist/src/benchmark-environment-bridge.js +287 -102
- package/dist/src/booking-surface/dsh-planner.js +24 -8
- package/dist/src/index.js +5 -2
- package/extension/manifest.json +2 -2
- package/package.json +3 -1
- package/ts/capabilities/anything.ts +89 -38
- package/ts/capabilities/hbcli.ts +53 -4
- package/ts/capabilities/session/health-watch.ts +9 -2
- 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 +17 -1
- package/ts/scripts/map-tools-vendor-package-proof.ts +268 -0
- package/ts/src/benchmark-agent-conformance.ts +175 -13
- package/ts/src/benchmark-environment-bridge.ts +220 -66
- package/ts/src/booking-surface/dsh-planner.ts +25 -13
- package/ts/src/index.ts +4 -2
- package/ts/src/turn-deadline.ts +4 -0
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { accessSync, constants, lstatSync, readFileSync, statSync } from 'node:fs';
|
|
2
2
|
import { getuid } from 'node:process';
|
|
3
3
|
import { isAbsolute } from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import { assertSupportedJsonSchema, validateJsonSchemaValue, ToolArgsError } from '@deepseek-ai/dsh-tools';
|
|
5
5
|
import { validateTerminalOutputConfig } from './benchmark-agent-conformance.js';
|
|
6
|
-
const SCHEMA_VERSION = '
|
|
6
|
+
const SCHEMA_VERSION = 'gotry_benchmark_environment_bridge_v4';
|
|
7
|
+
export const BENCHMARK_TOOL_RESULT_SCHEMA_VERSION = 'gotry_benchmark_tool_result_v1';
|
|
8
|
+
export const BENCHMARK_DOMAIN_RECOVERIES = [
|
|
9
|
+
'none',
|
|
10
|
+
'retry_same',
|
|
11
|
+
'revise_arguments',
|
|
12
|
+
'choose_alternative'
|
|
13
|
+
];
|
|
7
14
|
const IDENTIFIER = /^[A-Za-z][A-Za-z0-9_.-]*$/;
|
|
15
|
+
const RECOVERIES = new Set(BENCHMARK_DOMAIN_RECOVERIES);
|
|
8
16
|
const CONFIG_KEYS = [
|
|
9
|
-
'allowed_tools',
|
|
10
17
|
'argv_prefix',
|
|
11
18
|
'cwd',
|
|
12
19
|
'enabled',
|
|
@@ -15,14 +22,32 @@ const CONFIG_KEYS = [
|
|
|
15
22
|
'max_output_bytes',
|
|
16
23
|
'schema_version',
|
|
17
24
|
'terminal_output',
|
|
18
|
-
'timeout_ms'
|
|
19
|
-
|
|
20
|
-
const OPTIONAL_CONFIG_KEYS = [
|
|
21
|
-
'allowed_output_keys'
|
|
25
|
+
'timeout_ms',
|
|
26
|
+
'tools'
|
|
22
27
|
];
|
|
28
|
+
const INPUT_SCHEMA_MAX_BYTES = 16 * 1024;
|
|
29
|
+
const INPUT_SCHEMA_MAX_DEPTH = 8;
|
|
30
|
+
const INPUT_SCHEMA_MAX_NODES = 256;
|
|
31
|
+
const INPUT_SCHEMA_MAX_PROPERTIES = 256;
|
|
32
|
+
const INPUT_SCHEMA_MAX_ENUM_VALUES = 256;
|
|
33
|
+
const INPUT_SCHEMA_MAX_DESCRIPTION_LENGTH = 512;
|
|
34
|
+
const INPUT_SCHEMA_TYPES = new Set([
|
|
35
|
+
'string',
|
|
36
|
+
'number',
|
|
37
|
+
'integer',
|
|
38
|
+
'boolean',
|
|
39
|
+
'null',
|
|
40
|
+
'array',
|
|
41
|
+
'object'
|
|
42
|
+
]);
|
|
23
43
|
function plainObject(value) {
|
|
24
44
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
25
45
|
}
|
|
46
|
+
function deepFreezeJson(value) {
|
|
47
|
+
if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
|
|
48
|
+
for (const child of Object.values(value))deepFreezeJson(child);
|
|
49
|
+
return Object.freeze(value);
|
|
50
|
+
}
|
|
26
51
|
function existingExecutable(value) {
|
|
27
52
|
if (typeof value !== 'string' || !isAbsolute(value)) return false;
|
|
28
53
|
try {
|
|
@@ -41,13 +66,121 @@ function existingDirectory(value) {
|
|
|
41
66
|
return false;
|
|
42
67
|
}
|
|
43
68
|
}
|
|
44
|
-
function identifiers(value) {
|
|
45
|
-
return Array.isArray(value) && value.length > 0 && value.length <= 64 && new Set(value).size === value.length && value.every((item)=>typeof item === 'string' && item.length > 0 && IDENTIFIER.test(item));
|
|
69
|
+
function identifiers(value, allowEmpty = false) {
|
|
70
|
+
return Array.isArray(value) && (allowEmpty || value.length > 0) && value.length <= 64 && new Set(value).size === value.length && value.every((item)=>typeof item === 'string' && item.length > 0 && IDENTIFIER.test(item));
|
|
71
|
+
}
|
|
72
|
+
function exactKeys(value, allowed) {
|
|
73
|
+
return Object.keys(value).every((key)=>allowed.includes(key));
|
|
74
|
+
}
|
|
75
|
+
function validDescription(value, required = false) {
|
|
76
|
+
if (value === undefined) return !required;
|
|
77
|
+
return typeof value === 'string' && (!required || value.trim().length > 0) && value.length <= INPUT_SCHEMA_MAX_DESCRIPTION_LENGTH && !/[\u0000-\u001f\u007f]/.test(value);
|
|
46
78
|
}
|
|
47
|
-
function
|
|
79
|
+
function scalarMatches(type, value) {
|
|
80
|
+
if (type === 'string') return typeof value === 'string' && value.length <= 512;
|
|
81
|
+
if (type === 'number') return typeof value === 'number' && Number.isFinite(value);
|
|
82
|
+
if (type === 'integer') return typeof value === 'number' && Number.isSafeInteger(value);
|
|
83
|
+
if (type === 'boolean') return typeof value === 'boolean';
|
|
84
|
+
return type === 'null' && value === null;
|
|
85
|
+
}
|
|
86
|
+
function validInputSchema(value) {
|
|
48
87
|
if (!plainObject(value)) return false;
|
|
49
|
-
|
|
50
|
-
|
|
88
|
+
let serialized;
|
|
89
|
+
try {
|
|
90
|
+
serialized = JSON.stringify(value);
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (Buffer.byteLength(serialized, 'utf8') > INPUT_SCHEMA_MAX_BYTES) return false;
|
|
95
|
+
const pending = [
|
|
96
|
+
{
|
|
97
|
+
node: value,
|
|
98
|
+
depth: 0
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
let nodes = 0;
|
|
103
|
+
let properties = 0;
|
|
104
|
+
let enumValues = 0;
|
|
105
|
+
while(pending.length > 0){
|
|
106
|
+
const { node, depth } = pending.pop();
|
|
107
|
+
if (seen.has(node) || ++nodes > INPUT_SCHEMA_MAX_NODES || depth > INPUT_SCHEMA_MAX_DEPTH) return false;
|
|
108
|
+
seen.add(node);
|
|
109
|
+
if (!INPUT_SCHEMA_TYPES.has(String(node.type)) || !validDescription(node.description)) return false;
|
|
110
|
+
if (node.type === 'object') {
|
|
111
|
+
if (!exactKeys(node, [
|
|
112
|
+
'type',
|
|
113
|
+
'description',
|
|
114
|
+
'properties',
|
|
115
|
+
'required',
|
|
116
|
+
'additionalProperties'
|
|
117
|
+
]) || !plainObject(node.properties) || !Array.isArray(node.required) || node.additionalProperties !== false) return false;
|
|
118
|
+
const propertyEntries = Object.entries(node.properties);
|
|
119
|
+
properties += propertyEntries.length;
|
|
120
|
+
if (properties > INPUT_SCHEMA_MAX_PROPERTIES || propertyEntries.some(([key, child])=>!IDENTIFIER.test(key) || !plainObject(child))) return false;
|
|
121
|
+
const required = node.required;
|
|
122
|
+
if (required.length > propertyEntries.length || new Set(required).size !== required.length || required.some((key)=>typeof key !== 'string' || !Object.hasOwn(node.properties, key))) return false;
|
|
123
|
+
for (const [, child] of propertyEntries)pending.push({
|
|
124
|
+
node: child,
|
|
125
|
+
depth: depth + 1
|
|
126
|
+
});
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (node.type === 'array') {
|
|
130
|
+
if (!exactKeys(node, [
|
|
131
|
+
'type',
|
|
132
|
+
'description',
|
|
133
|
+
'items'
|
|
134
|
+
]) || !plainObject(node.items)) return false;
|
|
135
|
+
pending.push({
|
|
136
|
+
node: node.items,
|
|
137
|
+
depth: depth + 1
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!exactKeys(node, [
|
|
142
|
+
'type',
|
|
143
|
+
'description',
|
|
144
|
+
'enum',
|
|
145
|
+
'const'
|
|
146
|
+
]) || Object.hasOwn(node, 'enum') && Object.hasOwn(node, 'const')) return false;
|
|
147
|
+
if (Object.hasOwn(node, 'const') && !scalarMatches(String(node.type), node.const)) return false;
|
|
148
|
+
if (Object.hasOwn(node, 'enum')) {
|
|
149
|
+
if (!Array.isArray(node.enum) || node.enum.length === 0 || node.enum.length > 64) return false;
|
|
150
|
+
enumValues += node.enum.length;
|
|
151
|
+
if (enumValues > INPUT_SCHEMA_MAX_ENUM_VALUES || new Set(node.enum).size !== node.enum.length || node.enum.some((item)=>!scalarMatches(String(node.type), item))) return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return value.type === 'object';
|
|
155
|
+
}
|
|
156
|
+
function toolDescriptors(value) {
|
|
157
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 64) return false;
|
|
158
|
+
const names = new Set();
|
|
159
|
+
return value.every((item)=>{
|
|
160
|
+
if (!plainObject(item)) return false;
|
|
161
|
+
const keys = Object.keys(item).sort();
|
|
162
|
+
const required = [
|
|
163
|
+
'description',
|
|
164
|
+
'domain_outcomes',
|
|
165
|
+
'input_schema',
|
|
166
|
+
'name'
|
|
167
|
+
];
|
|
168
|
+
if (JSON.stringify(keys) !== JSON.stringify([
|
|
169
|
+
...required,
|
|
170
|
+
'output_keys'
|
|
171
|
+
].sort())) return false;
|
|
172
|
+
if (typeof item.name !== 'string' || !IDENTIFIER.test(item.name) || names.has(item.name)) return false;
|
|
173
|
+
names.add(item.name);
|
|
174
|
+
if (!validDescription(item.description, true) || !validInputSchema(item.input_schema)) return false;
|
|
175
|
+
if (!Array.isArray(item.domain_outcomes) || item.domain_outcomes.length > 64) return false;
|
|
176
|
+
const outcomeKeys = new Set();
|
|
177
|
+
if (!item.domain_outcomes.every((outcome)=>plainObject(outcome) && JSON.stringify(Object.keys(outcome).sort()) === JSON.stringify([
|
|
178
|
+
'code',
|
|
179
|
+
'recovery',
|
|
180
|
+
'status'
|
|
181
|
+
]) && (outcome.status === 'miss' || outcome.status === 'error') && typeof outcome.code === 'string' && IDENTIFIER.test(outcome.code) && typeof outcome.recovery === 'string' && RECOVERIES.has(outcome.recovery) && !outcomeKeys.has(outcome.code) && (outcomeKeys.add(outcome.code), true))) return false;
|
|
182
|
+
return identifiers(item.output_keys);
|
|
183
|
+
});
|
|
51
184
|
}
|
|
52
185
|
function argvPrefix(value) {
|
|
53
186
|
return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((item)=>typeof item === 'string' && item.length > 0 && item.length <= 256 && !item.includes('\0') && !item.includes('\r') && !item.includes('\n'));
|
|
@@ -69,14 +202,7 @@ function validate(value) {
|
|
|
69
202
|
const baseKeys = [
|
|
70
203
|
...CONFIG_KEYS
|
|
71
204
|
].sort();
|
|
72
|
-
|
|
73
|
-
...CONFIG_KEYS,
|
|
74
|
-
...OPTIONAL_CONFIG_KEYS
|
|
75
|
-
].sort();
|
|
76
|
-
if (!plainObject(value) || ![
|
|
77
|
-
baseKeys,
|
|
78
|
-
extendedKeys
|
|
79
|
-
].some((keys)=>JSON.stringify(configKeys) === JSON.stringify(keys)) || value.schema_version !== SCHEMA_VERSION || value.enabled !== true || !existingExecutable(value.executable) || !existingDirectory(value.cwd) || !argvPrefix(value.argv_prefix) || !identifiers(value.allowed_tools) || !validBound(value.timeout_ms, 120_000) || !validBound(value.max_output_bytes, 10 * 1024 * 1024) || !validateTerminalOutputConfig(value.terminal_output) || !exactIsolation(value.isolation) || Object.hasOwn(value, 'allowed_output_keys') && !allowedOutputKeys(value.allowed_output_keys, value.allowed_tools)) return false;
|
|
205
|
+
if (!plainObject(value) || JSON.stringify(configKeys) !== JSON.stringify(baseKeys) || value.schema_version !== SCHEMA_VERSION || value.enabled !== true || !existingExecutable(value.executable) || !existingDirectory(value.cwd) || !argvPrefix(value.argv_prefix) || !toolDescriptors(value.tools) || !validBound(value.timeout_ms, 120_000) || !validBound(value.max_output_bytes, 10 * 1024 * 1024) || !validateTerminalOutputConfig(value.terminal_output) || !exactIsolation(value.isolation)) return false;
|
|
80
206
|
return true;
|
|
81
207
|
}
|
|
82
208
|
export function loadBenchmarkEnvironmentConfig(path) {
|
|
@@ -87,7 +213,7 @@ export function loadBenchmarkEnvironmentConfig(path) {
|
|
|
87
213
|
if (typeof getuid !== 'function' || metadata.uid !== getuid()) return null;
|
|
88
214
|
if ((metadata.mode & 0o022) !== 0) return null;
|
|
89
215
|
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
90
|
-
return validate(parsed) ? parsed : null;
|
|
216
|
+
return validate(parsed) ? deepFreezeJson(parsed) : null;
|
|
91
217
|
} catch {
|
|
92
218
|
return null;
|
|
93
219
|
}
|
|
@@ -116,6 +242,48 @@ const FORBIDDEN_OUTPUT_KEY_FRAGMENTS = [
|
|
|
116
242
|
'reward',
|
|
117
243
|
'score'
|
|
118
244
|
];
|
|
245
|
+
export const BRIDGE_ERROR_CONTRACT = Object.freeze({
|
|
246
|
+
invalid_action: {
|
|
247
|
+
recoverable: true,
|
|
248
|
+
remedy: 'action 只允许 tools、call 或 errors'
|
|
249
|
+
},
|
|
250
|
+
disallowed_tool: {
|
|
251
|
+
recoverable: true,
|
|
252
|
+
remedy: '先 action=tools 列出 allowed_tools 清单,再从清单内选工具'
|
|
253
|
+
},
|
|
254
|
+
invalid_arguments: {
|
|
255
|
+
recoverable: true,
|
|
256
|
+
remedy: 'arguments 必须是可序列化 JSON 对象(≤64KB、深度≤12);收窄后重试'
|
|
257
|
+
},
|
|
258
|
+
timed_out: {
|
|
259
|
+
recoverable: true,
|
|
260
|
+
remedy: '上游超时;可原样重试一次或换其他工具'
|
|
261
|
+
},
|
|
262
|
+
output_truncated: {
|
|
263
|
+
recoverable: true,
|
|
264
|
+
remedy: '输出超出上限;让被调工具收窄查询范围后重试'
|
|
265
|
+
},
|
|
266
|
+
invalid_json: {
|
|
267
|
+
recoverable: true,
|
|
268
|
+
remedy: '上游输出不是合法 JSON;重试一次或换工具'
|
|
269
|
+
},
|
|
270
|
+
invalid_output: {
|
|
271
|
+
recoverable: true,
|
|
272
|
+
remedy: '上游输出结构不符;重试或换工具'
|
|
273
|
+
},
|
|
274
|
+
runner_failed: {
|
|
275
|
+
recoverable: true,
|
|
276
|
+
remedy: '上游进程非零退出;可重试一次,持续失败换工具'
|
|
277
|
+
},
|
|
278
|
+
spawn_failed: {
|
|
279
|
+
recoverable: false,
|
|
280
|
+
remedy: '可执行文件不可用——环境问题,调用方不可恢复'
|
|
281
|
+
},
|
|
282
|
+
forbidden_output: {
|
|
283
|
+
recoverable: false,
|
|
284
|
+
remedy: '输出含未授权键(策略边界)——不可恢复,换工具或放弃'
|
|
285
|
+
}
|
|
286
|
+
});
|
|
119
287
|
function inspectOutput(value) {
|
|
120
288
|
const pending = [
|
|
121
289
|
{
|
|
@@ -152,7 +320,8 @@ function inspectAllowedOutput(value, allowedKeys) {
|
|
|
152
320
|
const pending = [
|
|
153
321
|
{
|
|
154
322
|
value,
|
|
155
|
-
depth: 0
|
|
323
|
+
depth: 0,
|
|
324
|
+
coveredByDeclaredKey: false
|
|
156
325
|
}
|
|
157
326
|
];
|
|
158
327
|
let nodes = 0;
|
|
@@ -162,16 +331,21 @@ function inspectAllowedOutput(value, allowedKeys) {
|
|
|
162
331
|
if (Array.isArray(current.value)) {
|
|
163
332
|
for (const child of current.value)pending.push({
|
|
164
333
|
value: child,
|
|
165
|
-
depth: current.depth + 1
|
|
334
|
+
depth: current.depth + 1,
|
|
335
|
+
coveredByDeclaredKey: current.coveredByDeclaredKey
|
|
166
336
|
});
|
|
167
337
|
continue;
|
|
168
338
|
}
|
|
169
|
-
if (!plainObject(current.value))
|
|
339
|
+
if (!plainObject(current.value)) {
|
|
340
|
+
if (!current.coveredByDeclaredKey) return false;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
170
343
|
for (const [key, child] of Object.entries(current.value)){
|
|
171
344
|
if (!allowed.has(key)) return false;
|
|
172
345
|
pending.push({
|
|
173
346
|
value: child,
|
|
174
|
-
depth: current.depth + 1
|
|
347
|
+
depth: current.depth + 1,
|
|
348
|
+
coveredByDeclaredKey: true
|
|
175
349
|
});
|
|
176
350
|
}
|
|
177
351
|
}
|
|
@@ -211,56 +385,14 @@ function serializedArguments(value) {
|
|
|
211
385
|
};
|
|
212
386
|
}
|
|
213
387
|
}
|
|
214
|
-
export const BRIDGE_ERROR_CONTRACT = Object.freeze({
|
|
215
|
-
invalid_action: {
|
|
216
|
-
recoverable: true,
|
|
217
|
-
remedy: 'action 只允许 tools 或 call'
|
|
218
|
-
},
|
|
219
|
-
disallowed_tool: {
|
|
220
|
-
recoverable: true,
|
|
221
|
-
remedy: '先 action=tools 列出 allowed_tools 清单,再从清单内选工具'
|
|
222
|
-
},
|
|
223
|
-
invalid_arguments: {
|
|
224
|
-
recoverable: true,
|
|
225
|
-
remedy: 'arguments 必须是可序列化 JSON 对象(≤64KB、深度≤12);收窄后重试'
|
|
226
|
-
},
|
|
227
|
-
timed_out: {
|
|
228
|
-
recoverable: true,
|
|
229
|
-
remedy: '上游超时;可原样重试一次或换其他工具'
|
|
230
|
-
},
|
|
231
|
-
output_truncated: {
|
|
232
|
-
recoverable: true,
|
|
233
|
-
remedy: '输出超出上限;让被调工具收窄查询范围后重试'
|
|
234
|
-
},
|
|
235
|
-
invalid_json: {
|
|
236
|
-
recoverable: true,
|
|
237
|
-
remedy: '上游输出不是合法 JSON;重试一次或换工具'
|
|
238
|
-
},
|
|
239
|
-
invalid_output: {
|
|
240
|
-
recoverable: true,
|
|
241
|
-
remedy: '上游输出结构不符(如原始字符串);重试或换工具'
|
|
242
|
-
},
|
|
243
|
-
runner_failed: {
|
|
244
|
-
recoverable: true,
|
|
245
|
-
remedy: '上游进程非零退出;可重试一次,持续失败换工具'
|
|
246
|
-
},
|
|
247
|
-
spawn_failed: {
|
|
248
|
-
recoverable: false,
|
|
249
|
-
remedy: '可执行文件不可用——环境问题,调用方不可恢复'
|
|
250
|
-
},
|
|
251
|
-
forbidden_output: {
|
|
252
|
-
recoverable: false,
|
|
253
|
-
remedy: '输出含未授权键(策略边界)——不可恢复,换工具或放弃'
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
388
|
export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
257
389
|
const bridge = loadBenchmarkEnvironmentConfig(path);
|
|
258
390
|
if (!bridge) throw new Error('benchmark environment bridge configuration unavailable');
|
|
259
391
|
if (!subprocess) throw new Error('benchmark environment bridge subprocess unavailable');
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
description: '
|
|
263
|
-
|
|
392
|
+
const parameters = deepFreezeJson({
|
|
393
|
+
type: 'object',
|
|
394
|
+
description: 'Flat benchmark bridge wire: choose an action, then provide the mapped tool and its arguments.',
|
|
395
|
+
properties: {
|
|
264
396
|
action: {
|
|
265
397
|
type: 'string',
|
|
266
398
|
enum: [
|
|
@@ -268,23 +400,31 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
|
268
400
|
'call',
|
|
269
401
|
'errors'
|
|
270
402
|
],
|
|
271
|
-
|
|
272
|
-
description: 'tools=列出可调工具;call=执行被映射工具;errors=拉取可恢复错误契约表'
|
|
403
|
+
description: 'tools=列出可调工具;call=执行工具;errors=拉取错误契约'
|
|
273
404
|
},
|
|
274
405
|
tool: {
|
|
275
406
|
type: 'string',
|
|
276
|
-
|
|
407
|
+
enum: bridge.tools.map((item)=>item.name),
|
|
408
|
+
description: 'action=call 时选择的冻结工具名'
|
|
277
409
|
},
|
|
278
410
|
arguments: {
|
|
279
411
|
type: 'object',
|
|
280
412
|
additionalProperties: true,
|
|
281
|
-
description: '
|
|
413
|
+
description: 'action=call 时传给工具的参数对象'
|
|
282
414
|
}
|
|
283
415
|
},
|
|
416
|
+
required: [
|
|
417
|
+
'action'
|
|
418
|
+
],
|
|
419
|
+
additionalProperties: false
|
|
420
|
+
});
|
|
421
|
+
assertSupportedJsonSchema(parameters);
|
|
422
|
+
const definition = {
|
|
423
|
+
name: 'gotry_benchmark_environment',
|
|
424
|
+
description: 'When a prompt asks to run agent_env.cli, use action=call here with the mapped tool; arbitrary shell is not exposed.',
|
|
425
|
+
parameters,
|
|
284
426
|
output: {
|
|
285
|
-
schema: {
|
|
286
|
-
type: 'json'
|
|
287
|
-
},
|
|
427
|
+
schema: {},
|
|
288
428
|
render: (_args, value)=>[
|
|
289
429
|
{
|
|
290
430
|
type: 'text',
|
|
@@ -293,33 +433,56 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
|
293
433
|
]
|
|
294
434
|
},
|
|
295
435
|
async execute (args) {
|
|
296
|
-
const
|
|
297
|
-
if (
|
|
298
|
-
ok: true,
|
|
299
|
-
errors: BRIDGE_ERROR_CONTRACT
|
|
300
|
-
});
|
|
301
|
-
if (action === 'tools') return jsonObject({
|
|
302
|
-
ok: true,
|
|
303
|
-
tools: bridge.allowed_tools
|
|
304
|
-
});
|
|
305
|
-
if (action !== 'call') return jsonObject({
|
|
306
|
-
ok: false,
|
|
307
|
-
error: 'invalid_action'
|
|
308
|
-
});
|
|
436
|
+
const violations = validateJsonSchemaValue(parameters, args, 'arguments');
|
|
437
|
+
if (violations.length > 0) throw new ToolArgsError(violations);
|
|
309
438
|
const query = args;
|
|
310
|
-
|
|
439
|
+
const tool = query.tool;
|
|
440
|
+
const descriptor = bridge.tools.find((item)=>item.name === tool);
|
|
441
|
+
const keys = Object.keys(query);
|
|
442
|
+
if (query.action === 'tools') {
|
|
443
|
+
if (keys.length !== 1) throw new ToolArgsError([
|
|
444
|
+
'arguments: tools action accepts no tool or arguments'
|
|
445
|
+
]);
|
|
446
|
+
return jsonObject({
|
|
447
|
+
ok: true,
|
|
448
|
+
tools: bridge.tools
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
if (query.action === 'errors') {
|
|
452
|
+
if (keys.length !== 1) throw new ToolArgsError([
|
|
453
|
+
'arguments: errors action accepts no tool or arguments'
|
|
454
|
+
]);
|
|
455
|
+
return jsonObject({
|
|
456
|
+
ok: true,
|
|
457
|
+
errors: BRIDGE_ERROR_CONTRACT
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
if (query.action !== 'call') {
|
|
461
|
+
return jsonObject({
|
|
462
|
+
ok: false,
|
|
463
|
+
error: 'invalid_action'
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
if (keys.length !== 3 || !Object.hasOwn(query, 'tool') || !Object.hasOwn(query, 'arguments')) {
|
|
467
|
+
throw new ToolArgsError([
|
|
468
|
+
'arguments: call action requires exactly tool and arguments'
|
|
469
|
+
]);
|
|
470
|
+
}
|
|
471
|
+
if (typeof tool !== 'string' || !descriptor) {
|
|
311
472
|
return jsonObject({
|
|
312
473
|
ok: false,
|
|
313
474
|
error: 'disallowed_tool'
|
|
314
475
|
});
|
|
315
476
|
}
|
|
316
|
-
if (
|
|
477
|
+
if (!plainObject(query.arguments)) {
|
|
317
478
|
return jsonObject({
|
|
318
479
|
ok: false,
|
|
319
480
|
error: 'invalid_arguments'
|
|
320
481
|
});
|
|
321
482
|
}
|
|
322
|
-
const callArguments = query.arguments
|
|
483
|
+
const callArguments = query.arguments;
|
|
484
|
+
const argumentViolations = validateJsonSchemaValue(descriptor.input_schema, callArguments, 'arguments');
|
|
485
|
+
if (argumentViolations.length > 0) throw new ToolArgsError(argumentViolations);
|
|
323
486
|
const serialized = serializedArguments(callArguments);
|
|
324
487
|
if (serialized.reason) return jsonObject({
|
|
325
488
|
ok: false,
|
|
@@ -352,7 +515,7 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
|
352
515
|
bridge.executable,
|
|
353
516
|
...bridge.argv_prefix,
|
|
354
517
|
'call',
|
|
355
|
-
|
|
518
|
+
tool,
|
|
356
519
|
serialized.json
|
|
357
520
|
],
|
|
358
521
|
cwd: bridge.cwd,
|
|
@@ -402,13 +565,36 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
|
402
565
|
ok: false,
|
|
403
566
|
error: 'invalid_output'
|
|
404
567
|
});
|
|
405
|
-
|
|
568
|
+
if (parsed.schema_version !== BENCHMARK_TOOL_RESULT_SCHEMA_VERSION || typeof parsed.status !== 'string') {
|
|
569
|
+
return jsonObject({
|
|
570
|
+
ok: false,
|
|
571
|
+
error: 'invalid_output'
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
if (parsed.status === 'miss' || parsed.status === 'error') {
|
|
575
|
+
if (typeof parsed.code !== 'string' || typeof parsed.recovery !== 'string' || !descriptor.domain_outcomes.some((outcome)=>outcome.status === parsed.status && outcome.code === parsed.code && outcome.recovery === parsed.recovery)) return jsonObject({
|
|
576
|
+
ok: false,
|
|
577
|
+
error: 'invalid_output'
|
|
578
|
+
});
|
|
579
|
+
if (Object.keys(parsed).sort().join(',') !== 'code,recovery,schema_version,status') return jsonObject({
|
|
580
|
+
ok: false,
|
|
581
|
+
error: 'invalid_output'
|
|
582
|
+
});
|
|
583
|
+
return jsonObject({
|
|
584
|
+
ok: true,
|
|
585
|
+
outcome: parsed
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (parsed.status !== 'ok' || !Object.hasOwn(parsed, 'result') || Object.keys(parsed).sort().join(',') !== 'result,schema_version,status') return jsonObject({
|
|
589
|
+
ok: false,
|
|
590
|
+
error: 'invalid_output'
|
|
591
|
+
});
|
|
592
|
+
const visibleResult = parsed.result;
|
|
406
593
|
if (!plainObject(visibleResult) && !Array.isArray(visibleResult)) return jsonObject({
|
|
407
594
|
ok: false,
|
|
408
595
|
error: 'invalid_output'
|
|
409
596
|
});
|
|
410
|
-
|
|
411
|
-
if (outputKeys && !inspectAllowedOutput(visibleResult, outputKeys)) return jsonObject({
|
|
597
|
+
if (!inspectAllowedOutput(visibleResult, descriptor.output_keys)) return jsonObject({
|
|
412
598
|
ok: false,
|
|
413
599
|
error: 'forbidden_output'
|
|
414
600
|
});
|
|
@@ -429,12 +615,11 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
|
|
|
429
615
|
clearTimeout(timer);
|
|
430
616
|
}
|
|
431
617
|
}
|
|
432
|
-
}
|
|
618
|
+
};
|
|
619
|
+
register(definition);
|
|
433
620
|
return Object.freeze({
|
|
434
621
|
toolName: 'gotry_benchmark_environment',
|
|
435
|
-
allowedTools: Object.freeze(
|
|
436
|
-
...bridge.allowed_tools
|
|
437
|
-
]),
|
|
622
|
+
allowedTools: Object.freeze(bridge.tools.map((item)=>item.name)),
|
|
438
623
|
terminal: Object.freeze({
|
|
439
624
|
...bridge.terminal_output
|
|
440
625
|
})
|
|
@@ -384,9 +384,11 @@ function recoverFinalResponseDecision(response, task) {
|
|
|
384
384
|
}).slice(0, 600));
|
|
385
385
|
return null;
|
|
386
386
|
}
|
|
387
|
-
repairPlannerFactRefs(action);
|
|
387
|
+
const repairedRefs = repairPlannerFactRefs(action);
|
|
388
|
+
const repairedValidation = validateBookingReadAction(action);
|
|
389
|
+
if (!repairedValidation.ok) return null;
|
|
388
390
|
try {
|
|
389
|
-
assertPlannerSafeRefs(action);
|
|
391
|
+
assertPlannerSafeRefs(action, repairedRefs);
|
|
390
392
|
} catch (error) {
|
|
391
393
|
console.error('[booking-copilot] finalResponse recovery rejected (unsafe ref):', JSON.stringify({
|
|
392
394
|
actionId: action.actionId,
|
|
@@ -411,10 +413,17 @@ function recoverFinalResponseDecision(response, task) {
|
|
|
411
413
|
const PLANNER_SAFE_REF_PATTERN = /^[A-Za-z0-9][A-Za-z0-9:._-]*$/;
|
|
412
414
|
function repairPlannerFactRefs(action) {
|
|
413
415
|
const factRefs = action.factRefs;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
+
const repairedRefs = new Set();
|
|
417
|
+
if (!Array.isArray(factRefs)) return repairedRefs;
|
|
418
|
+
action.factRefs = factRefs.map((ref)=>{
|
|
419
|
+
if (typeof ref !== 'string' || PLANNER_SAFE_REF_PATTERN.test(ref)) return ref;
|
|
420
|
+
const alias = `modelref:${createHash('sha256').update(ref, 'utf8').digest('hex')}`;
|
|
421
|
+
repairedRefs.add(alias);
|
|
422
|
+
return alias;
|
|
423
|
+
});
|
|
424
|
+
return repairedRefs;
|
|
416
425
|
}
|
|
417
|
-
function assertPlannerSafeRefs(action) {
|
|
426
|
+
function assertPlannerSafeRefs(action, repairedRefs) {
|
|
418
427
|
const actionId = action.actionId;
|
|
419
428
|
if (typeof actionId !== 'string' || !PLANNER_SAFE_REF_PATTERN.test(actionId)) {
|
|
420
429
|
throw new Error(`planner_invalid_action:unsafe_action_id:${String(actionId).slice(0, 60)}`);
|
|
@@ -422,7 +431,7 @@ function assertPlannerSafeRefs(action) {
|
|
|
422
431
|
const factRefs = action.factRefs;
|
|
423
432
|
if (Array.isArray(factRefs)) {
|
|
424
433
|
for (const ref of factRefs){
|
|
425
|
-
if (typeof ref !== 'string' || !PLANNER_SAFE_REF_PATTERN.test(ref)) {
|
|
434
|
+
if (typeof ref !== 'string' || !PLANNER_SAFE_REF_PATTERN.test(ref) || ref.startsWith('modelref:') && !repairedRefs.has(ref) || ref.length > 512) {
|
|
426
435
|
throw new Error(`planner_invalid_action:unsafe_fact_ref:${String(ref).slice(0, 60)}`);
|
|
427
436
|
}
|
|
428
437
|
}
|
|
@@ -483,8 +492,15 @@ function parseToolDecision(event, task) {
|
|
|
483
492
|
}).slice(0, 1200));
|
|
484
493
|
throw new Error(`planner_invalid_action:${validation.errors.join('; ')}`);
|
|
485
494
|
}
|
|
486
|
-
repairPlannerFactRefs(decision.action);
|
|
487
|
-
|
|
495
|
+
const repairedRefs = repairPlannerFactRefs(decision.action);
|
|
496
|
+
const repairedValidation = validateBookingReadAction(decision.action);
|
|
497
|
+
if (!repairedValidation.ok) {
|
|
498
|
+
console.error(`[booking-copilot] repaired action rejected:`, JSON.stringify({
|
|
499
|
+
errors: repairedValidation.errors.slice(0, 8)
|
|
500
|
+
}).slice(0, 800));
|
|
501
|
+
throw new Error(`planner_invalid_action:${repairedValidation.errors.join('; ')}`);
|
|
502
|
+
}
|
|
503
|
+
assertPlannerSafeRefs(decision.action, repairedRefs);
|
|
488
504
|
const action = decision.action;
|
|
489
505
|
const capability = TOOL_TO_CAPABILITY.get(name);
|
|
490
506
|
if (!capability || !actionsForEmbeddedCapability(capability).includes(action.kind)) {
|
package/dist/src/index.js
CHANGED
|
@@ -1599,14 +1599,17 @@ export function apply(ctx, config) {
|
|
|
1599
1599
|
}
|
|
1600
1600
|
const itpA = await interpretEffect({
|
|
1601
1601
|
effect: 'ANYTHING_SEARCH',
|
|
1602
|
-
params:
|
|
1602
|
+
params: {
|
|
1603
|
+
...q,
|
|
1604
|
+
hbcliBin: config.hbcliBin
|
|
1605
|
+
}
|
|
1603
1606
|
});
|
|
1604
1607
|
if (!itpA.result) return declinedObservation('ANYTHING_SEARCH', itpA.trace);
|
|
1605
1608
|
const r = itpA.result;
|
|
1606
1609
|
const dir = await ensureStateDir(config.stateRoot);
|
|
1607
1610
|
await recordLatency(join(dir, 'bridge-latency.jsonl'), Date.now() - started, `anything:${r.via}`).catch(()=>{});
|
|
1608
1611
|
const top5 = (r.hits ?? []).slice(0, 5);
|
|
1609
|
-
const summary = r.verdict === 'hit' ? `${q.keyword} → hit (${r.hits?.length ?? 0} 候选项)\n${top5.map((h, i)=>` ${i + 1}. [${h.type}] ${h.name}${h.latitude !== undefined && h.longitude !== undefined ? ` @ (${h.latitude.toFixed(3)},${h.longitude.toFixed(3)})` : ''}`).join('\n')}\n${r.evidence}` : r.verdict === 'miss' ? `${q.keyword} → miss (酒店-be 一切正常但无候选)\n${r.evidence}` : `${q.keyword} → unavailable (${r.error})\n${r.evidence}`;
|
|
1612
|
+
const summary = r.verdict === 'hit' ? `${q.keyword} → hit (${r.hits?.length ?? 0} 候选项)\n${top5.map((h, i)=>` ${i + 1}. [${h.type}] ${h.name}${h.star ? ` ${h.star}★` : ''}${h.latitude !== undefined && h.longitude !== undefined ? ` @ (${h.latitude.toFixed(3)},${h.longitude.toFixed(3)})` : ''}${h.hotelId ? ` hotelId=${h.hotelId}` : h.destinationId ? ` destinationId=${h.destinationId}` : ''}`).join('\n')}\n${r.evidence}` : r.verdict === 'miss' ? `${q.keyword} → miss (酒店-be 一切正常但无候选)\n${r.evidence}` : `${q.keyword} → unavailable (${r.error})\n${r.evidence}`;
|
|
1610
1613
|
return JSON.parse(JSON.stringify({
|
|
1611
1614
|
ok: r.ok,
|
|
1612
1615
|
verdict: r.verdict,
|
package/extension/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "GoTry Session Bridge",
|
|
4
|
-
"version": "0.0.1.
|
|
4
|
+
"version": "0.0.1.21",
|
|
5
5
|
"description": "GoTry 会话检索桥(issue #21):在你自己的登录态里只读嗅探检索回包(机票/酒店/火车)与登录票据 cookie 名;零凭证经手,登录在官网由你完成,检索只读不写。",
|
|
6
6
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnuzBX8rT8+RZKEUip5VRA0cY056pExy9PNdN9YCiuY3dgglRbcIBhjuFJoR6EX3/MaUv82vJVyItRVrowXLTwDk4WZof0vwFgKL0zBK304PC46pUaAKTSu1PgXeL1+j5KTAGz/9a9+RnPdbj7jND5rM/MOmBHNPVPpp1NlwxRGIgF9OxHSOfXORcH4Iyte1aJSFUJbFtNS/DaE5D+KjyupPLyIqVN/dCiKnNsTCEHQB9ngeli665PFp76hkccHQQZ4sVuMLS4zBPcazUsp1kcnW+xsQoUATkODTCY+Pzk3zUVF1QOESTjRCVnbNi8tkO43RMRs2q63gX/3inFivcEwIDAQAB",
|
|
7
7
|
"permissions": [
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"run_at": "document_start"
|
|
47
47
|
}
|
|
48
48
|
],
|
|
49
|
-
"version_name": "0.0.1-rc.
|
|
49
|
+
"version_name": "0.0.1-rc.21"
|
|
50
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danceiny/gotry",
|
|
3
|
-
"version": "0.0.1-rc.
|
|
3
|
+
"version": "0.0.1-rc.21",
|
|
4
4
|
"description": "GoTry — 从出发到下一次出发的 AI 旅行 Agent(dsh 插件)。npm/源码入口共用锁定的 dsh runtime + README 安装路径。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "ts/src/index.ts",
|
|
@@ -89,6 +89,8 @@
|
|
|
89
89
|
"ts/capabilities/session/",
|
|
90
90
|
"extension/",
|
|
91
91
|
"ts/package.json",
|
|
92
|
+
"ts/dsh-runtime/vendor/dsh-map-tools",
|
|
93
|
+
"ts/scripts/map-tools-vendor-package-proof.ts",
|
|
92
94
|
"cordis.gotry-patch.yml",
|
|
93
95
|
"README.md",
|
|
94
96
|
"README.zh-CN.md",
|