@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.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/bin/gotry-bootstrap.js +83 -37
  4. package/bin/gotry-inner.js +11 -6
  5. package/cordis.gotry-patch.yml +7 -3
  6. package/dist/capabilities/anything.js +62 -29
  7. package/dist/capabilities/doctor.js +25 -3
  8. package/dist/capabilities/hbcli.js +28 -2
  9. package/dist/scripts/agent-planning-turn-deadline-e2e.js +2 -1
  10. package/dist/scripts/anything-tests.js +70 -21
  11. package/dist/scripts/benchmark-environment-bridge-e2e.js +136 -29
  12. package/dist/scripts/benchmark-environment-bridge-tests.js +1056 -269
  13. package/dist/scripts/bootstrap-tests.js +28 -2
  14. package/dist/scripts/doctor-tests.js +55 -1
  15. package/dist/scripts/hbcli-tests.js +38 -1
  16. package/dist/src/benchmark-agent-conformance.js +32 -12
  17. package/dist/src/benchmark-environment-bridge.js +317 -114
  18. package/dist/src/index.js +5 -2
  19. package/extension/manifest.json +2 -2
  20. package/package.json +2 -1
  21. package/ts/capabilities/anything.ts +89 -38
  22. package/ts/capabilities/hbcli.ts +53 -4
  23. package/ts/dsh-runtime/vendor/README.md +60 -0
  24. package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
  25. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +200 -0
  26. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +202 -0
  27. package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +216 -0
  28. package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
  29. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +371 -0
  30. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
  31. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +56 -0
  32. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
  33. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
  34. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +126 -0
  35. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
  36. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +109 -0
  37. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +27 -0
  38. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
  39. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
  40. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +170 -0
  41. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +53 -0
  42. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
  43. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
  44. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
  45. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
  46. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +20 -0
  47. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
  48. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
  49. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
  50. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
  51. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
  52. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +16 -0
  53. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +55 -0
  54. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
  55. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +91 -0
  56. package/ts/package.json +9 -1
  57. package/ts/src/benchmark-agent-conformance.ts +40 -9
  58. package/ts/src/benchmark-environment-bridge.ts +235 -66
  59. package/ts/src/index.ts +4 -2
  60. 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 { defineTool } from '@deepseek-ai/dsh-tools';
4
+ import { assertSupportedJsonSchema, validateJsonSchemaValue, ToolArgsError } from '@deepseek-ai/dsh-tools';
5
5
  import { validateTerminalOutputConfig } from './benchmark-agent-conformance.js';
6
- const SCHEMA_VERSION = 'gotry_benchmark_environment_bridge_v2';
6
+ const SCHEMA_VERSION = 'gotry_benchmark_environment_bridge_v3';
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,87 @@ 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
+ ]);
43
+ function descriptorSchema(tools) {
44
+ return {
45
+ oneOf: [
46
+ {
47
+ type: 'object',
48
+ description: 'List the frozen benchmark tool descriptors.',
49
+ properties: {
50
+ action: {
51
+ type: 'string',
52
+ const: 'tools'
53
+ }
54
+ },
55
+ required: [
56
+ 'action'
57
+ ],
58
+ additionalProperties: false
59
+ },
60
+ {
61
+ type: 'object',
62
+ description: 'List the frozen bridge error contract.',
63
+ properties: {
64
+ action: {
65
+ type: 'string',
66
+ const: 'errors'
67
+ }
68
+ },
69
+ required: [
70
+ 'action'
71
+ ],
72
+ additionalProperties: false
73
+ },
74
+ ...tools.map((tool)=>({
75
+ type: 'object',
76
+ description: tool.description,
77
+ properties: {
78
+ action: {
79
+ type: 'string',
80
+ const: 'call'
81
+ },
82
+ tool: {
83
+ type: 'string',
84
+ const: tool.name
85
+ },
86
+ arguments: tool.input_schema
87
+ },
88
+ required: [
89
+ 'action',
90
+ 'tool',
91
+ 'arguments'
92
+ ],
93
+ additionalProperties: false
94
+ }))
95
+ ]
96
+ };
97
+ }
23
98
  function plainObject(value) {
24
99
  return typeof value === 'object' && value !== null && !Array.isArray(value);
25
100
  }
