@wix/pathgrade 1.0.25 → 1.0.27
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 +44 -2
- package/dist/agents/claude/sdk-message-projector.js +5 -0
- package/dist/agents/claude/sdk-options.d.ts +1 -2
- package/dist/agents/claude.js +2 -1
- package/dist/agents/codex-app-server/agent.js +89 -52
- package/dist/agents/codex-app-server/scenario-mount.d.ts +40 -0
- package/dist/agents/codex-app-server/scenario-mount.js +238 -0
- package/dist/agents/codex.js +1 -0
- package/dist/agents/cursor-scenario.d.ts +20 -0
- package/dist/agents/cursor-scenario.js +96 -0
- package/dist/agents/cursor.js +21 -0
- package/dist/agents/opencode/scenario.d.ts +25 -0
- package/dist/agents/opencode/scenario.js +195 -0
- package/dist/agents/opencode.js +54 -2
- package/dist/commands/report.d.ts +10 -2
- package/dist/commands/report.js +25 -4
- package/dist/internal/direct-mcp-v2/acp-author-projector.d.ts +20 -0
- package/dist/internal/direct-mcp-v2/acp-author-projector.js +147 -0
- package/dist/internal/direct-mcp-v2/acp-author-session.d.ts +39 -0
- package/dist/internal/direct-mcp-v2/acp-author-session.js +201 -0
- package/dist/internal/direct-mcp-v2/acp-client.d.ts +70 -0
- package/dist/internal/direct-mcp-v2/acp-client.js +310 -0
- package/dist/internal/direct-mcp-v2/acp-direct-mcp.d.ts +38 -0
- package/dist/internal/direct-mcp-v2/acp-direct-mcp.js +326 -0
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.d.ts +68 -0
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.js +456 -0
- package/dist/internal/direct-mcp-v2/claude-profile.d.ts +9 -0
- package/dist/internal/direct-mcp-v2/claude-profile.js +97 -0
- package/dist/internal/direct-mcp-v2/cleanup.d.ts +62 -0
- package/dist/internal/direct-mcp-v2/cleanup.js +182 -0
- package/dist/internal/direct-mcp-v2/codex-direct-mcp.d.ts +41 -0
- package/dist/internal/direct-mcp-v2/codex-direct-mcp.js +436 -0
- package/dist/internal/direct-mcp-v2/codex-profile.d.ts +12 -0
- package/dist/internal/direct-mcp-v2/codex-profile.js +81 -0
- package/dist/internal/direct-mcp-v2/compiler.d.ts +12 -0
- package/dist/internal/direct-mcp-v2/compiler.js +449 -0
- package/dist/internal/direct-mcp-v2/cursor-direct-mcp.d.ts +14 -0
- package/dist/internal/direct-mcp-v2/cursor-direct-mcp.js +60 -0
- package/dist/internal/direct-mcp-v2/cursor-profile.d.ts +38 -0
- package/dist/internal/direct-mcp-v2/cursor-profile.js +79 -0
- package/dist/internal/direct-mcp-v2/evidence.d.ts +50 -0
- package/dist/internal/direct-mcp-v2/evidence.js +147 -0
- package/dist/internal/direct-mcp-v2/host-reducer.d.ts +61 -0
- package/dist/internal/direct-mcp-v2/host-reducer.js +214 -0
- package/dist/internal/direct-mcp-v2/json.d.ts +9 -0
- package/dist/internal/direct-mcp-v2/json.js +220 -0
- package/dist/internal/direct-mcp-v2/materialize.d.ts +39 -0
- package/dist/internal/direct-mcp-v2/materialize.js +22 -0
- package/dist/internal/direct-mcp-v2/opencode-direct-mcp.d.ts +14 -0
- package/dist/internal/direct-mcp-v2/opencode-direct-mcp.js +71 -0
- package/dist/internal/direct-mcp-v2/opencode-profile.d.ts +8 -0
- package/dist/internal/direct-mcp-v2/opencode-profile.js +52 -0
- package/dist/internal/direct-mcp-v2/projection.d.ts +40 -0
- package/dist/internal/direct-mcp-v2/projection.js +46 -0
- package/dist/internal/direct-mcp-v2/public-scenario-runtime.d.ts +51 -0
- package/dist/internal/direct-mcp-v2/public-scenario-runtime.js +353 -0
- package/dist/internal/direct-mcp-v2/runtime-lock.d.ts +51 -0
- package/dist/internal/direct-mcp-v2/runtime-lock.js +203 -0
- package/dist/internal/direct-mcp-v2/runtime-profile.d.ts +28 -0
- package/dist/internal/direct-mcp-v2/runtime-profile.js +163 -0
- package/dist/internal/direct-mcp-v2/scenario-http-host.d.ts +24 -0
- package/dist/internal/direct-mcp-v2/scenario-http-host.js +254 -0
- package/dist/internal/direct-mcp-v2/types.d.ts +233 -0
- package/dist/internal/direct-mcp-v2/types.js +1 -0
- package/dist/pathgrade.js +22 -1
- package/dist/reporters/github-comment.d.ts +12 -3
- package/dist/reporters/github-comment.js +80 -9
- package/dist/reporting/artifacts.js +5 -2
- package/dist/sdk/agent.js +56 -4
- package/dist/sdk/evaluate.d.ts +2 -0
- package/dist/sdk/evaluate.js +20 -12
- package/dist/sdk/index.d.ts +6 -0
- package/dist/sdk/index.js +4 -0
- package/dist/sdk/lifecycle.js +8 -3
- package/dist/sdk/managed-session.d.ts +6 -0
- package/dist/sdk/managed-session.js +157 -50
- package/dist/sdk/scenario-evidence.d.ts +27 -0
- package/dist/sdk/scenario-evidence.js +94 -0
- package/dist/sdk/scenario-machine-v2.d.ts +9 -0
- package/dist/sdk/scenario-machine-v2.js +37 -0
- package/dist/sdk/snapshots.d.ts +3 -0
- package/dist/sdk/snapshots.js +2 -0
- package/dist/sdk/types.d.ts +11 -1
- package/dist/tool-event-results.d.ts +1 -1
- package/dist/tool-event-results.js +2 -1
- package/dist/tool-events.d.ts +5 -0
- package/dist/tool-events.js +5 -0
- package/dist/types.d.ts +8 -0
- package/package.json +2 -2
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type TrackedResource = {
|
|
2
|
+
opaqueId: string;
|
|
3
|
+
kind: 'process' | 'descriptor' | 'socket' | 'listener' | 'transport' | 'invocation' | 'auth-copy';
|
|
4
|
+
close(): Promise<void>;
|
|
5
|
+
isOpen(): boolean;
|
|
6
|
+
};
|
|
7
|
+
export type ScanEntry = {
|
|
8
|
+
path: string;
|
|
9
|
+
status: 'clean' | 'bearer-found' | 'unreadable' | 'skipped';
|
|
10
|
+
reason?: string;
|
|
11
|
+
};
|
|
12
|
+
export type CleanupReport = {
|
|
13
|
+
scans: ScanEntry[];
|
|
14
|
+
released: string[];
|
|
15
|
+
failures: {
|
|
16
|
+
opaqueId: string;
|
|
17
|
+
reason: string;
|
|
18
|
+
}[];
|
|
19
|
+
scratchDeleted: boolean;
|
|
20
|
+
bearerFound: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type TrialArtifactPhase = 'provisional' | 'final';
|
|
23
|
+
export declare function persistTrialArtifact(path: string, content: string, phase: TrialArtifactPhase): Promise<void>;
|
|
24
|
+
export declare class TrialResourceRegistry {
|
|
25
|
+
private readonly resources;
|
|
26
|
+
register(resource: TrackedResource): void;
|
|
27
|
+
entries(): readonly TrackedResource[];
|
|
28
|
+
}
|
|
29
|
+
export declare function scanBearerSurfaces(roots: readonly string[], bearer: string): Promise<ScanEntry[]>;
|
|
30
|
+
export declare function cleanupTrial(options: {
|
|
31
|
+
registry: TrialResourceRegistry;
|
|
32
|
+
scanRoots: readonly string[];
|
|
33
|
+
scratchPath: string;
|
|
34
|
+
approvedScratchParent: string;
|
|
35
|
+
bearer: string;
|
|
36
|
+
persistRedacted(report: CleanupReport, phase: TrialArtifactPhase): Promise<void>;
|
|
37
|
+
zeroBearer?(): void;
|
|
38
|
+
removeScratch?(path: string): Promise<void>;
|
|
39
|
+
}): Promise<CleanupReport>;
|
|
40
|
+
export declare class IdempotentTrialCleaner {
|
|
41
|
+
private readonly options;
|
|
42
|
+
private result?;
|
|
43
|
+
constructor(options: Parameters<typeof cleanupTrial>[0]);
|
|
44
|
+
dispose(): Promise<CleanupReport>;
|
|
45
|
+
}
|
|
46
|
+
export declare class TrialExecutionError extends Error {
|
|
47
|
+
constructor(message: string, options: {
|
|
48
|
+
primary?: unknown;
|
|
49
|
+
secondary: readonly unknown[];
|
|
50
|
+
});
|
|
51
|
+
readonly secondary: readonly unknown[];
|
|
52
|
+
}
|
|
53
|
+
export declare function runWithUnconditionalSettlement<T>(options: {
|
|
54
|
+
sendTurn(): Promise<T>;
|
|
55
|
+
finalize(input: {
|
|
56
|
+
providerResult?: T;
|
|
57
|
+
providerError?: unknown;
|
|
58
|
+
cancellationRequested: boolean;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
cleanup(): Promise<void>;
|
|
61
|
+
cancellationRequested?: boolean;
|
|
62
|
+
}): Promise<T>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { lstat, opendir, readFile, realpath, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
3
|
+
import { removeSandboxRoot } from '../../providers/sandbox-lifecycle.js';
|
|
4
|
+
export async function persistTrialArtifact(path, content, phase) {
|
|
5
|
+
const pending = `${path}.pending`;
|
|
6
|
+
const destination = phase === 'provisional' ? pending : path;
|
|
7
|
+
const temporary = `${destination}.${process.pid}.tmp`;
|
|
8
|
+
try {
|
|
9
|
+
await writeFile(temporary, content, { mode: 0o600 });
|
|
10
|
+
await rename(temporary, destination);
|
|
11
|
+
if (phase === 'final')
|
|
12
|
+
await rm(pending, { force: true });
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
await rm(temporary, { force: true }).catch(() => undefined);
|
|
16
|
+
if (phase === 'final')
|
|
17
|
+
await rm(pending, { force: true }).catch(() => undefined);
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class TrialResourceRegistry {
|
|
22
|
+
resources = new Map();
|
|
23
|
+
register(resource) {
|
|
24
|
+
if (this.resources.has(resource.opaqueId))
|
|
25
|
+
throw new Error(`duplicate resource ID ${resource.opaqueId}`);
|
|
26
|
+
this.resources.set(resource.opaqueId, resource);
|
|
27
|
+
}
|
|
28
|
+
entries() { return [...this.resources.values()]; }
|
|
29
|
+
}
|
|
30
|
+
function within(root, path) {
|
|
31
|
+
const difference = relative(root, path);
|
|
32
|
+
return difference === '' || (!difference.startsWith('..') && !isAbsolute(difference));
|
|
33
|
+
}
|
|
34
|
+
export async function scanBearerSurfaces(roots, bearer) {
|
|
35
|
+
if (bearer.length === 0)
|
|
36
|
+
throw new Error('bearer must be non-empty');
|
|
37
|
+
const entries = [];
|
|
38
|
+
for (const declaredRoot of roots) {
|
|
39
|
+
try {
|
|
40
|
+
const root = await realpath(declaredRoot);
|
|
41
|
+
const pending = [root];
|
|
42
|
+
const visited = new Set();
|
|
43
|
+
while (pending.length > 0) {
|
|
44
|
+
const path = pending.pop();
|
|
45
|
+
try {
|
|
46
|
+
const stat = await lstat(path);
|
|
47
|
+
if (stat.isSymbolicLink()) {
|
|
48
|
+
const target = await realpath(path);
|
|
49
|
+
if (!within(root, target))
|
|
50
|
+
entries.push({ path, status: 'clean', reason: 'symlink target outside approved root was not followed' });
|
|
51
|
+
else if (!visited.has(target))
|
|
52
|
+
pending.push(target);
|
|
53
|
+
}
|
|
54
|
+
else if (stat.isDirectory()) {
|
|
55
|
+
const canonicalPath = await realpath(path);
|
|
56
|
+
if (visited.has(canonicalPath))
|
|
57
|
+
continue;
|
|
58
|
+
visited.add(canonicalPath);
|
|
59
|
+
const directory = await opendir(path);
|
|
60
|
+
for await (const entry of directory)
|
|
61
|
+
pending.push(resolve(path, entry.name));
|
|
62
|
+
}
|
|
63
|
+
else if (stat.isFile()) {
|
|
64
|
+
const content = await readFile(path);
|
|
65
|
+
entries.push({ path, status: content.includes(Buffer.from(bearer)) ? 'bearer-found' : 'clean' });
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
entries.push({ path, status: 'skipped', reason: 'not a regular file, directory, or symlink' });
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
entries.push({ path, status: 'unreadable', reason: error instanceof Error ? error.message : String(error) });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
entries.push({ path: declaredRoot, status: 'unreadable', reason: error instanceof Error ? error.message : String(error) });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return entries.sort((left, right) => left.path.localeCompare(right.path, 'en'));
|
|
80
|
+
}
|
|
81
|
+
export async function cleanupTrial(options) {
|
|
82
|
+
const released = [];
|
|
83
|
+
const failures = [];
|
|
84
|
+
for (const resource of [...options.registry.entries()].reverse()) {
|
|
85
|
+
try {
|
|
86
|
+
await resource.close();
|
|
87
|
+
if (resource.isOpen())
|
|
88
|
+
throw new Error('resource remained open after close');
|
|
89
|
+
released.push(resource.opaqueId);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
failures.push({ opaqueId: resource.opaqueId, reason: error instanceof Error ? error.message : String(error) });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const scans = await scanBearerSurfaces(options.scanRoots, options.bearer);
|
|
96
|
+
const bearerFound = scans.some((entry) => entry.status === 'bearer-found');
|
|
97
|
+
if (scans.some((entry) => entry.status === 'unreadable' || entry.status === 'skipped'))
|
|
98
|
+
failures.push({ opaqueId: 'surface-scan', reason: 'scan was incomplete' });
|
|
99
|
+
if (bearerFound)
|
|
100
|
+
failures.push({ opaqueId: 'surface-scan', reason: 'bearer found in writable surface' });
|
|
101
|
+
const report = { scans, released: released.sort(), failures, scratchDeleted: false, bearerFound };
|
|
102
|
+
if (!bearerFound) {
|
|
103
|
+
try {
|
|
104
|
+
await options.persistRedacted(report, 'provisional');
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
failures.push({ opaqueId: 'artifact-persistence', reason: error instanceof Error ? error.message : String(error) });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else
|
|
111
|
+
failures.push({ opaqueId: 'artifact-persistence', reason: 'persistence refused because bearer remained' });
|
|
112
|
+
options.zeroBearer?.();
|
|
113
|
+
try {
|
|
114
|
+
const parent = await realpath(options.approvedScratchParent);
|
|
115
|
+
const scratch = await realpath(options.scratchPath);
|
|
116
|
+
if (scratch === parent || !within(parent, scratch))
|
|
117
|
+
throw new Error('scratch path is outside its approved parent');
|
|
118
|
+
await (options.removeScratch ?? removeSandboxRoot)(scratch);
|
|
119
|
+
report.scratchDeleted = true;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
failures.push({ opaqueId: 'scratch', reason: error instanceof Error ? error.message : String(error) });
|
|
123
|
+
}
|
|
124
|
+
if (!bearerFound) {
|
|
125
|
+
try {
|
|
126
|
+
await options.persistRedacted(report, 'final');
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
failures.push({ opaqueId: 'artifact-finalization', reason: error instanceof Error ? error.message : String(error) });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return report;
|
|
133
|
+
}
|
|
134
|
+
export class IdempotentTrialCleaner {
|
|
135
|
+
options;
|
|
136
|
+
result;
|
|
137
|
+
constructor(options) {
|
|
138
|
+
this.options = options;
|
|
139
|
+
}
|
|
140
|
+
dispose() {
|
|
141
|
+
this.result ??= cleanupTrial(this.options);
|
|
142
|
+
return this.result;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export class TrialExecutionError extends Error {
|
|
146
|
+
constructor(message, options) {
|
|
147
|
+
super(message, { cause: options.primary });
|
|
148
|
+
this.name = 'TrialExecutionError';
|
|
149
|
+
this.secondary = options.secondary;
|
|
150
|
+
}
|
|
151
|
+
secondary;
|
|
152
|
+
}
|
|
153
|
+
export async function runWithUnconditionalSettlement(options) {
|
|
154
|
+
let providerResult;
|
|
155
|
+
let providerError;
|
|
156
|
+
const secondary = [];
|
|
157
|
+
try {
|
|
158
|
+
providerResult = await options.sendTurn();
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
providerError = error;
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
try {
|
|
165
|
+
await options.finalize({ providerResult, providerError, cancellationRequested: options.cancellationRequested ?? false });
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
secondary.push(error);
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await options.cleanup();
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
secondary.push(error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (providerError !== undefined || secondary.length > 0) {
|
|
178
|
+
throw new TrialExecutionError(providerError instanceof Error ? providerError.message : 'trial settlement failed', { primary: providerError, secondary });
|
|
179
|
+
}
|
|
180
|
+
// SAFETY: reaching this line proves sendTurn resolved and neither it nor settlement recorded an error.
|
|
181
|
+
return providerResult;
|
|
182
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type CleanupReport } from './cleanup.js';
|
|
2
|
+
import { type AppServerSessionHandle } from '../../agents/codex-app-server/transport.js';
|
|
3
|
+
import type { EvidenceEnvelope, ScenarioArtifact, ScenarioMachineV2 } from './types.js';
|
|
4
|
+
export declare const CODEX_DIRECT_MCP_FLAG = "PATHGRADE_INTERNAL_CODEX_DIRECT_MCP_V2";
|
|
5
|
+
export type CodexDirectMcpChecks = {
|
|
6
|
+
runtimeAdmitted: boolean;
|
|
7
|
+
secretFreeProject: boolean;
|
|
8
|
+
directCall: boolean;
|
|
9
|
+
structuredToolError: boolean;
|
|
10
|
+
fencedCancellation: boolean;
|
|
11
|
+
providerTerminal: boolean;
|
|
12
|
+
hostQuiescent: boolean;
|
|
13
|
+
postCancelReuse: boolean;
|
|
14
|
+
privateReplay: boolean;
|
|
15
|
+
cleanup: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type CodexDirectMcpTrialResult = {
|
|
18
|
+
selected: true;
|
|
19
|
+
profileFingerprint: string;
|
|
20
|
+
artifact: ScenarioArtifact;
|
|
21
|
+
evidence: readonly EvidenceEnvelope[];
|
|
22
|
+
checks: CodexDirectMcpChecks;
|
|
23
|
+
cleanup: CleanupReport;
|
|
24
|
+
cancellationStatus: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function isCodexDirectMcpSelected(environment?: NodeJS.ProcessEnv): boolean;
|
|
27
|
+
export declare function codexDirectMcpDisableReason(): string | undefined;
|
|
28
|
+
/** Test-only reset for proving the fail-closed process latch. */
|
|
29
|
+
export declare function __resetCodexDirectMcpDisablementForTesting(): void;
|
|
30
|
+
export declare function runCodexDirectMcpTrial(options: {
|
|
31
|
+
scenario: ScenarioMachineV2;
|
|
32
|
+
environment?: NodeJS.ProcessEnv;
|
|
33
|
+
model?: string;
|
|
34
|
+
artifactDirectory?: string;
|
|
35
|
+
createControlPlane?: (context: {
|
|
36
|
+
cwd: string;
|
|
37
|
+
env: NodeJS.ProcessEnv;
|
|
38
|
+
binary: string;
|
|
39
|
+
}) => AppServerSessionHandle;
|
|
40
|
+
}): Promise<CodexDirectMcpTrialResult>;
|
|
41
|
+
export declare function mapCodexProviderEnvironment(environment: NodeJS.ProcessEnv, codexHome: string): NodeJS.ProcessEnv;
|