@qvac/harness 1.0.0-rc.2 → 1.0.0-rc.3

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 (58) hide show
  1. package/README.md +12 -12
  2. package/bench/README.md +94 -0
  3. package/bench/bfcl/checker.ts +313 -0
  4. package/bench/bfcl/data.ts +93 -0
  5. package/bench/bfcl/observe.ts +93 -0
  6. package/bench/bfcl/run.ts +213 -0
  7. package/bench/bfcl/tools.ts +72 -0
  8. package/bench/bridge/bridge.ts +392 -0
  9. package/bench/bridge/openai.ts +150 -0
  10. package/bench/bridge/python/qvac_bench/__init__.py +3 -0
  11. package/bench/bridge/python/qvac_bench/airline.py +42 -0
  12. package/bench/bridge/python/qvac_bench/approval.py +26 -0
  13. package/bench/bridge/python/qvac_bench/bfcl_multi_turn.py +152 -0
  14. package/bench/bridge/python/qvac_bench/client.py +88 -0
  15. package/bench/bridge/python/qvac_bench/harbor_agent.py +85 -0
  16. package/bench/bridge/python/qvac_bench/session.py +107 -0
  17. package/bench/bridge/python/qvac_bench/speed.py +30 -0
  18. package/bench/bridge/python/qvac_bench/tau2_agent.py +106 -0
  19. package/bench/bridge/python/qvac_bench/tools.py +11 -0
  20. package/bench/bridge/python/run_bfcl_multi_turn.py +95 -0
  21. package/bench/bridge/python/run_tau2.py +150 -0
  22. package/bench/bridge/python/smoke.py +47 -0
  23. package/bench/bridge/python/test_airline.py +28 -0
  24. package/bench/bridge/python/test_approval.py +57 -0
  25. package/bench/bridge/python/test_bfcl_multi_turn.py +185 -0
  26. package/bench/bridge/python/test_harbor_agent.py +107 -0
  27. package/bench/bridge/serve.ts +52 -0
  28. package/bench/bridge/transcript.ts +52 -0
  29. package/bench/bun-tsconfig.json +15 -0
  30. package/bench/cpu-main.mjs +216 -0
  31. package/bench/cpu.ts +457 -0
  32. package/bench/evidence/2026-09-28-review15.md +225 -0
  33. package/bench/evidence/redact-bun1.3.14.json +92 -0
  34. package/bench/evidence/redact-node24.11.json +92 -0
  35. package/bench/graph-metrics.ts +43 -0
  36. package/bench/harness.ts +73 -0
  37. package/bench/issue129-matrix.mjs +722 -0
  38. package/bench/redact-adversarial.ts +256 -0
  39. package/bench/repeat-cpu.mjs +281 -0
  40. package/bench/report.ts +1075 -0
  41. package/bench/skill-eval/harness.ts +30 -0
  42. package/bench/skill-eval/laws.ts +37 -0
  43. package/bench/skill-eval/mermaid/client.ts +132 -0
  44. package/bench/skill-eval/mermaid/validate.mjs +115 -0
  45. package/bench/skill-eval/pack.ts +61 -0
  46. package/bench/skill-eval/packs/diagrams/cases.ts +281 -0
  47. package/bench/skill-eval/packs/diagrams/index.ts +50 -0
  48. package/bench/skill-eval/packs/diagrams/score.ts +74 -0
  49. package/bench/skill-eval/packs/index.ts +5 -0
  50. package/bench/skill-eval/packs/weather/cases.ts +279 -0
  51. package/bench/skill-eval/packs/weather/fixtures/wttr.json +566 -0
  52. package/bench/skill-eval/packs/weather/fixtures.ts +84 -0
  53. package/bench/skill-eval/packs/weather/index.ts +55 -0
  54. package/bench/skill-eval/packs/weather/score.ts +92 -0
  55. package/bench/skill-eval/report.ts +194 -0
  56. package/bench/skill-eval/run.ts +316 -0
  57. package/bench/speed.ts +80 -0
  58. package/package.json +4 -13
