@wix/pathgrade 1.0.28 → 1.0.30
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/dist/agents/claude.js +5 -4
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.d.ts +2 -1
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.js +35 -17
- package/dist/internal/direct-mcp-v2/claude-profile.d.ts +33 -7
- package/dist/internal/direct-mcp-v2/claude-profile.js +104 -72
- package/dist/internal/direct-mcp-v2/public-scenario-runtime.js +13 -16
- package/dist/sdk/scorer-utils.js +13 -3
- package/dist/types.d.ts +2 -0
- package/dist/utils/llm-providers/anthropic.js +5 -2
- package/dist/utils/llm.js +8 -2
- package/package.json +2 -2
package/dist/agents/claude.js
CHANGED
|
@@ -88,10 +88,11 @@ export class ClaudeAgent extends BaseAgent {
|
|
|
88
88
|
hostEnv,
|
|
89
89
|
sandboxProfile: this.deps.sandboxProfile,
|
|
90
90
|
});
|
|
91
|
-
const claudeCodeExecutable =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
const claudeCodeExecutable = sessionOptions?.admittedClaudeCodeExecutable
|
|
92
|
+
?? resolveClaudeCodeExecutable({
|
|
93
|
+
agentOptionsExecutable: this.opts.claudeCodeExecutable,
|
|
94
|
+
envExecutable,
|
|
95
|
+
});
|
|
95
96
|
const mcpMountOptions = {
|
|
96
97
|
workspacePath,
|
|
97
98
|
mcpConfigPath: sessionOptions?.mcpConfigPath,
|
|
@@ -2,7 +2,7 @@ import { type McpSdkServerConfigWithInstance, type Options as ClaudeSdkOptions,
|
|
|
2
2
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { type CleanupReport } from './cleanup.js';
|
|
4
4
|
import { type MaterializedProjection } from './materialize.js';
|
|
5
|
-
import type { EvidenceEnvelope, ScenarioArtifact, ScenarioMachineV2 } from './types.js';
|
|
5
|
+
import type { AdmittedRuntimeProfile, EvidenceEnvelope, ScenarioArtifact, ScenarioMachineV2 } from './types.js';
|
|
6
6
|
export declare const CLAUDE_DIRECT_MCP_FLAG = "PATHGRADE_INTERNAL_CLAUDE_DIRECT_MCP_V2";
|
|
7
7
|
export type ClaudeDirectMcpChecks = {
|
|
8
8
|
runtimeAdmitted: boolean;
|
|
@@ -19,6 +19,7 @@ export type ClaudeDirectMcpChecks = {
|
|
|
19
19
|
export type ClaudeDirectMcpTrialResult = {
|
|
20
20
|
selected: true;
|
|
21
21
|
profileFingerprint: string;
|
|
22
|
+
runtimeProfile: AdmittedRuntimeProfile;
|
|
22
23
|
artifact: ScenarioArtifact;
|
|
23
24
|
evidence: readonly EvidenceEnvelope[];
|
|
24
25
|
checks: ClaudeDirectMcpChecks;
|
|
@@ -11,19 +11,35 @@ import { compileScenario } from './compiler.js';
|
|
|
11
11
|
import { IdempotentTrialCleaner, persistTrialArtifact, TrialExecutionError, TrialResourceRegistry, scanBearerSurfaces } from './cleanup.js';
|
|
12
12
|
import { PrivateReplayVault, replayPrivateEvidence, verifyEvidenceChain } from './evidence.js';
|
|
13
13
|
import { ScenarioHostReducer } from './host-reducer.js';
|
|
14
|
+
import { sha256Canonical } from './json.js';
|
|
14
15
|
import { materializeProjection } from './materialize.js';
|
|
15
16
|
import { projectEndpoints } from './projection.js';
|
|
16
|
-
import {
|
|
17
|
+
import { admitRuntimeLock } from './runtime-lock.js';
|
|
17
18
|
import { createInvocationBarrier, startScenarioHttpHost } from './scenario-http-host.js';
|
|
18
|
-
import {
|
|
19
|
+
import { observeClaudeRuntimeControlPlane, resolveClaudeScenarioExecutable, selectClaudeRuntimeTarget, } from './claude-profile.js';
|
|
19
20
|
export const CLAUDE_DIRECT_MCP_FLAG = 'PATHGRADE_INTERNAL_CLAUDE_DIRECT_MCP_V2';
|
|
20
21
|
const disabledProfiles = new Map();
|
|
22
|
+
function runtimeLockFingerprint(lock) {
|
|
23
|
+
return sha256Canonical(lock);
|
|
24
|
+
}
|
|
21
25
|
export function isClaudeDirectMcpSelected(environment = process.env) {
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
if (environment[CLAUDE_DIRECT_MCP_FLAG] !== '1')
|
|
27
|
+
return false;
|
|
28
|
+
try {
|
|
29
|
+
const fingerprint = selectClaudeRuntimeTarget().lock;
|
|
30
|
+
return !disabledProfiles.has(runtimeLockFingerprint(fingerprint));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
24
35
|
}
|
|
25
36
|
export function claudeDirectMcpDisableReason() {
|
|
26
|
-
|
|
37
|
+
try {
|
|
38
|
+
return disabledProfiles.get(runtimeLockFingerprint(selectClaudeRuntimeTarget().lock));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
27
43
|
}
|
|
28
44
|
/** Test-only reset for proving the fail-closed process latch. */
|
|
29
45
|
// oxlint-disable-next-line inhuman/no-empty-wrappers -- This test seam exposes Map.clear without exposing mutable production state.
|
|
@@ -82,17 +98,18 @@ export function createClaudeParentBridges(options) {
|
|
|
82
98
|
}
|
|
83
99
|
export async function runClaudeDirectMcpTrial(options) {
|
|
84
100
|
const environment = options.environment ?? process.env;
|
|
85
|
-
const
|
|
101
|
+
const target = selectClaudeRuntimeTarget();
|
|
102
|
+
const fingerprint = runtimeLockFingerprint(target.lock);
|
|
86
103
|
if (!isClaudeDirectMcpSelected(environment)) {
|
|
87
|
-
const reason = disabledProfiles.get(
|
|
104
|
+
const reason = disabledProfiles.get(fingerprint);
|
|
88
105
|
throw new Error(reason ? `Claude direct MCP disabled for admitted profile: ${reason}` : `${CLAUDE_DIRECT_MCP_FLAG} is disabled`);
|
|
89
106
|
}
|
|
90
107
|
try {
|
|
91
|
-
return await executeTrial({ ...options, environment });
|
|
108
|
+
return await executeTrial({ ...options, environment, target });
|
|
92
109
|
}
|
|
93
110
|
catch (error) {
|
|
94
111
|
const reason = error instanceof Error ? error.message : String(error);
|
|
95
|
-
disabledProfiles.set(
|
|
112
|
+
disabledProfiles.set(fingerprint, reason.slice(0, 500));
|
|
96
113
|
throw error;
|
|
97
114
|
}
|
|
98
115
|
}
|
|
@@ -179,21 +196,21 @@ async function executeTrial(options) {
|
|
|
179
196
|
zeroBearer: () => { host.bearer.fill(0); macKey.fill(0); replayVault.dispose(); },
|
|
180
197
|
});
|
|
181
198
|
try {
|
|
182
|
-
const admission = await
|
|
199
|
+
const admission = await admitRuntimeLock(options.target.lock, {
|
|
183
200
|
provider: 'claude',
|
|
184
201
|
platform: process.platform,
|
|
185
202
|
architecture: process.arch,
|
|
203
|
+
resolvedExecutable: await resolveClaudeScenarioExecutable(options.target),
|
|
186
204
|
environment: runtimeEnv,
|
|
187
205
|
forbiddenSecrets: [host.bearer.toString(), `Bearer ${host.bearer.toString()}`],
|
|
188
206
|
initializeControlPlane: async () => {
|
|
189
207
|
await options.initializeControlPlane?.();
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
208
|
+
return observeClaudeRuntimeControlPlane({
|
|
209
|
+
createSdkMcpServer,
|
|
210
|
+
query: options.query ?? sdkQuery,
|
|
211
|
+
});
|
|
193
212
|
},
|
|
194
213
|
});
|
|
195
|
-
if (!admission.ok)
|
|
196
|
-
throw new Error(`RUNTIME_NOT_ADMITTED: ${admission.reason}`);
|
|
197
214
|
reducer.markAdmitted();
|
|
198
215
|
const projected = projectEndpoints(host.endpoints, admission.profile);
|
|
199
216
|
if (!Array.isArray(projected))
|
|
@@ -212,7 +229,7 @@ async function executeTrial(options) {
|
|
|
212
229
|
}
|
|
213
230
|
reducer.markReady();
|
|
214
231
|
const query = options.query ?? sdkQuery;
|
|
215
|
-
const queryOptions = { query, runtimeEnv, workspacePath, model: options.model, profilePath:
|
|
232
|
+
const queryOptions = { query, runtimeEnv, workspacePath, model: options.model, profilePath: admission.commandPath, mcpServers: bridges, spawnClaudeCodeProcess };
|
|
216
233
|
reducer.beginTurn('direct-call', 'claude');
|
|
217
234
|
const normal = await runQuery(queryOptions, `Invoke mcp__demo__${effectfulCase.pureToolId} exactly once with ${JSON.stringify(effectfulCase.pureArgs)}, then invoke mcp__demo__${effectfulCase.effectfulToolId} exactly once with ${JSON.stringify(effectfulCase.errorArgs)}. Wait for both results and do not use any other tool.`);
|
|
218
235
|
if (normal.status !== 'success')
|
|
@@ -262,7 +279,8 @@ async function executeTrial(options) {
|
|
|
262
279
|
throw new Error(`CLEANUP_FAILED: ${JSON.stringify(cleanup.failures)}`);
|
|
263
280
|
return {
|
|
264
281
|
selected: true,
|
|
265
|
-
profileFingerprint:
|
|
282
|
+
profileFingerprint: admission.identity.lockFingerprint,
|
|
283
|
+
runtimeProfile: admission.profile,
|
|
266
284
|
artifact: compiled.artifact,
|
|
267
285
|
evidence: reducer.ledger.events(),
|
|
268
286
|
checks: { runtimeAdmitted: true, secretFreeProject: true, directCall: true, structuredToolError: true, fencedCancellation: true, providerTerminal: true, hostQuiescent: hostQuiescentBeforeCleanup, postCancelReuse: true, privateReplay: true, cleanup: true },
|
|
@@ -1,9 +1,35 @@
|
|
|
1
|
-
import type { RuntimeLock } from './runtime-lock.js';
|
|
2
|
-
import type { JsonObject
|
|
1
|
+
import type { RuntimeLock, RuntimeLockControlPlane } from './runtime-lock.js';
|
|
2
|
+
import type { JsonObject } from './types.js';
|
|
3
3
|
export declare const CLAUDE_DIRECT_MCP_NORMALIZED_HANDSHAKE: JsonObject;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
export declare const CLAUDE_DIRECT_MCP_CAPABILITIES: {
|
|
5
|
+
readonly streamableHttp: true;
|
|
6
|
+
readonly controlPlaneHeaders: true;
|
|
7
|
+
readonly parentOwnedSdkMcp: true;
|
|
8
|
+
readonly cancellation: "abort-controller";
|
|
9
|
+
};
|
|
10
|
+
/** Code-owned relocatable identity lock captured by RFC 0006. */
|
|
7
11
|
export declare const CLAUDE_DIRECT_MCP_DARWIN_ARM64_LOCK: RuntimeLock;
|
|
8
|
-
/**
|
|
9
|
-
export declare
|
|
12
|
+
/** Provisional Linux x64 glibc lock; CI re-verifies every artifact before admission. */
|
|
13
|
+
export declare const CLAUDE_DIRECT_MCP_LINUX_X64_GLIBC_LOCK: RuntimeLock;
|
|
14
|
+
export type ClaudeRuntimeTarget = {
|
|
15
|
+
id: 'darwin-arm64' | 'linux-x64-glibc';
|
|
16
|
+
platform: 'darwin' | 'linux';
|
|
17
|
+
architecture: 'arm64' | 'x64';
|
|
18
|
+
libc?: 'glibc';
|
|
19
|
+
packageName: string;
|
|
20
|
+
lock: RuntimeLock;
|
|
21
|
+
};
|
|
22
|
+
export declare const CLAUDE_RUNTIME_TARGETS: readonly ClaudeRuntimeTarget[];
|
|
23
|
+
export type ClaudeRuntimeEnvironment = {
|
|
24
|
+
platform: string;
|
|
25
|
+
architecture: string;
|
|
26
|
+
libc?: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function detectClaudeRuntimeEnvironment(): ClaudeRuntimeEnvironment;
|
|
29
|
+
export declare function selectClaudeRuntimeTarget(environment?: ClaudeRuntimeEnvironment): ClaudeRuntimeTarget;
|
|
30
|
+
/** Resolve the selected optional-package binary through Node package resolution. */
|
|
31
|
+
export declare function resolveClaudeScenarioExecutable(target?: ClaudeRuntimeTarget): Promise<string>;
|
|
32
|
+
export declare function observeClaudeRuntimeControlPlane(options: {
|
|
33
|
+
createSdkMcpServer: unknown;
|
|
34
|
+
query: unknown;
|
|
35
|
+
}): RuntimeLockControlPlane;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { access, realpath } from 'node:fs/promises';
|
|
2
1
|
import { constants } from 'node:fs';
|
|
2
|
+
import { access, realpath } from 'node:fs/promises';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
export const CLAUDE_DIRECT_MCP_NORMALIZED_HANDSHAKE = {
|
|
@@ -13,85 +13,117 @@ export const CLAUDE_DIRECT_MCP_NORMALIZED_HANDSHAKE = {
|
|
|
13
13
|
cancellation: 'abort-controller',
|
|
14
14
|
sdkMethods: ['createSdkMcpServer', 'query'],
|
|
15
15
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
export const CLAUDE_DIRECT_MCP_CAPABILITIES = {
|
|
17
|
+
streamableHttp: true,
|
|
18
|
+
controlPlaneHeaders: true,
|
|
19
|
+
parentOwnedSdkMcp: true,
|
|
20
|
+
cancellation: 'abort-controller',
|
|
21
|
+
};
|
|
22
|
+
const SDK_SHA256 = '48bde6aeabf7e71ad5528bf52c8feb1642c21f505ea2495c70f39db7df226d97';
|
|
23
|
+
const ZOD_SHA256 = '2a3455cecff4f7021c92d0a3e2e5fc170c0448dc5cc261160127e08f4888a9c2';
|
|
24
|
+
const NORMALIZED_HANDSHAKE_SHA256 = '990b779b75145c8d7bcc8cfebcb5b9c1aae30eb1436393b38e7e997941ecc162';
|
|
25
|
+
const CLAUDE_VERSION = '2.1.141 (Claude Code)';
|
|
26
|
+
const COMMAND_ARTIFACT = 'claude-code-bundled-2.1.141';
|
|
27
|
+
function claudeLock(options) {
|
|
28
|
+
return {
|
|
29
|
+
format: 'pathgrade-direct-mcp-runtime-lock-v1',
|
|
30
|
+
provider: 'claude',
|
|
31
|
+
platform: options.platform,
|
|
32
|
+
architecture: options.architecture,
|
|
33
|
+
controlPlane: 'native-sdk-parent-bridge',
|
|
34
|
+
version: CLAUDE_VERSION,
|
|
35
|
+
commandArtifact: COMMAND_ARTIFACT,
|
|
25
36
|
args: [],
|
|
26
37
|
versionArgs: ['--version'],
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
sha256: '31ac95bb19a33b1d0cddd3f3ff594bf8bfd2be5051cd2af7867109641cab705e',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
label: 'zod-4.3.6-commonjs-entry',
|
|
42
|
-
absolutePath: '/Users/nadavlac/projects/agent-evals/node_modules/zod/index.cjs',
|
|
43
|
-
sha256: '2a3455cecff4f7021c92d0a3e2e5fc170c0448dc5cc261160127e08f4888a9c2',
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
requiredCapabilities: {
|
|
47
|
-
streamableHttp: true,
|
|
48
|
-
controlPlaneHeaders: true,
|
|
49
|
-
parentOwnedSdkMcp: true,
|
|
50
|
-
cancellation: 'abort-controller',
|
|
51
|
-
},
|
|
52
|
-
profileFingerprint: '463d1485d70e542cf2a356cae007469a7d94d0e848a26d9e9b7470dbc5262104',
|
|
53
|
-
runtimeCapabilityFingerprint: '990b779b75145c8d7bcc8cfebcb5b9c1aae30eb1436393b38e7e997941ecc162',
|
|
54
|
-
};
|
|
55
|
-
/** Code-owned relocatable identity lock for the admitted public Claude SDK slice. */
|
|
56
|
-
export const CLAUDE_DIRECT_MCP_DARWIN_ARM64_LOCK = {
|
|
57
|
-
format: 'pathgrade-direct-mcp-runtime-lock-v1',
|
|
58
|
-
provider: 'claude',
|
|
38
|
+
artifacts: [
|
|
39
|
+
{ id: 'claude-agent-sdk-0.2.141', relativePath: '@anthropic-ai/claude-agent-sdk/sdk.mjs', sha256: SDK_SHA256 },
|
|
40
|
+
{ id: COMMAND_ARTIFACT, relativePath: `${options.packageName}/claude`, sha256: options.commandSha256 },
|
|
41
|
+
{ id: 'zod-4.3.6-commonjs-entry', relativePath: 'zod/index.cjs', sha256: ZOD_SHA256 },
|
|
42
|
+
],
|
|
43
|
+
capabilities: CLAUDE_DIRECT_MCP_CAPABILITIES,
|
|
44
|
+
normalizedHandshakeSha256: NORMALIZED_HANDSHAKE_SHA256,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Code-owned relocatable identity lock captured by RFC 0006. */
|
|
48
|
+
export const CLAUDE_DIRECT_MCP_DARWIN_ARM64_LOCK = claudeLock({
|
|
59
49
|
platform: 'darwin',
|
|
60
50
|
architecture: 'arm64',
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
51
|
+
packageName: '@anthropic-ai/claude-agent-sdk-darwin-arm64',
|
|
52
|
+
commandSha256: '31ac95bb19a33b1d0cddd3f3ff594bf8bfd2be5051cd2af7867109641cab705e',
|
|
53
|
+
});
|
|
54
|
+
/** Provisional Linux x64 glibc lock; CI re-verifies every artifact before admission. */
|
|
55
|
+
export const CLAUDE_DIRECT_MCP_LINUX_X64_GLIBC_LOCK = claudeLock({
|
|
56
|
+
platform: 'linux',
|
|
57
|
+
architecture: 'x64',
|
|
58
|
+
packageName: '@anthropic-ai/claude-agent-sdk-linux-x64',
|
|
59
|
+
commandSha256: '832be26e8f15b2ae99e520a22b034fc4bfad1cb5b84de6b706487072c56bb42e',
|
|
60
|
+
});
|
|
61
|
+
export const CLAUDE_RUNTIME_TARGETS = [
|
|
62
|
+
{
|
|
63
|
+
id: 'darwin-arm64',
|
|
64
|
+
platform: 'darwin',
|
|
65
|
+
architecture: 'arm64',
|
|
66
|
+
packageName: '@anthropic-ai/claude-agent-sdk-darwin-arm64',
|
|
67
|
+
lock: CLAUDE_DIRECT_MCP_DARWIN_ARM64_LOCK,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'linux-x64-glibc',
|
|
71
|
+
platform: 'linux',
|
|
72
|
+
architecture: 'x64',
|
|
73
|
+
libc: 'glibc',
|
|
74
|
+
packageName: '@anthropic-ai/claude-agent-sdk-linux-x64',
|
|
75
|
+
lock: CLAUDE_DIRECT_MCP_LINUX_X64_GLIBC_LOCK,
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
export function detectClaudeRuntimeEnvironment() {
|
|
79
|
+
if (process.platform !== 'linux')
|
|
80
|
+
return { platform: process.platform, architecture: process.arch };
|
|
81
|
+
const report = process.report?.getReport();
|
|
82
|
+
const glibc = report?.header?.glibcVersionRuntime;
|
|
83
|
+
return {
|
|
84
|
+
platform: process.platform,
|
|
85
|
+
architecture: process.arch,
|
|
86
|
+
libc: typeof glibc === 'string' && glibc.length > 0 ? 'glibc' : 'unknown',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function selectClaudeRuntimeTarget(environment = detectClaudeRuntimeEnvironment()) {
|
|
90
|
+
const target = CLAUDE_RUNTIME_TARGETS.find((candidate) => (candidate.platform === environment.platform
|
|
91
|
+
&& candidate.architecture === environment.architecture
|
|
92
|
+
&& candidate.libc === environment.libc));
|
|
93
|
+
if (!target) {
|
|
94
|
+
const identity = [environment.platform, environment.architecture, environment.libc].filter(Boolean).join('-');
|
|
95
|
+
throw new Error(`RUNTIME_NOT_ADMITTED: unsupported Claude runtime target: ${identity}`);
|
|
96
|
+
}
|
|
97
|
+
return target;
|
|
98
|
+
}
|
|
99
|
+
/** Resolve the selected optional-package binary through Node package resolution. */
|
|
100
|
+
export async function resolveClaudeScenarioExecutable(target = selectClaudeRuntimeTarget()) {
|
|
101
|
+
let packageJson;
|
|
102
|
+
try {
|
|
103
|
+
packageJson = createRequire(import.meta.url).resolve(`${target.packageName}/package.json`);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
throw new Error(`RUNTIME_NOT_ADMITTED: missing Claude runtime package: ${target.packageName}`);
|
|
107
|
+
}
|
|
108
|
+
const candidate = join(dirname(packageJson), 'claude');
|
|
90
109
|
try {
|
|
91
110
|
await access(candidate, constants.X_OK);
|
|
92
111
|
return realpath(candidate);
|
|
93
112
|
}
|
|
94
113
|
catch {
|
|
95
|
-
throw new Error(
|
|
114
|
+
throw new Error(`RUNTIME_NOT_ADMITTED: Claude runtime executable is unavailable: ${target.id}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function observeClaudeRuntimeControlPlane(options) {
|
|
118
|
+
if (typeof options.createSdkMcpServer !== 'function' || typeof options.query !== 'function') {
|
|
119
|
+
throw new Error('Claude SDK parent bridge methods unavailable');
|
|
120
|
+
}
|
|
121
|
+
if (typeof AbortController !== 'function' || typeof AbortController.prototype.abort !== 'function') {
|
|
122
|
+
throw new Error('Claude AbortController cancellation unavailable');
|
|
96
123
|
}
|
|
124
|
+
return {
|
|
125
|
+
normalizedHandshake: CLAUDE_DIRECT_MCP_NORMALIZED_HANDSHAKE,
|
|
126
|
+
capabilities: CLAUDE_DIRECT_MCP_CAPABILITIES,
|
|
127
|
+
close: async () => undefined,
|
|
128
|
+
};
|
|
97
129
|
}
|
|
@@ -8,7 +8,7 @@ import { projectEndpoints } from './projection.js';
|
|
|
8
8
|
import { admitRuntimeLock } from './runtime-lock.js';
|
|
9
9
|
import { startScenarioHttpHost } from './scenario-http-host.js';
|
|
10
10
|
import { createClaudeParentBridges } from './claude-direct-mcp.js';
|
|
11
|
-
import {
|
|
11
|
+
import { observeClaudeRuntimeControlPlane, resolveClaudeScenarioExecutable, selectClaudeRuntimeTarget } from './claude-profile.js';
|
|
12
12
|
const defaultCoordinatorDependencies = {
|
|
13
13
|
startHost: startScenarioHttpHost,
|
|
14
14
|
scanBearerSurfaces,
|
|
@@ -286,8 +286,10 @@ function scenarioScanRoots(workspacePath, environment) {
|
|
|
286
286
|
].filter((path) => typeof path === 'string' && path.length > 0))];
|
|
287
287
|
}
|
|
288
288
|
export async function startPublicClaudeScenarioRuntime(options, dependencies = {}) {
|
|
289
|
-
const
|
|
290
|
-
const
|
|
289
|
+
const target = dependencies.lock ? undefined : selectClaudeRuntimeTarget();
|
|
290
|
+
const lock = dependencies.lock ?? target.lock;
|
|
291
|
+
const resolveExecutable = dependencies.resolveExecutable
|
|
292
|
+
?? (async () => resolveClaudeScenarioExecutable(target));
|
|
291
293
|
const coordinator = await startScenarioCoordinator({
|
|
292
294
|
...options,
|
|
293
295
|
provider: 'claude',
|
|
@@ -301,16 +303,10 @@ export async function startPublicClaudeScenarioRuntime(options, dependencies = {
|
|
|
301
303
|
environment: options.environment,
|
|
302
304
|
forbiddenSecrets: [bearer.slice('Bearer '.length), bearer],
|
|
303
305
|
...(dependencies.commandVersion ? { commandVersion: dependencies.commandVersion } : {}),
|
|
304
|
-
initializeControlPlane: async () => {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
normalizedHandshake: CLAUDE_DIRECT_MCP_NORMALIZED_HANDSHAKE,
|
|
310
|
-
capabilities: lock.capabilities,
|
|
311
|
-
close: async () => undefined,
|
|
312
|
-
};
|
|
313
|
-
},
|
|
306
|
+
initializeControlPlane: async () => observeClaudeRuntimeControlPlane({
|
|
307
|
+
createSdkMcpServer,
|
|
308
|
+
query: sdkQuery,
|
|
309
|
+
}),
|
|
314
310
|
});
|
|
315
311
|
const projected = projectEndpoints(context.endpoints, admission.profile);
|
|
316
312
|
if (!Array.isArray(projected))
|
|
@@ -320,7 +316,7 @@ export async function startPublicClaudeScenarioRuntime(options, dependencies = {
|
|
|
320
316
|
}));
|
|
321
317
|
const bridges = createClaudeParentBridges({ artifact: options.artifact, projections: materialized });
|
|
322
318
|
return {
|
|
323
|
-
value: bridges,
|
|
319
|
+
value: { bridges, commandPath: admission.commandPath },
|
|
324
320
|
async interrupt() { },
|
|
325
321
|
async waitForTerminal() { },
|
|
326
322
|
async close() {
|
|
@@ -340,9 +336,10 @@ export async function startPublicClaudeScenarioRuntime(options, dependencies = {
|
|
|
340
336
|
},
|
|
341
337
|
});
|
|
342
338
|
return {
|
|
343
|
-
claudeServers: coordinator.attachment,
|
|
339
|
+
claudeServers: coordinator.attachment.bridges,
|
|
344
340
|
configureSession(sessionOptions) {
|
|
345
|
-
sessionOptions.scenarioMcpServers = coordinator.attachment;
|
|
341
|
+
sessionOptions.scenarioMcpServers = coordinator.attachment.bridges;
|
|
342
|
+
sessionOptions.admittedClaudeCodeExecutable = coordinator.attachment.commandPath;
|
|
346
343
|
},
|
|
347
344
|
beginTurn: coordinator.beginTurn,
|
|
348
345
|
settleTurn: coordinator.settleTurn,
|
package/dist/sdk/scorer-utils.js
CHANGED
|
@@ -2,10 +2,20 @@ export function clamp(n) {
|
|
|
2
2
|
return Math.max(0, Math.min(1, n));
|
|
3
3
|
}
|
|
4
4
|
export function getErrorMessage(error) {
|
|
5
|
-
if (error instanceof Error) {
|
|
6
|
-
return error
|
|
5
|
+
if (!(error instanceof Error)) {
|
|
6
|
+
return String(error);
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
const seen = new Set();
|
|
9
|
+
let message = error.message;
|
|
10
|
+
let cause = error.cause;
|
|
11
|
+
while (cause instanceof Error && !seen.has(cause)) {
|
|
12
|
+
seen.add(cause);
|
|
13
|
+
if (cause.message && !message.includes(cause.message)) {
|
|
14
|
+
message = message ? `${message}: ${cause.message}` : cause.message;
|
|
15
|
+
}
|
|
16
|
+
cause = cause.cause;
|
|
17
|
+
}
|
|
18
|
+
return message || String(error);
|
|
9
19
|
}
|
|
10
20
|
export function matchesExpectation(event, expectation) {
|
|
11
21
|
if (event.action !== expectation.action)
|
package/dist/types.d.ts
CHANGED
|
@@ -423,6 +423,8 @@ export interface AgentSessionOptions {
|
|
|
423
423
|
scriptedMcpHost?: import('./providers/scripted-mcp-mock-host.js').ScriptedMcpMockHost;
|
|
424
424
|
/** Parent-owned public ScenarioMachineV2 bridge; never serialized into child config. */
|
|
425
425
|
scenarioMcpServers?: Record<string, import('@anthropic-ai/claude-agent-sdk').McpSdkServerConfigWithInstance>;
|
|
426
|
+
/** RuntimeLock-admitted Claude executable; wins over developer overrides for ScenarioMachineV2 sessions. */
|
|
427
|
+
admittedClaudeCodeExecutable?: string;
|
|
426
428
|
/** Parent-owned admitted Codex app-server mount; projected only into thread/start memory. */
|
|
427
429
|
codexScenarioMount?: import('./agents/codex-app-server/scenario-mount.js').CodexScenarioMount;
|
|
428
430
|
/** Runtime-only values inherited by drivers and persistence sinks for redaction. */
|
|
@@ -73,7 +73,7 @@ async function postAnthropic(apiKey, useCache, body, env) {
|
|
|
73
73
|
});
|
|
74
74
|
if (!response.ok) {
|
|
75
75
|
const errBody = await response.text().catch(() => '');
|
|
76
|
-
throw new Error(`Anthropic API error (${response.status}): ${errBody.slice(0, 300)}`);
|
|
76
|
+
throw new Error(`Anthropic API error (${response.status}) for model ${String(body.model)}: ${errBody.slice(0, 300)}`);
|
|
77
77
|
}
|
|
78
78
|
return await response.json();
|
|
79
79
|
}
|
|
@@ -116,7 +116,10 @@ export const anthropicProvider = {
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
catch (error) {
|
|
119
|
-
|
|
119
|
+
// Reporting seams only propagate error.message, never error.cause.
|
|
120
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
121
|
+
const modelTag = reason.includes(model) ? '' : ` (model ${model})`;
|
|
122
|
+
throw new Error(`Anthropic API call failed${modelTag}: ${reason}`, { cause: error });
|
|
120
123
|
}
|
|
121
124
|
},
|
|
122
125
|
async callWithTools(messages, opts) {
|
package/dist/utils/llm.js
CHANGED
|
@@ -114,6 +114,7 @@ export function createLLMClient(arg, legacyAgentName) {
|
|
|
114
114
|
const toTry = narrowed.length > 0 ? narrowed : candidates;
|
|
115
115
|
let lastError;
|
|
116
116
|
let lastFailed;
|
|
117
|
+
const failures = [];
|
|
117
118
|
for (const provider of toTry) {
|
|
118
119
|
if (!await provider.isAvailable(opts.env))
|
|
119
120
|
continue;
|
|
@@ -128,6 +129,7 @@ export function createLLMClient(arg, legacyAgentName) {
|
|
|
128
129
|
catch (error) {
|
|
129
130
|
lastError = error;
|
|
130
131
|
lastFailed = { name: provider.name, reason: lastError.message };
|
|
132
|
+
failures.push(lastFailed);
|
|
131
133
|
// Fall through to next provider
|
|
132
134
|
}
|
|
133
135
|
}
|
|
@@ -149,11 +151,15 @@ export function createLLMClient(arg, legacyAgentName) {
|
|
|
149
151
|
catch (error) {
|
|
150
152
|
lastError = error;
|
|
151
153
|
lastFailed = { name: provider.name, reason: lastError.message };
|
|
154
|
+
failures.push(lastFailed);
|
|
152
155
|
}
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
|
-
if (lastError)
|
|
156
|
-
|
|
158
|
+
if (lastError) {
|
|
159
|
+
if (failures.length <= 1)
|
|
160
|
+
throw lastError;
|
|
161
|
+
throw new Error(`All LLM providers failed: ${failures.map((f) => `${f.name}: ${f.reason}`).join('; ')}`, { cause: lastError });
|
|
162
|
+
}
|
|
157
163
|
if (agentName === 'claude') {
|
|
158
164
|
throw new Error('Claude CLI not found on PATH. Install with: npm install -g @anthropic-ai/claude-code');
|
|
159
165
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/pathgrade",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.30",
|
|
4
4
|
"packageManager": "yarn@4.12.0",
|
|
5
5
|
"description": "Evaluate whether AI agents discover and use your skills correctly",
|
|
6
6
|
"exports": {
|
|
@@ -140,5 +140,5 @@
|
|
|
140
140
|
"typescript": "^5.9.3",
|
|
141
141
|
"zod": "4.3.6"
|
|
142
142
|
},
|
|
143
|
-
"falconPackageHash": "
|
|
143
|
+
"falconPackageHash": "c85431c64d766806f41ca2adad9d390e73dc8d09a2feea2ae9285a1c"
|
|
144
144
|
}
|