101
+ function deepFreezeJson(value) {
102
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
103
+ for (const child of Object.values(value))deepFreezeJson(child);
104
+ return Object.freeze(value);
105
+ }
26
106
  function existingExecutable(value) {
27
107
  if (typeof value !== 'string' || !isAbsolute(value)) return false;
28
108
  try {
@@ -41,13 +121,121 @@ function existingDirectory(value) {
41
121
  return false;
42
122
  }
43
123
  }
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));
124
+ function identifiers(value, allowEmpty = false) {
125
+ 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));
126
+ }
127
+ function exactKeys(value, allowed) {
128
+ return Object.keys(value).every((key)=>allowed.includes(key));
129
+ }
130
+ function validDescription(value, required = false) {
131
+ if (value === undefined) return !required;
132
+ return typeof value === 'string' && (!required || value.trim().length > 0) && value.length <= INPUT_SCHEMA_MAX_DESCRIPTION_LENGTH && !/[\u0000-\u001f\u007f]/.test(value);
46
133
  }
47
- function allowedOutputKeys(value, allowedTools) {
134
+ function scalarMatches(type, value) {
135
+ if (type === 'string') return typeof value === 'string' && value.length <= 512;
136
+ if (type === 'number') return typeof value === 'number' && Number.isFinite(value);
137
+ if (type === 'integer') return typeof value === 'number' && Number.isSafeInteger(value);
138
+ if (type === 'boolean') return typeof value === 'boolean';
139
+ return type === 'null' && value === null;
140
+ }
141
+ function validInputSchema(value) {
48
142
  if (!plainObject(value)) return false;
49
- const keys = Object.keys(value);
50
- return keys.length > 0 && keys.length <= 64 && keys.every((tool)=>allowedTools.includes(tool) && identifiers(value[tool]));
143
+ let serialized;
144
+ try {
145
+ serialized = JSON.stringify(value);
146
+ } catch {
147
+ return false;
148
+ }
149
+ if (Buffer.byteLength(serialized, 'utf8') > INPUT_SCHEMA_MAX_BYTES) return false;
150
+ const pending = [
151
+ {
152
+ node: value,
153
+ depth: 0
154
+ }
155
+ ];
156
+ const seen = new Set();
157
+ let nodes = 0;
158
+ let properties = 0;
159
+ let enumValues = 0;
160
+ while(pending.length > 0){
161
+ const { node, depth } = pending.pop();
162
+ if (seen.has(node) || ++nodes > INPUT_SCHEMA_MAX_NODES || depth > INPUT_SCHEMA_MAX_DEPTH) return false;
163
+ seen.add(node);
164
+ if (!INPUT_SCHEMA_TYPES.has(String(node.type)) || !validDescription(node.description)) return false;
165
+ if (node.type === 'object') {
166
+ if (!exactKeys(node, [
167
+ 'type',
168
+ 'description',
169
+ 'properties',
170
+ 'required',
171
+ 'additionalProperties'
172
+ ]) || !plainObject(node.properties) || !Array.isArray(node.required) || node.additionalProperties !== false) return false;
173
+ const propertyEntries = Object.entries(node.properties);
174
+ properties += propertyEntries.length;
175
+ if (properties > INPUT_SCHEMA_MAX_PROPERTIES || propertyEntries.some(([key, child])=>!IDENTIFIER.test(key) || !plainObject(child))) return false;
176
+ const required = node.required;
177
+ if (required.length > propertyEntries.length || new Set(required).size !== required.length || required.some((key)=>typeof key !== 'string' || !Object.hasOwn(node.properties, key))) return false;
178
+ for (const [, child] of propertyEntries)pending.push({
179
+ node: child,
180
+ depth: depth + 1
181
+ });
182
+ continue;
183
+ }
184
+ if (node.type === 'array') {
185
+ if (!exactKeys(node, [
186
+ 'type',
187
+ 'description',
188
+ 'items'
189
+ ]) || !plainObject(node.items)) return false;
190
+ pending.push({
191
+ node: node.items,
192
+ depth: depth + 1
193
+ });
194
+ continue;
195
+ }
196
+ if (!exactKeys(node, [
197
+ 'type',
198
+ 'description',
199
+ 'enum',
200
+ 'const'
201
+ ]) || Object.hasOwn(node, 'enum') && Object.hasOwn(node, 'const')) return false;
202
+ if (Object.hasOwn(node, 'const') && !scalarMatches(String(node.type), node.const)) return false;
203
+ if (Object.hasOwn(node, 'enum')) {
204
+ if (!Array.isArray(node.enum) || node.enum.length === 0 || node.enum.length > 64) return false;
205
+ enumValues += node.enum.length;
206
+ 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;
207
+ }
208
+ }
209
+ return value.type === 'object';
210
+ }
211
+ function toolDescriptors(value) {
212
+ if (!Array.isArray(value) || value.length === 0 || value.length > 64) return false;
213
+ const names = new Set();
214
+ return value.every((item)=>{
215
+ if (!plainObject(item)) return false;
216
+ const keys = Object.keys(item).sort();
217
+ const required = [
218
+ 'description',
219
+ 'domain_outcomes',
220
+ 'input_schema',
221
+ 'name'
222
+ ];
223
+ if (JSON.stringify(keys) !== JSON.stringify([
224
+ ...required,
225
+ 'output_keys'
226
+ ].sort())) return false;
227
+ if (typeof item.name !== 'string' || !IDENTIFIER.test(item.name) || names.has(item.name)) return false;
228
+ names.add(item.name);
229
+ if (!validDescription(item.description, true) || !validInputSchema(item.input_schema)) return false;
230
+ if (!Array.isArray(item.domain_outcomes) || item.domain_outcomes.length > 64) return false;
231
+ const outcomeKeys = new Set();
232
+ if (!item.domain_outcomes.every((outcome)=>plainObject(outcome) && JSON.stringify(Object.keys(outcome).sort()) === JSON.stringify([
233
+ 'code',
234
+ 'recovery',
235
+ 'status'
236
+ ]) && (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;
237
+ return identifiers(item.output_keys);
238
+ });
51
239
  }
52
240
  function argvPrefix(value) {
53
241
  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 +257,7 @@ function validate(value) {
69
257
  const baseKeys = [
70
258
  ...CONFIG_KEYS
71
259
  ].sort();
72
- const extendedKeys = [
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;
260
+ 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
261
  return true;
81
262
  }
82
263
  export function loadBenchmarkEnvironmentConfig(path) {
@@ -87,7 +268,7 @@ export function loadBenchmarkEnvironmentConfig(path) {
87
268
  if (typeof getuid !== 'function' || metadata.uid !== getuid()) return null;
88
269
  if ((metadata.mode & 0o022) !== 0) return null;
89
270
  const parsed = JSON.parse(readFileSync(path, 'utf8'));
90
- return validate(parsed) ? parsed : null;
271
+ return validate(parsed) ? deepFreezeJson(parsed) : null;
91
272
  } catch {
92
273
  return null;
93
274
  }
@@ -116,6 +297,48 @@ const FORBIDDEN_OUTPUT_KEY_FRAGMENTS = [
116
297
  'reward',
117
298
  'score'
118
299
  ];
300
+ export const BRIDGE_ERROR_CONTRACT = Object.freeze({
301
+ invalid_action: {
302
+ recoverable: true,
303
+ remedy: 'action 只允许 tools、call 或 errors'
304
+ },
305
+ disallowed_tool: {
306
+ recoverable: true,
307
+ remedy: '先 action=tools 列出 allowed_tools 清单,再从清单内选工具'
308
+ },
309
+ invalid_arguments: {
310
+ recoverable: true,
311
+ remedy: 'arguments 必须是可序列化 JSON 对象(≤64KB、深度≤12);收窄后重试'
312
+ },
313
+ timed_out: {
314
+ recoverable: true,
315
+ remedy: '上游超时;可原样重试一次或换其他工具'
316
+ },
317
+ output_truncated: {
318
+ recoverable: true,
319
+ remedy: '输出超出上限;让被调工具收窄查询范围后重试'
320
+ },
321
+ invalid_json: {
322
+ recoverable: true,
323
+ remedy: '上游输出不是合法 JSON;重试一次或换工具'
324
+ },
325
+ invalid_output: {
326
+ recoverable: true,
327
+ remedy: '上游输出结构不符;重试或换工具'
328
+ },
329
+ runner_failed: {
330
+ recoverable: true,
331
+ remedy: '上游进程非零退出;可重试一次,持续失败换工具'
332
+ },
333
+ spawn_failed: {
334
+ recoverable: false,
335
+ remedy: '可执行文件不可用——环境问题,调用方不可恢复'
336
+ },
337
+ forbidden_output: {
338
+ recoverable: false,
339
+ remedy: '输出含未授权键(策略边界)——不可恢复,换工具或放弃'
340
+ }
341
+ });
119
342
  function inspectOutput(value) {
120
343
  const pending = [
121
344
  {
@@ -152,7 +375,8 @@ function inspectAllowedOutput(value, allowedKeys) {
152
375
  const pending = [
153
376
  {
154
377
  value,
155
- depth: 0
378
+ depth: 0,
379
+ coveredByDeclaredKey: false
156
380
  }
157
381
  ];
158
382
  let nodes = 0;
@@ -162,16 +386,21 @@ function inspectAllowedOutput(value, allowedKeys) {
162
386
  if (Array.isArray(current.value)) {
163
387
  for (const child of current.value)pending.push({
164
388
  value: child,
165
- depth: current.depth + 1
389
+ depth: current.depth + 1,
390
+ coveredByDeclaredKey: current.coveredByDeclaredKey
166
391
  });
167
392
  continue;
168
393
  }
169
- if (!plainObject(current.value)) continue;
394
+ if (!plainObject(current.value)) {
395
+ if (!current.coveredByDeclaredKey) return false;
396
+ continue;
397
+ }
170
398
  for (const [key, child] of Object.entries(current.value)){
171
399
  if (!allowed.has(key)) return false;
172
400
  pending.push({
173
401
  value: child,
174
- depth: current.depth + 1
402
+ depth: current.depth + 1,
403
+ coveredByDeclaredKey: true
175
404
  });
176
405
  }
177
406
  }
@@ -211,80 +440,20 @@ function serializedArguments(value) {
211
440
  };
212
441
  }
213
442
  }
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
443
  export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
257
444
  const bridge = loadBenchmarkEnvironmentConfig(path);
258
445
  if (!bridge) throw new Error('benchmark environment bridge configuration unavailable');
259
446
  if (!subprocess) throw new Error('benchmark environment bridge subprocess unavailable');
260
- register(defineTool({
447
+ const parameters = deepFreezeJson({
448
+ oneOf: descriptorSchema(bridge.tools).oneOf
449
+ });
450
+ assertSupportedJsonSchema(parameters);
451
+ const definition = {
261
452
  name: 'gotry_benchmark_environment',
262
- description: 'When a prompt asks to run agent_env.cli, use action=call here with the mapped tool; arbitrary shell is not exposed. ' + 'Recoverable domain-error contract: every failure returns { ok:false, error:<code>, ... } from a closed vocabulary; ' + 'call action=errors to fetch the per-code recoverable flag and remedy before retrying.',
263
- parameters: {
264
- action: {
265
- type: 'string',
266
- enum: [
267
- 'tools',
268
- 'call',
269
- 'errors'
270
- ],
271
- required: true,
272
- description: 'tools=列出可调工具;call=执行被映射工具;errors=拉取可恢复错误契约表'
273
- },
274
- tool: {
275
- type: 'string',
276
- description: '被调工具名(必须在 allowed_tools 清单内;action=call 时必填)'
277
- },
278
- arguments: {
279
- type: 'object',
280
- additionalProperties: true,
281
- description: '传给被调工具的参数对象(action=call 时可选;≤64KB、深度≤12)'
282
- }
283
- },
453
+ description: 'When a prompt asks to run agent_env.cli, use action=call here with the mapped tool; arbitrary shell is not exposed.',
454
+ parameters,
284
455
  output: {
285
- schema: {
286
- type: 'json'
287
- },
456
+ schema: {},
288
457
  render: (_args, value)=>[
289
458
  {
290
459
  type: 'text',
@@ -293,33 +462,45 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
293
462
  ]
294
463
  },
295
464
  async execute (args) {
296
- const action = args?.action;
297
- if (action === 'errors') return jsonObject({
465
+ const violations = validateJsonSchemaValue(parameters, args, 'arguments');
466
+ if (violations.length > 0) throw new ToolArgsError(violations);
467
+ const query = args;
468
+ const tool = query.tool;
469
+ const descriptor = bridge.tools.find((item)=>item.name === tool);
470
+ if (query.action === 'tools') return jsonObject({
298
471
  ok: true,
299
- errors: BRIDGE_ERROR_CONTRACT
472
+ tools: bridge.tools
300
473
  });
301
- if (action === 'tools') return jsonObject({
474
+ if (query.action === 'errors') return jsonObject({
302
475
  ok: true,
303
- tools: bridge.allowed_tools
304
- });
305
- if (action !== 'call') return jsonObject({
306
- ok: false,
307
- error: 'invalid_action'
476
+ errors: BRIDGE_ERROR_CONTRACT
308
477
  });
309
- const query = args;
310
- if (typeof query.tool !== 'string' || !bridge.allowed_tools.includes(query.tool)) {
478
+ if (query.action !== 'call') {
479
+ return jsonObject({
480
+ ok: false,
481
+ error: 'invalid_action'
482
+ });
483
+ }
484
+ if (typeof tool !== 'string' || !descriptor) {
311
485
  return jsonObject({
312
486
  ok: false,
313
487
  error: 'disallowed_tool'
314
488
  });
315
489
  }
316
- if (query.arguments !== undefined && !plainObject(query.arguments)) {
490
+ if (!plainObject(query.arguments)) {
491
+ return jsonObject({
492
+ ok: false,
493
+ error: 'invalid_arguments'
494
+ });
495
+ }
496
+ const callArguments = query.arguments;
497
+ const argumentViolations = validateJsonSchemaValue(descriptor.input_schema, callArguments, 'arguments');
498
+ if (argumentViolations.length > 0) {
317
499
  return jsonObject({
318
500
  ok: false,
319
501
  error: 'invalid_arguments'
320
502
  });
321
503
  }
322
- const callArguments = query.arguments === undefined ? {} : query.arguments;
323
504
  const serialized = serializedArguments(callArguments);
324
505
  if (serialized.reason) return jsonObject({
325
506
  ok: false,
@@ -352,7 +533,7 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
352
533
  bridge.executable,
353
534
  ...bridge.argv_prefix,
354
535
  'call',
355
- query.tool,
536
+ tool,
356
537
  serialized.json
357
538
  ],
358
539
  cwd: bridge.cwd,
@@ -402,13 +583,36 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
402
583
  ok: false,
403
584
  error: 'invalid_output'
404
585
  });
405
- const visibleResult = parsed.result ?? parsed;
586
+ if (parsed.schema_version !== BENCHMARK_TOOL_RESULT_SCHEMA_VERSION || typeof parsed.status !== 'string') {
587
+ return jsonObject({
588
+ ok: false,
589
+ error: 'invalid_output'
590
+ });
591
+ }
592
+ if (parsed.status === 'miss' || parsed.status === 'error') {
593
+ 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({
594
+ ok: false,
595
+ error: 'invalid_output'
596
+ });
597
+ if (Object.keys(parsed).sort().join(',') !== 'code,recovery,schema_version,status') return jsonObject({
598
+ ok: false,
599
+ error: 'invalid_output'
600
+ });
601
+ return jsonObject({
602
+ ok: true,
603
+ outcome: parsed
604
+ });
605
+ }
606
+ if (parsed.status !== 'ok' || !Object.hasOwn(parsed, 'result') || Object.keys(parsed).sort().join(',') !== 'result,schema_version,status') return jsonObject({
607
+ ok: false,
608
+ error: 'invalid_output'
609
+ });
610
+ const visibleResult = parsed.result;
406
611
  if (!plainObject(visibleResult) && !Array.isArray(visibleResult)) return jsonObject({
407
612
  ok: false,
408
613
  error: 'invalid_output'
409
614
  });
410
- const outputKeys = bridge.allowed_output_keys && Object.hasOwn(bridge.allowed_output_keys, query.tool) ? bridge.allowed_output_keys[query.tool] : undefined;
411
- if (outputKeys && !inspectAllowedOutput(visibleResult, outputKeys)) return jsonObject({
615
+ if (!inspectAllowedOutput(visibleResult, descriptor.output_keys)) return jsonObject({
412
616
  ok: false,
413
617
  error: 'forbidden_output'
414
618
  });
@@ -429,12 +633,11 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
429
633
  clearTimeout(timer);
430
634
  }
431
635
  }
432
- }));
636
+ };
637
+ register(definition);
433
638
  return Object.freeze({
434
639
  toolName: 'gotry_benchmark_environment',
435
- allowedTools: Object.freeze([
436
- ...bridge.allowed_tools
437
- ]),
640
+ allowedTools: Object.freeze(bridge.tools.map((item)=>item.name)),
438
641
  terminal: Object.freeze({
439
642
  ...bridge.terminal_output
440
643
  })
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: q
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,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "GoTry Session Bridge",
4
- "version": "0.0.1.19",
4
+ "version": "0.0.1.20",
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.19"
49
+ "version_name": "0.0.1-rc.20"
50
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danceiny/gotry",
3
- "version": "0.0.1-rc.19",
3
+ "version": "0.0.1-rc.20",
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,7 @@
89
89
  "ts/capabilities/session/",
90
90
  "extension/",
91
91
  "ts/package.json",
92
+ "ts/dsh-runtime/vendor/dsh-map-tools",
92
93
  "cordis.gotry-patch.yml",
93
94
  "README.md",
94
95
  "README.zh-CN.md",