@@ -0,0 +1,256 @@
1
+ import process from 'process'
2
+ import { CompletionEngine } from '../lib/capabilities/completion-engine/index.ts'
3
+ import type { CompletionSdk, ToolDefinition } from '../lib/contract/index.ts'
4
+ import { isRecord } from '../lib/guards.ts'
5
+ import { RunLog, type EngineBoot, type EngineStep, type RunRecord } from '../lib/run-log/index.ts'
6
+
7
+ const SIZES = [100_000, 500_000, 1_000_000]
8
+ const PATTERNS = {
9
+ prose: 'A pagination cursor remains ordinary free text. ',
10
+ jwtPrefix: 'eyJ-',
11
+ markup: '<parameter=',
12
+ punctuation: 'the hyphen-token\\path stays byte-for-byte plain text - '
13
+ } as const
14
+ const SECRET = 'credential-value-must-not-be-persisted'
15
+ const SAMPLES = 3
16
+ const WARMUPS = 1
17
+ const encoder = new TextEncoder()
18
+
19
+ function runtimeInfo() {
20
+ const bun = Reflect.get(globalThis, 'Bun')
21
+ if (bun && typeof bun === 'object' && 'version' in bun && typeof bun.version === 'string') {
22
+ return { runtime: 'bun', version: bun.version }
23
+ }
24
+
25
+ return { runtime: 'node', version: process.version }
26
+ }
27
+
28
+ function stressText(pattern: string, bytes: number): string {
29
+ return pattern.repeat(Math.ceil(bytes / pattern.length)).slice(0, bytes)
30
+ }
31
+
32
+ function hasPair(value: unknown, key: string, expected: unknown): boolean {
33
+ if (Array.isArray(value)) {
34
+ return value.some((entry) => hasPair(entry, key, expected))
35
+ }
36
+
37
+ if (!isRecord(value)) return false
38
+
39
+ return Object.entries(value).some(
40
+ ([childKey, child]) =>
41
+ (childKey === key && (child === expected || (isRecord(child) && child.$ === expected))) ||
42
+ hasPair(child, key, expected)
43
+ )
44
+ }
45
+
46
+ function sdkFor(payload: string): CompletionSdk {
47
+ let round = 0
48
+ return {
49
+ completion: ({ tools }) => {
50
+ const toolCalls =
51
+ round === 0 && tools
52
+ ? [
53
+ {
54
+ id: 'sensitive-call',
55
+ name: 'credential_tool',
56
+ arguments: { credential: SECRET, password: 739184, token: 1024, note: payload }
57
+ }
58
+ ]
59
+ : []
60
+ const contentText = round++ === 0 ? '' : 'done'
61
+
62
+ return {
63
+ tokenStream: (async function* () {
64
+ if (contentText) {
65
+ yield contentText
66
+ }
67
+ })(),
68
+ final: Promise.resolve({ contentText, toolCalls, raw: { fullText: contentText } })
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ function createTool(execute: (args: Record<string, unknown>) => void): ToolDefinition {
75
+ return {
76
+ name: 'credential_tool',
77
+ description: 'Use the credential without displaying it',
78
+ parameters: {
79
+ type: 'object',
80
+ properties: {
81
+ credential: { type: 'string', sensitive: true },
82
+ password: { type: 'number' },
83
+ token: { type: 'number' },
84
+ note: { type: 'string' }
85
+ },
86
+ required: ['credential', 'note']
87
+ },
88
+ toolset: null,
89
+ autoApprove: true,
90
+ effect: () => 'read',
91
+ execute: (args) => {
92
+ execute(args)
93
+ return Promise.resolve({ content: payloadForResult(args) })
94
+ }
95
+ }
96
+ }
97
+
98
+ function payloadForResult(args: Record<string, unknown>): string {
99
+ return typeof args.note === 'string' ? args.note : ''
100
+ }
101
+
102
+ interface Observation {
103
+ records: RunRecord[]
104
+ boot: EngineBoot
105
+ executedSecret: boolean
106
+ executedPassword: boolean
107
+ executedToken: boolean
108
+ sawRedactedCredential: boolean
109
+ sawRedactedPassword: boolean
110
+ sawTypedToken: boolean
111
+ }
112
+
113
+ function createObservation() {
114
+ const state: Observation = {
115
+ records: [],
116
+ boot: RunLog.createEngineBoot(),
117
+ executedSecret: false,
118
+ executedPassword: false,
119
+ executedToken: false,
120
+ sawRedactedCredential: false,
121
+ sawRedactedPassword: false,
122
+ sawTypedToken: false
123
+ }
124
+ const tool = createTool((args) => {
125
+ state.executedSecret = args.credential === SECRET
126
+ state.executedPassword = args.password === 739184
127
+ state.executedToken = args.token === 1024
128
+ })
129
+ const onStep = (step: EngineStep) => observeStep(state, step)
130
+
131
+ return { state, tool, onStep }
132
+ }
133
+
134
+ function observeStep(state: Observation, step: EngineStep): Promise<void> {
135
+ const next = RunLog.applyEngineStep(state.boot, step)
136
+ if (step.expectedLength !== state.records.length) {
137
+ throw new Error('noncontiguous host step')
138
+ }
139
+
140
+ for (const record of step.records) {
141
+ if (hasPair(record, 'credential', SECRET)) {
142
+ throw new Error('A schema-marked credential reached typed durable fields')
143
+ }
144
+
145
+ if (hasPair(record, 'password', 739184)) {
146
+ throw new Error('A numeric password reached typed durable fields')
147
+ }
148
+
149
+ state.sawRedactedCredential ||= hasPair(record, 'credential', '[redacted]')
150
+ state.sawRedactedPassword ||= hasPair(record, 'password', '[redacted]')
151
+ state.sawTypedToken ||= hasPair(record, 'token', 1024)
152
+ }
153
+
154
+ state.records.push(...step.records)
155
+ state.boot = next
156
+
157
+ return Promise.resolve()
158
+ }
159
+
160
+ function assertObservation(state: Observation, label: string, payload: string): void {
161
+ if (!state.executedSecret) {
162
+ throw new Error('The tool did not receive its unredacted credential')
163
+ }
164
+
165
+ if (!state.executedPassword || !state.executedToken) {
166
+ throw new Error('Numeric tool arguments changed at runtime')
167
+ }
168
+
169
+ const metadata = state.records.find((record) => record.type === 'metadata')
170
+ if (!metadata || !isRecord(metadata.request)) {
171
+ throw new Error('Missing request metadata')
172
+ }
173
+
174
+ const messages = metadata.request.messages
175
+ if (!Array.isArray(messages) || !isRecord(messages[0]) || messages[0].content !== payload) {
176
+ throw new Error(`Free text changed for ${label}/${payload.length}`)
177
+ }
178
+
179
+ if (!state.sawRedactedCredential) {
180
+ throw new Error('The schema-marked tool argument was not redacted')
181
+ }
182
+
183
+ if (!state.sawRedactedPassword) {
184
+ throw new Error('A numeric password was not redacted')
185
+ }
186
+
187
+ if (!state.sawTypedToken) {
188
+ throw new Error('An ambiguous token count was over-redacted')
189
+ }
190
+ }
191
+
192
+ async function runOne(label: string, payload: string, sample: number): Promise<number> {
193
+ const { state, tool, onStep } = createObservation()
194
+ const started = process.cpuUsage()
195
+ for await (const _event of CompletionEngine.run(
196
+ sdkFor(payload),
197
+ {
198
+ runId: `redact-${label}-${payload.length}-${sample}`,
199
+ modelId: 'benchmark-model',
200
+ messages: [{ role: 'user', content: payload }],
201
+ tools: [tool]
202
+ },
203
+ { onStep, requestDigestKey: new Uint8Array(32).fill(7) }
204
+ )) {
205
+ // Consume the real completion iterator; the durable callback is the production redaction path.
206
+ }
207
+
208
+ const usage = process.cpuUsage(started)
209
+ assertObservation(state, label, payload)
210
+
211
+ return (usage.user + usage.system) / 1000
212
+ }
213
+
214
+ function median(values: number[]): number {
215
+ const ordered = [...values].sort((left, right) => left - right)
216
+ return ordered[Math.floor(ordered.length / 2)]
217
+ }
218
+
219
+ async function main(): Promise<void> {
220
+ const results: Array<Record<string, number | string>> = []
221
+ for (const [label, pattern] of Object.entries(PATTERNS)) {
222
+ for (const size of SIZES) {
223
+ const payload = stressText(pattern, size)
224
+ for (let sample = 0; sample < WARMUPS; sample++) {
225
+ await runOne(label, payload, -sample - 1)
226
+ }
227
+
228
+ const times: number[] = []
229
+ for (let sample = 0; sample < SAMPLES; sample++) {
230
+ times.push(await runOne(label, payload, sample))
231
+ }
232
+
233
+ results.push({
234
+ pattern: label,
235
+ bytes: encoder.encode(payload).byteLength,
236
+ samples: SAMPLES,
237
+ medianCpuMs: median(times),
238
+ maxCpuMs: Math.max(...times)
239
+ })
240
+ }
241
+ }
242
+
243
+ console.log(
244
+ JSON.stringify({
245
+ ...runtimeInfo(),
246
+ adapter: 'completion() production redactor + schema registration + awaited host onStep',
247
+ warmups: WARMUPS,
248
+ results
249
+ })
250
+ )
251
+ }
252
+
253
+ main().catch((error) => {
254
+ console.error(error)
255
+ process.exitCode = 1
256
+ })
@@ -0,0 +1,281 @@
1
+ import { spawnSync } from 'node:child_process'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
6
+ const engineBench = path.join(root, 'bench/cpu.ts')
7
+ const mainBench = path.join(root, 'bench/cpu-main.mjs')
8
+ const bunConfig = path.join(root, 'bench/bun-tsconfig.json')
9
+ const options = parseOptions(process.argv.slice(2))
10
+ const bench = options.driver === 'main' ? mainBench : engineBench
11
+
12
+ const command =
13
+ options.runtime === 'bun'
14
+ ? { executable: 'bun', prefix: [`--tsconfig-override=${bunConfig}`] }
15
+ : { executable: process.execPath, prefix: ['--experimental-strip-types'] }
16
+
17
+ const samples = []
18
+ for (let i = 0; i < options.processes; i++) {
19
+ const args = [
20
+ ...command.prefix,
21
+ bench,
22
+ String(options.runs),
23
+ `--spacing=${options.spacing}`,
24
+ `--workload=${options.workload}`,
25
+ '--json'
26
+ ]
27
+ if (options.driver === 'engine') {
28
+ args.splice(args.length - 1, 0, `--mode=${options.mode}`)
29
+ }
30
+
31
+ const child = spawnSync(command.executable, args, {
32
+ cwd: root,
33
+ encoding: 'utf8'
34
+ })
35
+ if (child.error) {
36
+ throw child.error
37
+ }
38
+
39
+ if (child.status !== 0) {
40
+ throw new Error(
41
+ `${command.executable} exited with ${child.status}:\n${child.stderr || child.stdout}`
42
+ )
43
+ }
44
+
45
+ const lines = child.stdout.trim().split('\n')
46
+ const result = JSON.parse(lines.at(-1))
47
+ validateChildReport(result, i + 1)
48
+ samples.push({ process: i + 1, ...result })
49
+ }
50
+
51
+ validateProcessReports(samples)
52
+
53
+ const keys = new Map()
54
+ for (const sample of samples) {
55
+ for (const result of sample.results) {
56
+ const key = [result.workload, result.mode, result.spacing ?? ''].join(':')
57
+ if (!keys.has(key)) {
58
+ keys.set(key, [])
59
+ }
60
+
61
+ keys.get(key).push(result)
62
+ }
63
+ }
64
+
65
+ const medians = [...keys].map(([key, results]) => {
66
+ const first = results[0]
67
+ const aggregate = { workload: first.workload, mode: first.mode, spacing: first.spacing }
68
+
69
+ const cpuSamples = results.flatMap((result) => result.cpuSamplesMs)
70
+ aggregate.rawSampleCount = cpuSamples.length
71
+ aggregate.cpuMs = median(cpuSamples)
72
+ aggregate.processMedianMs = median(results.map((result) => result.cpuMs))
73
+ if (typeof first.usPerToken === 'number' && first.cpuMs > 0) {
74
+ aggregate.usPerToken = aggregate.cpuMs * (first.usPerToken / first.cpuMs)
75
+ }
76
+
77
+ for (const metric of ['logBytes', 'records', 'contentEvents']) {
78
+ const values = results.map((result) => result[metric]).filter(Number.isFinite)
79
+ if (values.length) {
80
+ aggregate[metric] = median(values)
81
+ }
82
+ }
83
+
84
+ return { key, ...aggregate }
85
+ })
86
+
87
+ console.log(
88
+ JSON.stringify({
89
+ runtime: samples[0].runtime,
90
+ version: samples[0].version,
91
+ driver: options.driver,
92
+ revision: options.revision,
93
+ processes: options.processes,
94
+ runsPerProcess: options.runs,
95
+ reduction: 'pooled median of all raw cpuSamplesMs; process medians are reported separately',
96
+ workload: options.workload,
97
+ samples,
98
+ medians
99
+ })
100
+ )
101
+
102
+ function validateOptions(options) {
103
+ if (!['node', 'bun'].includes(options.runtime)) {
104
+ throw new Error('runtime must be node or bun')
105
+ }
106
+
107
+ if (!['engine', 'main'].includes(options.driver)) {
108
+ throw new Error('driver must be engine or main')
109
+ }
110
+
111
+ if (!['tools', 'stream', 'all'].includes(options.workload)) {
112
+ throw new Error('workload must be tools, stream or all')
113
+ }
114
+
115
+ if (!['immediate', 'timeout', 'all'].includes(options.spacing)) {
116
+ throw new Error('spacing must be immediate, timeout or all')
117
+ }
118
+
119
+ if (!['plain', 'bench', 'production', 'all'].includes(options.mode)) {
120
+ throw new Error('mode must be plain, bench, production or all')
121
+ }
122
+
123
+ for (const key of ['processes', 'runs']) {
124
+ if (!Number.isInteger(options[key]) || options[key] < 1) {
125
+ throw new Error(`${key} must be a positive integer`)
126
+ }
127
+ }
128
+
129
+ return options
130
+ }
131
+
132
+ function expectedResultKeys() {
133
+ const workloads = options.workload === 'all' ? ['tools', 'stream'] : [options.workload]
134
+ let modes
135
+ if (options.driver === 'main') {
136
+ modes = ['main']
137
+ } else if (options.mode === 'all') {
138
+ modes = ['plain', 'bench', 'production']
139
+ } else {
140
+ modes = [options.mode]
141
+ }
142
+
143
+ const spacings = options.spacing === 'all' ? ['immediate', 'timeout'] : [options.spacing]
144
+ const expected = []
145
+
146
+ for (const mode of modes) {
147
+ for (const workload of workloads) {
148
+ const workloadSpacings = workload === 'tools' ? [''] : spacings
149
+ for (const spacing of workloadSpacings) {
150
+ expected.push([workload, mode, spacing].join(':'))
151
+ }
152
+ }
153
+ }
154
+
155
+ return expected.sort()
156
+ }
157
+
158
+ function resultKey(result) {
159
+ return [result.workload, result.mode, result.spacing ?? ''].join(':')
160
+ }
161
+
162
+ function validateChildReport(report, processNumber) {
163
+ if (!report || report.runtime !== options.runtime || typeof report.version !== 'string') {
164
+ throw new Error(`process ${processNumber} reported an unexpected runtime identity`)
165
+ }
166
+
167
+ if (report.runs !== options.runs || !Array.isArray(report.results)) {
168
+ throw new Error(`process ${processNumber} reported an unexpected run count or results list`)
169
+ }
170
+
171
+ const expectedKeys = expectedResultKeys()
172
+ const actualKeys = report.results.map(resultKey)
173
+ if (
174
+ actualKeys.length !== expectedKeys.length ||
175
+ new Set(actualKeys).size !== actualKeys.length ||
176
+ actualKeys.sort().join('\n') !== expectedKeys.join('\n')
177
+ ) {
178
+ throw new Error(
179
+ `process ${processNumber} returned an incomplete or inconsistent workload matrix`
180
+ )
181
+ }
182
+
183
+ for (const result of report.results) {
184
+ if (
185
+ result.runs !== options.runs ||
186
+ !Array.isArray(result.cpuSamplesMs) ||
187
+ result.cpuSamplesMs.length !== options.runs ||
188
+ !result.cpuSamplesMs.every((sample) => Number.isFinite(sample) && sample >= 0)
189
+ ) {
190
+ throw new Error(
191
+ `process ${processNumber} result ${resultKey(result)} did not preserve ${options.runs} finite raw CPU samples`
192
+ )
193
+ }
194
+
195
+ if (result.cpuMs !== median(result.cpuSamplesMs)) {
196
+ throw new Error(
197
+ `process ${processNumber} result ${resultKey(result)} has an invalid process median`
198
+ )
199
+ }
200
+ }
201
+ }
202
+
203
+ function validateProcessReports(samples) {
204
+ const expectedKeys = expectedResultKeys()
205
+ const referenceVersion = samples[0].version
206
+
207
+ for (const sample of samples) {
208
+ if (sample.version !== referenceVersion) {
209
+ throw new Error('benchmark processes reported different runtime versions')
210
+ }
211
+
212
+ const keys = sample.results.map(resultKey).sort()
213
+ if (keys.join('\n') !== expectedKeys.join('\n')) {
214
+ throw new Error('benchmark processes returned different workload matrices')
215
+ }
216
+ }
217
+ }
218
+
219
+ function applyOption(options, arg) {
220
+ const [key, value] = arg.split('=', 2)
221
+ const values = {
222
+ '--runtime': () => {
223
+ options.runtime = value
224
+ },
225
+ '--driver': () => {
226
+ options.driver = value
227
+ },
228
+ '--processes': () => {
229
+ options.processes = Number(value)
230
+ },
231
+ '--runs': () => {
232
+ options.runs = Number(value)
233
+ },
234
+ '--mode': () => {
235
+ options.mode = value
236
+ },
237
+ '--spacing': () => {
238
+ options.spacing = value
239
+ },
240
+ '--workload': () => {
241
+ options.workload = value
242
+ },
243
+ '--revision': () => {
244
+ options.revision = value
245
+ }
246
+ }
247
+ const setValue = values[key]
248
+ if (!setValue) {
249
+ throw new Error(`Unknown option: ${arg}`)
250
+ }
251
+
252
+ setValue()
253
+ }
254
+
255
+ function parseOptions(args) {
256
+ const options = {
257
+ runtime: 'node',
258
+ driver: 'engine',
259
+ processes: 3,
260
+ runs: 3,
261
+ mode: 'all',
262
+ spacing: 'all',
263
+ workload: 'all',
264
+ revision: ''
265
+ }
266
+ for (const arg of args) {
267
+ applyOption(options, arg)
268
+ }
269
+
270
+ return validateOptions(options)
271
+ }
272
+
273
+ function median(values) {
274
+ if (values.length === 0) {
275
+ throw new Error('Cannot calculate a median without samples')
276
+ }
277
+
278
+ const sorted = [...values].sort((a, b) => a - b)
279
+
280
+ return sorted[Math.floor(sorted.length / 2)]
281
+ }