@guilz-dev/belay 0.3.0 → 0.6.0
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 +7 -1
- package/dist/adapters/cursor/runtime-entry.js +1 -0
- package/dist/adapters/layouts/scope.js +3 -2
- package/dist/adapters/shared/gate-runtime.d.ts +12 -16
- package/dist/adapters/shared/gate-runtime.js +499 -43
- package/dist/bundle/claude-runtime.mjs +8004 -2676
- package/dist/bundle/codex-runtime.mjs +8021 -2683
- package/dist/bundle/cursor-runtime.mjs +7993 -2667
- package/dist/cli.js +180 -6
- package/dist/commands/approve.d.ts +1 -0
- package/dist/commands/approve.js +45 -26
- package/dist/commands/classify-for-report.js +1 -1
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +264 -58
- package/dist/commands/doctor.js +47 -2
- package/dist/commands/explain.js +17 -2
- package/dist/commands/harvest.d.ts +29 -0
- package/dist/commands/harvest.js +72 -0
- package/dist/commands/judge.d.ts +11 -1
- package/dist/commands/judge.js +24 -4
- package/dist/commands/metrics.js +34 -2
- package/dist/commands/quality.d.ts +38 -0
- package/dist/commands/quality.js +105 -0
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.js +51 -0
- package/dist/commands/simulate.d.ts +1 -0
- package/dist/commands/simulate.js +7 -2
- package/dist/commands/standing-allow.d.ts +10 -0
- package/dist/commands/standing-allow.js +26 -0
- package/dist/commands/tui.d.ts +48 -0
- package/dist/commands/tui.js +150 -0
- package/dist/config-io.js +59 -17
- package/dist/conformance/guarantee-posture.d.ts +21 -0
- package/dist/conformance/guarantee-posture.js +46 -0
- package/dist/conformance/guarantee-table.d.ts +1 -0
- package/dist/conformance/guarantee-table.js +14 -9
- package/dist/core/approval-replay-cli.d.ts +7 -0
- package/dist/core/approval-replay-cli.js +36 -0
- package/dist/core/approval-replay.d.ts +45 -0
- package/dist/core/approval-replay.js +141 -0
- package/dist/core/approval-service.d.ts +15 -0
- package/dist/core/approval-service.js +53 -4
- package/dist/core/approval.d.ts +26 -3
- package/dist/core/approval.js +61 -7
- package/dist/core/audit-analysis.d.ts +7 -1
- package/dist/core/audit-analysis.js +111 -0
- package/dist/core/audit-metrics.d.ts +7 -0
- package/dist/core/audit-metrics.js +24 -4
- package/dist/core/audit-query.d.ts +1 -0
- package/dist/core/audit-query.js +3 -0
- package/dist/core/audit-replay-context.d.ts +35 -0
- package/dist/core/audit-replay-context.js +88 -0
- package/dist/core/audit-types.d.ts +24 -1
- package/dist/core/audit-types.js +1 -1
- package/dist/core/capability/approval-state-mutation.d.ts +13 -0
- package/dist/core/capability/approval-state-mutation.js +92 -0
- package/dist/core/capability/approval-v3.d.ts +15 -0
- package/dist/core/capability/approval-v3.js +74 -0
- package/dist/core/capability/attestation.d.ts +13 -0
- package/dist/core/capability/attestation.js +55 -0
- package/dist/core/capability/boundary-attestation-sign.d.ts +21 -0
- package/dist/core/capability/boundary-attestation-sign.js +84 -0
- package/dist/core/capability/boundary-driver-container.d.ts +7 -0
- package/dist/core/capability/boundary-driver-container.js +122 -0
- package/dist/core/capability/boundary-driver.d.ts +29 -0
- package/dist/core/capability/boundary-driver.js +43 -0
- package/dist/core/capability/boundary-egress.d.ts +21 -0
- package/dist/core/capability/boundary-egress.js +109 -0
- package/dist/core/capability/boundary-grant-materialize.d.ts +13 -0
- package/dist/core/capability/boundary-grant-materialize.js +78 -0
- package/dist/core/capability/boundary-profile.d.ts +15 -0
- package/dist/core/capability/boundary-profile.js +25 -0
- package/dist/core/capability/boundary-run.d.ts +23 -0
- package/dist/core/capability/boundary-run.js +23 -0
- package/dist/core/capability/boundary-session.d.ts +57 -0
- package/dist/core/capability/boundary-session.js +129 -0
- package/dist/core/capability/capability-request-hash.d.ts +2 -0
- package/dist/core/capability/capability-request-hash.js +8 -0
- package/dist/core/capability/gate-latency-ratchet.d.ts +7 -0
- package/dist/core/capability/gate-latency-ratchet.js +18 -0
- package/dist/core/capability/gate-policy-shadow.d.ts +25 -0
- package/dist/core/capability/gate-policy-shadow.js +108 -0
- package/dist/core/capability/gate-shadow-audit.d.ts +1 -0
- package/dist/core/capability/gate-shadow-audit.js +1 -0
- package/dist/core/capability/gate-shadow-ratchet.d.ts +14 -0
- package/dist/core/capability/gate-shadow-ratchet.js +81 -0
- package/dist/core/capability/grant-lease.d.ts +9 -0
- package/dist/core/capability/grant-lease.js +46 -0
- package/dist/core/capability/grant-loader.d.ts +12 -0
- package/dist/core/capability/grant-loader.js +23 -0
- package/dist/core/capability/grant-match.d.ts +6 -0
- package/dist/core/capability/grant-match.js +93 -0
- package/dist/core/capability/grant.d.ts +16 -0
- package/dist/core/capability/grant.js +16 -0
- package/dist/core/capability/index.d.ts +10 -1
- package/dist/core/capability/index.js +8 -0
- package/dist/core/capability/limits.d.ts +7 -0
- package/dist/core/capability/limits.js +55 -0
- package/dist/core/capability/paths.d.ts +2 -2
- package/dist/core/capability/paths.js +9 -9
- package/dist/core/capability/policy-bridge.d.ts +18 -0
- package/dist/core/capability/policy-bridge.js +51 -0
- package/dist/core/capability/policy-engine.d.ts +70 -0
- package/dist/core/capability/policy-engine.js +476 -0
- package/dist/core/capability/policy-types.d.ts +20 -0
- package/dist/core/capability/policy-types.js +1 -0
- package/dist/core/capability/request.d.ts +45 -0
- package/dist/core/capability/request.js +1 -0
- package/dist/core/capability/resolver.d.ts +4 -0
- package/dist/core/capability/resolver.js +4 -0
- package/dist/core/capability/trusted-workspace-roots.d.ts +25 -0
- package/dist/core/capability/trusted-workspace-roots.js +149 -0
- package/dist/core/capability/types.d.ts +11 -1
- package/dist/core/capability-approval.d.ts +2 -1
- package/dist/core/capability-approval.js +100 -2
- package/dist/core/classify-subagent.d.ts +2 -1
- package/dist/core/classify-subagent.js +40 -22
- package/dist/core/classify-tool.js +157 -64
- package/dist/core/config.d.ts +32 -3
- package/dist/core/config.js +105 -3
- package/dist/core/gate-contract.d.ts +3 -0
- package/dist/core/gate-contract.js +3 -0
- package/dist/core/gate-engine.js +12 -7
- package/dist/core/harvest.d.ts +53 -0
- package/dist/core/harvest.js +276 -0
- package/dist/core/index.d.ts +9 -3
- package/dist/core/index.js +6 -2
- package/dist/core/judge-doctor.d.ts +3 -0
- package/dist/core/judge-doctor.js +90 -11
- package/dist/core/judge-fallback-hints.d.ts +13 -0
- package/dist/core/judge-fallback-hints.js +108 -0
- package/dist/core/path-utils.d.ts +12 -0
- package/dist/core/path-utils.js +60 -7
- package/dist/core/reclassify.d.ts +3 -0
- package/dist/core/reclassify.js +44 -14
- package/dist/core/recovery/capability.d.ts +3 -0
- package/dist/core/recovery/capability.js +22 -0
- package/dist/core/recovery/fail-closed.d.ts +6 -0
- package/dist/core/recovery/fail-closed.js +25 -0
- package/dist/core/recovery/index.d.ts +3 -0
- package/dist/core/recovery/index.js +2 -0
- package/dist/core/recovery/types.d.ts +20 -0
- package/dist/core/recovery/types.js +1 -0
- package/dist/core/replay-scrub.d.ts +9 -0
- package/dist/core/replay-scrub.js +43 -0
- package/dist/core/shell-tokenizer.d.ts +2 -0
- package/dist/core/shell-tokenizer.js +82 -23
- package/dist/core/standing-allow.d.ts +50 -0
- package/dist/core/standing-allow.js +178 -0
- package/dist/core/transactional/diff-evaluator.js +4 -1
- package/dist/core/transactional/eligibility.js +4 -0
- package/dist/core/transactional/git-worktree.d.ts +10 -3
- package/dist/core/transactional/git-worktree.js +81 -11
- package/dist/core/transactional/reasons.d.ts +1 -0
- package/dist/core/transactional/reasons.js +4 -0
- package/dist/core/transactional/runner.js +22 -6
- package/dist/core/transactional/types.d.ts +4 -0
- package/dist/core/types.d.ts +36 -3
- package/dist/core/verdict/adapter.d.ts +3 -4
- package/dist/core/verdict/adapter.js +19 -6
- package/dist/core/verdict/containment.d.ts +4 -3
- package/dist/core/verdict/containment.js +17 -12
- package/dist/core/verdict/cursor-acp-client.d.ts +67 -0
- package/dist/core/verdict/cursor-acp-client.js +345 -0
- package/dist/core/verdict/egress-classify.d.ts +2 -2
- package/dist/core/verdict/egress-classify.js +2 -2
- package/dist/core/verdict/git-classifier.d.ts +26 -0
- package/dist/core/verdict/git-classifier.js +533 -0
- package/dist/core/verdict/judge-audit.d.ts +1 -0
- package/dist/core/verdict/judge-audit.js +33 -1
- package/dist/core/verdict/judge-baseline.d.ts +20 -0
- package/dist/core/verdict/judge-baseline.js +49 -0
- package/dist/core/verdict/judge-broker-service.d.ts +42 -0
- package/dist/core/verdict/judge-broker-service.js +291 -0
- package/dist/core/verdict/judge-catalog.js +1 -0
- package/dist/core/verdict/judge-cli-fingerprint.d.ts +6 -0
- package/dist/core/verdict/judge-cli-fingerprint.js +67 -0
- package/dist/core/verdict/judge-cli.d.ts +23 -0
- package/dist/core/verdict/judge-cli.js +139 -16
- package/dist/core/verdict/judge-factory.js +13 -4
- package/dist/core/verdict/judge-provider-matrix.d.ts +20 -0
- package/dist/core/verdict/judge-provider-matrix.js +66 -0
- package/dist/core/verdict/judge-runtime-config.d.ts +44 -0
- package/dist/core/verdict/judge-runtime-config.js +96 -0
- package/dist/core/verdict/judge-session-broker.d.ts +48 -0
- package/dist/core/verdict/judge-session-broker.js +200 -0
- package/dist/core/verdict/judge-session-guard.d.ts +27 -0
- package/dist/core/verdict/judge-session-guard.js +91 -0
- package/dist/core/verdict/judge-session-kill-switch.d.ts +11 -0
- package/dist/core/verdict/judge-session-kill-switch.js +43 -0
- package/dist/core/verdict/judge-session-mutex.d.ts +9 -0
- package/dist/core/verdict/judge-session-mutex.js +23 -0
- package/dist/core/verdict/judge-shadow.d.ts +24 -0
- package/dist/core/verdict/judge-shadow.js +116 -0
- package/dist/core/verdict/judge-transport.d.ts +44 -0
- package/dist/core/verdict/judge-transport.js +268 -0
- package/dist/core/verdict/judge.d.ts +14 -14
- package/dist/core/verdict/judge.js +5 -67
- package/dist/core/verdict/launcher-resolve.d.ts +2 -0
- package/dist/core/verdict/launcher-resolve.js +16 -0
- package/dist/core/verdict/parser.js +6 -1
- package/dist/core/verdict/prescan.d.ts +15 -0
- package/dist/core/verdict/prescan.js +68 -0
- package/dist/core/verdict/shell-policy.d.ts +25 -0
- package/dist/core/verdict/shell-policy.js +40 -0
- package/dist/core/verdict/shell-semantics.d.ts +24 -0
- package/dist/core/verdict/shell-semantics.js +225 -0
- package/dist/core/verdict/types.d.ts +26 -1
- package/dist/core/verdict/verdict.js +300 -179
- package/dist/corpus/adversarial-probe.d.ts +86 -0
- package/dist/corpus/adversarial-probe.js +220 -0
- package/dist/corpus/evaluate.d.ts +45 -12
- package/dist/corpus/evaluate.js +61 -8
- package/dist/corpus/gate-latency-budget.d.ts +39 -0
- package/dist/corpus/gate-latency-budget.js +33 -0
- package/dist/corpus/gates.d.ts +35 -0
- package/dist/corpus/gates.js +81 -0
- package/dist/corpus/judge-accuracy.d.ts +8 -0
- package/dist/corpus/judge-accuracy.js +39 -0
- package/dist/corpus/must-allow-commands.d.ts +5 -0
- package/dist/corpus/must-allow-commands.js +13 -0
- package/dist/corpus/mutators.d.ts +28 -0
- package/dist/corpus/mutators.js +162 -0
- package/dist/corpus/ratchet.d.ts +42 -0
- package/dist/corpus/ratchet.js +116 -0
- package/dist/corpus/runtime-match.d.ts +19 -0
- package/dist/corpus/runtime-match.js +43 -0
- package/dist/corpus/standing-allow-catalog.generated.d.ts +13 -0
- package/dist/corpus/standing-allow-catalog.generated.js +99 -0
- package/dist/corpus/types.d.ts +47 -0
- package/dist/corpus/types.js +157 -0
- package/dist/installer.js +4 -1
- package/dist/judge-broker-daemon.d.ts +1 -0
- package/dist/judge-broker-daemon.js +137 -0
- package/dist/services/sandbox-service.d.ts +5 -0
- package/dist/services/sandbox-service.js +30 -2
- package/dist/types.d.ts +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -3
- package/skills/belay/SKILL.md +7 -1
- package/skills/belay/belay-approve.md +17 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { unlink } from 'node:fs/promises';
|
|
4
|
+
import net from 'node:net';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { runCliJsonWithTimeouts } from './judge-cli.js';
|
|
8
|
+
import { normalizeJudgeSessionConfig } from './judge-runtime-config.js';
|
|
9
|
+
import { getRepoJudgeSessionBroker, JudgeSessionBroker, resetJudgeSessionBrokersForTests, stopRepoJudgeSessionBroker, } from './judge-session-broker.js';
|
|
10
|
+
export const JUDGE_BROKER_SOCKET = 'judge-broker.sock';
|
|
11
|
+
export const JUDGE_BROKER_STATUS = 'judge-broker.json';
|
|
12
|
+
export const JUDGE_BROKER_PID = 'judge-broker.pid';
|
|
13
|
+
export const JUDGE_BROKER_SESSION = 'judge-broker-session.json';
|
|
14
|
+
export function judgeBrokerPaths(stateDir) {
|
|
15
|
+
return {
|
|
16
|
+
stateDir,
|
|
17
|
+
socketPath: path.join(stateDir, JUDGE_BROKER_SOCKET),
|
|
18
|
+
statusPath: path.join(stateDir, JUDGE_BROKER_STATUS),
|
|
19
|
+
pidPath: path.join(stateDir, JUDGE_BROKER_PID),
|
|
20
|
+
sessionConfigPath: path.join(stateDir, JUDGE_BROKER_SESSION),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function daemonScriptPath() {
|
|
24
|
+
return fileURLToPath(new URL('../../judge-broker-daemon.js', import.meta.url));
|
|
25
|
+
}
|
|
26
|
+
function isProcessAlive(pid) {
|
|
27
|
+
try {
|
|
28
|
+
process.kill(pid, 0);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function useInProcessBroker(env = process.env) {
|
|
36
|
+
return Boolean(env.VITEST || env.VITEST_WORKER_ID || env.BELAY_JUDGE_BROKER_IN_PROCESS === '1');
|
|
37
|
+
}
|
|
38
|
+
async function readBrokerStatus(statusPath) {
|
|
39
|
+
if (!existsSync(statusPath)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const { readFile } = await import('node:fs/promises');
|
|
44
|
+
const raw = JSON.parse(await readFile(statusPath, 'utf8'));
|
|
45
|
+
if (typeof raw.pid !== 'number' || typeof raw.socketPath !== 'string') {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return raw;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function readBrokerSessionConfig(sessionConfigPath) {
|
|
55
|
+
if (!existsSync(sessionConfigPath)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const { readFile } = await import('node:fs/promises');
|
|
60
|
+
const raw = JSON.parse(await readFile(sessionConfigPath, 'utf8'));
|
|
61
|
+
return normalizeJudgeSessionConfig({ ...raw, enabled: true });
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function brokerSessionConfigPayload(sessionConfig) {
|
|
68
|
+
return JSON.stringify(normalizeJudgeSessionConfig({ ...sessionConfig, enabled: true }));
|
|
69
|
+
}
|
|
70
|
+
async function writeBrokerSessionConfig(paths, sessionConfig) {
|
|
71
|
+
const { writeFile } = await import('node:fs/promises');
|
|
72
|
+
await writeFile(paths.sessionConfigPath, brokerSessionConfigPayload(sessionConfig), {
|
|
73
|
+
encoding: 'utf8',
|
|
74
|
+
mode: 0o600,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async function connectBrokerSocket(socketPath, timeoutMs) {
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
const socket = net.createConnection(socketPath);
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
socket.destroy();
|
|
82
|
+
reject(new Error('judge broker connect timed out'));
|
|
83
|
+
}, timeoutMs);
|
|
84
|
+
socket.once('connect', () => {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
resolve(socket);
|
|
87
|
+
});
|
|
88
|
+
socket.once('error', (error) => {
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
reject(error);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function rpcEvaluate(socketPath, request, timeoutMs, connectTimeoutMs) {
|
|
95
|
+
const socket = await connectBrokerSocket(socketPath, connectTimeoutMs);
|
|
96
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
97
|
+
const payload = JSON.stringify({
|
|
98
|
+
type: 'evaluate',
|
|
99
|
+
id,
|
|
100
|
+
request,
|
|
101
|
+
timeoutMs,
|
|
102
|
+
});
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
let buffer = '';
|
|
105
|
+
const timer = setTimeout(() => {
|
|
106
|
+
socket.destroy();
|
|
107
|
+
reject(new Error('judge broker evaluate timed out'));
|
|
108
|
+
}, timeoutMs + connectTimeoutMs);
|
|
109
|
+
const onData = (chunk) => {
|
|
110
|
+
buffer += String(chunk);
|
|
111
|
+
const lines = buffer.split('\n');
|
|
112
|
+
buffer = lines.pop() ?? '';
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
if (!line.trim()) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const message = JSON.parse(line);
|
|
119
|
+
if (message.id !== id) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
socket.destroy();
|
|
124
|
+
if (message.type === 'error') {
|
|
125
|
+
reject(new Error(message.error ?? 'judge broker error'));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (message.result) {
|
|
129
|
+
resolve(message.result);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// wait for more data
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
socket.on('data', onData);
|
|
138
|
+
socket.on('error', (error) => {
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
reject(error);
|
|
141
|
+
});
|
|
142
|
+
socket.write(`${payload}\n`);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
export async function ensureJudgeBrokerDaemon(stateDir, repoRoot, sessionConfig) {
|
|
146
|
+
const paths = judgeBrokerPaths(stateDir);
|
|
147
|
+
const brokerSessionConfig = normalizeJudgeSessionConfig({ ...sessionConfig, enabled: true });
|
|
148
|
+
const nextConfigPayload = brokerSessionConfigPayload(brokerSessionConfig);
|
|
149
|
+
const existingConfig = await readBrokerSessionConfig(paths.sessionConfigPath);
|
|
150
|
+
const configChanged = existingConfig !== null && brokerSessionConfigPayload(existingConfig) !== nextConfigPayload;
|
|
151
|
+
const status = await readBrokerStatus(paths.statusPath);
|
|
152
|
+
if (status && isProcessAlive(status.pid) && existsSync(paths.socketPath) && !configChanged) {
|
|
153
|
+
await writeBrokerSessionConfig(paths, brokerSessionConfig);
|
|
154
|
+
return paths;
|
|
155
|
+
}
|
|
156
|
+
if (status?.pid && isProcessAlive(status.pid)) {
|
|
157
|
+
await stopJudgeBrokerDaemon(stateDir);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
await cleanupJudgeBrokerArtifacts(paths);
|
|
161
|
+
}
|
|
162
|
+
await writeBrokerSessionConfig(paths, brokerSessionConfig);
|
|
163
|
+
const child = spawn(process.execPath, [daemonScriptPath()], {
|
|
164
|
+
detached: true,
|
|
165
|
+
stdio: 'ignore',
|
|
166
|
+
env: {
|
|
167
|
+
...process.env,
|
|
168
|
+
BELAY_JUDGE_BROKER_REPO_ROOT: repoRoot,
|
|
169
|
+
BELAY_JUDGE_BROKER_STATE_DIR: stateDir,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
child.unref();
|
|
173
|
+
const deadline = Date.now() + sessionConfig.connectTimeoutMs;
|
|
174
|
+
while (Date.now() < deadline) {
|
|
175
|
+
if (existsSync(paths.socketPath)) {
|
|
176
|
+
const live = await readBrokerStatus(paths.statusPath);
|
|
177
|
+
if (live && isProcessAlive(live.pid)) {
|
|
178
|
+
return paths;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
182
|
+
}
|
|
183
|
+
throw new Error('judge broker daemon failed to start');
|
|
184
|
+
}
|
|
185
|
+
export async function cleanupJudgeBrokerArtifacts(paths) {
|
|
186
|
+
for (const artifact of [
|
|
187
|
+
paths.socketPath,
|
|
188
|
+
paths.statusPath,
|
|
189
|
+
paths.pidPath,
|
|
190
|
+
paths.sessionConfigPath,
|
|
191
|
+
]) {
|
|
192
|
+
if (existsSync(artifact)) {
|
|
193
|
+
await unlink(artifact).catch(() => undefined);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
export async function stopJudgeBrokerDaemon(stateDir) {
|
|
198
|
+
const paths = judgeBrokerPaths(stateDir);
|
|
199
|
+
const status = await readBrokerStatus(paths.statusPath);
|
|
200
|
+
if (status?.pid && isProcessAlive(status.pid)) {
|
|
201
|
+
try {
|
|
202
|
+
process.kill(status.pid, 'SIGTERM');
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// already stopped
|
|
206
|
+
}
|
|
207
|
+
const deadline = Date.now() + 1_000;
|
|
208
|
+
while (isProcessAlive(status.pid) && Date.now() < deadline) {
|
|
209
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
210
|
+
}
|
|
211
|
+
if (isProcessAlive(status.pid)) {
|
|
212
|
+
try {
|
|
213
|
+
process.kill(status.pid, 'SIGKILL');
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// already stopped
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
await cleanupJudgeBrokerArtifacts(paths);
|
|
221
|
+
return status?.pid ? 1 : 0;
|
|
222
|
+
}
|
|
223
|
+
export async function evaluateViaJudgeBroker(options) {
|
|
224
|
+
const runCommand = options.runCommand ??
|
|
225
|
+
((invocation, timeout) => runCliJsonWithTimeouts(invocation, { evalTimeoutMs: timeout }));
|
|
226
|
+
if (useInProcessBroker()) {
|
|
227
|
+
const broker = getRepoJudgeSessionBroker(options.repoRoot, {
|
|
228
|
+
config: options.sessionConfig,
|
|
229
|
+
runCommand,
|
|
230
|
+
});
|
|
231
|
+
return broker.evaluate(options.request, options.timeoutMs);
|
|
232
|
+
}
|
|
233
|
+
const paths = await ensureJudgeBrokerDaemon(options.stateDir, options.repoRoot, options.sessionConfig);
|
|
234
|
+
return rpcEvaluate(paths.socketPath, options.request, options.timeoutMs, options.sessionConfig.connectTimeoutMs);
|
|
235
|
+
}
|
|
236
|
+
export function resetJudgeBrokerForTests() {
|
|
237
|
+
resetJudgeSessionBrokersForTests();
|
|
238
|
+
}
|
|
239
|
+
async function rpcInvalidate(socketPath, sessionKey, reason, connectTimeoutMs) {
|
|
240
|
+
const socket = await connectBrokerSocket(socketPath, connectTimeoutMs);
|
|
241
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
242
|
+
const payload = JSON.stringify({ type: 'invalidate', id, sessionKey, reason });
|
|
243
|
+
await new Promise((resolve, reject) => {
|
|
244
|
+
let buffer = '';
|
|
245
|
+
const timer = setTimeout(() => {
|
|
246
|
+
socket.destroy();
|
|
247
|
+
reject(new Error('judge broker invalidate timed out'));
|
|
248
|
+
}, connectTimeoutMs);
|
|
249
|
+
const onData = (chunk) => {
|
|
250
|
+
buffer += String(chunk);
|
|
251
|
+
const lines = buffer.split('\n');
|
|
252
|
+
buffer = lines.pop() ?? '';
|
|
253
|
+
for (const line of lines) {
|
|
254
|
+
if (!line.trim()) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
const message = JSON.parse(line);
|
|
259
|
+
if (message.id !== id) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
clearTimeout(timer);
|
|
263
|
+
socket.destroy();
|
|
264
|
+
resolve();
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// wait for more data
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
socket.on('data', onData);
|
|
272
|
+
socket.on('error', (error) => {
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
reject(error);
|
|
275
|
+
});
|
|
276
|
+
socket.write(`${payload}\n`);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
export async function invalidateJudgeSession(options) {
|
|
280
|
+
if (useInProcessBroker()) {
|
|
281
|
+
const broker = getRepoJudgeSessionBroker(options.repoRoot, {
|
|
282
|
+
config: { ...options.sessionConfig, enabled: true },
|
|
283
|
+
runCommand: (invocation, timeout) => runCliJsonWithTimeouts(invocation, { evalTimeoutMs: timeout }),
|
|
284
|
+
});
|
|
285
|
+
broker.invalidateSession(options.sessionKey, options.reason);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const paths = await ensureJudgeBrokerDaemon(options.stateDir, options.repoRoot, options.sessionConfig);
|
|
289
|
+
await rpcInvalidate(paths.socketPath, options.sessionKey, options.reason, options.sessionConfig.connectTimeoutMs);
|
|
290
|
+
}
|
|
291
|
+
export { JudgeSessionBroker, stopRepoJudgeSessionBroker };
|
|
@@ -102,6 +102,7 @@ export function resolveJudgeFromCatalog(input) {
|
|
|
102
102
|
const model = input.model?.trim() || spec.defaultModel;
|
|
103
103
|
const endpoint = input.endpoint !== undefined ? input.endpoint?.trim() || null : spec.defaultEndpoint;
|
|
104
104
|
return {
|
|
105
|
+
mode: 'shadow',
|
|
105
106
|
provider: spec.driver,
|
|
106
107
|
providerId: spec.id,
|
|
107
108
|
model,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JudgeProviderId } from './judge-catalog.js';
|
|
2
|
+
export type CliFingerprintResolver = (providerId: Exclude<JudgeProviderId, 'ollama'>) => Promise<string>;
|
|
3
|
+
export declare function setCliFingerprintResolverForTests(resolver: CliFingerprintResolver | null): void;
|
|
4
|
+
export declare function resolveCliVersionFingerprint(providerId: Exclude<JudgeProviderId, 'ollama'>, timeoutMs?: number): Promise<string>;
|
|
5
|
+
export declare function resolveJudgeSessionFingerprint(providerId: Exclude<JudgeProviderId, 'ollama'>, timeoutMs?: number): Promise<string>;
|
|
6
|
+
export declare function resetCliFingerprintCache(): void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
const CLI_BINARIES = {
|
|
3
|
+
codex: 'codex',
|
|
4
|
+
cursor: 'cursor-agent',
|
|
5
|
+
claude: 'claude',
|
|
6
|
+
};
|
|
7
|
+
let fingerprintResolver = null;
|
|
8
|
+
const fingerprintCache = new Map();
|
|
9
|
+
export function setCliFingerprintResolverForTests(resolver) {
|
|
10
|
+
fingerprintResolver = resolver;
|
|
11
|
+
fingerprintCache.clear();
|
|
12
|
+
}
|
|
13
|
+
async function runVersionProbe(binary, timeoutMs) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const child = spawn(binary, ['--version'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
16
|
+
let stdout = '';
|
|
17
|
+
let stderr = '';
|
|
18
|
+
const timer = setTimeout(() => {
|
|
19
|
+
child.kill('SIGTERM');
|
|
20
|
+
reject(new Error(`${binary} --version timed out`));
|
|
21
|
+
}, timeoutMs);
|
|
22
|
+
child.stdout.on('data', (chunk) => {
|
|
23
|
+
stdout += String(chunk);
|
|
24
|
+
});
|
|
25
|
+
child.stderr.on('data', (chunk) => {
|
|
26
|
+
stderr += String(chunk);
|
|
27
|
+
});
|
|
28
|
+
child.on('error', (error) => {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
reject(error);
|
|
31
|
+
});
|
|
32
|
+
child.on('close', (code) => {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
const output = (stdout || stderr).trim();
|
|
35
|
+
if (code === 0 && output) {
|
|
36
|
+
resolve(output.split(/\r?\n/)[0]?.trim() || output);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
reject(new Error(stderr.trim() || `${binary} --version exited with code ${code ?? 'unknown'}`));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export async function resolveCliVersionFingerprint(providerId, timeoutMs = 3_000) {
|
|
44
|
+
const cacheKey = providerId;
|
|
45
|
+
const cached = fingerprintCache.get(cacheKey);
|
|
46
|
+
if (cached) {
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
if (fingerprintResolver) {
|
|
50
|
+
const resolved = await fingerprintResolver(providerId);
|
|
51
|
+
fingerprintCache.set(cacheKey, resolved);
|
|
52
|
+
return resolved;
|
|
53
|
+
}
|
|
54
|
+
const binary = CLI_BINARIES[providerId];
|
|
55
|
+
const version = await runVersionProbe(binary, timeoutMs);
|
|
56
|
+
fingerprintCache.set(cacheKey, version);
|
|
57
|
+
return version;
|
|
58
|
+
}
|
|
59
|
+
export async function resolveJudgeSessionFingerprint(providerId, timeoutMs = 3_000) {
|
|
60
|
+
if (providerId === 'cursor' && !fingerprintResolver) {
|
|
61
|
+
return 'cursor-acp-v1';
|
|
62
|
+
}
|
|
63
|
+
return resolveCliVersionFingerprint(providerId, timeoutMs);
|
|
64
|
+
}
|
|
65
|
+
export function resetCliFingerprintCache() {
|
|
66
|
+
fingerprintCache.clear();
|
|
67
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ScrubOptions } from '../types.js';
|
|
2
2
|
import { parseTier1Json, type TracedTier1Judge } from './judge.js';
|
|
3
3
|
import type { JudgeProviderId } from './judge-catalog.js';
|
|
4
|
+
import type { BelayJudgeRuntimeConfig } from './judge-runtime-config.js';
|
|
4
5
|
export interface CliJudgeOptions {
|
|
5
6
|
providerId: JudgeProviderId;
|
|
6
7
|
modelRequested: string;
|
|
@@ -9,6 +10,10 @@ export interface CliJudgeOptions {
|
|
|
9
10
|
sensitivePaths: string[];
|
|
10
11
|
scrubOptions: ScrubOptions;
|
|
11
12
|
runCliCommand?: CliJudgeRunCommand;
|
|
13
|
+
runtime?: BelayJudgeRuntimeConfig;
|
|
14
|
+
repoRoot?: string;
|
|
15
|
+
stateDir?: string;
|
|
16
|
+
judgeMode?: string;
|
|
12
17
|
}
|
|
13
18
|
export interface CliInvocation {
|
|
14
19
|
binary: string;
|
|
@@ -16,8 +21,26 @@ export interface CliInvocation {
|
|
|
16
21
|
stdin?: string;
|
|
17
22
|
}
|
|
18
23
|
export type CliJudgeRunCommand = (invocation: CliInvocation, timeoutMs: number) => Promise<string>;
|
|
24
|
+
export type CliRunErrorKind = 'timeout' | 'spawn_error' | 'exit_nonzero';
|
|
25
|
+
export declare class CliRunError extends Error {
|
|
26
|
+
kind: CliRunErrorKind;
|
|
27
|
+
exitCode?: number;
|
|
28
|
+
stderr?: string;
|
|
29
|
+
stdout?: string;
|
|
30
|
+
command?: string;
|
|
31
|
+
constructor(kind: CliRunErrorKind, message: string, details?: {
|
|
32
|
+
exitCode?: number;
|
|
33
|
+
stderr?: string;
|
|
34
|
+
stdout?: string;
|
|
35
|
+
command?: string;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
19
38
|
export declare function buildCliInvocation(providerId: Exclude<JudgeProviderId, 'ollama'>, prompt: string, model: string): CliInvocation;
|
|
20
39
|
export declare function parseCliJudgeOutput(providerId: Exclude<JudgeProviderId, 'ollama'>, raw: string): ReturnType<typeof parseTier1Json>;
|
|
40
|
+
export declare function runCliJsonWithTimeouts(invocation: CliInvocation, budgets: {
|
|
41
|
+
connectTimeoutMs?: number;
|
|
42
|
+
evalTimeoutMs: number;
|
|
43
|
+
}): Promise<string>;
|
|
21
44
|
export declare function createCodexCliJudge(options: Omit<CliJudgeOptions, 'providerId'>): TracedTier1Judge;
|
|
22
45
|
export declare function createCursorCliJudge(options: Omit<CliJudgeOptions, 'providerId'>): TracedTier1Judge;
|
|
23
46
|
export declare function createClaudeCliJudge(options: Omit<CliJudgeOptions, 'providerId'>): TracedTier1Judge;
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { buildTier1Prompt, parseTier1Json, prescanInterpreterCode, } from './judge.js';
|
|
3
3
|
import { scrubOutboundForJudge } from './judge-outbound.js';
|
|
4
|
+
import { DEFAULT_JUDGE_RUNTIME_CONFIG } from './judge-runtime-config.js';
|
|
5
|
+
import { transportFallbackToFailClosedReason } from './judge-session-guard.js';
|
|
6
|
+
import { evaluateWithJudgeTransport } from './judge-transport.js';
|
|
7
|
+
export class CliRunError extends Error {
|
|
8
|
+
kind;
|
|
9
|
+
exitCode;
|
|
10
|
+
stderr;
|
|
11
|
+
stdout;
|
|
12
|
+
command;
|
|
13
|
+
constructor(kind, message, details = {}) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'CliRunError';
|
|
16
|
+
this.kind = kind;
|
|
17
|
+
this.exitCode = details.exitCode;
|
|
18
|
+
this.stderr = details.stderr;
|
|
19
|
+
this.stdout = details.stdout;
|
|
20
|
+
this.command = details.command;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
4
23
|
const CLI_BINARIES = {
|
|
5
24
|
codex: 'codex',
|
|
6
25
|
cursor: 'cursor-agent',
|
|
7
26
|
claude: 'claude',
|
|
8
27
|
};
|
|
28
|
+
const FORCE_KILL_GRACE_MS = 250;
|
|
9
29
|
function failClosedVerdict(reason) {
|
|
10
30
|
return {
|
|
11
31
|
local_recoverable: false,
|
|
@@ -162,17 +182,67 @@ export function parseCliJudgeOutput(providerId, raw) {
|
|
|
162
182
|
return parseCliJsonLines(raw);
|
|
163
183
|
}
|
|
164
184
|
async function runCliJson(invocation, timeoutMs) {
|
|
185
|
+
return runCliJsonWithTimeouts(invocation, { evalTimeoutMs: timeoutMs });
|
|
186
|
+
}
|
|
187
|
+
export async function runCliJsonWithTimeouts(invocation, budgets) {
|
|
165
188
|
const { binary, args, stdin } = invocation;
|
|
189
|
+
const connectTimeoutMs = budgets.connectTimeoutMs ?? 0;
|
|
190
|
+
if (connectTimeoutMs > 0) {
|
|
191
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
192
|
+
}
|
|
166
193
|
return new Promise((resolve, reject) => {
|
|
167
194
|
const child = spawn(binary, args, {
|
|
168
195
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
169
196
|
});
|
|
197
|
+
const command = [binary, ...args].join(' ');
|
|
170
198
|
let stdout = '';
|
|
171
199
|
let stderr = '';
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
200
|
+
let settled = false;
|
|
201
|
+
let timer = null;
|
|
202
|
+
let forceKillTimer = null;
|
|
203
|
+
const cleanup = () => {
|
|
204
|
+
if (timer) {
|
|
205
|
+
clearTimeout(timer);
|
|
206
|
+
}
|
|
207
|
+
if (forceKillTimer) {
|
|
208
|
+
clearTimeout(forceKillTimer);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const rejectOnce = (error) => {
|
|
212
|
+
if (settled)
|
|
213
|
+
return;
|
|
214
|
+
settled = true;
|
|
215
|
+
cleanup();
|
|
216
|
+
reject(error);
|
|
217
|
+
};
|
|
218
|
+
const resolveOnce = (value) => {
|
|
219
|
+
if (settled)
|
|
220
|
+
return;
|
|
221
|
+
settled = true;
|
|
222
|
+
cleanup();
|
|
223
|
+
resolve(value);
|
|
224
|
+
};
|
|
225
|
+
timer = setTimeout(() => {
|
|
226
|
+
rejectOnce(new CliRunError('timeout', `${binary} timed out`, {
|
|
227
|
+
stderr: stderr.trim() || undefined,
|
|
228
|
+
stdout: stdout.trim() || undefined,
|
|
229
|
+
command,
|
|
230
|
+
}));
|
|
231
|
+
try {
|
|
232
|
+
child.kill('SIGTERM');
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
forceKillTimer = setTimeout(() => {
|
|
238
|
+
try {
|
|
239
|
+
child.kill('SIGKILL');
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// process already exited
|
|
243
|
+
}
|
|
244
|
+
}, FORCE_KILL_GRACE_MS);
|
|
245
|
+
}, budgets.evalTimeoutMs);
|
|
176
246
|
child.stdout.on('data', (chunk) => {
|
|
177
247
|
stdout += String(chunk);
|
|
178
248
|
});
|
|
@@ -184,16 +254,23 @@ async function runCliJson(invocation, timeoutMs) {
|
|
|
184
254
|
}
|
|
185
255
|
child.stdin.end();
|
|
186
256
|
child.on('error', (error) => {
|
|
187
|
-
|
|
188
|
-
|
|
257
|
+
rejectOnce(new CliRunError('spawn_error', error instanceof Error ? error.message : String(error), {
|
|
258
|
+
command,
|
|
259
|
+
}));
|
|
189
260
|
});
|
|
190
261
|
child.on('close', (code) => {
|
|
191
|
-
|
|
262
|
+
if (settled)
|
|
263
|
+
return;
|
|
192
264
|
if (code === 0 && stdout.trim()) {
|
|
193
|
-
|
|
265
|
+
resolveOnce(stdout.trim());
|
|
194
266
|
return;
|
|
195
267
|
}
|
|
196
|
-
|
|
268
|
+
rejectOnce(new CliRunError('exit_nonzero', stderr.trim() || `${binary} exited with code ${code ?? 'unknown'}`, {
|
|
269
|
+
exitCode: typeof code === 'number' ? code : undefined,
|
|
270
|
+
stderr: stderr.trim() || undefined,
|
|
271
|
+
stdout: stdout.trim() || undefined,
|
|
272
|
+
command,
|
|
273
|
+
}));
|
|
197
274
|
});
|
|
198
275
|
});
|
|
199
276
|
}
|
|
@@ -202,6 +279,10 @@ function createCliJudge(options) {
|
|
|
202
279
|
const timeoutMs = options.timeoutMs ?? 25000;
|
|
203
280
|
const providerId = options.providerId;
|
|
204
281
|
const runCommand = options.runCliCommand ?? runCliJson;
|
|
282
|
+
const runtime = options.runtime ?? DEFAULT_JUDGE_RUNTIME_CONFIG;
|
|
283
|
+
const repoRoot = options.repoRoot ?? process.cwd();
|
|
284
|
+
const stateDir = options.stateDir ?? repoRoot;
|
|
285
|
+
const judgeMode = options.judgeMode ?? 'enforce';
|
|
205
286
|
const judge = {
|
|
206
287
|
lastTrace: initialTrace(options),
|
|
207
288
|
async evaluate(input) {
|
|
@@ -233,27 +314,68 @@ function createCliJudge(options) {
|
|
|
233
314
|
}
|
|
234
315
|
const prompt = buildTier1Prompt(scrubbed.text);
|
|
235
316
|
try {
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
317
|
+
const transportResult = await evaluateWithJudgeTransport({
|
|
318
|
+
prompt,
|
|
319
|
+
context: {
|
|
320
|
+
providerId,
|
|
321
|
+
model: options.modelResolved,
|
|
322
|
+
repoRoot: input.context.repoRoot || repoRoot,
|
|
323
|
+
stateDir,
|
|
324
|
+
judgeMode,
|
|
325
|
+
runtime,
|
|
326
|
+
judgeTimeoutMs: timeoutMs,
|
|
327
|
+
},
|
|
328
|
+
}, { runCommand });
|
|
329
|
+
const traceTransport = transportResult.transport === 'acp'
|
|
330
|
+
? 'cursor-acp'
|
|
331
|
+
: transportResult.transport === 'session'
|
|
332
|
+
? `${providerId}-cli-session`
|
|
333
|
+
: transport;
|
|
334
|
+
if (!transportResult.verdict) {
|
|
335
|
+
const fallbackReason = transportResult.fallbackReason ??
|
|
336
|
+
`${providerId}_cli_parse_error`;
|
|
239
337
|
judge.lastTrace = {
|
|
240
338
|
provider: 'fallback',
|
|
241
339
|
modelRequested: options.modelRequested,
|
|
242
340
|
modelResolved: options.modelResolved,
|
|
243
341
|
latencyMs: Date.now() - started,
|
|
244
|
-
fallbackReason
|
|
342
|
+
fallbackReason,
|
|
343
|
+
judgeFallbackReason: fallbackReason,
|
|
344
|
+
judgeSessionUsed: transportResult.sessionUsed,
|
|
345
|
+
judgeSessionReused: transportResult.sessionReused,
|
|
346
|
+
judgeSessionRefHash: transportResult.sessionRefHash,
|
|
347
|
+
judgeSessionResetReason: transportResult.sessionResetReason,
|
|
348
|
+
judgeConnectMs: transportResult.connectMs,
|
|
349
|
+
judgeEvalMs: transportResult.evalMs,
|
|
350
|
+
judgeParseMs: transportResult.parseMs,
|
|
351
|
+
judgeShadowCompared: transportResult.shadowCompared,
|
|
352
|
+
judgeShadowMismatch: transportResult.shadowMismatch,
|
|
353
|
+
judgeShadowMismatchRateWindow: transportResult.shadowMismatchRateWindow,
|
|
354
|
+
judgeKillSwitchTriggered: transportResult.killSwitchTriggered,
|
|
245
355
|
};
|
|
246
|
-
return failClosedVerdict(
|
|
356
|
+
return failClosedVerdict(transportFallbackToFailClosedReason(providerId, fallbackReason));
|
|
247
357
|
}
|
|
248
358
|
judge.lastTrace = {
|
|
249
359
|
provider: traceProvider(options.providerId),
|
|
250
360
|
modelRequested: options.modelRequested,
|
|
251
361
|
modelResolved: options.modelResolved,
|
|
252
362
|
latencyMs: Date.now() - started,
|
|
253
|
-
transport,
|
|
363
|
+
transport: traceTransport,
|
|
254
364
|
outboundRedacted: true,
|
|
365
|
+
judgeSessionUsed: transportResult.sessionUsed,
|
|
366
|
+
judgeSessionReused: transportResult.sessionReused,
|
|
367
|
+
judgeSessionRefHash: transportResult.sessionRefHash,
|
|
368
|
+
judgeSessionResetReason: transportResult.sessionResetReason,
|
|
369
|
+
judgeFallbackReason: transportResult.fallbackReason,
|
|
370
|
+
judgeConnectMs: transportResult.connectMs,
|
|
371
|
+
judgeEvalMs: transportResult.evalMs,
|
|
372
|
+
judgeParseMs: transportResult.parseMs,
|
|
373
|
+
judgeShadowCompared: transportResult.shadowCompared,
|
|
374
|
+
judgeShadowMismatch: transportResult.shadowMismatch,
|
|
375
|
+
judgeShadowMismatchRateWindow: transportResult.shadowMismatchRateWindow,
|
|
376
|
+
judgeKillSwitchTriggered: transportResult.killSwitchTriggered,
|
|
255
377
|
};
|
|
256
|
-
return
|
|
378
|
+
return transportResult.verdict;
|
|
257
379
|
}
|
|
258
380
|
catch {
|
|
259
381
|
judge.lastTrace = {
|
|
@@ -262,6 +384,7 @@ function createCliJudge(options) {
|
|
|
262
384
|
modelResolved: options.modelResolved,
|
|
263
385
|
latencyMs: Date.now() - started,
|
|
264
386
|
fallbackReason: `${providerId}_cli_unavailable`,
|
|
387
|
+
judgeFallbackReason: `${providerId}_cli_unavailable`,
|
|
265
388
|
};
|
|
266
389
|
return failClosedVerdict(`${providerId}_cli_unavailable`);
|
|
267
390
|
}
|