@danceiny/gotry 0.0.1-rc.20 → 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 +53 -8
- package/cordis.gotry-patch.yml +12 -5
- package/dist/capabilities/doctor.js +88 -16
- package/dist/capabilities/session/health-watch.js +10 -3
- package/dist/scripts/benchmark-environment-bridge-e2e.js +53 -15
- package/dist/scripts/benchmark-environment-bridge-tests.js +450 -28
- 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 +2 -1
- package/dist/scripts/hbcli-e2e-tests.js +1 -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 +155 -5
- package/dist/src/benchmark-environment-bridge.js +53 -71
- package/dist/src/booking-surface/dsh-planner.js +24 -8
- package/extension/manifest.json +2 -2
- package/package.json +2 -1
- package/ts/capabilities/session/health-watch.ts +9 -2
- package/ts/package.json +15 -7
- package/ts/scripts/map-tools-vendor-package-proof.ts +268 -0
- package/ts/src/benchmark-agent-conformance.ts +135 -4
- package/ts/src/benchmark-environment-bridge.ts +25 -40
- package/ts/src/booking-surface/dsh-planner.ts +25 -13
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
assertSupportedJsonSchema,
|
|
6
6
|
validateJsonSchemaValue,
|
|
7
7
|
ToolArgsError,
|
|
8
|
-
type JsonSchemaNode,
|
|
9
8
|
type ObjectJsonSchema,
|
|
10
9
|
type ToolDefinition,
|
|
11
10
|
} from '@deepseek-ai/dsh-tools'
|
|
@@ -15,7 +14,9 @@ import {
|
|
|
15
14
|
type TerminalOutputConfig,
|
|
16
15
|
} from './benchmark-agent-conformance.ts'
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
// v4(#215 Round 12):terminal_output 携带 closed body schema(结构关键字白名单,
|
|
18
|
+
// 禁数据值注解面);v3 及更早配置缺该面,按契约不兼容 fail-closed。
|
|
19
|
+
const SCHEMA_VERSION = 'gotry_benchmark_environment_bridge_v4'
|
|
19
20
|
export const BENCHMARK_TOOL_RESULT_SCHEMA_VERSION = 'gotry_benchmark_tool_result_v1'
|
|
20
21
|
export const BENCHMARK_DOMAIN_RECOVERIES = ['none', 'retry_same', 'revise_arguments', 'choose_alternative'] as const
|
|
21
22
|
type BenchmarkDomainRecovery = typeof BENCHMARK_DOMAIN_RECOVERIES[number]
|
|
@@ -55,38 +56,6 @@ export interface BenchmarkToolDescriptor {
|
|
|
55
56
|
domain_outcomes: Array<{ status: 'miss' | 'error'; code: string; recovery: BenchmarkDomainRecovery }>
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
function descriptorSchema(tools: readonly BenchmarkToolDescriptor[]): JsonSchemaNode {
|
|
59
|
-
return {
|
|
60
|
-
oneOf: [
|
|
61
|
-
{
|
|
62
|
-
type: 'object',
|
|
63
|
-
description: 'List the frozen benchmark tool descriptors.',
|
|
64
|
-
properties: { action: { type: 'string', const: 'tools' } },
|
|
65
|
-
required: ['action'],
|
|
66
|
-
additionalProperties: false,
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
type: 'object',
|
|
70
|
-
description: 'List the frozen bridge error contract.',
|
|
71
|
-
properties: { action: { type: 'string', const: 'errors' } },
|
|
72
|
-
required: ['action'],
|
|
73
|
-
additionalProperties: false,
|
|
74
|
-
},
|
|
75
|
-
...tools.map<JsonSchemaNode>(tool => ({
|
|
76
|
-
type: 'object',
|
|
77
|
-
description: tool.description,
|
|
78
|
-
properties: {
|
|
79
|
-
action: { type: 'string', const: 'call' },
|
|
80
|
-
tool: { type: 'string', const: tool.name },
|
|
81
|
-
arguments: tool.input_schema,
|
|
82
|
-
},
|
|
83
|
-
required: ['action', 'tool', 'arguments'],
|
|
84
|
-
additionalProperties: false,
|
|
85
|
-
})),
|
|
86
|
-
],
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
59
|
function plainObject(value: unknown): value is Record<string, unknown> {
|
|
91
60
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
92
61
|
}
|
|
@@ -426,7 +395,15 @@ export function registerBenchmarkEnvironmentBridge(
|
|
|
426
395
|
if (!subprocess) throw new Error('benchmark environment bridge subprocess unavailable')
|
|
427
396
|
|
|
428
397
|
const parameters: Record<string, unknown> = deepFreezeJson({
|
|
429
|
-
|
|
398
|
+
type: 'object',
|
|
399
|
+
description: 'Flat benchmark bridge wire: choose an action, then provide the mapped tool and its arguments.',
|
|
400
|
+
properties: {
|
|
401
|
+
action: { type: 'string', enum: ['tools', 'call', 'errors'], description: 'tools=列出可调工具;call=执行工具;errors=拉取错误契约' },
|
|
402
|
+
tool: { type: 'string', enum: bridge.tools.map(item => item.name), description: 'action=call 时选择的冻结工具名' },
|
|
403
|
+
arguments: { type: 'object', additionalProperties: true, description: 'action=call 时传给工具的参数对象' },
|
|
404
|
+
},
|
|
405
|
+
required: ['action'],
|
|
406
|
+
additionalProperties: false,
|
|
430
407
|
})
|
|
431
408
|
assertSupportedJsonSchema(parameters)
|
|
432
409
|
|
|
@@ -444,11 +421,21 @@ export function registerBenchmarkEnvironmentBridge(
|
|
|
444
421
|
const query = args as Record<string, unknown>
|
|
445
422
|
const tool = query.tool
|
|
446
423
|
const descriptor = bridge.tools.find(item => item.name === tool)
|
|
447
|
-
|
|
448
|
-
if (query.action === '
|
|
424
|
+
const keys = Object.keys(query)
|
|
425
|
+
if (query.action === 'tools') {
|
|
426
|
+
if (keys.length !== 1) throw new ToolArgsError(['arguments: tools action accepts no tool or arguments'])
|
|
427
|
+
return jsonObject({ ok: true, tools: bridge.tools })
|
|
428
|
+
}
|
|
429
|
+
if (query.action === 'errors') {
|
|
430
|
+
if (keys.length !== 1) throw new ToolArgsError(['arguments: errors action accepts no tool or arguments'])
|
|
431
|
+
return jsonObject({ ok: true, errors: BRIDGE_ERROR_CONTRACT })
|
|
432
|
+
}
|
|
449
433
|
if (query.action !== 'call') {
|
|
450
434
|
return jsonObject({ ok: false, error: 'invalid_action' })
|
|
451
435
|
}
|
|
436
|
+
if (keys.length !== 3 || !Object.hasOwn(query, 'tool') || !Object.hasOwn(query, 'arguments')) {
|
|
437
|
+
throw new ToolArgsError(['arguments: call action requires exactly tool and arguments'])
|
|
438
|
+
}
|
|
452
439
|
if (typeof tool !== 'string' || !descriptor) {
|
|
453
440
|
return jsonObject({ ok: false, error: 'disallowed_tool' })
|
|
454
441
|
}
|
|
@@ -457,9 +444,7 @@ export function registerBenchmarkEnvironmentBridge(
|
|
|
457
444
|
}
|
|
458
445
|
const callArguments = query.arguments
|
|
459
446
|
const argumentViolations = validateJsonSchemaValue(descriptor.input_schema, callArguments, 'arguments')
|
|
460
|
-
if (argumentViolations.length > 0)
|
|
461
|
-
return jsonObject({ ok: false, error: 'invalid_arguments' })
|
|
462
|
-
}
|
|
447
|
+
if (argumentViolations.length > 0) throw new ToolArgsError(argumentViolations)
|
|
463
448
|
const serialized = serializedArguments(callArguments)
|
|
464
449
|
if (serialized.reason) return jsonObject({ ok: false, error: 'invalid_arguments', reason: serialized.reason })
|
|
465
450
|
const controller = new AbortController()
|
|
@@ -407,9 +407,11 @@ function recoverFinalResponseDecision(response: string, task: BookingCopilotTask
|
|
|
407
407
|
console.error('[booking-copilot] finalResponse recovery rejected (invalid action):', JSON.stringify({ kind: action.kind, errors: validation.errors.slice(0, 6) }).slice(0, 600))
|
|
408
408
|
return null
|
|
409
409
|
}
|
|
410
|
-
repairPlannerFactRefs(action)
|
|
410
|
+
const repairedRefs = repairPlannerFactRefs(action)
|
|
411
|
+
const repairedValidation = validateBookingReadAction(action as unknown as BookingReadAction)
|
|
412
|
+
if (!repairedValidation.ok) return null
|
|
411
413
|
try {
|
|
412
|
-
assertPlannerSafeRefs(action)
|
|
414
|
+
assertPlannerSafeRefs(action, repairedRefs)
|
|
413
415
|
} catch (error) {
|
|
414
416
|
console.error('[booking-copilot] finalResponse recovery rejected (unsafe ref):', JSON.stringify({ actionId: action.actionId, factRefs: action.factRefs }).slice(0, 600))
|
|
415
417
|
return null
|
|
@@ -426,17 +428,22 @@ function recoverFinalResponseDecision(response: string, task: BookingCopilotTask
|
|
|
426
428
|
const PLANNER_SAFE_REF_PATTERN = /^[A-Za-z0-9][A-Za-z0-9:._-]*$/
|
|
427
429
|
|
|
428
430
|
// Models cite prompt facts in URI-ish syntax (`fact://turn_X/request`,
|
|
429
|
-
// `turn_X#request`)
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
// safe-ref gate into the retry path.
|
|
433
|
-
function repairPlannerFactRefs(action: Record<string, unknown>): void {
|
|
431
|
+
// `turn_X#request`). Preserve already-safe refs exactly; unsafe refs enter the
|
|
432
|
+
// reserved modelref namespace with the full SHA-256 of the raw UTF-8 value.
|
|
433
|
+
function repairPlannerFactRefs(action: Record<string, unknown>): Set<string> {
|
|
434
434
|
const factRefs = action.factRefs
|
|
435
|
-
|
|
436
|
-
|
|
435
|
+
const repairedRefs = new Set<string>()
|
|
436
|
+
if (!Array.isArray(factRefs)) return repairedRefs
|
|
437
|
+
action.factRefs = factRefs.map((ref) => {
|
|
438
|
+
if (typeof ref !== 'string' || PLANNER_SAFE_REF_PATTERN.test(ref)) return ref
|
|
439
|
+
const alias = `modelref:${createHash('sha256').update(ref, 'utf8').digest('hex')}`
|
|
440
|
+
repairedRefs.add(alias)
|
|
441
|
+
return alias
|
|
442
|
+
})
|
|
443
|
+
return repairedRefs
|
|
437
444
|
}
|
|
438
445
|
|
|
439
|
-
function assertPlannerSafeRefs(action: Record<string, unknown>): void {
|
|
446
|
+
function assertPlannerSafeRefs(action: Record<string, unknown>, repairedRefs: Set<string>): void {
|
|
440
447
|
const actionId = action.actionId
|
|
441
448
|
if (typeof actionId !== 'string' || !PLANNER_SAFE_REF_PATTERN.test(actionId)) {
|
|
442
449
|
throw new Error(`planner_invalid_action:unsafe_action_id:${String(actionId).slice(0, 60)}`)
|
|
@@ -444,7 +451,7 @@ function assertPlannerSafeRefs(action: Record<string, unknown>): void {
|
|
|
444
451
|
const factRefs = action.factRefs
|
|
445
452
|
if (Array.isArray(factRefs)) {
|
|
446
453
|
for (const ref of factRefs) {
|
|
447
|
-
if (typeof ref !== 'string' || !PLANNER_SAFE_REF_PATTERN.test(ref)) {
|
|
454
|
+
if (typeof ref !== 'string' || !PLANNER_SAFE_REF_PATTERN.test(ref) || (ref.startsWith('modelref:') && !repairedRefs.has(ref)) || ref.length > 512) {
|
|
448
455
|
throw new Error(`planner_invalid_action:unsafe_fact_ref:${String(ref).slice(0, 60)}`)
|
|
449
456
|
}
|
|
450
457
|
}
|
|
@@ -502,8 +509,13 @@ function parseToolDecision(event: unknown, task: BookingCopilotTaskState): Booki
|
|
|
502
509
|
// boundary, past the retry budget. Repair the common fragment syntax first,
|
|
503
510
|
// then enforce the same charset here so remaining violations retry as
|
|
504
511
|
// parse-class failures instead of failing the turn as PLANNER_FAILED.
|
|
505
|
-
repairPlannerFactRefs(decision.action)
|
|
506
|
-
|
|
512
|
+
const repairedRefs = repairPlannerFactRefs(decision.action)
|
|
513
|
+
const repairedValidation = validateBookingReadAction(decision.action)
|
|
514
|
+
if (!repairedValidation.ok) {
|
|
515
|
+
console.error(`[booking-copilot] repaired action rejected:`, JSON.stringify({ errors: repairedValidation.errors.slice(0, 8) }).slice(0, 800))
|
|
516
|
+
throw new Error(`planner_invalid_action:${repairedValidation.errors.join('; ')}`)
|
|
517
|
+
}
|
|
518
|
+
assertPlannerSafeRefs(decision.action, repairedRefs)
|
|
507
519
|
const action = decision.action as unknown as BookingReadAction
|
|
508
520
|
const capability = TOOL_TO_CAPABILITY.get(name as DshEmbeddedBookingToolName)
|
|
509
521
|
if (!capability || !actionsForEmbeddedCapability(capability).includes(action.kind)) {
|