@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,20 @@
|
|
|
1
|
+
import type { AgentSessionOptions } from '../types.js';
|
|
2
|
+
import type { AcpToolNameNormalizer } from '../internal/direct-mcp-v2/acp-author-projector.js';
|
|
3
|
+
import { attachAcpAuthorSession, type AcpAuthorSession } from '../internal/direct-mcp-v2/acp-author-session.js';
|
|
4
|
+
import { startScenarioCoordinator, type PublicScenarioRuntimeFactory } from '../internal/direct-mcp-v2/public-scenario-runtime.js';
|
|
5
|
+
import { resolveCursorExecutable } from '../internal/direct-mcp-v2/cursor-profile.js';
|
|
6
|
+
export declare function setCursorScenarioSession(options: AgentSessionOptions, session: AcpAuthorSession): void;
|
|
7
|
+
export declare function getCursorScenarioSession(options: AgentSessionOptions | undefined): AcpAuthorSession | undefined;
|
|
8
|
+
/** Cursor's admitted ACP build exposes MCP calls only as the stable title `MCP: tool`. */
|
|
9
|
+
export declare const normalizeCursorAcpToolName: AcpToolNameNormalizer;
|
|
10
|
+
type CursorScenarioRuntimeDependencies = {
|
|
11
|
+
attachAcpAuthorSession: typeof attachAcpAuthorSession;
|
|
12
|
+
resolveCursorExecutable: typeof resolveCursorExecutable;
|
|
13
|
+
startScenarioCoordinator: typeof startScenarioCoordinator;
|
|
14
|
+
};
|
|
15
|
+
export type CursorScenarioRuntimeFactoryOptions = {
|
|
16
|
+
requestedModel?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function createPublicCursorScenarioRuntimeFactory(factoryOptions?: CursorScenarioRuntimeFactoryOptions, dependencyOverrides?: Partial<CursorScenarioRuntimeDependencies>): PublicScenarioRuntimeFactory;
|
|
19
|
+
export declare const startPublicCursorScenarioRuntime: typeof import("../internal/direct-mcp-v2/public-scenario-runtime.js").startPublicClaudeScenarioRuntime;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { attachAcpAuthorSession, } from '../internal/direct-mcp-v2/acp-author-session.js';
|
|
2
|
+
import { startScenarioCoordinator, } from '../internal/direct-mcp-v2/public-scenario-runtime.js';
|
|
3
|
+
import { CURSOR_ACP_AUTHOR_DARWIN_ARM64_LOCK, resolveCursorExecutable, } from '../internal/direct-mcp-v2/cursor-profile.js';
|
|
4
|
+
import { mapCursorProviderEnvironment } from '../internal/direct-mcp-v2/cursor-direct-mcp.js';
|
|
5
|
+
const scenarioSessions = new WeakMap();
|
|
6
|
+
export function setCursorScenarioSession(options, session) {
|
|
7
|
+
if (scenarioSessions.has(options))
|
|
8
|
+
throw new Error('Cursor scenario session is already configured');
|
|
9
|
+
scenarioSessions.set(options, session);
|
|
10
|
+
}
|
|
11
|
+
export function getCursorScenarioSession(options) {
|
|
12
|
+
return options ? scenarioSessions.get(options) : undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Cursor's admitted ACP build exposes MCP calls only as the stable title `MCP: tool`. */
|
|
15
|
+
export const normalizeCursorAcpToolName = (title) => title === 'MCP: tool'
|
|
16
|
+
? { action: 'mcp_tool_call', providerToolName: 'cursor_mcp_tool' }
|
|
17
|
+
: { action: 'unknown', providerToolName: title };
|
|
18
|
+
const defaultDependencies = {
|
|
19
|
+
attachAcpAuthorSession,
|
|
20
|
+
resolveCursorExecutable,
|
|
21
|
+
startScenarioCoordinator,
|
|
22
|
+
};
|
|
23
|
+
export function createPublicCursorScenarioRuntimeFactory(factoryOptions = {}, dependencyOverrides = {}) {
|
|
24
|
+
if (factoryOptions.requestedModel !== undefined) {
|
|
25
|
+
throw new Error('Cursor Scenario V2 does not support model overrides until the ACP model field is admitted');
|
|
26
|
+
}
|
|
27
|
+
const deps = { ...defaultDependencies, ...dependencyOverrides };
|
|
28
|
+
return async function startPublicCursorScenarioRuntime(options) {
|
|
29
|
+
const environment = cursorScenarioEnvironment(options.environment);
|
|
30
|
+
const resolvedExecutable = await deps.resolveCursorExecutable(environment);
|
|
31
|
+
const coordinator = await deps.startScenarioCoordinator({
|
|
32
|
+
...options,
|
|
33
|
+
provider: 'cursor',
|
|
34
|
+
attach: async (context) => deps.attachAcpAuthorSession({
|
|
35
|
+
provider: 'cursor',
|
|
36
|
+
context,
|
|
37
|
+
runtimeLock: CURSOR_ACP_AUTHOR_DARWIN_ARM64_LOCK,
|
|
38
|
+
resolvedExecutable,
|
|
39
|
+
cwd: options.workspacePath,
|
|
40
|
+
environment,
|
|
41
|
+
scanRoots: cursorScanRoots(options.workspacePath, environment),
|
|
42
|
+
normalizeToolName: normalizeCursorAcpToolName,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
let configuredOptions;
|
|
46
|
+
return {
|
|
47
|
+
configureSession(sessionOptions) {
|
|
48
|
+
if (sessionOptions.model) {
|
|
49
|
+
throw new Error('Cursor Scenario V2 does not support model overrides until the ACP model field is admitted');
|
|
50
|
+
}
|
|
51
|
+
if (configuredOptions && configuredOptions !== sessionOptions) {
|
|
52
|
+
throw new Error('Cursor Scenario V2 runtime is already configured for another session');
|
|
53
|
+
}
|
|
54
|
+
configuredOptions = sessionOptions;
|
|
55
|
+
setCursorScenarioSession(sessionOptions, coordinator.attachment);
|
|
56
|
+
},
|
|
57
|
+
beginTurn: coordinator.beginTurn,
|
|
58
|
+
settleTurn: coordinator.settleTurn,
|
|
59
|
+
failTurn: coordinator.failTurn,
|
|
60
|
+
evidence: coordinator.evidence,
|
|
61
|
+
async dispose() {
|
|
62
|
+
try {
|
|
63
|
+
await coordinator.dispose();
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
if (configuredOptions)
|
|
67
|
+
scenarioSessions.delete(configuredOptions);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export const startPublicCursorScenarioRuntime = createPublicCursorScenarioRuntimeFactory();
|
|
74
|
+
function cursorScenarioEnvironment(source) {
|
|
75
|
+
const environment = mapCursorProviderEnvironment(source);
|
|
76
|
+
for (const name of [
|
|
77
|
+
'HOME', 'TMPDIR', 'TMP', 'TEMP',
|
|
78
|
+
'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME',
|
|
79
|
+
]) {
|
|
80
|
+
if (source[name])
|
|
81
|
+
environment[name] = source[name];
|
|
82
|
+
}
|
|
83
|
+
return environment;
|
|
84
|
+
}
|
|
85
|
+
function cursorScanRoots(workspacePath, environment) {
|
|
86
|
+
return [...new Set([
|
|
87
|
+
workspacePath,
|
|
88
|
+
environment.HOME,
|
|
89
|
+
environment.TMPDIR,
|
|
90
|
+
environment.TMP,
|
|
91
|
+
environment.TEMP,
|
|
92
|
+
environment.XDG_CONFIG_HOME,
|
|
93
|
+
environment.XDG_DATA_HOME,
|
|
94
|
+
environment.XDG_CACHE_HOME,
|
|
95
|
+
].filter((path) => typeof path === 'string' && path.length > 0))];
|
|
96
|
+
}
|
package/dist/agents/cursor.js
CHANGED
|
@@ -2,6 +2,7 @@ import { BaseAgent, getWorkspacePath } from '../types.js';
|
|
|
2
2
|
import { TOOL_NAME_MAP, buildSummary, enrichSkillEvents } from '../tool-events.js';
|
|
3
3
|
import { getVisibleAssistantMessage } from '../sdk/visible-turn.js';
|
|
4
4
|
import { prependRuntimePolicies } from '../sdk/runtime-policy.js';
|
|
5
|
+
import { getCursorScenarioSession } from './cursor-scenario.js';
|
|
5
6
|
import { assertUnsupportedLiveMcpSafetyRuntime, mountMcpForCursor, } from '../providers/mcp-runtime-mounting.js';
|
|
6
7
|
/**
|
|
7
8
|
* Cursor tool_call discriminants observed in the discovery spike. Probed in
|
|
@@ -150,6 +151,25 @@ export function parseCursorStreamJson(stdout) {
|
|
|
150
151
|
const CURSOR_EXECUTABLE = 'cursor-agent';
|
|
151
152
|
export class CursorAgent extends BaseAgent {
|
|
152
153
|
async createSession(runtime, runCommand, options) {
|
|
154
|
+
const scenarioSession = getCursorScenarioSession(options);
|
|
155
|
+
if (scenarioSession) {
|
|
156
|
+
if (options?.model) {
|
|
157
|
+
throw new Error('Cursor Scenario V2 does not support model overrides until the ACP model field is admitted');
|
|
158
|
+
}
|
|
159
|
+
let turnNumber = 0;
|
|
160
|
+
const runScenarioTurn = async (message) => {
|
|
161
|
+
const appliedRuntimePolicies = turnNumber === 0 ? [...(options?.runtimePolicies ?? [])] : [];
|
|
162
|
+
const effectiveMessage = appliedRuntimePolicies.length > 0
|
|
163
|
+
? prependRuntimePolicies(message, appliedRuntimePolicies, { agent: 'cursor' })
|
|
164
|
+
: message;
|
|
165
|
+
const result = await scenarioSession.runTurn(effectiveMessage, ++turnNumber);
|
|
166
|
+
return { ...result, runtimePoliciesApplied: appliedRuntimePolicies };
|
|
167
|
+
};
|
|
168
|
+
return {
|
|
169
|
+
start: async ({ message }) => runScenarioTurn(message),
|
|
170
|
+
reply: async ({ message }) => runScenarioTurn(message),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
153
173
|
const workspacePath = getWorkspacePath(runtime);
|
|
154
174
|
let sessionId;
|
|
155
175
|
return {
|
|
@@ -221,6 +241,7 @@ export class CursorAgent extends BaseAgent {
|
|
|
221
241
|
timedOut: result.timedOut,
|
|
222
242
|
toolEvents,
|
|
223
243
|
runtimePoliciesApplied: appliedRuntimePolicies,
|
|
244
|
+
...(options?.model ? { resolvedModel: options.model } : {}),
|
|
224
245
|
...(parsed.tokenUsage
|
|
225
246
|
? { inputTokens: parsed.tokenUsage.inputTokens, outputTokens: parsed.tokenUsage.outputTokens }
|
|
226
247
|
: {}),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AgentSessionOptions, AgentTurnResult } from '../../types.js';
|
|
2
|
+
import { attachAcpAuthorSession, type AttachAcpAuthorSessionOptions } from '../../internal/direct-mcp-v2/acp-author-session.js';
|
|
3
|
+
import { type PublicScenarioRuntimeFactory } from '../../internal/direct-mcp-v2/public-scenario-runtime.js';
|
|
4
|
+
export type OpenCodeScenarioRuntimeFactoryOptions = {
|
|
5
|
+
opencodeExecutable: string;
|
|
6
|
+
requestedModel?: string;
|
|
7
|
+
};
|
|
8
|
+
type OpenCodeScenarioRuntimePolicy = {
|
|
9
|
+
readonly model: string;
|
|
10
|
+
readonly oauth: boolean;
|
|
11
|
+
environment(): Record<string, string>;
|
|
12
|
+
beforeTurn(remainingMs: number): Promise<void>;
|
|
13
|
+
afterTurn(): Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
export type OpenCodeScenarioRuntimeDependencies = {
|
|
16
|
+
attachAuthorSession(options: AttachAcpAuthorSessionOptions): ReturnType<typeof attachAcpAuthorSession>;
|
|
17
|
+
createRuntimePolicy(environment: Record<string, string>, requestedModel?: string): Promise<OpenCodeScenarioRuntimePolicy>;
|
|
18
|
+
assertCleanManagedHost(): Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
export type OpenCodeScenarioSession = {
|
|
21
|
+
runTurn(message: string): Promise<AgentTurnResult>;
|
|
22
|
+
};
|
|
23
|
+
export declare function getOpenCodeScenarioSession(options: AgentSessionOptions): OpenCodeScenarioSession | undefined;
|
|
24
|
+
export declare function createPublicOpenCodeScenarioRuntimeFactory(factoryOptions: OpenCodeScenarioRuntimeFactoryOptions, dependencyOverrides?: Partial<OpenCodeScenarioRuntimeDependencies>): PublicScenarioRuntimeFactory;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { removeSandboxRoot } from '../../providers/sandbox-lifecycle.js';
|
|
4
|
+
import { attachAcpAuthorSession, } from '../../internal/direct-mcp-v2/acp-author-session.js';
|
|
5
|
+
import { startScenarioCoordinator, } from '../../internal/direct-mcp-v2/public-scenario-runtime.js';
|
|
6
|
+
import { OPENCODE_ACP_DARWIN_ARM64_RUNTIME_LOCK } from '../../internal/direct-mcp-v2/opencode-profile.js';
|
|
7
|
+
import { OPENCODE_PERMISSION, OpenCodeRuntimePolicy } from './runtime-policy.js';
|
|
8
|
+
import { assertCleanManagedOpenCodeHost } from './host-safety.js';
|
|
9
|
+
import { DEFAULT_OPENCODE_MODEL, sanitizeOpenCodeToolName } from './contract.js';
|
|
10
|
+
const defaultDependencies = {
|
|
11
|
+
attachAuthorSession: attachAcpAuthorSession,
|
|
12
|
+
createRuntimePolicy: OpenCodeRuntimePolicy.create,
|
|
13
|
+
assertCleanManagedHost: assertCleanManagedOpenCodeHost,
|
|
14
|
+
};
|
|
15
|
+
const configuredSessions = new WeakMap();
|
|
16
|
+
export function getOpenCodeScenarioSession(options) {
|
|
17
|
+
return configuredSessions.get(options);
|
|
18
|
+
}
|
|
19
|
+
export function createPublicOpenCodeScenarioRuntimeFactory(factoryOptions, dependencyOverrides = {}) {
|
|
20
|
+
if (factoryOptions.requestedModel !== undefined) {
|
|
21
|
+
throw new Error(`OpenCode Scenario V2 does not support model overrides; use the default ${DEFAULT_OPENCODE_MODEL} model.`);
|
|
22
|
+
}
|
|
23
|
+
const dependencies = { ...defaultDependencies, ...dependencyOverrides };
|
|
24
|
+
return async (options) => {
|
|
25
|
+
const environment = definedEnvironment(options.environment);
|
|
26
|
+
if (!environment.ANTHROPIC_API_KEY?.trim()) {
|
|
27
|
+
throw new Error(`OpenCode Scenario V2 currently requires API-key authentication using the default model ${DEFAULT_OPENCODE_MODEL}.`);
|
|
28
|
+
}
|
|
29
|
+
await dependencies.assertCleanManagedHost();
|
|
30
|
+
await assertNoProjectConfig(options.workspacePath);
|
|
31
|
+
const xdgDirectories = isolatedOpenCodeDirectories(environment);
|
|
32
|
+
await Promise.all(xdgDirectories.map((directory) => fs.ensureDir(directory)));
|
|
33
|
+
let coordinator;
|
|
34
|
+
try {
|
|
35
|
+
const runtimePolicy = await dependencies.createRuntimePolicy(environment);
|
|
36
|
+
if (runtimePolicy.oauth || runtimePolicy.model !== DEFAULT_OPENCODE_MODEL) {
|
|
37
|
+
throw new Error(`OpenCode Scenario V2 currently requires API-key authentication using the default model ${DEFAULT_OPENCODE_MODEL}.`);
|
|
38
|
+
}
|
|
39
|
+
const providerEnvironment = buildScenarioEnvironment(environment, runtimePolicy, xdgDirectories);
|
|
40
|
+
const mcpToolNames = scenarioToolNames(options.artifact);
|
|
41
|
+
coordinator = await startScenarioCoordinator({
|
|
42
|
+
artifact: options.artifact,
|
|
43
|
+
workspacePath: options.workspacePath,
|
|
44
|
+
environment: {
|
|
45
|
+
HOME: environment.HOME,
|
|
46
|
+
TMPDIR: path.dirname(options.workspacePath),
|
|
47
|
+
},
|
|
48
|
+
onEvidence: options.onEvidence,
|
|
49
|
+
provider: 'opencode',
|
|
50
|
+
attach: (context) => dependencies.attachAuthorSession({
|
|
51
|
+
provider: 'opencode',
|
|
52
|
+
context,
|
|
53
|
+
runtimeLock: OPENCODE_ACP_DARWIN_ARM64_RUNTIME_LOCK,
|
|
54
|
+
resolvedExecutable: factoryOptions.opencodeExecutable,
|
|
55
|
+
cwd: options.workspacePath,
|
|
56
|
+
environment: providerEnvironment,
|
|
57
|
+
scanRoots: scenarioScanRoots(options.workspacePath, providerEnvironment, xdgDirectories),
|
|
58
|
+
normalizeToolName: (title) => normalizeOpenCodeToolName(title, mcpToolNames),
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
let configuredOptions;
|
|
62
|
+
let disposed = false;
|
|
63
|
+
let nextTurnNumber = 0;
|
|
64
|
+
const scenarioSession = {
|
|
65
|
+
async runTurn(message) {
|
|
66
|
+
if (disposed)
|
|
67
|
+
throw new Error('OpenCode Scenario V2 session is disposed');
|
|
68
|
+
const sessionOptions = configuredOptions;
|
|
69
|
+
sessionOptions?.getAbortSignal?.()?.throwIfAborted();
|
|
70
|
+
await runtimePolicy.beforeTurn(sessionOptions?.getRemainingMs?.() ?? 0);
|
|
71
|
+
try {
|
|
72
|
+
sessionOptions?.getAbortSignal?.()?.throwIfAborted();
|
|
73
|
+
return await coordinator.attachment.runTurn(message, ++nextTurnNumber);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
await runtimePolicy.afterTurn();
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
const attachedCoordinator = coordinator;
|
|
81
|
+
return {
|
|
82
|
+
configureSession(sessionOptions) {
|
|
83
|
+
if (disposed)
|
|
84
|
+
throw new Error('OpenCode Scenario V2 runtime is disposed');
|
|
85
|
+
if (configuredOptions && configuredOptions !== sessionOptions) {
|
|
86
|
+
throw new Error('OpenCode Scenario V2 runtime is already configured for another session');
|
|
87
|
+
}
|
|
88
|
+
configuredOptions = sessionOptions;
|
|
89
|
+
configuredSessions.set(sessionOptions, scenarioSession);
|
|
90
|
+
},
|
|
91
|
+
beginTurn: attachedCoordinator.beginTurn,
|
|
92
|
+
settleTurn: attachedCoordinator.settleTurn,
|
|
93
|
+
failTurn: attachedCoordinator.failTurn,
|
|
94
|
+
evidence: attachedCoordinator.evidence,
|
|
95
|
+
async dispose() {
|
|
96
|
+
if (disposed)
|
|
97
|
+
return;
|
|
98
|
+
disposed = true;
|
|
99
|
+
const failures = [];
|
|
100
|
+
try {
|
|
101
|
+
await attachedCoordinator.dispose();
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
failures.push(error);
|
|
105
|
+
}
|
|
106
|
+
if (configuredOptions)
|
|
107
|
+
configuredSessions.delete(configuredOptions);
|
|
108
|
+
try {
|
|
109
|
+
await Promise.all(xdgDirectories.map((directory) => removeSandboxRoot(directory)));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
failures.push(error);
|
|
113
|
+
}
|
|
114
|
+
if (failures.length > 0)
|
|
115
|
+
throw new AggregateError(failures, 'OpenCode Scenario V2 cleanup failed');
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const cleanupErrors = [];
|
|
121
|
+
try {
|
|
122
|
+
await coordinator?.dispose();
|
|
123
|
+
}
|
|
124
|
+
catch (cleanupError) {
|
|
125
|
+
cleanupErrors.push(cleanupError);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
await Promise.all(xdgDirectories.map((directory) => removeSandboxRoot(directory)));
|
|
129
|
+
}
|
|
130
|
+
catch (cleanupError) {
|
|
131
|
+
cleanupErrors.push(cleanupError);
|
|
132
|
+
}
|
|
133
|
+
if (cleanupErrors.length > 0) {
|
|
134
|
+
throw new AggregateError([error, ...cleanupErrors], 'OpenCode Scenario V2 setup and cleanup failed');
|
|
135
|
+
}
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function definedEnvironment(environment) {
|
|
141
|
+
return Object.fromEntries(Object.entries(environment).filter((entry) => entry[1] !== undefined));
|
|
142
|
+
}
|
|
143
|
+
function isolatedOpenCodeDirectories(environment) {
|
|
144
|
+
if (!environment.HOME)
|
|
145
|
+
throw new Error('OpenCode Scenario V2 requires a managed HOME');
|
|
146
|
+
return [
|
|
147
|
+
path.join(environment.HOME, '.local', 'share'),
|
|
148
|
+
path.join(environment.HOME, '.config'),
|
|
149
|
+
path.join(environment.HOME, '.local', 'state'),
|
|
150
|
+
path.join(environment.HOME, '.cache'),
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
function buildScenarioEnvironment(environment, runtimePolicy, directories) {
|
|
154
|
+
const [data, config, state, cache] = directories;
|
|
155
|
+
const result = {
|
|
156
|
+
...environment,
|
|
157
|
+
XDG_DATA_HOME: data,
|
|
158
|
+
XDG_CONFIG_HOME: config,
|
|
159
|
+
XDG_STATE_HOME: state,
|
|
160
|
+
XDG_CACHE_HOME: cache,
|
|
161
|
+
...runtimePolicy.environment(),
|
|
162
|
+
OPENCODE_PERMISSION,
|
|
163
|
+
};
|
|
164
|
+
delete result.OPENCODE_CONFIG_CONTENT;
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
async function assertNoProjectConfig(workspacePath) {
|
|
168
|
+
for (const name of ['opencode.json', 'opencode.jsonc', '.opencode']) {
|
|
169
|
+
if (await fs.pathExists(path.join(workspacePath, name))) {
|
|
170
|
+
throw new Error(`OpenCode workspace configuration is not allowed: ${name}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function scenarioToolNames(artifact) {
|
|
175
|
+
return new Set(Object.entries(artifact.canonicalIr.servers).flatMap(([serverId, server]) => Object.keys(server.tools).map((toolId) => `${sanitizeOpenCodeToolName(serverId)}_${sanitizeOpenCodeToolName(toolId)}`)));
|
|
176
|
+
}
|
|
177
|
+
function normalizeOpenCodeToolName(title, mcpToolNames) {
|
|
178
|
+
if (mcpToolNames.has(title))
|
|
179
|
+
return { action: 'mcp_tool_call', providerToolName: title };
|
|
180
|
+
const nativeActions = {
|
|
181
|
+
bash: 'run_shell', read: 'read_file', write: 'write_file', edit: 'edit_file',
|
|
182
|
+
grep: 'search_code', glob: 'list_files', list: 'list_files', skill: 'use_skill',
|
|
183
|
+
todowrite: 'update_todos', lsp: 'search_code',
|
|
184
|
+
};
|
|
185
|
+
const action = nativeActions[title.toLowerCase()];
|
|
186
|
+
return { action: action ?? 'unknown', providerToolName: title };
|
|
187
|
+
}
|
|
188
|
+
function scenarioScanRoots(workspacePath, environment, xdgDirectories) {
|
|
189
|
+
return [...new Set([
|
|
190
|
+
path.dirname(workspacePath),
|
|
191
|
+
workspacePath,
|
|
192
|
+
environment.HOME,
|
|
193
|
+
...xdgDirectories,
|
|
194
|
+
].filter((entry) => typeof entry === 'string' && entry.length > 0))];
|
|
195
|
+
}
|
package/dist/agents/opencode.js
CHANGED
|
@@ -6,13 +6,14 @@ import { buildSummary, enrichSkillEvents } from '../tool-events.js';
|
|
|
6
6
|
import { collectSensitiveEnvValues, sanitizePersistenceValue, sanitizeToolEventResult, } from '../tool-event-results.js';
|
|
7
7
|
import { readStagedMcpServers } from '../providers/mcp-config.js';
|
|
8
8
|
import { removeSandboxRoot } from '../providers/sandbox-lifecycle.js';
|
|
9
|
-
import { attachTurnResultSensitiveValues } from '../sdk/turn-result-secrets.js';
|
|
9
|
+
import { attachTurnResultSensitiveValues, cloneTurnResultWithSensitiveValues } from '../sdk/turn-result-secrets.js';
|
|
10
10
|
import { attachOriginalMcpInput } from '../sdk/mcp-event-input.js';
|
|
11
11
|
import { attachToolEventSensitiveValues } from '../sdk/tool-event-secrets.js';
|
|
12
12
|
import { currentOpenCodePlatformKey, OPENCODE_RUNTIME_LOCK, } from './opencode/contract.js';
|
|
13
13
|
import { OpenCodeRuntimePolicy, OPENCODE_PERMISSION } from './opencode/runtime-policy.js';
|
|
14
14
|
import { killOpenCodeProcessGroup, registerOpenCodeProcessGroup, unregisterOpenCodeProcessGroup, } from './opencode/process-groups.js';
|
|
15
15
|
import { assertCleanManagedOpenCodeHost, sha256File } from './opencode/host-safety.js';
|
|
16
|
+
import { getOpenCodeScenarioSession } from './opencode/scenario.js';
|
|
16
17
|
export { assertCleanManagedOpenCodeHost, managedOpenCodeConfigPaths } from './opencode/host-safety.js';
|
|
17
18
|
const OUTPUT_CAP_BYTES = 16 * 1024 * 1024;
|
|
18
19
|
const NATIVE_TOOL_ACTIONS = {
|
|
@@ -450,7 +451,9 @@ class OpenCodeSession {
|
|
|
450
451
|
throw new Error('OpenCode protocol error: resumed session ID changed');
|
|
451
452
|
}
|
|
452
453
|
this.sessionId = parsed.sessionId;
|
|
453
|
-
return parsed.result
|
|
454
|
+
return cloneTurnResultWithSensitiveValues(parsed.result, {
|
|
455
|
+
resolvedModel: this.runtimePolicy.model,
|
|
456
|
+
});
|
|
454
457
|
}
|
|
455
458
|
catch (error) {
|
|
456
459
|
this.failed = true;
|
|
@@ -512,11 +515,60 @@ class OpenCodeSession {
|
|
|
512
515
|
await this.cleanupState();
|
|
513
516
|
}
|
|
514
517
|
}
|
|
518
|
+
class OpenCodeScenarioAgentSession {
|
|
519
|
+
scenario;
|
|
520
|
+
started = false;
|
|
521
|
+
running = false;
|
|
522
|
+
failed = false;
|
|
523
|
+
disposed = false;
|
|
524
|
+
constructor(scenario) {
|
|
525
|
+
this.scenario = scenario;
|
|
526
|
+
}
|
|
527
|
+
start({ message }) {
|
|
528
|
+
if (this.started)
|
|
529
|
+
return Promise.reject(new Error('OpenCode session has already started'));
|
|
530
|
+
this.started = true;
|
|
531
|
+
return this.execute(message);
|
|
532
|
+
}
|
|
533
|
+
reply({ message }) {
|
|
534
|
+
if (!this.started)
|
|
535
|
+
return Promise.reject(new Error('OpenCode session has not started'));
|
|
536
|
+
return this.execute(message);
|
|
537
|
+
}
|
|
538
|
+
async execute(message) {
|
|
539
|
+
if (this.disposed)
|
|
540
|
+
throw new Error('OpenCode session is disposed');
|
|
541
|
+
if (this.failed)
|
|
542
|
+
throw new Error('OpenCode session is unusable after a failed turn');
|
|
543
|
+
if (this.running)
|
|
544
|
+
throw new Error('OpenCode does not allow concurrent turns');
|
|
545
|
+
this.running = true;
|
|
546
|
+
try {
|
|
547
|
+
const result = await this.scenario.runTurn(message);
|
|
548
|
+
if (result.exitCode !== 0 && !/"stopReason"\s*:\s*"cancelled"/.test(result.rawOutput))
|
|
549
|
+
this.failed = true;
|
|
550
|
+
return result;
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
this.failed = true;
|
|
554
|
+
throw error;
|
|
555
|
+
}
|
|
556
|
+
finally {
|
|
557
|
+
this.running = false;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async dispose() {
|
|
561
|
+
this.disposed = true;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
515
564
|
export class OpenCodeAgent extends BaseAgent {
|
|
516
565
|
async createSession(runtime, _runCommand, options = {}) {
|
|
517
566
|
if (!options.opencodeExecutable) {
|
|
518
567
|
throw new Error('OpenCode requires opencodeExecutable');
|
|
519
568
|
}
|
|
569
|
+
const scenario = getOpenCodeScenarioSession(options);
|
|
570
|
+
if (scenario)
|
|
571
|
+
return new OpenCodeScenarioAgentSession(scenario);
|
|
520
572
|
return new OpenCodeSession(runtime, options);
|
|
521
573
|
}
|
|
522
574
|
}
|
|
@@ -24,6 +24,14 @@ export interface ReportOptions {
|
|
|
24
24
|
noComment?: boolean;
|
|
25
25
|
/** Override the comment-id used in the dedup marker. See `resolveCommentId`. */
|
|
26
26
|
commentId?: string;
|
|
27
|
+
/** Provider-neutral URL for a richer external report. */
|
|
28
|
+
detailsUrl?: string;
|
|
29
|
+
/** Provider-neutral status notice included in the report body. */
|
|
30
|
+
notice?: string;
|
|
31
|
+
/** Surface missing context, malformed results, and GitHub API failures. */
|
|
32
|
+
strict?: boolean;
|
|
33
|
+
/** Skip a comment update when the pull request has advanced. */
|
|
34
|
+
expectedHeadSha?: string;
|
|
27
35
|
}
|
|
28
36
|
/**
|
|
29
37
|
* Resolve the comment-id used for the dedup marker.
|
|
@@ -33,7 +41,7 @@ export interface ReportOptions {
|
|
|
33
41
|
*/
|
|
34
42
|
export declare function resolveCommentId(explicit?: string): string;
|
|
35
43
|
/**
|
|
36
|
-
* Entry point for `pathgrade report`.
|
|
37
|
-
*
|
|
44
|
+
* Entry point for `pathgrade report`. The default display-only mode never
|
|
45
|
+
* fails CI. Strict mode is intended for orchestrators that own publication.
|
|
38
46
|
*/
|
|
39
47
|
export declare function runReport(cwd: string, opts?: ReportOptions): Promise<void>;
|
package/dist/commands/report.js
CHANGED
|
@@ -66,8 +66,8 @@ function printMarkdownAndPassRate(markdown, passRate) {
|
|
|
66
66
|
console.log(String(passRate));
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
* Entry point for `pathgrade report`.
|
|
70
|
-
*
|
|
69
|
+
* Entry point for `pathgrade report`. The default display-only mode never
|
|
70
|
+
* fails CI. Strict mode is intended for orchestrators that own publication.
|
|
71
71
|
*/
|
|
72
72
|
export async function runReport(cwd, opts = {}) {
|
|
73
73
|
const resolvedPath = path.resolve(cwd, opts.resultsPath ?? DEFAULT_RESULTS_PATH);
|
|
@@ -77,6 +77,9 @@ export async function runReport(cwd, opts = {}) {
|
|
|
77
77
|
// Branch point: when true, post the comment and print only the pass rate.
|
|
78
78
|
const shouldPost = inCI && !noComment;
|
|
79
79
|
const prContext = shouldPost ? resolvePrContext(process.env) : null;
|
|
80
|
+
if (opts.strict && shouldPost && !prContext) {
|
|
81
|
+
throw new Error('pathgrade report: strict PR commenting requires GITHUB_TOKEN, GITHUB_REPOSITORY, and pull-request context');
|
|
82
|
+
}
|
|
80
83
|
let report = null;
|
|
81
84
|
let loadError = null;
|
|
82
85
|
try {
|
|
@@ -95,6 +98,8 @@ export async function runReport(cwd, opts = {}) {
|
|
|
95
98
|
await postOrUpdateComment(prContext, {
|
|
96
99
|
commentId,
|
|
97
100
|
body: markdown,
|
|
101
|
+
strict: opts.strict,
|
|
102
|
+
expectedHeadSha: opts.expectedHeadSha,
|
|
98
103
|
});
|
|
99
104
|
console.log('0');
|
|
100
105
|
}
|
|
@@ -108,15 +113,31 @@ export async function runReport(cwd, opts = {}) {
|
|
|
108
113
|
await postOrUpdateComment(prContext, {
|
|
109
114
|
commentId,
|
|
110
115
|
body: MISSING_RESULTS_BODY,
|
|
116
|
+
strict: opts.strict,
|
|
117
|
+
expectedHeadSha: opts.expectedHeadSha,
|
|
111
118
|
});
|
|
112
119
|
}
|
|
113
120
|
// Always emit a pass rate line so downstream capture doesn't explode.
|
|
114
121
|
console.log('0');
|
|
122
|
+
if (opts.strict)
|
|
123
|
+
throw new Error(`pathgrade report: ${loadError}`);
|
|
115
124
|
return;
|
|
116
125
|
}
|
|
117
|
-
const markdown = formatReportMarkdown(report, {
|
|
126
|
+
const markdown = formatReportMarkdown(report, {
|
|
127
|
+
commentId,
|
|
128
|
+
detailsUrl: opts.detailsUrl,
|
|
129
|
+
notice: opts.notice,
|
|
130
|
+
});
|
|
118
131
|
if (prContext) {
|
|
119
|
-
await postOrUpdateComment(prContext, {
|
|
132
|
+
const postResult = await postOrUpdateComment(prContext, {
|
|
133
|
+
commentId,
|
|
134
|
+
body: markdown,
|
|
135
|
+
strict: opts.strict,
|
|
136
|
+
expectedHeadSha: opts.expectedHeadSha,
|
|
137
|
+
});
|
|
138
|
+
if (postResult === 'stale') {
|
|
139
|
+
console.error('pathgrade report: pull-request head advanced; skipped stale comment update');
|
|
140
|
+
}
|
|
120
141
|
// Only the pass rate goes to stdout — the markdown lives on the PR.
|
|
121
142
|
console.log(String(report.overall_pass_rate));
|
|
122
143
|
return;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentTurnResult } from '../../types.js';
|
|
2
|
+
import { type ToolAction } from '../../tool-events.js';
|
|
3
|
+
import type { ProviderId } from './types.js';
|
|
4
|
+
import type { AcpPromptResult, JsonRpcMessage } from './acp-client.js';
|
|
5
|
+
export declare const ACP_AUTHOR_TRACE_MAX_CHARS: number;
|
|
6
|
+
export type AcpToolName = {
|
|
7
|
+
action: ToolAction;
|
|
8
|
+
providerToolName: string;
|
|
9
|
+
};
|
|
10
|
+
export type AcpToolNameNormalizer = (title: string) => AcpToolName;
|
|
11
|
+
export type AcpAuthorProjectionInput = {
|
|
12
|
+
provider: Extract<ProviderId, 'cursor' | 'opencode'>;
|
|
13
|
+
terminal: AcpPromptResult;
|
|
14
|
+
notifications: readonly JsonRpcMessage[];
|
|
15
|
+
normalizeToolName(title: string): AcpToolName;
|
|
16
|
+
protectedValues?: readonly string[];
|
|
17
|
+
sensitiveValues?: readonly string[];
|
|
18
|
+
turnNumber?: number;
|
|
19
|
+
};
|
|
20
|
+
export declare function projectAcpAuthorTurn(input: AcpAuthorProjectionInput): AgentTurnResult;
|