@wix/pathgrade 1.0.10 → 1.0.12
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 +10 -11
- package/dist/agents/claude/sdk-message-projector.d.ts +5 -0
- package/dist/agents/claude/sdk-message-projector.js +32 -6
- package/dist/agents/claude/tool-results.d.ts +19 -0
- package/dist/agents/claude/tool-results.js +117 -0
- package/dist/agents/claude.d.ts +4 -0
- package/dist/agents/claude.js +17 -2
- package/dist/agents/opencode/contract.d.ts +42 -6
- package/dist/agents/opencode/contract.js +59 -16
- package/dist/agents/opencode/runtime-policy.d.ts +18 -0
- package/dist/agents/opencode/runtime-policy.js +156 -0
- package/dist/agents/opencode.js +40 -55
- package/dist/providers/credentials.d.ts +13 -1
- package/dist/providers/credentials.js +91 -8
- package/dist/providers/sandbox.d.ts +2 -0
- package/dist/providers/workspace.js +19 -2
- package/dist/sdk/agent.js +11 -16
- package/dist/sdk/chat.js +10 -12
- package/dist/sdk/judge-tools.js +23 -2
- package/dist/sdk/managed-session.js +7 -4
- package/dist/sdk/persona.d.ts +1 -0
- package/dist/sdk/persona.js +5 -1
- package/dist/sdk/tool-event-log.d.ts +3 -0
- package/dist/sdk/tool-event-log.js +7 -0
- package/dist/tool-events.d.ts +19 -0
- package/dist/types.d.ts +2 -0
- package/dist/utils/llm-providers/anthropic.js +15 -11
- package/dist/utils/llm-providers/openai.js +13 -4
- package/dist/utils/llm.d.ts +2 -2
- package/dist/utils/llm.js +9 -4
- package/dist/viewer.html +4 -4
- package/package.json +2 -2
package/dist/sdk/judge-tools.js
CHANGED
|
@@ -206,7 +206,28 @@ export async function getToolEvents(events, actionFilter) {
|
|
|
206
206
|
const filtered = actionFilter
|
|
207
207
|
? events.filter((e) => e.action.includes(actionFilter))
|
|
208
208
|
: events;
|
|
209
|
-
|
|
209
|
+
const serialized = JSON.stringify(filtered);
|
|
210
|
+
const maxChars = 200 * 1024;
|
|
211
|
+
if (serialized.length <= maxChars)
|
|
212
|
+
return serialized;
|
|
213
|
+
const included = [];
|
|
214
|
+
for (const event of filtered) {
|
|
215
|
+
const candidate = JSON.stringify({
|
|
216
|
+
events: [...included, event],
|
|
217
|
+
truncated: true,
|
|
218
|
+
totalEvents: filtered.length,
|
|
219
|
+
includedEvents: included.length + 1,
|
|
220
|
+
});
|
|
221
|
+
if (candidate.length > maxChars)
|
|
222
|
+
break;
|
|
223
|
+
included.push(event);
|
|
224
|
+
}
|
|
225
|
+
return JSON.stringify({
|
|
226
|
+
events: included,
|
|
227
|
+
truncated: true,
|
|
228
|
+
totalEvents: filtered.length,
|
|
229
|
+
includedEvents: included.length,
|
|
230
|
+
});
|
|
210
231
|
}
|
|
211
232
|
export const DEFAULT_TOOL_REGISTRY = new Map([
|
|
212
233
|
['readFile', {
|
|
@@ -269,7 +290,7 @@ export const DEFAULT_TOOL_REGISTRY = new Map([
|
|
|
269
290
|
['getToolEvents', {
|
|
270
291
|
schema: {
|
|
271
292
|
name: 'getToolEvents',
|
|
272
|
-
description: 'Retrieve the agent session tool events as JSON. Optional actionFilter substring-matches against event.action.',
|
|
293
|
+
description: 'Retrieve the agent session tool events as JSON. Optional actionFilter substring-matches against event.action. Oversized responses use a valid { events, truncated, totalEvents, includedEvents } JSON envelope.',
|
|
273
294
|
input_schema: {
|
|
274
295
|
type: 'object',
|
|
275
296
|
properties: { actionFilter: { type: 'string' } },
|
|
@@ -2,6 +2,7 @@ import { createAgentSession } from '../types.js';
|
|
|
2
2
|
import { createAgentEnvironment } from '../agents/registry.js';
|
|
3
3
|
import { withAbortTimeout } from '../utils/timeout.js';
|
|
4
4
|
import { buildModelAgentResultLogEntry } from './agent-result-log.js';
|
|
5
|
+
import { buildToolEventLogEntry } from './tool-event-log.js';
|
|
5
6
|
import { planRuntimePolicies } from './runtime-policy.js';
|
|
6
7
|
import { getVisibleAssistantMessage } from './visible-turn.js';
|
|
7
8
|
import { createAskBus } from './ask-bus/bus.js';
|
|
@@ -27,6 +28,7 @@ export function createManagedSession(deps) {
|
|
|
27
28
|
...(deps.opencodeExecutable !== undefined ? { opencodeExecutable: deps.opencodeExecutable } : {}),
|
|
28
29
|
...(deps.opencodeMcpToolNames !== undefined ? { opencodeMcpToolNames: deps.opencodeMcpToolNames } : {}),
|
|
29
30
|
getAbortSignal: () => currentSignal,
|
|
31
|
+
getRemainingMs: () => Math.max(0, deadlineMs - Date.now()),
|
|
30
32
|
};
|
|
31
33
|
let session = null;
|
|
32
34
|
let setupDone = false;
|
|
@@ -84,14 +86,15 @@ export function createManagedSession(deps) {
|
|
|
84
86
|
messages.push({ role: 'user', content: message });
|
|
85
87
|
const turnResult = await executeTurn(message);
|
|
86
88
|
const response = getVisibleAssistantMessage(turnResult);
|
|
89
|
+
const turnCompletedAt = new Date().toISOString();
|
|
90
|
+
for (const toolEvent of turnResult.toolEvents) {
|
|
91
|
+
log.push(buildToolEventLogEntry(toolEvent, turnCompletedAt));
|
|
92
|
+
}
|
|
87
93
|
log.push(buildModelAgentResultLogEntry({
|
|
88
|
-
timestamp:
|
|
94
|
+
timestamp: turnCompletedAt,
|
|
89
95
|
turnResult,
|
|
90
96
|
assistantMessage: response,
|
|
91
97
|
}));
|
|
92
|
-
for (const toolEvent of turnResult.toolEvents) {
|
|
93
|
-
log.push({ type: 'tool_event', timestamp: new Date().toISOString(), tool_event: toolEvent });
|
|
94
|
-
}
|
|
95
98
|
messages.push({ role: 'agent', content: response });
|
|
96
99
|
if (turnResult.exitCode !== 0) {
|
|
97
100
|
if (turnResult.timedOut)
|
package/dist/sdk/persona.d.ts
CHANGED
package/dist/sdk/persona.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { createConversationWindow } from './conversation-window.js';
|
|
2
2
|
export function createPersona(config) {
|
|
3
3
|
const llm = config.llm;
|
|
4
|
+
const configuredWindowModel = config.conversationWindow === false
|
|
5
|
+
? undefined
|
|
6
|
+
: config.conversationWindow?.model;
|
|
7
|
+
const summaryModel = config.model ?? configuredWindowModel ?? config.defaultSummaryModel;
|
|
4
8
|
const window = config.conversationWindow !== false
|
|
5
|
-
? createConversationWindow({ ...config.conversationWindow, model:
|
|
9
|
+
? createConversationWindow({ ...config.conversationWindow, model: summaryModel, llm })
|
|
6
10
|
: null;
|
|
7
11
|
return {
|
|
8
12
|
async reply(chat) {
|
package/dist/tool-events.d.ts
CHANGED
|
@@ -3,13 +3,32 @@ export interface ToolEvent {
|
|
|
3
3
|
action: ToolAction;
|
|
4
4
|
provider: 'claude' | 'codex' | 'cursor' | 'opencode';
|
|
5
5
|
providerToolName: string;
|
|
6
|
+
/** Provider correlation identifier joining a tool invocation to its result. */
|
|
7
|
+
toolUseId?: string;
|
|
6
8
|
turnNumber?: number;
|
|
7
9
|
arguments?: Record<string, unknown>;
|
|
10
|
+
/** Lifecycle state observed by PathGrade. Absent on legacy provider events. */
|
|
11
|
+
status?: 'completed' | 'error' | 'incomplete';
|
|
12
|
+
/** Local wall-clock receive time for the invocation boundary. */
|
|
13
|
+
startedAt?: string;
|
|
14
|
+
/** Local wall-clock receive time for the matching result boundary. */
|
|
15
|
+
completedAt?: string;
|
|
16
|
+
/** Monotonic receive-time delta between invocation and result. */
|
|
17
|
+
durationMs?: number;
|
|
18
|
+
/** Bounded provider result data. Fields remain absent when unavailable. */
|
|
19
|
+
result?: ToolEventResult;
|
|
8
20
|
summary: string;
|
|
9
21
|
confidence: 'high' | 'medium' | 'low';
|
|
10
22
|
rawSnippet: string;
|
|
11
23
|
skillName?: string;
|
|
12
24
|
}
|
|
25
|
+
export interface ToolEventResult {
|
|
26
|
+
content?: string;
|
|
27
|
+
stdout?: string;
|
|
28
|
+
stderr?: string;
|
|
29
|
+
exitCode?: number;
|
|
30
|
+
truncated?: boolean;
|
|
31
|
+
}
|
|
13
32
|
export declare function summarizeToolEvents(events: ToolEvent[]): string;
|
|
14
33
|
/**
|
|
15
34
|
* Map from provider-specific tool names to normalized Pathgrade actions.
|
package/dist/types.d.ts
CHANGED
|
@@ -379,6 +379,8 @@ export interface AgentSessionOptions {
|
|
|
379
379
|
abortSignal?: AbortSignal;
|
|
380
380
|
/** Supplies the current timeout/cancellation signal for reused sessions. */
|
|
381
381
|
getAbortSignal?: () => AbortSignal | undefined;
|
|
382
|
+
/** Supplies the remaining managed-session deadline for credential validity checks. */
|
|
383
|
+
getRemainingMs?: () => number;
|
|
382
384
|
/** Absolute pinned runtime path for the OpenCode adapter. */
|
|
383
385
|
opencodeExecutable?: string;
|
|
384
386
|
/** Exact generated MCP tool names accepted by the OpenCode event normalizer. */
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
function getApiKey(env) {
|
|
2
|
-
return env
|
|
3
|
-
|| env
|
|
4
|
-
||
|
|
5
|
-
|| process.env.APP_ANTHROPIC_API_KEY;
|
|
2
|
+
return env === undefined
|
|
3
|
+
? process.env.ANTHROPIC_API_KEY || process.env.APP_ANTHROPIC_API_KEY
|
|
4
|
+
: env.ANTHROPIC_API_KEY || env.APP_ANTHROPIC_API_KEY;
|
|
6
5
|
}
|
|
7
6
|
function resolveBaseUrl(env) {
|
|
8
|
-
return env
|
|
9
|
-
|| env
|
|
10
|
-
||
|
|
11
|
-
|| process.env.APP_ANTHROPIC_BASE_URL
|
|
7
|
+
return (env === undefined
|
|
8
|
+
? process.env.ANTHROPIC_BASE_URL || process.env.APP_ANTHROPIC_BASE_URL
|
|
9
|
+
: env.ANTHROPIC_BASE_URL || env.APP_ANTHROPIC_BASE_URL)
|
|
12
10
|
|| 'https://api.anthropic.com';
|
|
13
11
|
}
|
|
12
|
+
function normalizeModel(model) {
|
|
13
|
+
const normalized = model.trim();
|
|
14
|
+
return normalized.toLowerCase().startsWith('anthropic/')
|
|
15
|
+
? normalized.slice('anthropic/'.length)
|
|
16
|
+
: normalized;
|
|
17
|
+
}
|
|
14
18
|
function resolveMessagesUrl(env) {
|
|
15
19
|
const baseUrl = resolveBaseUrl(env).replace(/\/+$/, '');
|
|
16
20
|
const apiRoot = baseUrl.endsWith('/v1') ? baseUrl : `${baseUrl}/v1`;
|
|
@@ -79,14 +83,14 @@ export const anthropicProvider = {
|
|
|
79
83
|
return !!getApiKey(env);
|
|
80
84
|
},
|
|
81
85
|
supportsModel(model) {
|
|
82
|
-
return model
|
|
86
|
+
return normalizeModel(model).toLowerCase().startsWith('claude');
|
|
83
87
|
},
|
|
84
88
|
async call(prompt, opts) {
|
|
85
89
|
const apiKey = getApiKey(opts.env);
|
|
86
90
|
if (!apiKey) {
|
|
87
91
|
throw new Error('No ANTHROPIC_API_KEY available');
|
|
88
92
|
}
|
|
89
|
-
const model = opts.model || DEFAULT_ANTHROPIC_MODEL;
|
|
93
|
+
const model = normalizeModel(opts.model || DEFAULT_ANTHROPIC_MODEL);
|
|
90
94
|
const modelConfig = getAnthropicModelConfig(model);
|
|
91
95
|
assertRawAnthropicModel(model, modelConfig);
|
|
92
96
|
const temperature = resolveTemperature(model, modelConfig, opts.temperature);
|
|
@@ -120,7 +124,7 @@ export const anthropicProvider = {
|
|
|
120
124
|
if (!apiKey) {
|
|
121
125
|
throw new Error('No ANTHROPIC_API_KEY available');
|
|
122
126
|
}
|
|
123
|
-
const model = opts.model || DEFAULT_ANTHROPIC_MODEL;
|
|
127
|
+
const model = normalizeModel(opts.model || DEFAULT_ANTHROPIC_MODEL);
|
|
124
128
|
const modelConfig = getAnthropicModelConfig(model);
|
|
125
129
|
assertRawAnthropicModel(model, modelConfig);
|
|
126
130
|
const temperature = resolveTemperature(model, modelConfig, opts.temperature);
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
function getApiKey(env) {
|
|
2
|
-
return env
|
|
2
|
+
return env === undefined ? process.env.OPENAI_API_KEY : env.OPENAI_API_KEY;
|
|
3
|
+
}
|
|
4
|
+
function normalizeModel(model) {
|
|
5
|
+
const normalized = model.trim();
|
|
6
|
+
return normalized.toLowerCase().startsWith('openai/')
|
|
7
|
+
? normalized.slice('openai/'.length)
|
|
8
|
+
: normalized;
|
|
3
9
|
}
|
|
4
10
|
export const openaiProvider = {
|
|
5
11
|
name: 'openai',
|
|
@@ -7,7 +13,7 @@ export const openaiProvider = {
|
|
|
7
13
|
return !!getApiKey(env);
|
|
8
14
|
},
|
|
9
15
|
supportsModel(model) {
|
|
10
|
-
const normalized = model
|
|
16
|
+
const normalized = normalizeModel(model).toLowerCase();
|
|
11
17
|
return (normalized.startsWith('gpt-')
|
|
12
18
|
|| normalized.startsWith('chatgpt-')
|
|
13
19
|
|| normalized.startsWith('o1')
|
|
@@ -19,9 +25,12 @@ export const openaiProvider = {
|
|
|
19
25
|
if (!apiKey) {
|
|
20
26
|
throw new Error('No OPENAI_API_KEY available');
|
|
21
27
|
}
|
|
22
|
-
const model = opts.model || 'gpt-4o';
|
|
28
|
+
const model = normalizeModel(opts.model || 'gpt-4o');
|
|
23
29
|
const temperature = opts.temperature ?? 0;
|
|
24
|
-
const
|
|
30
|
+
const configuredBaseUrl = opts.env === undefined
|
|
31
|
+
? process.env.OPENAI_BASE_URL
|
|
32
|
+
: opts.env.OPENAI_BASE_URL;
|
|
33
|
+
const baseUrl = (configuredBaseUrl || 'https://api.openai.com/v1').replace(/\/$/, '');
|
|
25
34
|
try {
|
|
26
35
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
27
36
|
method: 'POST',
|
package/dist/utils/llm.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare function createLLMClient(opts: CreateLLMClientOptions): LLMPort;
|
|
|
25
25
|
export declare function createLLMClient(providers: LLMProviderAdapter[], agentName?: string): LLMPort;
|
|
26
26
|
export declare function callLLM(prompt: string, opts?: LLMCallOptions): Promise<LLMCallResult>;
|
|
27
27
|
/**
|
|
28
|
-
* Create an LLM client scoped to the providers that match the given agent.
|
|
28
|
+
* Create an LLM client scoped to the providers that match the given agent and model.
|
|
29
29
|
*
|
|
30
30
|
* - claude → CLI + Anthropic API (no OpenAI fallthrough)
|
|
31
31
|
* - codex → OpenAI API
|
|
@@ -33,4 +33,4 @@ export declare function callLLM(prompt: string, opts?: LLMCallOptions): Promise<
|
|
|
33
33
|
* If `agentEnv` is provided, it is merged into every LLM call so that
|
|
34
34
|
* the agent's env propagates to persona/judge/summarization calls.
|
|
35
35
|
*/
|
|
36
|
-
export declare function createAgentLLM(agentName: string, agentEnv?: Record<string, string
|
|
36
|
+
export declare function createAgentLLM(agentName: string, agentEnv?: Record<string, string>, agentModel?: string): LLMPort;
|
package/dist/utils/llm.js
CHANGED
|
@@ -17,6 +17,10 @@ function inferProviderFromModel(model) {
|
|
|
17
17
|
const normalized = model?.trim().toLowerCase();
|
|
18
18
|
if (!normalized)
|
|
19
19
|
return undefined;
|
|
20
|
+
if (normalized.startsWith('anthropic/'))
|
|
21
|
+
return 'anthropic';
|
|
22
|
+
if (normalized.startsWith('openai/'))
|
|
23
|
+
return 'openai';
|
|
20
24
|
if (normalized.startsWith('claude'))
|
|
21
25
|
return 'anthropic';
|
|
22
26
|
if (normalized.startsWith('gpt-')
|
|
@@ -178,7 +182,7 @@ export async function callLLM(prompt, opts = {}) {
|
|
|
178
182
|
return defaultClient.call(prompt, opts);
|
|
179
183
|
}
|
|
180
184
|
/**
|
|
181
|
-
* Create an LLM client scoped to the providers that match the given agent.
|
|
185
|
+
* Create an LLM client scoped to the providers that match the given agent and model.
|
|
182
186
|
*
|
|
183
187
|
* - claude → CLI + Anthropic API (no OpenAI fallthrough)
|
|
184
188
|
* - codex → OpenAI API
|
|
@@ -186,17 +190,18 @@ export async function callLLM(prompt, opts = {}) {
|
|
|
186
190
|
* If `agentEnv` is provided, it is merged into every LLM call so that
|
|
187
191
|
* the agent's env propagates to persona/judge/summarization calls.
|
|
188
192
|
*/
|
|
189
|
-
export function createAgentLLM(agentName, agentEnv) {
|
|
193
|
+
export function createAgentLLM(agentName, agentEnv, agentModel) {
|
|
190
194
|
// Tool-using judges need a provider that implements callWithTools.
|
|
191
195
|
// For claude, anthropicProvider is added as a tool-use-capable fallback
|
|
192
196
|
// alongside the CLI. The CLI still wins for plain call() when available.
|
|
193
197
|
// Cursor inherits the Claude chain by design (judge consistency across
|
|
194
198
|
// harnesses — see PRD §"LLM-backend routing"). Cursor evals therefore
|
|
195
199
|
// depend on Claude CLI or ANTHROPIC_API_KEY being available at judge time.
|
|
196
|
-
const
|
|
200
|
+
const openCodeUsesOpenAI = agentName === 'opencode' && agentModel === 'openai/gpt-5.4';
|
|
201
|
+
const baseAdapters = agentName === 'codex' || openCodeUsesOpenAI
|
|
197
202
|
? [openaiProvider]
|
|
198
203
|
: [cliProvider, anthropicProvider];
|
|
199
|
-
const adapters = agentEnv
|
|
204
|
+
const adapters = agentEnv !== undefined
|
|
200
205
|
? baseAdapters.map((a) => ({
|
|
201
206
|
...a,
|
|
202
207
|
isAvailable: (env) => a.isAvailable({ ...agentEnv, ...env }),
|
package/dist/viewer.html
CHANGED
|
@@ -1127,10 +1127,10 @@
|
|
|
1127
1127
|
+ (e.value?.toFixed(2) || '0.00') + '</span>'
|
|
1128
1128
|
+ (e.output ? '<div class="scorer-details" style="margin-top:0.25rem">' + esc(e.output) + '</div>' : '');
|
|
1129
1129
|
break;
|
|
1130
|
-
case 'tool_event':
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1130
|
+
case 'tool_event': {
|
|
1131
|
+
const tool = e.tool_event || {}; const toolResult = tool.result || {}; const resultText = toolResult.content || toolResult.stdout || ''; const stderrText = toolResult.stderr || ''; const duration = typeof tool.durationMs === 'number' ? `${Math.round(tool.durationMs)}ms` : ''; const exit = typeof toolResult.exitCode === 'number' ? `exit ${toolResult.exitCode}` : ''; const meta = [tool.status || 'unknown', duration, exit].filter(Boolean).join(' · '); const output = resultText || stderrText ? '<div class="log-command-output"><pre class="code-block">' + esc(resultText) + (stderrText ? '<span class="stderr-text">' + esc(stderrText) + '</span>' : '') + '</pre></div>' : '';
|
|
1132
|
+
body = '<details class="log-command-details"><summary class="log-command-summary"><span class="badge badge-type">' + esc(tool.action || 'unknown') + '</span> <span class="scorer-details">' + esc(tool.summary || '') + '</span><span class="log-command-meta">' + esc(meta) + '</span></summary>' + output + '</details>'; break;
|
|
1133
|
+
}
|
|
1134
1134
|
case 'judge_tool_call': {
|
|
1135
1135
|
const tc = e.judge_tool_call || {};
|
|
1136
1136
|
const okBadge = tc.ok
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/pathgrade",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"packageManager": "yarn@4.12.0",
|
|
5
5
|
"description": "Evaluate whether AI agents discover and use your skills correctly",
|
|
6
6
|
"exports": {
|
|
@@ -132,5 +132,5 @@
|
|
|
132
132
|
"typescript": "^5.9.3",
|
|
133
133
|
"zod": "4.3.6"
|
|
134
134
|
},
|
|
135
|
-
"falconPackageHash": "
|
|
135
|
+
"falconPackageHash": "48e95ba80576ede589f0a5e47d60b9c94fd53c1ef435d8ca9af35392"
|
|
136
136
|
}
|