@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
|
@@ -12,6 +12,22 @@ const BIN = join(ROOT, 'bin', 'gotry-inner.js');
|
|
|
12
12
|
const TOOL = 'gotry_benchmark_environment';
|
|
13
13
|
const MARKER = 'BENCHMARK_BRIDGE_LOOKUP_OK';
|
|
14
14
|
const TIMEOUT_MS = 30_000;
|
|
15
|
+
const LOOKUP_INPUT_SCHEMA = {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
city: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
enum: [
|
|
21
|
+
'Dubai',
|
|
22
|
+
'Singapore'
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
required: [
|
|
27
|
+
'city'
|
|
28
|
+
],
|
|
29
|
+
additionalProperties: false
|
|
30
|
+
};
|
|
15
31
|
const TSX_LOADER = pathToFileURL(createRequire(import.meta.url).resolve('tsx')).href;
|
|
16
32
|
function runRuntimeProbe(options) {
|
|
17
33
|
const fixture = mkdtempSync(join(tmpdir(), 'gotry-runtime-probe-'));
|
|
@@ -61,7 +77,7 @@ function assertRuntimeSelectionAndVersionGuards() {
|
|
|
61
77
|
const legacyFallback = runRuntimeProbe({
|
|
62
78
|
vendorVersion: '0.1.2-alpha.1'
|
|
63
79
|
});
|
|
64
|
-
assert.
|
|
80
|
+
assert.deepEqual(legacyFallback, null, 'non-benchmark source checkout fail-closes instead of using the removed legacy vendored dsh fallback');
|
|
65
81
|
const wrongBenchmarkVersion = runRuntimeProbe({
|
|
66
82
|
rootVersion: '0.1.2-alpha.1',
|
|
67
83
|
vendorVersion: '0.1.2-alpha.1',
|
|
@@ -183,7 +199,7 @@ function finalText(text) {
|
|
|
183
199
|
]
|
|
184
200
|
}) + 'data: [DONE]\n\n';
|
|
185
201
|
}
|
|
186
|
-
function toolCall(callId = 'bridge-call-1') {
|
|
202
|
+
function toolCall(callId = 'bridge-call-1', city = 'Dubai') {
|
|
187
203
|
return sse({
|
|
188
204
|
id: `bridge-${callId}`,
|
|
189
205
|
object: 'chat.completion.chunk',
|
|
@@ -202,7 +218,7 @@ function toolCall(callId = 'bridge-call-1') {
|
|
|
202
218
|
action: 'call',
|
|
203
219
|
tool: 'lookup',
|
|
204
220
|
arguments: {
|
|
205
|
-
city
|
|
221
|
+
city
|
|
206
222
|
}
|
|
207
223
|
})
|
|
208
224
|
}
|
|
@@ -235,9 +251,25 @@ function toolResultPresent(body) {
|
|
|
235
251
|
function anyToolResultPresent(body) {
|
|
236
252
|
return (body.messages ?? []).some((m)=>m.role === 'tool');
|
|
237
253
|
}
|
|
254
|
+
const BRIDGE_E2E_BODY_SCHEMA = {
|
|
255
|
+
type: 'object',
|
|
256
|
+
properties: {
|
|
257
|
+
status: {
|
|
258
|
+
type: 'string'
|
|
259
|
+
},
|
|
260
|
+
payload: {
|
|
261
|
+
type: 'string'
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
required: [],
|
|
265
|
+
additionalProperties: false
|
|
266
|
+
};
|
|
267
|
+
const TERMINAL_OUTLINE = 'object{?status:string,?payload:string}';
|
|
238
268
|
async function runCase(mode, executableOverride, extraEnv = {}) {
|
|
239
269
|
const requests = [];
|
|
270
|
+
let servedToolCalls = 0;
|
|
240
271
|
let spawnTarget = '';
|
|
272
|
+
const domainRecoveryMode = mode === 'domain-recovery' || mode === 'domain-recovery-failed';
|
|
241
273
|
const server = createServer((req, res)=>{
|
|
242
274
|
const chunks = [];
|
|
243
275
|
req.on('data', (c)=>chunks.push(Buffer.from(c)));
|
|
@@ -253,8 +285,15 @@ async function runCase(mode, executableOverride, extraEnv = {}) {
|
|
|
253
285
|
if (mode === 'spawn-failed' && names(body).includes(TOOL) && !anyToolResultPresent(body) && spawnTarget) rmSync(spawnTarget, {
|
|
254
286
|
force: true
|
|
255
287
|
});
|
|
256
|
-
if (
|
|
257
|
-
|
|
288
|
+
if (domainRecoveryMode && servedToolCalls === 1 && names(body).includes(TOOL) && anyToolResultPresent(body) && !toolResultPresent(body)) {
|
|
289
|
+
servedToolCalls += 1;
|
|
290
|
+
res.end(toolCall('bridge-call-2', 'Singapore'));
|
|
291
|
+
} else if (mode !== 'disabled' && mode !== 'invalid-path' && mode !== 'invalid-schema' && mode !== 'unsafe-config' && names(body).includes(TOOL) && !anyToolResultPresent(body)) {
|
|
292
|
+
servedToolCalls += 1;
|
|
293
|
+
res.end(toolCall());
|
|
294
|
+
} else {
|
|
295
|
+
res.end(finalText('<benchmark_terminal>{"status":"succeeded"}</benchmark_terminal>'));
|
|
296
|
+
}
|
|
258
297
|
});
|
|
259
298
|
});
|
|
260
299
|
await new Promise((resolve)=>server.listen(0, '127.0.0.1', resolve));
|
|
@@ -269,15 +308,16 @@ async function runCase(mode, executableOverride, extraEnv = {}) {
|
|
|
269
308
|
});
|
|
270
309
|
writeResolutionProbe(probe, probeResult, mode === 'disabled');
|
|
271
310
|
const runner = join(cwd, 'synthetic-runner.js');
|
|
311
|
+
const runnerTrace = join(cwd, 'synthetic-runner-trace.jsonl');
|
|
272
312
|
spawnTarget = join(cwd, 'synthetic-spawn-target.js');
|
|
273
313
|
const configPath = join(cwd, mode === 'invalid-path' ? 'benchmark-env-config-\n.json' : 'benchmark-env-config.json');
|
|
274
|
-
const runnerBody = mode === 'timeout' ? `setTimeout(() => {}, 60_000)` : mode === 'runner-failed' ? `process.stderr.write('PRIVATE_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'); process.exit(17)` : mode === 'output-truncated' ? `process.stdout.write('x'.repeat(20_000))` : mode === 'unexpected-output' ? `process.stdout.write(JSON.stringify({ result: { marker: '${MARKER}', leaked: [], unexpected: 'must-not-reflect' } }))` : `const forbidden = ['GOTRY_BENCHMARK_ENV_CONFIG', 'GOTRY_BENCHMARK_BRIDGE_PARENT_SECRET', 'LLM_API_KEY', 'LLM_BASE_URL', 'LLM_MODEL', 'DEEPSEEK_BASE_URL', 'GOTRY_LLM_MODEL', 'DATABASE_URL', 'SSH_AUTH_SOCK', 'AWS_PROFILE', 'HTTPS_PROXY']; const leaked = forbidden.filter(name => process.env[name] !== undefined); process.stdout.write(JSON.stringify({ result: { marker: '${MARKER}', leaked } }))`;
|
|
275
|
-
writeFileSync(runner, `if (process.argv.length !== 5 || process.argv[2] !== 'call' || process.argv[3] !== 'lookup' || JSON.parse(process.argv[4]).city !== 'Dubai') process.exit(2); ${runnerBody}`);
|
|
314
|
+
const runnerBody = mode === 'domain-recovery-failed' ? `if (args.city === 'Dubai') process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'miss', code: 'NOT_FOUND', recovery: 'revise_arguments' })); else { process.stderr.write('PRIVATE_RECOVERY_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'); process.exit(17) }` : mode === 'domain-recovery' ? `if (args.city === 'Dubai') process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'miss', code: 'NOT_FOUND', recovery: 'revise_arguments' })); else process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}', leaked: [] } }))` : mode === 'timeout' ? `setTimeout(() => {}, 60_000)` : mode === 'runner-failed' ? `process.stderr.write('PRIVATE_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'); process.exit(17)` : mode === 'output-truncated' ? `process.stdout.write('x'.repeat(20_000))` : mode === 'unexpected-output' ? `process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}', leaked: [], unexpected: 'must-not-reflect' } }))` : `const forbidden = ['GOTRY_BENCHMARK_ENV_CONFIG', 'GOTRY_BENCHMARK_BRIDGE_PARENT_SECRET', 'LLM_API_KEY', 'LLM_BASE_URL', 'LLM_MODEL', 'DEEPSEEK_BASE_URL', 'GOTRY_LLM_MODEL', 'DATABASE_URL', 'SSH_AUTH_SOCK', 'AWS_PROFILE', 'HTTPS_PROXY']; const leaked = forbidden.filter(name => process.env[name] !== undefined); process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}', leaked } }))`;
|
|
315
|
+
writeFileSync(runner, `if (process.argv.length !== 5 || process.argv[2] !== 'call' || process.argv[3] !== 'lookup' || (!${JSON.stringify(domainRecoveryMode)} && JSON.parse(process.argv[4]).city !== 'Dubai') || (${JSON.stringify(domainRecoveryMode)} && !['Dubai', 'Singapore'].includes(JSON.parse(process.argv[4]).city))) process.exit(2); const fs = require('node:fs'); const args = JSON.parse(process.argv[4]); fs.appendFileSync(${JSON.stringify(runnerTrace)}, JSON.stringify({ city: args.city }) + '\\n'); ${runnerBody}`);
|
|
276
316
|
writeFileSync(spawnTarget, '#!/usr/bin/env node\nprocess.exit(0)\n', {
|
|
277
317
|
mode: 0o700
|
|
278
318
|
});
|
|
279
319
|
writeFileSync(configPath, JSON.stringify({
|
|
280
|
-
schema_version: mode === 'invalid-schema' ? 'invalid' : '
|
|
320
|
+
schema_version: mode === 'invalid-schema' ? 'invalid' : 'gotry_benchmark_environment_bridge_v4',
|
|
281
321
|
enabled: true,
|
|
282
322
|
executable: mode === 'spawn-failed' ? spawnTarget : process.execPath,
|
|
283
323
|
cwd,
|
|
@@ -286,20 +326,30 @@ async function runCase(mode, executableOverride, extraEnv = {}) {
|
|
|
286
326
|
] : [
|
|
287
327
|
runner
|
|
288
328
|
],
|
|
289
|
-
|
|
290
|
-
|
|
329
|
+
tools: [
|
|
330
|
+
{
|
|
331
|
+
name: 'lookup',
|
|
332
|
+
description: 'Lookup.',
|
|
333
|
+
input_schema: LOOKUP_INPUT_SCHEMA,
|
|
334
|
+
output_keys: [
|
|
335
|
+
'marker',
|
|
336
|
+
'leaked'
|
|
337
|
+
],
|
|
338
|
+
domain_outcomes: [
|
|
339
|
+
{
|
|
340
|
+
status: 'miss',
|
|
341
|
+
code: 'NOT_FOUND',
|
|
342
|
+
recovery: domainRecoveryMode ? 'revise_arguments' : 'none'
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
}
|
|
291
346
|
],
|
|
292
|
-
allowed_output_keys: {
|
|
293
|
-
lookup: [
|
|
294
|
-
'marker',
|
|
295
|
-
'leaked'
|
|
296
|
-
]
|
|
297
|
-
},
|
|
298
347
|
timeout_ms: mode === 'timeout' ? 50 : 10_000,
|
|
299
348
|
max_output_bytes: mode === 'output-truncated' ? 1_024 : 4_096,
|
|
300
349
|
terminal_output: {
|
|
301
350
|
tag: 'benchmark_terminal',
|
|
302
|
-
max_bytes: 4_096
|
|
351
|
+
max_bytes: 4_096,
|
|
352
|
+
body_schema: BRIDGE_E2E_BODY_SCHEMA
|
|
303
353
|
},
|
|
304
354
|
isolation: {
|
|
305
355
|
mode: 'host-enforced',
|
|
@@ -398,13 +448,16 @@ async function runCase(mode, executableOverride, extraEnv = {}) {
|
|
|
398
448
|
calendar: 0,
|
|
399
449
|
map: 0
|
|
400
450
|
});
|
|
451
|
+
const runnerArguments = existsSync(runnerTrace) ? readFileSync(runnerTrace, 'utf8').split('\n').filter(Boolean).map((line)=>JSON.parse(line)) : [];
|
|
401
452
|
return {
|
|
402
453
|
exit,
|
|
403
454
|
stdout,
|
|
404
455
|
stderr,
|
|
405
456
|
output: stdout + stderr,
|
|
406
457
|
requests,
|
|
407
|
-
optionalResolutionHits
|
|
458
|
+
optionalResolutionHits,
|
|
459
|
+
servedToolCalls,
|
|
460
|
+
runnerArguments
|
|
408
461
|
};
|
|
409
462
|
} finally{
|
|
410
463
|
await new Promise((resolve)=>server.close(()=>resolve()));
|
|
@@ -437,6 +490,31 @@ async function assertRuntimeContract(executableOverride) {
|
|
|
437
490
|
assert.deepEqual(enabledToolNames, [
|
|
438
491
|
TOOL
|
|
439
492
|
], `${target} enabled runtime must expose exactly the benchmark tool; observed tool names=${JSON.stringify(enabledToolNames)}`);
|
|
493
|
+
const bridgeTool = enabled.requests.find((request)=>names(request).includes(TOOL))?.tools?.find((tool)=>names({
|
|
494
|
+
tools: [
|
|
495
|
+
tool
|
|
496
|
+
]
|
|
497
|
+
}).includes(TOOL));
|
|
498
|
+
const flatSchema = bridgeTool?.function?.parameters;
|
|
499
|
+
assert.equal(flatSchema?.type, 'object', `${target} bridge exposes an object-root wire schema`);
|
|
500
|
+
assert.equal(flatSchema?.oneOf, undefined, `${target} bridge wire has no top-level oneOf`);
|
|
501
|
+
assert.deepEqual(flatSchema?.required, [
|
|
502
|
+
'action'
|
|
503
|
+
]);
|
|
504
|
+
assert.equal(flatSchema?.additionalProperties, false);
|
|
505
|
+
assert.deepEqual(flatSchema?.properties?.action?.enum, [
|
|
506
|
+
'tools',
|
|
507
|
+
'call',
|
|
508
|
+
'errors'
|
|
509
|
+
]);
|
|
510
|
+
assert.deepEqual(flatSchema?.properties?.tool?.enum, [
|
|
511
|
+
'lookup'
|
|
512
|
+
]);
|
|
513
|
+
assert.deepEqual(flatSchema?.properties?.arguments, {
|
|
514
|
+
type: 'object',
|
|
515
|
+
additionalProperties: true,
|
|
516
|
+
description: 'action=call 时传给工具的参数对象'
|
|
517
|
+
});
|
|
440
518
|
assert.equal(enabledToolNames.some((name)=>name.startsWith('calendar_') || name.startsWith('map_')), false, `${target} benchmark projection must not expose calendar/map tools`);
|
|
441
519
|
assert.deepEqual(enabled.optionalResolutionHits, {
|
|
442
520
|
calendar: 0,
|
|
@@ -460,6 +538,37 @@ async function assertRuntimeContract(executableOverride) {
|
|
|
460
538
|
return (prompt.match(/You are GoTry, a task-agnostic travel planning assistant\./g) ?? []).length === 1 && (prompt.match(/Use only the current conversation and tools available in this benchmark session\./g) ?? []).length === 1;
|
|
461
539
|
}), `${target} benchmark persona has each stable sentence exactly once per request`);
|
|
462
540
|
assert.match(enabled.output, /benchmark_terminal/);
|
|
541
|
+
const recovered = await runCase('domain-recovery', executableOverride);
|
|
542
|
+
assert.equal(recovered.exit, 0, `${target} model-driven domain miss recovery exits successfully; output=${recovered.output.slice(-2_000)}`);
|
|
543
|
+
assert.equal(recovered.servedToolCalls, 2, `${target} model emits exactly two tool calls around one declared miss`);
|
|
544
|
+
assert.deepEqual(recovered.runnerArguments, [
|
|
545
|
+
{
|
|
546
|
+
city: 'Dubai'
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
city: 'Singapore'
|
|
550
|
+
}
|
|
551
|
+
], `${target} model revises the declared city before the second adapter invocation`);
|
|
552
|
+
assert.ok(recovered.requests.some((request)=>(request.messages ?? []).some((message)=>{
|
|
553
|
+
if (message.role !== 'tool') return false;
|
|
554
|
+
const serialized = JSON.stringify(message);
|
|
555
|
+
return /status\\?":\\?"miss/.test(serialized) && /recovery\\?":\\?"revise_arguments/.test(serialized);
|
|
556
|
+
})), `${target} declared typed miss reaches model history`);
|
|
557
|
+
assert.match(recovered.stdout, /<benchmark_terminal>/, `${target} corrected second call reaches tagged terminal output`);
|
|
558
|
+
const failedRecovery = await runCase('domain-recovery-failed', executableOverride);
|
|
559
|
+
assert.equal(failedRecovery.exit, 1, `${target} infrastructure failure after a declared miss cannot be masked by the earlier domain outcome`);
|
|
560
|
+
assert.equal(failedRecovery.servedToolCalls, 2, `${target} failed recovery still exercises exactly two model-owned tool calls`);
|
|
561
|
+
assert.deepEqual(failedRecovery.runnerArguments, [
|
|
562
|
+
{
|
|
563
|
+
city: 'Dubai'
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
city: 'Singapore'
|
|
567
|
+
}
|
|
568
|
+
], `${target} failed recovery reaches the revised second adapter invocation`);
|
|
569
|
+
assert.equal(failedRecovery.stdout, '', `${target} failed recovery releases no terminal stdout`);
|
|
570
|
+
assert.match(failedRecovery.stderr, /benchmark terminal output unavailable \(child_bridge_runner_failed\)/, `${target} failed recovery preserves the second runner failure classification`);
|
|
571
|
+
assert.equal(failedRecovery.output.includes('PRIVATE_RECOVERY_RUNNER_DIAGNOSTIC_DO_NOT_REFLECT'), false, `${target} failed recovery never reflects private runner stderr`);
|
|
463
572
|
const debugRedaction = await runCase('debug-redaction', executableOverride);
|
|
464
573
|
assert.equal(debugRedaction.exit, 0, `${target} benchmark debug mode preserves successful execution`);
|
|
465
574
|
assert.equal(debugRedaction.output.includes('PRIVATE_QUERY_SENTINEL_DO_NOT_REFLECT'), false, `${target} benchmark debug output never reflects the private task`);
|
|
@@ -661,7 +770,8 @@ function conformanceResponse(mode, request, plannerCount) {
|
|
|
661
770
|
'b',
|
|
662
771
|
'd',
|
|
663
772
|
'f',
|
|
664
|
-
'large'
|
|
773
|
+
'large',
|
|
774
|
+
'schema'
|
|
665
775
|
].includes(mode) && plannerCount === 1;
|
|
666
776
|
if (call && !hasToolResult) return toolCall();
|
|
667
777
|
if (mode === 'f' && plannerCount === 3) return toolCall('bridge-call-retry');
|
|
@@ -670,6 +780,9 @@ function conformanceResponse(mode, request, plannerCount) {
|
|
|
670
780
|
payload: LARGE_TERMINAL_PAYLOAD
|
|
671
781
|
})}</benchmark_terminal>`);
|
|
672
782
|
}
|
|
783
|
+
if (mode === 'schema') {
|
|
784
|
+
return finalText('<benchmark_terminal>{"status":"succeeded","budget":{"total_cost":1}}</benchmark_terminal>');
|
|
785
|
+
}
|
|
673
786
|
const valid = mode === 'a' ? hasToolResult : mode === 'b' ? plannerCount >= 3 : mode === 'e' ? true : false;
|
|
674
787
|
return finalText(mode === 'c' || mode === 'd' ? 'bad benchmark body' : taggedTerminal(valid));
|
|
675
788
|
}
|
|
@@ -729,28 +842,38 @@ async function runConformanceCase(mode, executableOverride) {
|
|
|
729
842
|
const runner = join(cwd, 'synthetic-runner.js');
|
|
730
843
|
const runnerCount = join(cwd, 'runner-count.txt');
|
|
731
844
|
const configPath = join(cwd, 'benchmark-env-config.json');
|
|
732
|
-
writeFileSync(runner, `const fs = require('node:fs'); const path = ${JSON.stringify(runnerCount)}; const count = fs.existsSync(path) ? Number(fs.readFileSync(path, 'utf8')) : 0; fs.writeFileSync(path, String(count + 1)); process.stdout.write(JSON.stringify({ result: { marker: '${MARKER}' } }))`);
|
|
845
|
+
writeFileSync(runner, `const fs = require('node:fs'); const path = ${JSON.stringify(runnerCount)}; const count = fs.existsSync(path) ? Number(fs.readFileSync(path, 'utf8')) : 0; fs.writeFileSync(path, String(count + 1)); process.stdout.write(JSON.stringify({ schema_version: 'gotry_benchmark_tool_result_v1', status: 'ok', result: { marker: '${MARKER}' } }))`);
|
|
733
846
|
writeFileSync(configPath, JSON.stringify({
|
|
734
|
-
schema_version: '
|
|
847
|
+
schema_version: 'gotry_benchmark_environment_bridge_v4',
|
|
735
848
|
enabled: true,
|
|
736
849
|
executable: process.execPath,
|
|
737
850
|
cwd,
|
|
738
851
|
argv_prefix: [
|
|
739
852
|
runner
|
|
740
853
|
],
|
|
741
|
-
|
|
742
|
-
|
|
854
|
+
tools: [
|
|
855
|
+
{
|
|
856
|
+
name: 'lookup',
|
|
857
|
+
description: 'Lookup.',
|
|
858
|
+
input_schema: LOOKUP_INPUT_SCHEMA,
|
|
859
|
+
output_keys: [
|
|
860
|
+
'marker'
|
|
861
|
+
],
|
|
862
|
+
domain_outcomes: [
|
|
863
|
+
{
|
|
864
|
+
status: 'miss',
|
|
865
|
+
code: 'NOT_FOUND',
|
|
866
|
+
recovery: 'none'
|
|
867
|
+
}
|
|
868
|
+
]
|
|
869
|
+
}
|
|
743
870
|
],
|
|
744
|
-
allowed_output_keys: {
|
|
745
|
-
lookup: [
|
|
746
|
-
'marker'
|
|
747
|
-
]
|
|
748
|
-
},
|
|
749
871
|
timeout_ms: 2_000,
|
|
750
872
|
max_output_bytes: 4_096,
|
|
751
873
|
terminal_output: {
|
|
752
874
|
tag: 'benchmark_terminal',
|
|
753
|
-
max_bytes: mode === 'large' ? 128 * 1024 : 4_096
|
|
875
|
+
max_bytes: mode === 'large' ? 128 * 1024 : 4_096,
|
|
876
|
+
body_schema: BRIDGE_E2E_BODY_SCHEMA
|
|
754
877
|
},
|
|
755
878
|
isolation: {
|
|
756
879
|
mode: 'host-enforced',
|
|
@@ -888,6 +1011,7 @@ async function assertOutputConformance(executableOverride) {
|
|
|
888
1011
|
assert.equal(a.servedToolCalls, 1, 'A exposes exactly one bridge call');
|
|
889
1012
|
assert.equal(a.runnerInvocations, 1, 'A executes the bridge subprocess exactly once');
|
|
890
1013
|
assert.ok(a.stdout.includes('<benchmark_terminal>'), 'A forwards only tagged terminal output');
|
|
1014
|
+
assert.ok(a.requests.some((request)=>JSON.stringify(request).includes(`matching exactly ${TERMINAL_OUTLINE}`)), 'A system prompt projects the exact terminal schema outline');
|
|
891
1015
|
const b = await runConformanceCase('b', executableOverride);
|
|
892
1016
|
assert.equal(b.exit, 0, 'B malformed terminal correction then valid terminal exits 0');
|
|
893
1017
|
assert.equal(b.servedToolCalls, 1, `B exposes exactly one bridge call; request shapes=${JSON.stringify(b.requests.map((request)=>({
|
|
@@ -895,6 +1019,12 @@ async function assertOutputConformance(executableOverride) {
|
|
|
895
1019
|
roles: (request.messages ?? []).map((message)=>message.role)
|
|
896
1020
|
})))}`);
|
|
897
1021
|
assert.equal(b.runnerInvocations, 1, 'B format-only correction does not rerun the bridge subprocess');
|
|
1022
|
+
assert.ok(b.requests.some((request)=>JSON.stringify(request).includes('BENCHMARK_CONFORMANCE_TERMINAL') && JSON.stringify(request).includes(`matching exactly ${TERMINAL_OUTLINE}`)), 'B terminal correction projects the same schema outline as the system prompt');
|
|
1023
|
+
const schema = await runConformanceCase('schema', executableOverride);
|
|
1024
|
+
assert.notEqual(schema.exit, 0, 'schema-invalid terminal body (extra root key) is rejected after the single correction');
|
|
1025
|
+
assert.match(schema.stderr, /benchmark terminal output unavailable \(child_conformance_failure\)/, 'schema-invalid terminal emits the stable conformance reason code');
|
|
1026
|
+
assert.equal(schema.runnerInvocations, 1, 'schema-invalid terminal still executed the bridge exactly once');
|
|
1027
|
+
assert.equal(schema.stdout.includes('<benchmark_terminal>'), false, 'schema-invalid terminal body is never released to stdout');
|
|
898
1028
|
for (const mode of [
|
|
899
1029
|
'c',
|
|
900
1030
|
'd'
|
|
@@ -939,7 +1069,7 @@ if (packaged) {
|
|
|
939
1069
|
try {
|
|
940
1070
|
const configPath = join(missingServiceRoot, 'bridge.json');
|
|
941
1071
|
writeFileSync(configPath, JSON.stringify({
|
|
942
|
-
schema_version: '
|
|
1072
|
+
schema_version: 'gotry_benchmark_environment_bridge_v4',
|
|
943
1073
|
enabled: true,
|
|
944
1074
|
executable: process.execPath,
|
|
945
1075
|
cwd: missingServiceRoot,
|
|
@@ -947,14 +1077,29 @@ if (packaged) {
|
|
|
947
1077
|
'-e',
|
|
948
1078
|
'process.exit(0)'
|
|
949
1079
|
],
|
|
950
|
-
|
|
951
|
-
|
|
1080
|
+
tools: [
|
|
1081
|
+
{
|
|
1082
|
+
name: 'lookup',
|
|
1083
|
+
description: 'Lookup.',
|
|
1084
|
+
input_schema: LOOKUP_INPUT_SCHEMA,
|
|
1085
|
+
output_keys: [
|
|
1086
|
+
'marker'
|
|
1087
|
+
],
|
|
1088
|
+
domain_outcomes: [
|
|
1089
|
+
{
|
|
1090
|
+
status: 'miss',
|
|
1091
|
+
code: 'NOT_FOUND',
|
|
1092
|
+
recovery: 'none'
|
|
1093
|
+
}
|
|
1094
|
+
]
|
|
1095
|
+
}
|
|
952
1096
|
],
|
|
953
1097
|
timeout_ms: 100,
|
|
954
1098
|
max_output_bytes: 4_096,
|
|
955
1099
|
terminal_output: {
|
|
956
1100
|
tag: 'benchmark_terminal',
|
|
957
|
-
max_bytes: 4_096
|
|
1101
|
+
max_bytes: 4_096,
|
|
1102
|
+
body_schema: BRIDGE_E2E_BODY_SCHEMA
|
|
958
1103
|
},
|
|
959
1104
|
isolation: {
|
|
960
1105
|
mode: 'host-enforced',
|