@wix/pathgrade 1.0.17 → 1.0.19
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 -4
- package/dist/agents/claude/sdk-message-projector.js +12 -4
- package/dist/agents/claude/tool-results.d.ts +1 -2
- package/dist/agents/claude/tool-results.js +9 -40
- package/dist/agents/claude.js +6 -4
- package/dist/agents/codex-app-server/agent.d.ts +5 -0
- package/dist/agents/codex-app-server/agent.js +145 -179
- package/dist/agents/codex-app-server/item-lifecycle.d.ts +30 -0
- package/dist/agents/codex-app-server/item-lifecycle.js +95 -0
- package/dist/agents/codex-app-server/item-projection.d.ts +62 -0
- package/dist/agents/codex-app-server/item-projection.js +135 -0
- package/dist/agents/codex-app-server/managed-auth.d.ts +12 -0
- package/dist/agents/codex-app-server/managed-auth.js +53 -0
- package/dist/agents/codex-app-server/transport.d.ts +2 -0
- package/dist/agents/codex-app-server/transport.js +30 -3
- package/dist/agents/opencode/host-safety.d.ts +3 -0
- package/dist/agents/opencode/host-safety.js +30 -0
- package/dist/agents/opencode.d.ts +2 -4
- package/dist/agents/opencode.js +47 -38
- package/dist/openai-oauth/chatgpt-oauth-llm.d.ts +25 -0
- package/dist/openai-oauth/chatgpt-oauth-llm.js +398 -0
- package/dist/openai-oauth/codex-auth-broker.d.ts +32 -0
- package/dist/openai-oauth/codex-auth-broker.js +110 -0
- package/dist/openai-oauth/index.d.ts +2 -0
- package/dist/openai-oauth/index.js +1 -0
- package/dist/providers/credentials.d.ts +4 -1
- package/dist/providers/credentials.js +4 -3
- package/dist/providers/sandbox.d.ts +2 -0
- package/dist/providers/scripted-mcp-mock-host.js +6 -3
- package/dist/providers/workspace.d.ts +1 -0
- package/dist/providers/workspace.js +9 -1
- package/dist/sdk/agent-result-log.js +4 -2
- package/dist/sdk/agent.js +7 -0
- package/dist/sdk/judge-tools.js +14 -3
- package/dist/sdk/managed-session.d.ts +2 -0
- package/dist/sdk/managed-session.js +22 -5
- package/dist/sdk/mcp-safety.js +2 -18
- package/dist/sdk/snapshots.d.ts +1 -0
- package/dist/sdk/snapshots.js +3 -2
- package/dist/sdk/tool-event-log.js +5 -2
- package/dist/sdk/tool-event-secrets.d.ts +4 -0
- package/dist/sdk/tool-event-secrets.js +14 -0
- package/dist/sdk/turn-result-secrets.d.ts +5 -0
- package/dist/sdk/turn-result-secrets.js +12 -0
- package/dist/tool-event-results.d.ts +10 -0
- package/dist/tool-event-results.js +171 -0
- package/dist/types.d.ts +2 -0
- package/dist/utils/llm.js +11 -0
- package/docs/OPENAI_OAUTH_JUDGE.md +91 -0
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -35,7 +35,8 @@ By default, Pathgrade tries to reuse the agent CLI's native auth before falling
|
|
|
35
35
|
- **Codex**
|
|
36
36
|
- forwards `OPENAI_API_KEY` when present
|
|
37
37
|
- or runs `codex login --with-api-key` inside the sandbox when an API key is present
|
|
38
|
-
-
|
|
38
|
+
- without API credentials, the default `app-server` transport reuses the active Codex login through short-lived auth-only stdio sessions; `codex exec` retains its cached `~/.codex/auth.json` behavior
|
|
39
|
+
- keyless Codex evals automatically use that managed login for plain and tool-using judges; an explicit `evaluate(..., { llm })` still wins
|
|
39
40
|
- **Cursor**
|
|
40
41
|
- forwards `CURSOR_API_KEY` when set
|
|
41
42
|
- macOS: reuses `cursor-agent login` OAuth tokens from the login Keychain
|
|
@@ -47,7 +48,9 @@ By default, Pathgrade tries to reuse the agent CLI's native auth before falling
|
|
|
47
48
|
|
|
48
49
|
If you set `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL`, or `CURSOR_API_BASE_URL`, set the matching API key too.
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
An [experimental ChatGPT judge that reuses Codex-managed authentication](docs/OPENAI_OAUTH_JUDGE.md) is also available. It requires no API key, auth-file option, or second login and is selected automatically for keyless Codex evals.
|
|
52
|
+
|
|
53
|
+
Do not add `copyFromHome: ['.codex']` just to reuse Codex authentication. Keyless app-server runs stage no credential file; the legacy exec transport stages only `~/.codex/auth.json`. A deliberate full `.codex` copy remains supported, but it also imports host config, MCP definitions, plugins, caches, sessions, and other potentially large or sensitive state; Pathgrade prints a warning when requested.
|
|
51
54
|
|
|
52
55
|
Override credentials per test with `env`:
|
|
53
56
|
|
|
@@ -64,7 +67,7 @@ const agent = await createAgent({
|
|
|
64
67
|
|
|
65
68
|
Codex supports two transports and Pathgrade defaults to `app-server`:
|
|
66
69
|
|
|
67
|
-
- `app-server` (default) — uses `codex app-server` and keeps native thread state. Required for `AskUserReaction` handshakes (`request_user_input` reaches the model).
|
|
70
|
+
- `app-server` (default) — uses `codex app-server` and keeps native thread state. Required for `AskUserReaction` handshakes (`request_user_input` reaches the model). Without API credentials it acquires and refreshes access sessions through the active Codex login.
|
|
68
71
|
- `exec` — uses `codex exec` and re-injects the transcript every turn. Kept for stateless CI matrices that don't need the handshake.
|
|
69
72
|
|
|
70
73
|
Precedence: `createAgent({ transport })` > `PATHGRADE_CODEX_TRANSPORT` env > default (`app-server`). An invalid env value throws at `createAgent` time.
|
|
@@ -80,7 +83,7 @@ If `transport: 'exec'` is resolved and any `AskUserReaction` is present in `Conv
|
|
|
80
83
|
|
|
81
84
|
Migrating from `exec` to `app-server`:
|
|
82
85
|
|
|
83
|
-
-
|
|
86
|
+
- Sign in with `codex login` for subscription-backed app-server and judge calls, export `OPENAI_API_KEY` for API billing, or select `transport: 'exec'` for the old cached-auth behavior.
|
|
84
87
|
- The `noninteractive-user-question` runtime policy no longer attaches under `app-server`. Snapshots that captured model output influenced by that policy text may need re-recording.
|
|
85
88
|
- `MAX_TURN_RETRIES` does not apply under `app-server` — a crashed turn ends the conversation with `completionReason: 'agent_crashed'`.
|
|
86
89
|
|
|
@@ -22,6 +22,9 @@ const SDK_ERROR_SUBTYPES = [
|
|
|
22
22
|
'error_max_structured_output_retries',
|
|
23
23
|
];
|
|
24
24
|
import { TOOL_NAME_MAP, buildSummary, enrichSkillEvents } from '../../tool-events.js';
|
|
25
|
+
import { sanitizePersistenceValue } from '../../tool-event-results.js';
|
|
26
|
+
import { attachTurnResultSensitiveValues } from '../../sdk/turn-result-secrets.js';
|
|
27
|
+
import { attachToolEventSensitiveValues } from '../../sdk/tool-event-secrets.js';
|
|
25
28
|
import { attachOriginalMcpInput } from '../../sdk/mcp-event-input.js';
|
|
26
29
|
import { parseClaudeSdkMcpToolName } from './mcp-tool-name.js';
|
|
27
30
|
import { applyObservedToolResult, extractObservedToolResults, } from './tool-results.js';
|
|
@@ -132,13 +135,15 @@ export function projectSdkMessages(input) {
|
|
|
132
135
|
const trimmedAssistant = assistantText.trim();
|
|
133
136
|
const trimmedResult = resultText.trim();
|
|
134
137
|
const visible = isError ? '' : (trimmedAssistant || trimmedResult);
|
|
135
|
-
const rawOutput = resultText || assistantText;
|
|
138
|
+
const rawOutput = sanitizePersistenceValue(resultText || assistantText, input.sensitiveValues);
|
|
136
139
|
const enriched = enrichSkillEvents([
|
|
137
140
|
...toolEvents,
|
|
138
141
|
...(input.deniedMcpEvents?.all() ?? []),
|
|
139
142
|
]);
|
|
140
|
-
const finalToolEvents = prependSlashCommandSkillEvent(enriched, input.firstMessage, initSkills);
|
|
141
|
-
const traceOutput = input.messages
|
|
143
|
+
const finalToolEvents = prependSlashCommandSkillEvent(enriched, input.firstMessage, initSkills).map((event) => attachToolEventSensitiveValues(event, input.sensitiveValues ?? []));
|
|
144
|
+
const traceOutput = sanitizePersistenceValue(input.messages, input.sensitiveValues)
|
|
145
|
+
.map((message) => JSON.stringify(message))
|
|
146
|
+
.join('\n');
|
|
142
147
|
const result = {
|
|
143
148
|
rawOutput,
|
|
144
149
|
assistantMessage: visible,
|
|
@@ -155,7 +160,10 @@ export function projectSdkMessages(input) {
|
|
|
155
160
|
...(costUsd !== undefined ? { costUsd } : {}),
|
|
156
161
|
...(errorSubtype !== undefined ? { errorSubtype } : {}),
|
|
157
162
|
};
|
|
158
|
-
return {
|
|
163
|
+
return {
|
|
164
|
+
result: attachTurnResultSensitiveValues(result, input.sensitiveValues ?? []),
|
|
165
|
+
sessionId,
|
|
166
|
+
};
|
|
159
167
|
}
|
|
160
168
|
/**
|
|
161
169
|
* If the first user message of the turn is `/<name>` and `<name>` is one of
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
2
2
|
import type { ToolEvent } from '../../tool-events.js';
|
|
3
|
-
export
|
|
3
|
+
export { collectSensitiveEnvValues, TOOL_RESULT_MAX_CHARS } from '../../tool-event-results.js';
|
|
4
4
|
export interface ClaudeSdkMessageTiming {
|
|
5
5
|
receivedAt: string;
|
|
6
6
|
receivedMonotonicMs: number;
|
|
@@ -16,4 +16,3 @@ export interface ObservedToolResult {
|
|
|
16
16
|
export declare function hasToolLifecycleBoundary(message: SDKMessage): boolean;
|
|
17
17
|
export declare function extractObservedToolResults(message: SDKMessage, timing?: ClaudeSdkMessageTiming): ObservedToolResult[];
|
|
18
18
|
export declare function applyObservedToolResult(event: ToolEvent, observed: ObservedToolResult, startedMonotonicMs?: number, sensitiveValues?: readonly string[]): void;
|
|
19
|
-
export declare function collectSensitiveEnvValues(env: Readonly<Record<string, string>>): string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { sanitizeToolEventResult, } from '../../tool-event-results.js';
|
|
2
|
+
export { collectSensitiveEnvValues, TOOL_RESULT_MAX_CHARS } from '../../tool-event-results.js';
|
|
3
3
|
export function hasToolLifecycleBoundary(message) {
|
|
4
4
|
if (message.type !== 'assistant' && message.type !== 'user')
|
|
5
5
|
return false;
|
|
@@ -52,50 +52,19 @@ export function applyObservedToolResult(event, observed, startedMonotonicMs, sen
|
|
|
52
52
|
event.arguments.status = event.status;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
export function collectSensitiveEnvValues(env) {
|
|
56
|
-
return [...new Set(Object.entries(env)
|
|
57
|
-
.filter(([key, value]) => SECRET_ENV_KEY_PATTERN.test(key) && value.length > 0)
|
|
58
|
-
.map(([, value]) => value))]
|
|
59
|
-
.sort((a, b) => b.length - a.length);
|
|
60
|
-
}
|
|
61
55
|
function buildBoundedToolResult(observed, sensitiveValues) {
|
|
62
56
|
const raw = observed.structuredResult;
|
|
63
|
-
const result = {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
result[key] = bounded.value;
|
|
70
|
-
truncated ||= bounded.truncated;
|
|
57
|
+
const result = {
|
|
58
|
+
...(typeof raw?.stdout === 'string' ? { stdout: raw.stdout } : {}),
|
|
59
|
+
...(typeof raw?.stderr === 'string' ? { stderr: raw.stderr } : {}),
|
|
60
|
+
...(typeof observed.content === 'string' && observed.content !== raw?.stdout
|
|
61
|
+
? { content: observed.content }
|
|
62
|
+
: {}),
|
|
71
63
|
};
|
|
72
|
-
addBounded('stdout', raw?.stdout);
|
|
73
|
-
addBounded('stderr', raw?.stderr);
|
|
74
|
-
if (observed.content !== raw?.stdout)
|
|
75
|
-
addBounded('content', observed.content);
|
|
76
64
|
const exitCode = raw?.exitCode ?? raw?.exit_code;
|
|
77
65
|
if (typeof exitCode === 'number' && Number.isFinite(exitCode))
|
|
78
66
|
result.exitCode = exitCode;
|
|
79
|
-
|
|
80
|
-
result.truncated = true;
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
function redactSensitiveValues(value, sensitiveValues) {
|
|
84
|
-
let redacted = value;
|
|
85
|
-
for (const secret of sensitiveValues) {
|
|
86
|
-
if (secret.length > 0)
|
|
87
|
-
redacted = redacted.split(secret).join('<redacted>');
|
|
88
|
-
}
|
|
89
|
-
return redacted;
|
|
90
|
-
}
|
|
91
|
-
function boundText(value) {
|
|
92
|
-
if (value.length <= TOOL_RESULT_MAX_CHARS)
|
|
93
|
-
return { value, truncated: false };
|
|
94
|
-
const marker = '\n[truncated by PathGrade]';
|
|
95
|
-
return {
|
|
96
|
-
value: `${value.slice(0, TOOL_RESULT_MAX_CHARS - marker.length)}${marker}`,
|
|
97
|
-
truncated: true,
|
|
98
|
-
};
|
|
67
|
+
return sanitizeToolEventResult(result, sensitiveValues);
|
|
99
68
|
}
|
|
100
69
|
function extractTextContent(content) {
|
|
101
70
|
if (typeof content === 'string')
|
package/dist/agents/claude.js
CHANGED
|
@@ -28,6 +28,8 @@ import { createSandboxedClaudeSpawn } from '../providers/sandboxed-claude-spawn.
|
|
|
28
28
|
import { assertClaudeLiveMcpSafetyPreflight, assertStdioMcpServersStartForClaudeSdk, mountMcpForClaudeSdk, } from '../providers/mcp-runtime-mounting.js';
|
|
29
29
|
import { buildClaudeSdkOptions, resolveClaudeCodeExecutable, } from './claude/sdk-options.js';
|
|
30
30
|
import { projectSdkMessages } from './claude/sdk-message-projector.js';
|
|
31
|
+
import { attachToolEventSensitiveValues } from '../sdk/tool-event-secrets.js';
|
|
32
|
+
import { cloneTurnResultWithSensitiveValues } from '../sdk/turn-result-secrets.js';
|
|
31
33
|
import { collectSensitiveEnvValues, hasToolLifecycleBoundary, } from './claude/tool-results.js';
|
|
32
34
|
import { createAskUserAnswerStore } from './claude/ask-user-answer-store.js';
|
|
33
35
|
import { createClaudeToolPermissionBridge } from './claude/tool-permission-bridge.js';
|
|
@@ -195,7 +197,8 @@ export class ClaudeAgent extends BaseAgent {
|
|
|
195
197
|
deniedMcpEvents,
|
|
196
198
|
sensitiveValues,
|
|
197
199
|
});
|
|
198
|
-
projected.result.toolEvents = [...scriptedEvents, ...projected.result.toolEvents]
|
|
200
|
+
projected.result.toolEvents = [...scriptedEvents, ...projected.result.toolEvents]
|
|
201
|
+
.map((event) => attachToolEventSensitiveValues(event, sensitiveValues));
|
|
199
202
|
// Capture the SDK-reported session id BEFORE checking for a bus
|
|
200
203
|
// rejection so the next turn's `Options.resume` points at this
|
|
201
204
|
// turn's session even when the turn ended in an ask-bus error.
|
|
@@ -214,12 +217,11 @@ export class ClaudeAgent extends BaseAgent {
|
|
|
214
217
|
const errorMessage = bridgeError instanceof Error
|
|
215
218
|
? bridgeError.message
|
|
216
219
|
: String(bridgeError);
|
|
217
|
-
return {
|
|
218
|
-
...projected.result,
|
|
220
|
+
return cloneTurnResultWithSensitiveValues(projected.result, {
|
|
219
221
|
exitCode: 1,
|
|
220
222
|
errorSubtype: 'bus_rejection',
|
|
221
223
|
rawOutput: errorMessage,
|
|
222
|
-
};
|
|
224
|
+
});
|
|
223
225
|
}
|
|
224
226
|
return projected.result;
|
|
225
227
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AgentCommandRunner, AgentSession, AgentSessionOptions, BaseAgent, EnvironmentHandle } from '../../types.js';
|
|
2
2
|
import { type AppServerSessionHandle } from './transport.js';
|
|
3
|
+
import { type LifecycleClock } from './item-lifecycle.js';
|
|
3
4
|
type SandboxMode = 'workspace-write' | 'danger-full-access';
|
|
4
5
|
export interface PermissionGrantLogEntry {
|
|
5
6
|
type: 'permissions_granted';
|
|
@@ -25,6 +26,10 @@ export interface CodexAppServerAgentDeps {
|
|
|
25
26
|
sandboxMode?: SandboxMode;
|
|
26
27
|
/** Observer for per-grant audit entries (§7 of design decisions). */
|
|
27
28
|
onPermissionGrant?: (entry: PermissionGrantLogEntry) => void;
|
|
29
|
+
/** Injectable clocks keep wall timestamps and monotonic durations independently testable. */
|
|
30
|
+
clock?: LifecycleClock;
|
|
31
|
+
/** Inject Codex-managed ChatGPT access sessions for deterministic tests. */
|
|
32
|
+
authBroker?: import('../../openai-oauth/codex-auth-broker.js').CodexAuthBroker;
|
|
28
33
|
}
|
|
29
34
|
export declare class CodexAppServerAgent extends BaseAgent {
|
|
30
35
|
private deps;
|