@danceiny/gotry 0.0.1-rc.16 → 0.0.1-rc.18
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 +161 -116
- package/README.zh-CN.md +173 -144
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +113 -66
- package/bin/gotry-inner.js +436 -70
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +50 -0
- package/bin/gotry.js +1 -1
- package/cordis.gotry-patch.yml +5 -1
- package/dist/capabilities/agent-reach-deep.js +1 -1
- package/dist/capabilities/agent-reach.js +1 -1
- package/dist/capabilities/anything.js +1 -1
- package/dist/capabilities/artifacts.js +1 -1
- package/dist/capabilities/effect.js +1 -1
- package/dist/capabilities/fact-log.js +1 -1
- package/dist/capabilities/flyai.js +20 -6
- package/dist/capabilities/hbcli.js +1 -1
- package/dist/capabilities/incident-log.js +1 -1
- package/dist/capabilities/model-override.js +18 -0
- package/dist/capabilities/opensky.js +1 -1
- package/dist/capabilities/resilience.js +1 -1
- package/dist/capabilities/session/action-cache.js +1 -1
- package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
- package/dist/capabilities/session/adapters/meituan-local.js +1 -1
- package/dist/capabilities/session/benchmark.js +1 -1
- package/dist/capabilities/session/extension-bridge.js +19 -6
- package/dist/capabilities/session/extension-channel.js +3 -3
- package/dist/capabilities/session/extension-distribution.js +234 -0
- package/dist/capabilities/session/extract.js +1 -1
- package/dist/capabilities/session/golden-score.js +92 -0
- package/dist/capabilities/session/health-watch.js +1 -1
- package/dist/capabilities/session/read-guard.js +1 -1
- package/dist/capabilities/session/static-flight-golden.js +137 -0
- package/dist/capabilities/session/transport.js +1 -1
- package/dist/capabilities/session/wizard.js +21 -251
- package/dist/capabilities/session-consent.js +1 -1
- package/dist/capabilities/session-login.js +11 -5
- package/dist/capabilities/session-search.js +46 -4
- package/dist/capabilities/weather.js +168 -46
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +102 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +1 -1
- package/dist/scripts/agent-planning-budget-e2e.js +227 -0
- package/dist/scripts/agent-planning-budget-tests.js +173 -0
- package/dist/scripts/agent-reach-deep-tests.js +1 -1
- package/dist/scripts/agent-reach-tests.js +1 -1
- package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
- package/dist/scripts/anything-tests.js +1 -1
- package/dist/scripts/async-collect.js +1 -1
- package/dist/scripts/benchmark-environment-bridge-e2e.js +981 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +2544 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +335 -0
- package/dist/scripts/booking-copilot-availability-policy-v2-tests.js +1355 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +89 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +113 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +356 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +557 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +140 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +202 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +102 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +374 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +447 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +205 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +317 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +283 -0
- package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +3935 -0
- package/dist/scripts/booking-saga-tests.js +1 -1
- package/dist/scripts/booking-surface-contract-proof-tests.js +393 -0
- package/dist/scripts/booking-surface-v2-contract-proof-tests.js +1174 -0
- package/dist/scripts/bootstrap-tests.js +45 -39
- package/dist/scripts/build-changelog.js +1 -1
- package/dist/scripts/changelog-tests.js +1 -1
- package/dist/scripts/companion-tests.js +1 -1
- package/dist/scripts/diff-test.js +1 -1
- package/dist/scripts/dsh-runtime-closure-tests.js +225 -0
- package/dist/scripts/dsh-runtime-closure.js +150 -0
- package/dist/scripts/effect-tests.js +1 -1
- package/dist/scripts/engine-run.js +1 -1
- package/dist/scripts/engine-tests.js +1 -1
- package/dist/scripts/evaluation-cadence-tests.js +347 -0
- package/dist/scripts/evaluation-contract-tests.js +574 -0
- package/dist/scripts/extension-distribution-cli.js +35 -0
- package/dist/scripts/extension-distribution-tests.js +341 -0
- package/dist/scripts/extension-tests.js +128 -23
- package/dist/scripts/fact-gate-tests.js +1 -1
- package/dist/scripts/flyai-tests.js +1 -1
- package/dist/scripts/hbcli-e2e-tests.js +1 -1
- package/dist/scripts/hbcli-tests.js +1 -1
- package/dist/scripts/health-watch-cli.js +1 -1
- package/dist/scripts/i18n-tests.js +1 -1
- package/dist/scripts/incident-tests.js +1 -1
- package/dist/scripts/journey-tests.js +1 -1
- package/dist/scripts/ledger-tests.js +1 -1
- package/dist/scripts/ledger-workflow-crash.js +1 -1
- package/dist/scripts/memory-capture-tests.js +1 -1
- package/dist/scripts/memory-decay-tests.js +1 -1
- package/dist/scripts/memory-metrics.js +1 -1
- package/dist/scripts/memory-value-report.js +1 -1
- package/dist/scripts/model-override-e2e.js +176 -0
- package/dist/scripts/nightly-evidence-tests.js +1 -1
- package/dist/scripts/nightly-evidence.js +1 -1
- package/dist/scripts/nudge-digest.js +1 -1
- package/dist/scripts/onboarding-tests.js +21 -53
- package/dist/scripts/opensky-check.js +1 -1
- package/dist/scripts/opensky-tests.js +1 -1
- package/dist/scripts/pnpm-dsh-closure-proof.js +20 -0
- package/dist/scripts/price-drift-tests.js +1 -1
- package/dist/scripts/price-drift-watch.js +1 -1
- package/dist/scripts/probe-poi-tests.js +1 -1
- package/dist/scripts/product-metrics.js +1 -1
- package/dist/scripts/publish-preverify.js +40 -4
- package/dist/scripts/realtime-pricing-tests.js +1 -1
- package/dist/scripts/replay-async.js +1 -1
- package/dist/scripts/replay-real.js +1 -1
- package/dist/scripts/replay.js +1 -1
- package/dist/scripts/session-attach-diagnose.js +1 -1
- package/dist/scripts/session-attach-poc.js +1 -1
- package/dist/scripts/session-benchmark.js +1 -1
- package/dist/scripts/session-extract-tests.js +1 -1
- package/dist/scripts/session-login.js +1 -1
- package/dist/scripts/session-tests.js +70 -20
- package/dist/scripts/sf-live-benchmark.js +338 -0
- package/dist/scripts/sf-live-cli-tests.js +21 -0
- package/dist/scripts/sf-soft-score-tests.js +108 -0
- package/dist/scripts/sf-summary.js +93 -0
- package/dist/scripts/skeleton-check.js +1 -1
- package/dist/scripts/skeleton-integration-test.js +1 -1
- package/dist/scripts/skills-contract-tests.js +1 -1
- package/dist/scripts/smoke-session-gate-tests.js +29 -0
- package/dist/scripts/smoke.js +79 -33
- package/dist/scripts/state-cli-tests.js +1 -1
- package/dist/scripts/state-cli.js +1 -1
- package/dist/scripts/static-golden-tests.js +299 -0
- package/dist/scripts/time-eval-tests.js +1 -1
- package/dist/scripts/travel-timeline-tests.js +1 -1
- package/dist/scripts/unified-tests.js +1 -1
- package/dist/scripts/weather-tests.js +694 -44
- package/dist/scripts/z3-race-tests.js +1 -1
- package/dist/src/artifact-gate.js +1 -1
- package/dist/src/benchmark-agent-conformance.js +370 -0
- package/dist/src/benchmark-environment-bridge.js +384 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +173 -0
- package/dist/src/benchmark-tool-isolation.js +124 -0
- package/dist/src/bookable-facts.js +1 -1
- package/dist/src/booking-saga.js +1 -1
- package/dist/src/booking-surface/availability-policy-v2.js +830 -0
- package/dist/src/booking-surface/canonical-schema.js +113 -0
- package/dist/src/booking-surface/contracts-v2.js +89 -0
- package/dist/src/booking-surface/contracts.js +46 -0
- package/dist/src/booking-surface/dsh-planner.js +453 -0
- package/dist/src/booking-surface/dsh-plugin.js +93 -0
- package/dist/src/booking-surface/error-codes.js +94 -0
- package/dist/src/booking-surface/index.js +15 -0
- package/dist/src/booking-surface/profile.js +68 -0
- package/dist/src/booking-surface/runtime-v2.js +1771 -0
- package/dist/src/booking-surface/runtime.js +351 -0
- package/dist/src/booking-surface/server-v2.js +334 -0
- package/dist/src/booking-surface/server.js +302 -0
- package/dist/src/booking-surface/startup.js +159 -0
- package/dist/src/booking-surface/validation-v2.js +319 -0
- package/dist/src/booking-surface/validation.js +809 -0
- package/dist/src/bridge.js +1 -1
- package/dist/src/companions.js +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/dsh-llm.js +1 -1
- package/dist/src/engine.js +1 -1
- package/dist/src/evaluation-cadence.js +234 -0
- package/dist/src/evaluation-contracts.js +906 -0
- package/dist/src/i18n.js +1 -1
- package/dist/src/index.js +50 -19
- package/dist/src/journey.js +1 -1
- package/dist/src/loop.js +1 -1
- package/dist/src/memory-capture.js +1 -1
- package/dist/src/memory-decay.js +1 -1
- package/dist/src/memory-utility.js +1 -1
- package/dist/src/mock-llm.js +1 -1
- package/dist/src/model.js +1 -1
- package/dist/src/realtime-pricing.js +1 -1
- package/dist/src/slot-spec.js +1 -1
- package/dist/src/state-ledger.js +2 -1
- package/dist/src/time-anchor.js +1 -1
- package/dist/src/tool-budget.js +136 -0
- package/dist/src/tool-packet.js +1 -1
- package/dist/src/travel-slots.js +1 -1
- package/dist/src/travel-timeline.js +1 -1
- package/dist/src/unified.js +1 -1
- package/dist/src/wish-pool.js +1 -1
- package/dist/src/z3-shared.js +1 -1
- package/extension/README.md +31 -7
- package/package.json +286 -11
- package/schemas/booking.surface.v1.schema.json +927 -0
- package/schemas/booking.surface.v2.schema.json +61 -0
- package/ts/capabilities/flyai.ts +16 -3
- package/ts/capabilities/session/extension-bridge.ts +37 -14
- package/ts/capabilities/session/extension-channel.ts +6 -3
- package/ts/capabilities/session/extension-distribution.ts +264 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +1 -1
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/wizard.ts +34 -176
- package/ts/capabilities/session-login.ts +15 -5
- package/ts/capabilities/session-search.ts +40 -3
- package/ts/capabilities/weather.ts +141 -52
- package/ts/package.json +3 -3
- package/ts/src/benchmark-agent-conformance.ts +448 -0
- package/ts/src/benchmark-environment-bridge.ts +348 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/booking-surface/availability-policy-v2.ts +523 -0
- package/ts/src/booking-surface/canonical-schema.js +113 -0
- package/ts/src/booking-surface/contracts-v2.ts +118 -0
- package/ts/src/booking-surface/contracts.ts +380 -0
- package/ts/src/booking-surface/dsh-planner.ts +452 -0
- package/ts/src/booking-surface/dsh-plugin.js +93 -0
- package/ts/src/booking-surface/error-codes.ts +101 -0
- package/ts/src/booking-surface/index.ts +12 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/runtime-v2.ts +1466 -0
- package/ts/src/booking-surface/runtime.ts +483 -0
- package/ts/src/booking-surface/server-v2.ts +247 -0
- package/ts/src/booking-surface/server.ts +324 -0
- package/ts/src/booking-surface/startup.ts +196 -0
- package/ts/src/booking-surface/validation-v2.ts +205 -0
- package/ts/src/booking-surface/validation.ts +453 -0
- package/ts/src/index.ts +64 -11
- package/ts/src/state-ledger.ts +1 -0
- package/ts/src/tool-budget.ts +165 -0
- package/dist/scripts/wizard-bootstrap.js +0 -32
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/** Provider-neutral execution and terminal contract for benchmark agents. */
|
|
2
|
+
|
|
3
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
4
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
5
|
+
import {
|
|
6
|
+
classifyBenchmarkTurnEnd,
|
|
7
|
+
createBenchmarkDiagnosticArbiter,
|
|
8
|
+
emitBenchmarkChildDiagnostic,
|
|
9
|
+
type BenchmarkChildFailureCode,
|
|
10
|
+
} from './benchmark-headless-child-diagnostics.ts'
|
|
11
|
+
|
|
12
|
+
export const MAX_CONFORMANCE_RETRIES = 1 as const
|
|
13
|
+
export const BENCHMARK_BRIDGE_CALL_REQUIRED = 'BENCHMARK_BRIDGE_CALL_REQUIRED'
|
|
14
|
+
export const BENCHMARK_BRIDGE_CALL_FAILED = 'BENCHMARK_BRIDGE_CALL_FAILED'
|
|
15
|
+
export const BENCHMARK_BRIDGE_TIMED_OUT = 'BENCHMARK_BRIDGE_TIMED_OUT'
|
|
16
|
+
export const BENCHMARK_BRIDGE_RUNNER_FAILED = 'BENCHMARK_BRIDGE_RUNNER_FAILED'
|
|
17
|
+
export const BENCHMARK_BRIDGE_SPAWN_FAILED = 'BENCHMARK_BRIDGE_SPAWN_FAILED'
|
|
18
|
+
export const BENCHMARK_BRIDGE_OUTPUT_TRUNCATED = 'BENCHMARK_BRIDGE_OUTPUT_TRUNCATED'
|
|
19
|
+
export const BENCHMARK_TERMINAL_INVALID = 'BENCHMARK_TERMINAL_INVALID'
|
|
20
|
+
export const BENCHMARK_BRIDGE_RETRY_CALL_NOT_ALLOWED = 'BENCHMARK_BRIDGE_RETRY_CALL_NOT_ALLOWED'
|
|
21
|
+
export const BENCHMARK_CONFORMANCE_STATE_UNAVAILABLE = 'BENCHMARK_CONFORMANCE_STATE_UNAVAILABLE'
|
|
22
|
+
|
|
23
|
+
const IDENTIFIER = /^[A-Za-z][A-Za-z0-9_.-]*$/
|
|
24
|
+
const MAX_TERMINAL_BYTES = 1024 * 1024
|
|
25
|
+
|
|
26
|
+
export interface TerminalOutputConfig {
|
|
27
|
+
tag: string
|
|
28
|
+
max_bytes: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type TerminalOutputValue =
|
|
32
|
+
| { ok: true; value: Record<string, unknown> }
|
|
33
|
+
| { ok: false; error: 'invalid_terminal_output' }
|
|
34
|
+
|
|
35
|
+
export interface BenchmarkBridgeProjection {
|
|
36
|
+
readonly toolName: string
|
|
37
|
+
readonly allowedTools: readonly string[]
|
|
38
|
+
readonly terminal: Readonly<TerminalOutputConfig>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type RetryMode = 'none' | 'call' | 'terminal'
|
|
42
|
+
|
|
43
|
+
interface TurnState {
|
|
44
|
+
readonly turn: number
|
|
45
|
+
readonly validCallIds: Set<string>
|
|
46
|
+
retryCount: number
|
|
47
|
+
retryMode: RetryMode
|
|
48
|
+
successfulResultStep?: number
|
|
49
|
+
bridgeCallFailed: boolean
|
|
50
|
+
bridgeFailureCode?: string
|
|
51
|
+
retryRedispatchAttempted: boolean
|
|
52
|
+
lastAssistant?: {
|
|
53
|
+
step: number
|
|
54
|
+
text: string
|
|
55
|
+
interrupted: boolean
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type BenchmarkConformanceDecision =
|
|
60
|
+
| { kind: 'accept' }
|
|
61
|
+
| { kind: 'steer'; mode: Exclude<RetryMode, 'none'> }
|
|
62
|
+
| { kind: 'reject'; code: string }
|
|
63
|
+
|
|
64
|
+
export interface BenchmarkAgentConformance {
|
|
65
|
+
readonly retryMode: RetryMode
|
|
66
|
+
readonly retryCount: number
|
|
67
|
+
observe(event: unknown): void
|
|
68
|
+
stopping(turn: number): BenchmarkConformanceDecision
|
|
69
|
+
guardBridgeExecution(): string | undefined
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function plainObject(value: unknown): value is Record<string, unknown> {
|
|
73
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function validateTerminalOutputConfig(value: unknown): value is TerminalOutputConfig {
|
|
77
|
+
if (!plainObject(value)) return false
|
|
78
|
+
const keys = Object.keys(value).sort()
|
|
79
|
+
return JSON.stringify(keys) === JSON.stringify(['max_bytes', 'tag'])
|
|
80
|
+
&& typeof value.tag === 'string'
|
|
81
|
+
&& IDENTIFIER.test(value.tag)
|
|
82
|
+
&& typeof value.max_bytes === 'number'
|
|
83
|
+
&& Number.isInteger(value.max_bytes)
|
|
84
|
+
&& value.max_bytes >= 1
|
|
85
|
+
&& value.max_bytes <= MAX_TERMINAL_BYTES
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function invalidTerminal(): TerminalOutputValue {
|
|
89
|
+
return { ok: false, error: 'invalid_terminal_output' }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Parse exactly one configured tag pair containing one JSON object. */
|
|
93
|
+
export function parseBenchmarkTerminal(raw: string, config: TerminalOutputConfig): TerminalOutputValue {
|
|
94
|
+
if (!validateTerminalOutputConfig(config)) return invalidTerminal()
|
|
95
|
+
if (Buffer.byteLength(raw, 'utf8') > config.max_bytes) return invalidTerminal()
|
|
96
|
+
|
|
97
|
+
const trimmed = raw.trim()
|
|
98
|
+
const opening = `<${config.tag}>`
|
|
99
|
+
const closing = `</${config.tag}>`
|
|
100
|
+
if (!trimmed.startsWith(opening) || !trimmed.endsWith(closing)) return invalidTerminal()
|
|
101
|
+
if (trimmed.indexOf(opening) !== trimmed.lastIndexOf(opening)
|
|
102
|
+
|| trimmed.indexOf(closing) !== trimmed.lastIndexOf(closing)) return invalidTerminal()
|
|
103
|
+
const body = trimmed.slice(opening.length, -closing.length)
|
|
104
|
+
if (body.length === 0 || body.includes('```')) return invalidTerminal()
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const value: unknown = JSON.parse(body)
|
|
108
|
+
return plainObject(value) ? { ok: true, value } : invalidTerminal()
|
|
109
|
+
} catch {
|
|
110
|
+
return invalidTerminal()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Backward-compatible name used by the bridge contract tests. */
|
|
115
|
+
export const validateTerminalOutput = parseBenchmarkTerminal
|
|
116
|
+
|
|
117
|
+
function eventData(event: unknown): { type: string; data: Record<string, unknown> } | undefined {
|
|
118
|
+
if (!plainObject(event) || typeof event.type !== 'string' || !plainObject(event.data)) return undefined
|
|
119
|
+
return { type: event.type, data: event.data }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function eventTurn(data: Record<string, unknown>): number | undefined {
|
|
123
|
+
return typeof data.turn === 'number' && Number.isInteger(data.turn) ? data.turn : undefined
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function parseToolArguments(value: unknown): Record<string, unknown> | undefined {
|
|
127
|
+
if (plainObject(value)) return value
|
|
128
|
+
if (typeof value !== 'string') return undefined
|
|
129
|
+
try {
|
|
130
|
+
const parsed: unknown = JSON.parse(value)
|
|
131
|
+
return plainObject(parsed) ? parsed : undefined
|
|
132
|
+
} catch {
|
|
133
|
+
return undefined
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function assistantText(data: Record<string, unknown>): { text: string; interrupted: boolean } | undefined {
|
|
138
|
+
if (!plainObject(data.message) || !Array.isArray(data.message.content)) return undefined
|
|
139
|
+
const text = data.message.content
|
|
140
|
+
.filter(block => plainObject(block) && block.type === 'text' && typeof block.text === 'string')
|
|
141
|
+
.map(block => String((block as Record<string, unknown>).text))
|
|
142
|
+
.join('')
|
|
143
|
+
return { text, interrupted: data.interrupted === true }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function bridgeResultStatus(data: Record<string, unknown>, callId: string): { ok: true } | { ok: false; code: string } | undefined {
|
|
147
|
+
if (!plainObject(data.message) || !plainObject(data.message.source)) return undefined
|
|
148
|
+
if (data.message.source.callId !== callId || !Array.isArray(data.message.content)) return undefined
|
|
149
|
+
const block = data.message.content.find(candidate => plainObject(candidate)
|
|
150
|
+
&& candidate.type === 'tool-result'
|
|
151
|
+
&& candidate.toolCallId === callId)
|
|
152
|
+
if (!plainObject(block) || block.isError === true || !Array.isArray(block.content)) return undefined
|
|
153
|
+
const text = block.content
|
|
154
|
+
.filter(candidate => plainObject(candidate) && candidate.type === 'text' && typeof candidate.text === 'string')
|
|
155
|
+
.map(candidate => String((candidate as Record<string, unknown>).text))
|
|
156
|
+
.join('')
|
|
157
|
+
try {
|
|
158
|
+
const parsed: unknown = JSON.parse(text)
|
|
159
|
+
if (!plainObject(parsed)) return undefined
|
|
160
|
+
if (parsed.ok === true) return { ok: true }
|
|
161
|
+
if (parsed.ok === false && typeof parsed.error === 'string') {
|
|
162
|
+
const code = parsed.error === 'timed_out'
|
|
163
|
+
? BENCHMARK_BRIDGE_TIMED_OUT
|
|
164
|
+
: parsed.error === 'runner_failed'
|
|
165
|
+
? BENCHMARK_BRIDGE_RUNNER_FAILED
|
|
166
|
+
: parsed.error === 'spawn_failed'
|
|
167
|
+
? BENCHMARK_BRIDGE_SPAWN_FAILED
|
|
168
|
+
: parsed.error === 'output_truncated'
|
|
169
|
+
? BENCHMARK_BRIDGE_OUTPUT_TRUNCATED
|
|
170
|
+
: BENCHMARK_BRIDGE_CALL_FAILED
|
|
171
|
+
return { ok: false, code }
|
|
172
|
+
}
|
|
173
|
+
return undefined
|
|
174
|
+
} catch {
|
|
175
|
+
return undefined
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function benchmarkChildFailureForConformanceCode(code: string): BenchmarkChildFailureCode {
|
|
180
|
+
if (code === BENCHMARK_BRIDGE_TIMED_OUT) return 'child_bridge_timed_out'
|
|
181
|
+
if (code === BENCHMARK_BRIDGE_RUNNER_FAILED) return 'child_bridge_runner_failed'
|
|
182
|
+
if (code === BENCHMARK_BRIDGE_SPAWN_FAILED) return 'child_bridge_spawn_failed'
|
|
183
|
+
if (code === BENCHMARK_BRIDGE_OUTPUT_TRUNCATED) return 'child_bridge_output_truncated'
|
|
184
|
+
if (code === BENCHMARK_BRIDGE_CALL_FAILED) return 'child_bridge_failure'
|
|
185
|
+
return 'child_conformance_failure'
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function newTurn(turn: number): TurnState {
|
|
189
|
+
return {
|
|
190
|
+
turn,
|
|
191
|
+
validCallIds: new Set(),
|
|
192
|
+
retryCount: 0,
|
|
193
|
+
retryMode: 'none',
|
|
194
|
+
bridgeCallFailed: false,
|
|
195
|
+
retryRedispatchAttempted: false,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Deterministic controller used by both runtime wiring and offline contracts. */
|
|
200
|
+
export function createBenchmarkAgentConformance(projection: BenchmarkBridgeProjection): BenchmarkAgentConformance {
|
|
201
|
+
let state: TurnState | undefined
|
|
202
|
+
|
|
203
|
+
const controller: BenchmarkAgentConformance = {
|
|
204
|
+
get retryMode() { return state?.retryMode ?? 'none' },
|
|
205
|
+
get retryCount() { return state?.retryCount ?? 0 },
|
|
206
|
+
|
|
207
|
+
observe(rawEvent: unknown): void {
|
|
208
|
+
const event = eventData(rawEvent)
|
|
209
|
+
if (!event) return
|
|
210
|
+
const turn = eventTurn(event.data)
|
|
211
|
+
|
|
212
|
+
if (event.type === 'turn/start' && turn !== undefined) {
|
|
213
|
+
state = newTurn(turn)
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
if (event.type === 'turn/end') {
|
|
217
|
+
if (state && turn === state.turn) state = undefined
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
if (!state || turn !== state.turn) return
|
|
221
|
+
|
|
222
|
+
if (event.type === 'tool/call') {
|
|
223
|
+
if (event.data.name !== projection.toolName) return
|
|
224
|
+
if (state.retryMode === 'terminal') {
|
|
225
|
+
state.retryRedispatchAttempted = true
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
if (typeof event.data.callId !== 'string') return
|
|
229
|
+
const args = parseToolArguments(event.data.arguments)
|
|
230
|
+
const query = args && plainObject(args.query) ? args.query : undefined
|
|
231
|
+
if (query?.action === 'call'
|
|
232
|
+
&& typeof query.tool === 'string'
|
|
233
|
+
&& projection.allowedTools.includes(query.tool)) {
|
|
234
|
+
state.validCallIds.add(event.data.callId)
|
|
235
|
+
}
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (event.type === 'tool/result') {
|
|
240
|
+
if (!plainObject(event.data.message) || !plainObject(event.data.message.source)) return
|
|
241
|
+
const callId = event.data.message.source.callId
|
|
242
|
+
if (typeof callId !== 'string' || !state.validCallIds.has(callId)) return
|
|
243
|
+
const resultStatus = bridgeResultStatus(event.data, callId)
|
|
244
|
+
if (resultStatus?.ok === true) {
|
|
245
|
+
state.successfulResultStep = typeof event.data.step === 'number'
|
|
246
|
+
? event.data.step
|
|
247
|
+
: state.successfulResultStep
|
|
248
|
+
} else {
|
|
249
|
+
state.bridgeCallFailed = true
|
|
250
|
+
state.bridgeFailureCode = resultStatus?.code ?? BENCHMARK_BRIDGE_CALL_FAILED
|
|
251
|
+
}
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (event.type === 'assistant/message') {
|
|
256
|
+
const assistant = assistantText(event.data)
|
|
257
|
+
if (!assistant || typeof event.data.step !== 'number') return
|
|
258
|
+
state.lastAssistant = { step: event.data.step, ...assistant }
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
stopping(turn: number): BenchmarkConformanceDecision {
|
|
263
|
+
if (!state || state.turn !== turn) {
|
|
264
|
+
return { kind: 'reject', code: BENCHMARK_CONFORMANCE_STATE_UNAVAILABLE }
|
|
265
|
+
}
|
|
266
|
+
if (state.retryRedispatchAttempted) {
|
|
267
|
+
return { kind: 'reject', code: BENCHMARK_BRIDGE_RETRY_CALL_NOT_ALLOWED }
|
|
268
|
+
}
|
|
269
|
+
if (state.successfulResultStep === undefined) {
|
|
270
|
+
if (state.bridgeCallFailed) {
|
|
271
|
+
return { kind: 'reject', code: state.bridgeFailureCode ?? BENCHMARK_BRIDGE_CALL_FAILED }
|
|
272
|
+
}
|
|
273
|
+
if (state.retryCount >= MAX_CONFORMANCE_RETRIES) {
|
|
274
|
+
return { kind: 'reject', code: BENCHMARK_BRIDGE_CALL_REQUIRED }
|
|
275
|
+
}
|
|
276
|
+
state.retryCount += 1
|
|
277
|
+
state.retryMode = 'call'
|
|
278
|
+
return { kind: 'steer', mode: 'call' }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const terminal = state.lastAssistant
|
|
282
|
+
const terminalIsValid = terminal !== undefined
|
|
283
|
+
&& !terminal.interrupted
|
|
284
|
+
&& terminal.step > state.successfulResultStep
|
|
285
|
+
&& parseBenchmarkTerminal(terminal.text, projection.terminal).ok
|
|
286
|
+
if (terminalIsValid) return { kind: 'accept' }
|
|
287
|
+
if (state.retryCount >= MAX_CONFORMANCE_RETRIES) {
|
|
288
|
+
return { kind: 'reject', code: BENCHMARK_TERMINAL_INVALID }
|
|
289
|
+
}
|
|
290
|
+
state.retryCount += 1
|
|
291
|
+
state.retryMode = 'terminal'
|
|
292
|
+
return { kind: 'steer', mode: 'terminal' }
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
guardBridgeExecution(): string | undefined {
|
|
296
|
+
return state?.retryMode === 'terminal'
|
|
297
|
+
? BENCHMARK_BRIDGE_RETRY_CALL_NOT_ALLOWED
|
|
298
|
+
: undefined
|
|
299
|
+
},
|
|
300
|
+
}
|
|
301
|
+
return controller
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
type Disposer = () => void | Promise<void>
|
|
305
|
+
type ScopedAgent = {
|
|
306
|
+
id?: unknown
|
|
307
|
+
session?: object
|
|
308
|
+
steer?: (message: unknown) => void
|
|
309
|
+
ctx?: {
|
|
310
|
+
tools?: { guard?: (check: (execution: { name: string }) => string | undefined) => unknown }
|
|
311
|
+
effect?: (action: () => unknown, label?: string) => unknown
|
|
312
|
+
on?: (event: string, listener: (...args: any[]) => unknown, options?: { prepend?: boolean }) => unknown
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
type EventBus = {
|
|
316
|
+
on?: (event: string, listener: (...args: any[]) => unknown, options?: { prepend?: boolean }) => unknown
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function requireDisposer(value: unknown, capability: string): Disposer {
|
|
320
|
+
if (typeof value !== 'function') throw new Error(`benchmark conformance ${capability} disposer unavailable`)
|
|
321
|
+
return value as Disposer
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function correctionMessage(mode: Exclude<RetryMode, 'none'>, projection: BenchmarkBridgeProjection) {
|
|
325
|
+
const text = mode === 'call'
|
|
326
|
+
? `BENCHMARK_CONFORMANCE_CALL: Execute exactly one native ${projection.toolName} action:"call" now. Describing an intended CLI, shell, or Python command does not execute it.`
|
|
327
|
+
: `BENCHMARK_CONFORMANCE_TERMINAL: Reuse the existing successful tool result. Do not call any tool. Reply only <${projection.terminal.tag}>{"result":"..."}</${projection.terminal.tag}> with one JSON object.`
|
|
328
|
+
return createUserMessage({
|
|
329
|
+
content: [{ type: 'text' as const, text }],
|
|
330
|
+
source: { kind: 'plugin' as const, plugin: 'gotry-benchmark-agent-conformance' },
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function systemSection(projection: BenchmarkBridgeProjection): { name: string; text: string } {
|
|
335
|
+
const allowed = projection.allowedTools.join(', ')
|
|
336
|
+
return {
|
|
337
|
+
name: 'benchmark:agent-conformance',
|
|
338
|
+
text: [
|
|
339
|
+
'Benchmark execution contract:',
|
|
340
|
+
`- Translate every task instruction to use a CLI, shell, Python, or agent_env.cli into the native tool ${projection.toolName}; do not merely describe the intended command.`,
|
|
341
|
+
`- Call it with exactly {"query":{"action":"call","tool":"<one of: ${allowed}>","arguments":{...}}}.`,
|
|
342
|
+
'- action:"tools" is discovery only and does not satisfy the required environment call.',
|
|
343
|
+
`- After a successful tool result, reply only <${projection.terminal.tag}>{...one JSON object...}</${projection.terminal.tag}> with no prose or code fence.`,
|
|
344
|
+
'- If a terminal-format correction arrives, reuse the existing result and do not call the tool again.',
|
|
345
|
+
].join('\n'),
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Install conformance only after the exact benchmark bridge isolation layer. */
|
|
350
|
+
export function installBenchmarkAgentConformance(
|
|
351
|
+
ctx: Context,
|
|
352
|
+
projection: BenchmarkBridgeProjection,
|
|
353
|
+
diagnosticWriter: (code: BenchmarkChildFailureCode) => void = emitBenchmarkChildDiagnostic,
|
|
354
|
+
): void {
|
|
355
|
+
const bus = ctx as unknown as EventBus
|
|
356
|
+
if (typeof bus.on !== 'function') throw new Error('benchmark conformance event bus unavailable')
|
|
357
|
+
|
|
358
|
+
const byAgent = new WeakMap<object, BenchmarkAgentConformance>()
|
|
359
|
+
const bySession = new WeakMap<object, BenchmarkAgentConformance>()
|
|
360
|
+
const sessionKeys = new WeakMap<object, string>()
|
|
361
|
+
let sessionOrdinal = 0
|
|
362
|
+
const diagnostics = createBenchmarkDiagnosticArbiter(diagnosticWriter)
|
|
363
|
+
|
|
364
|
+
bus.on('agent/created', (payload: { agent?: ScopedAgent }) => {
|
|
365
|
+
const agent = payload.agent
|
|
366
|
+
if (!agent || typeof agent !== 'object' || !agent.session) {
|
|
367
|
+
throw new Error('benchmark conformance created agent unavailable')
|
|
368
|
+
}
|
|
369
|
+
if (typeof agent.steer !== 'function') throw new Error('benchmark conformance steering unavailable')
|
|
370
|
+
if (typeof agent.ctx?.tools?.guard !== 'function') throw new Error('benchmark conformance scoped guard unavailable')
|
|
371
|
+
if (typeof agent.ctx.effect !== 'function') throw new Error('benchmark conformance scoped effect unavailable')
|
|
372
|
+
if (typeof agent.ctx.on !== 'function') throw new Error('benchmark conformance scoped event bus unavailable')
|
|
373
|
+
|
|
374
|
+
const controller = createBenchmarkAgentConformance(projection)
|
|
375
|
+
byAgent.set(agent, controller)
|
|
376
|
+
bySession.set(agent.session, controller)
|
|
377
|
+
sessionKeys.set(agent.session, `session-${++sessionOrdinal}`)
|
|
378
|
+
try {
|
|
379
|
+
requireDisposer(agent.ctx.effect.call(agent.ctx, function* () {
|
|
380
|
+
yield requireDisposer(agent.ctx!.tools!.guard!(execution => {
|
|
381
|
+
if (execution.name !== projection.toolName) return undefined
|
|
382
|
+
return controller.guardBridgeExecution()
|
|
383
|
+
}), 'scoped guard')
|
|
384
|
+
yield requireDisposer(agent.ctx!.on!.call(agent.ctx, 'system-prompt/assemble', async (
|
|
385
|
+
_assembly: unknown,
|
|
386
|
+
_context: unknown,
|
|
387
|
+
next?: () => Promise<unknown>,
|
|
388
|
+
) => {
|
|
389
|
+
if (typeof next !== 'function') throw new Error('benchmark conformance assembly continuation unavailable')
|
|
390
|
+
const result = await next()
|
|
391
|
+
if (!plainObject(result)) throw new Error('benchmark conformance assembly result unavailable')
|
|
392
|
+
const sections = Array.isArray(result.sections) ? result.sections : []
|
|
393
|
+
return { ...result, sections: [...sections, systemSection(projection)] }
|
|
394
|
+
}, { prepend: true }), 'scoped assembly listener')
|
|
395
|
+
}, 'benchmark-agent-conformance'), 'scoped effect')
|
|
396
|
+
} catch (error) {
|
|
397
|
+
byAgent.delete(agent)
|
|
398
|
+
bySession.delete(agent.session)
|
|
399
|
+
throw error
|
|
400
|
+
}
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
bus.on('session/event', (session: object, event: unknown) => {
|
|
404
|
+
bySession.get(session)?.observe(event)
|
|
405
|
+
if (typeof event === 'object' && event !== null && (event as { type?: unknown }).type === 'turn/end') {
|
|
406
|
+
const key = sessionKeys.get(session)
|
|
407
|
+
if (key !== undefined) {
|
|
408
|
+
diagnostics.offer(key, classifyBenchmarkTurnEnd((event as { data?: { reason?: unknown } }).data?.reason))
|
|
409
|
+
diagnostics.flush(key)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
})
|
|
413
|
+
bus.on('session/disposed', (session: object) => {
|
|
414
|
+
const key = sessionKeys.get(session)
|
|
415
|
+
if (key !== undefined) diagnostics.flush(key)
|
|
416
|
+
sessionKeys.delete(session)
|
|
417
|
+
bySession.delete(session)
|
|
418
|
+
})
|
|
419
|
+
bus.on('agent/disposed', (payload: { agent?: ScopedAgent }) => {
|
|
420
|
+
const agent = payload.agent
|
|
421
|
+
if (!agent || typeof agent !== 'object') return
|
|
422
|
+
byAgent.delete(agent)
|
|
423
|
+
if (agent.session) bySession.delete(agent.session)
|
|
424
|
+
})
|
|
425
|
+
bus.on('agent/turn-stopping', (payload: { agent?: ScopedAgent; session?: object; turn?: number }) => {
|
|
426
|
+
const agent = payload.agent
|
|
427
|
+
const session = agent?.session ?? payload.session
|
|
428
|
+
const key = session && typeof session === 'object' ? sessionKeys.get(session) : undefined
|
|
429
|
+
if (!agent || typeof agent !== 'object' || typeof payload.turn !== 'number') {
|
|
430
|
+
if (key !== undefined) diagnostics.offer(key, 'child_conformance_failure')
|
|
431
|
+
else diagnosticWriter('child_conformance_failure')
|
|
432
|
+
throw new Error(BENCHMARK_CONFORMANCE_STATE_UNAVAILABLE)
|
|
433
|
+
}
|
|
434
|
+
const controller = byAgent.get(agent)
|
|
435
|
+
if (!controller) {
|
|
436
|
+
if (key !== undefined) diagnostics.offer(key, 'child_conformance_failure')
|
|
437
|
+
else diagnosticWriter('child_conformance_failure')
|
|
438
|
+
throw new Error(BENCHMARK_CONFORMANCE_STATE_UNAVAILABLE)
|
|
439
|
+
}
|
|
440
|
+
const decision = controller.stopping(payload.turn)
|
|
441
|
+
if (decision.kind === 'reject') {
|
|
442
|
+
if (key !== undefined) diagnostics.offer(key, benchmarkChildFailureForConformanceCode(decision.code))
|
|
443
|
+
else diagnosticWriter(benchmarkChildFailureForConformanceCode(decision.code))
|
|
444
|
+
throw new Error(decision.code)
|
|
445
|
+
}
|
|
446
|
+
if (decision.kind === 'steer') agent.steer!(correctionMessage(decision.mode, projection))
|
|
447
|
+
}, { prepend: true })
|
|
448
|
+
}
|