@wix/pathgrade 0.29.0
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/bin/pathgrade.js +2 -0
- package/dist/affected/anchor.d.ts +18 -0
- package/dist/affected/anchor.js +42 -0
- package/dist/affected/config.d.ts +25 -0
- package/dist/affected/config.js +98 -0
- package/dist/affected/format.d.ts +45 -0
- package/dist/affected/format.js +87 -0
- package/dist/affected/git.d.ts +33 -0
- package/dist/affected/git.js +68 -0
- package/dist/affected/glob.d.ts +17 -0
- package/dist/affected/glob.js +25 -0
- package/dist/affected/meta.d.ts +26 -0
- package/dist/affected/meta.js +158 -0
- package/dist/affected/select.d.ts +30 -0
- package/dist/affected/select.js +101 -0
- package/dist/affected/sidecar.d.ts +32 -0
- package/dist/affected/sidecar.js +86 -0
- package/dist/affected/types.d.ts +43 -0
- package/dist/affected/types.js +7 -0
- package/dist/agents/claude/ask-user-answer-store.d.ts +28 -0
- package/dist/agents/claude/ask-user-answer-store.js +29 -0
- package/dist/agents/claude/ask-user-bridge.d.ts +56 -0
- package/dist/agents/claude/ask-user-bridge.js +162 -0
- package/dist/agents/claude/sdk-message-projector.d.ts +43 -0
- package/dist/agents/claude/sdk-message-projector.js +208 -0
- package/dist/agents/claude/sdk-options.d.ts +52 -0
- package/dist/agents/claude/sdk-options.js +79 -0
- package/dist/agents/claude.d.ts +56 -0
- package/dist/agents/claude.js +143 -0
- package/dist/agents/codex-app-server/agent.d.ts +35 -0
- package/dist/agents/codex-app-server/agent.js +388 -0
- package/dist/agents/codex-app-server/fixtures/app-server-client.d.ts +19 -0
- package/dist/agents/codex-app-server/fixtures/app-server-client.js +104 -0
- package/dist/agents/codex-app-server/fixtures/run-gate.d.ts +25 -0
- package/dist/agents/codex-app-server/fixtures/run-gate.js +48 -0
- package/dist/agents/codex-app-server/protocol/ClientRequest.d.ts +27 -0
- package/dist/agents/codex-app-server/protocol/ClientRequest.js +7 -0
- package/dist/agents/codex-app-server/protocol/DynamicToolCallParams.d.ts +9 -0
- package/dist/agents/codex-app-server/protocol/DynamicToolCallParams.js +4 -0
- package/dist/agents/codex-app-server/protocol/GrantedPermissionProfile.d.ts +6 -0
- package/dist/agents/codex-app-server/protocol/GrantedPermissionProfile.js +7 -0
- package/dist/agents/codex-app-server/protocol/McpElicitationRequestParams.d.ts +23 -0
- package/dist/agents/codex-app-server/protocol/McpElicitationRequestParams.js +8 -0
- package/dist/agents/codex-app-server/protocol/Op.d.ts +11 -0
- package/dist/agents/codex-app-server/protocol/Op.js +5 -0
- package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalParams.d.ts +10 -0
- package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalParams.js +6 -0
- package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalResponse.d.ts +15 -0
- package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalResponse.js +4 -0
- package/dist/agents/codex-app-server/protocol/SandboxMode.d.ts +1 -0
- package/dist/agents/codex-app-server/protocol/SandboxMode.js +4 -0
- package/dist/agents/codex-app-server/protocol/ServerRequest.d.ts +42 -0
- package/dist/agents/codex-app-server/protocol/ServerRequest.js +7 -0
- package/dist/agents/codex-app-server/protocol/ThreadStartParams.d.ts +45 -0
- package/dist/agents/codex-app-server/protocol/ThreadStartParams.js +10 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputAnswer.d.ts +6 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputAnswer.js +4 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputOption.d.ts +7 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputOption.js +4 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputParams.d.ts +10 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputParams.js +4 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputQuestion.d.ts +12 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputQuestion.js +4 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputResponse.d.ts +9 -0
- package/dist/agents/codex-app-server/protocol/ToolRequestUserInputResponse.js +4 -0
- package/dist/agents/codex-app-server/protocol/TurnCompletedNotification.d.ts +5 -0
- package/dist/agents/codex-app-server/protocol/TurnCompletedNotification.js +4 -0
- package/dist/agents/codex-app-server/protocol/index.d.ts +16 -0
- package/dist/agents/codex-app-server/protocol/index.js +11 -0
- package/dist/agents/codex-app-server/transport.d.ts +84 -0
- package/dist/agents/codex-app-server/transport.js +246 -0
- package/dist/agents/codex-app-server/wire-translators.d.ts +19 -0
- package/dist/agents/codex-app-server/wire-translators.js +52 -0
- package/dist/agents/codex.d.ts +7 -0
- package/dist/agents/codex.js +164 -0
- package/dist/agents/cursor.d.ts +39 -0
- package/dist/agents/cursor.js +233 -0
- package/dist/agents/registry.d.ts +17 -0
- package/dist/agents/registry.js +27 -0
- package/dist/agents/transcript-agent.d.ts +14 -0
- package/dist/agents/transcript-agent.js +59 -0
- package/dist/analytics/engine.d.ts +18 -0
- package/dist/analytics/engine.js +66 -0
- package/dist/commands/affected.d.ts +31 -0
- package/dist/commands/affected.js +150 -0
- package/dist/commands/analyze.d.ts +21 -0
- package/dist/commands/analyze.js +167 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +242 -0
- package/dist/commands/preview-reactions.d.ts +10 -0
- package/dist/commands/preview-reactions.js +88 -0
- package/dist/commands/preview.d.ts +5 -0
- package/dist/commands/preview.js +17 -0
- package/dist/commands/report.d.ts +39 -0
- package/dist/commands/report.js +108 -0
- package/dist/commands/run-args.d.ts +22 -0
- package/dist/commands/run-args.js +69 -0
- package/dist/commands/run-changed.d.ts +26 -0
- package/dist/commands/run-changed.js +138 -0
- package/dist/commands/validate.d.ts +13 -0
- package/dist/commands/validate.js +233 -0
- package/dist/core/mcp-mock.d.ts +3 -0
- package/dist/core/mcp-mock.js +23 -0
- package/dist/core/mcp-mock.types.d.ts +15 -0
- package/dist/core/mcp-mock.types.js +1 -0
- package/dist/core/skills.d.ts +15 -0
- package/dist/core/skills.js +76 -0
- package/dist/evals/discovery.d.ts +7 -0
- package/dist/evals/discovery.js +69 -0
- package/dist/mcp-mock-server.d.ts +1 -0
- package/dist/mcp-mock-server.js +116 -0
- package/dist/pathgrade.d.ts +10 -0
- package/dist/pathgrade.js +243 -0
- package/dist/plugin/index.d.ts +9 -0
- package/dist/plugin/index.js +81 -0
- package/dist/plugin/lifecycle.d.ts +45 -0
- package/dist/plugin/lifecycle.js +146 -0
- package/dist/plugin/reporter.d.ts +36 -0
- package/dist/plugin/reporter.js +275 -0
- package/dist/plugin/setup.d.ts +1 -0
- package/dist/plugin/setup.js +3 -0
- package/dist/providers/copy-filter.d.ts +18 -0
- package/dist/providers/copy-filter.js +66 -0
- package/dist/providers/credentials.d.ts +36 -0
- package/dist/providers/credentials.js +202 -0
- package/dist/providers/mcp-config.d.ts +34 -0
- package/dist/providers/mcp-config.js +55 -0
- package/dist/providers/sandbox-exec.d.ts +8 -0
- package/dist/providers/sandbox-exec.js +82 -0
- package/dist/providers/sandbox.d.ts +22 -0
- package/dist/providers/sandbox.js +158 -0
- package/dist/providers/sandboxed-claude-spawn.d.ts +24 -0
- package/dist/providers/sandboxed-claude-spawn.js +57 -0
- package/dist/providers/workspace.d.ts +15 -0
- package/dist/providers/workspace.js +73 -0
- package/dist/reporters/browser.d.ts +2 -0
- package/dist/reporters/browser.js +85 -0
- package/dist/reporters/cli.d.ts +2 -0
- package/dist/reporters/cli.js +131 -0
- package/dist/reporters/diagnostics.d.ts +46 -0
- package/dist/reporters/diagnostics.js +147 -0
- package/dist/reporters/github-comment.d.ts +67 -0
- package/dist/reporters/github-comment.js +252 -0
- package/dist/reporters/loader.d.ts +8 -0
- package/dist/reporters/loader.js +36 -0
- package/dist/reporters/results-path.d.ts +2 -0
- package/dist/reporters/results-path.js +10 -0
- package/dist/reporters/verbose-emitter.d.ts +69 -0
- package/dist/reporters/verbose-emitter.js +103 -0
- package/dist/sdk/agent-crash.d.ts +19 -0
- package/dist/sdk/agent-crash.js +20 -0
- package/dist/sdk/agent-resolution.d.ts +12 -0
- package/dist/sdk/agent-resolution.js +21 -0
- package/dist/sdk/agent-result-log.d.ts +19 -0
- package/dist/sdk/agent-result-log.js +45 -0
- package/dist/sdk/agent.d.ts +4 -0
- package/dist/sdk/agent.js +354 -0
- package/dist/sdk/ask-bus/bus.d.ts +21 -0
- package/dist/sdk/ask-bus/bus.js +129 -0
- package/dist/sdk/ask-bus/handler.d.ts +31 -0
- package/dist/sdk/ask-bus/handler.js +244 -0
- package/dist/sdk/ask-bus/parsers.d.ts +9 -0
- package/dist/sdk/ask-bus/parsers.js +30 -0
- package/dist/sdk/ask-bus/projection.d.ts +26 -0
- package/dist/sdk/ask-bus/projection.js +45 -0
- package/dist/sdk/ask-bus/types.d.ts +69 -0
- package/dist/sdk/ask-bus/types.js +1 -0
- package/dist/sdk/chat.d.ts +29 -0
- package/dist/sdk/chat.js +93 -0
- package/dist/sdk/conversation-window.d.ts +14 -0
- package/dist/sdk/conversation-window.js +38 -0
- package/dist/sdk/converse.d.ts +42 -0
- package/dist/sdk/converse.js +329 -0
- package/dist/sdk/eval-runtime.d.ts +10 -0
- package/dist/sdk/eval-runtime.js +17 -0
- package/dist/sdk/evaluate.d.ts +13 -0
- package/dist/sdk/evaluate.js +325 -0
- package/dist/sdk/index.d.ts +31 -0
- package/dist/sdk/index.js +20 -0
- package/dist/sdk/judge-pipeline.d.ts +7 -0
- package/dist/sdk/judge-pipeline.js +178 -0
- package/dist/sdk/judge-prompt-builder.d.ts +7 -0
- package/dist/sdk/judge-prompt-builder.js +76 -0
- package/dist/sdk/judge-tool-runner.d.ts +19 -0
- package/dist/sdk/judge-tool-runner.js +51 -0
- package/dist/sdk/judge-tool-session.d.ts +47 -0
- package/dist/sdk/judge-tool-session.js +213 -0
- package/dist/sdk/judge-tools.d.ts +29 -0
- package/dist/sdk/judge-tools.js +283 -0
- package/dist/sdk/managed-session.d.ts +50 -0
- package/dist/sdk/managed-session.js +110 -0
- package/dist/sdk/persona.d.ts +5 -0
- package/dist/sdk/persona.js +33 -0
- package/dist/sdk/reaction-loader.d.ts +4 -0
- package/dist/sdk/reaction-loader.js +119 -0
- package/dist/sdk/reaction-preview.d.ts +6 -0
- package/dist/sdk/reaction-preview.js +94 -0
- package/dist/sdk/run-scorer.d.ts +10 -0
- package/dist/sdk/run-scorer.js +124 -0
- package/dist/sdk/runtime-policy.d.ts +21 -0
- package/dist/sdk/runtime-policy.js +89 -0
- package/dist/sdk/scorer-utils.d.ts +6 -0
- package/dist/sdk/scorer-utils.js +39 -0
- package/dist/sdk/scorers.d.ts +64 -0
- package/dist/sdk/scorers.js +80 -0
- package/dist/sdk/snapshots.d.ts +41 -0
- package/dist/sdk/snapshots.js +148 -0
- package/dist/sdk/types.d.ts +416 -0
- package/dist/sdk/types.js +17 -0
- package/dist/sdk/visible-turn.d.ts +4 -0
- package/dist/sdk/visible-turn.js +23 -0
- package/dist/tool-events.d.ts +45 -0
- package/dist/tool-events.js +167 -0
- package/dist/types.d.ts +372 -0
- package/dist/types.js +37 -0
- package/dist/utils/cli.d.ts +55 -0
- package/dist/utils/cli.js +131 -0
- package/dist/utils/env.d.ts +8 -0
- package/dist/utils/env.js +26 -0
- package/dist/utils/llm-mocks.d.ts +43 -0
- package/dist/utils/llm-mocks.js +108 -0
- package/dist/utils/llm-providers/anthropic.d.ts +2 -0
- package/dist/utils/llm-providers/anthropic.js +132 -0
- package/dist/utils/llm-providers/cli.d.ts +20 -0
- package/dist/utils/llm-providers/cli.js +162 -0
- package/dist/utils/llm-providers/openai.d.ts +2 -0
- package/dist/utils/llm-providers/openai.js +56 -0
- package/dist/utils/llm-types.d.ts +107 -0
- package/dist/utils/llm-types.js +1 -0
- package/dist/utils/llm.d.ts +36 -0
- package/dist/utils/llm.js +209 -0
- package/dist/utils/shutdown.d.ts +15 -0
- package/dist/utils/shutdown.js +41 -0
- package/dist/utils/timeout.d.ts +8 -0
- package/dist/utils/timeout.js +32 -0
- package/dist/viewer.html +1178 -0
- package/package.json +88 -0
- package/templates/eval.ts.template +28 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { prepareWorkspace } from '../providers/workspace.js';
|
|
2
|
+
import { resolveAgentName, resolveCodexTransport } from './agent-resolution.js';
|
|
3
|
+
import { lifecycle } from '../plugin/lifecycle.js';
|
|
4
|
+
import { ChatSessionImpl } from './chat.js';
|
|
5
|
+
import { runConversation } from './converse.js';
|
|
6
|
+
import { createPersona } from './persona.js';
|
|
7
|
+
import { evaluate } from './evaluate.js';
|
|
8
|
+
import { createManagedSession } from './managed-session.js';
|
|
9
|
+
import { createAgentLLM } from '../utils/llm.js';
|
|
10
|
+
import { buildRunSnapshot } from './snapshots.js';
|
|
11
|
+
import { buildModelAgentResultLogEntry } from './agent-result-log.js';
|
|
12
|
+
import { getVisibleAssistantMessage } from './visible-turn.js';
|
|
13
|
+
import { createVerboseEmitter } from '../reporters/verbose-emitter.js';
|
|
14
|
+
import fs from 'fs-extra';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
/**
|
|
17
|
+
* Test-only injection point: override the sink used by the next emitter
|
|
18
|
+
* built inside `createAgent`. Pass `null` to restore the default (stderr).
|
|
19
|
+
* Intended for unit tests only — do not use in production code paths.
|
|
20
|
+
*/
|
|
21
|
+
let verboseSinkOverride = null;
|
|
22
|
+
export function __setVerboseSinkForTesting(sink) {
|
|
23
|
+
verboseSinkOverride = sink;
|
|
24
|
+
}
|
|
25
|
+
class AgentImpl {
|
|
26
|
+
ws;
|
|
27
|
+
agentName;
|
|
28
|
+
llm;
|
|
29
|
+
timeoutSetting;
|
|
30
|
+
modelOpt;
|
|
31
|
+
conversationWindowOpt;
|
|
32
|
+
interactionMode = null;
|
|
33
|
+
_messages = [];
|
|
34
|
+
_log = [];
|
|
35
|
+
disposed = false;
|
|
36
|
+
debugOpt;
|
|
37
|
+
debugName;
|
|
38
|
+
debugBaseDir;
|
|
39
|
+
lastConversationResult = null;
|
|
40
|
+
verbose;
|
|
41
|
+
transport;
|
|
42
|
+
constructor(ws, agentName, llm, timeoutSetting, conversationWindow, modelOpt, debugOpt, debugName, debugBaseDir, verbose, transport) {
|
|
43
|
+
this.ws = ws;
|
|
44
|
+
this.agentName = agentName;
|
|
45
|
+
this.llm = llm;
|
|
46
|
+
this.timeoutSetting = timeoutSetting;
|
|
47
|
+
this.modelOpt = modelOpt;
|
|
48
|
+
this.conversationWindowOpt = conversationWindow;
|
|
49
|
+
this.debugOpt = debugOpt;
|
|
50
|
+
this.debugName = debugName;
|
|
51
|
+
this.debugBaseDir = debugBaseDir;
|
|
52
|
+
this.verbose = verbose;
|
|
53
|
+
this.transport = transport;
|
|
54
|
+
}
|
|
55
|
+
get messages() {
|
|
56
|
+
return this._messages;
|
|
57
|
+
}
|
|
58
|
+
get log() {
|
|
59
|
+
return this._log;
|
|
60
|
+
}
|
|
61
|
+
get workspace() {
|
|
62
|
+
return this.ws.path;
|
|
63
|
+
}
|
|
64
|
+
guardInteraction(mode) {
|
|
65
|
+
if (this.interactionMode && this.interactionMode !== mode) {
|
|
66
|
+
throw new Error(`Cannot use ${mode}() after ${this.interactionMode}() — an agent supports only one interaction method`);
|
|
67
|
+
}
|
|
68
|
+
this.interactionMode = mode;
|
|
69
|
+
}
|
|
70
|
+
createSession(timeoutSec, askUserTimeoutMs) {
|
|
71
|
+
return createManagedSession({
|
|
72
|
+
ws: this.ws,
|
|
73
|
+
agentName: this.agentName,
|
|
74
|
+
timeoutSec,
|
|
75
|
+
messages: this._messages,
|
|
76
|
+
log: this._log,
|
|
77
|
+
model: this.modelOpt,
|
|
78
|
+
conversationWindow: this.conversationWindowOpt,
|
|
79
|
+
llm: this.llm,
|
|
80
|
+
...(askUserTimeoutMs !== undefined ? { askUserTimeoutMs } : {}),
|
|
81
|
+
...(this.transport !== undefined ? { transport: this.transport } : {}),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
resolveTimeoutSec(mode, maxTurns) {
|
|
85
|
+
if (this.timeoutSetting !== 'auto') {
|
|
86
|
+
return this.timeoutSetting;
|
|
87
|
+
}
|
|
88
|
+
if (mode !== 'runConversation') {
|
|
89
|
+
throw new Error("timeout: 'auto' is only supported for runConversation()");
|
|
90
|
+
}
|
|
91
|
+
const turns = maxTurns ?? 30;
|
|
92
|
+
return Math.ceil((turns * 80_000 + 200_000) / 1000);
|
|
93
|
+
}
|
|
94
|
+
async executeLoggedTurnResult(session, message, turnNumber, kind) {
|
|
95
|
+
const timestamp = () => new Date().toISOString();
|
|
96
|
+
this._messages.push({ role: 'user', content: message });
|
|
97
|
+
this._log.push({
|
|
98
|
+
type: kind,
|
|
99
|
+
timestamp: timestamp(),
|
|
100
|
+
instruction: message,
|
|
101
|
+
...(kind === 'user_reply' ? { turn_number: turnNumber } : {}),
|
|
102
|
+
});
|
|
103
|
+
this.verbose.turnStart({ turn: turnNumber, kind, message });
|
|
104
|
+
const turnStart = Date.now();
|
|
105
|
+
const turnResult = await session.executeTurn(message);
|
|
106
|
+
const response = getVisibleAssistantMessage(turnResult);
|
|
107
|
+
const durationMs = Date.now() - turnStart;
|
|
108
|
+
this._log.push(buildModelAgentResultLogEntry({
|
|
109
|
+
timestamp: timestamp(),
|
|
110
|
+
turnNumber,
|
|
111
|
+
durationMs,
|
|
112
|
+
turnResult,
|
|
113
|
+
assistantMessage: response,
|
|
114
|
+
}));
|
|
115
|
+
for (const toolEvent of turnResult.toolEvents) {
|
|
116
|
+
this._log.push({
|
|
117
|
+
type: 'tool_event',
|
|
118
|
+
timestamp: timestamp(),
|
|
119
|
+
tool_event: toolEvent,
|
|
120
|
+
});
|
|
121
|
+
this.verbose.toolEvent({ action: toolEvent.action, summary: toolEvent.summary });
|
|
122
|
+
}
|
|
123
|
+
this._messages.push({ role: 'agent', content: response });
|
|
124
|
+
this.verbose.turnEnd({
|
|
125
|
+
turn: turnNumber,
|
|
126
|
+
durationMs,
|
|
127
|
+
outputLines: response.split('\n').length,
|
|
128
|
+
messagePreview: response,
|
|
129
|
+
});
|
|
130
|
+
if (turnResult.exitCode !== 0) {
|
|
131
|
+
const timeoutSec = this.resolveTimeoutSec(this.interactionMode ?? 'prompt');
|
|
132
|
+
if (turnResult.timedOut) {
|
|
133
|
+
throw new Error(`Agent (limit: ${timeoutSec}s) timed out (agent killed)`);
|
|
134
|
+
}
|
|
135
|
+
throw new Error(`Agent exited with code ${turnResult.exitCode}`);
|
|
136
|
+
}
|
|
137
|
+
return turnResult;
|
|
138
|
+
}
|
|
139
|
+
async executeLoggedTurn(session, message, turnNumber, kind) {
|
|
140
|
+
const turnResult = await this.executeLoggedTurnResult(session, message, turnNumber, kind);
|
|
141
|
+
return getVisibleAssistantMessage(turnResult);
|
|
142
|
+
}
|
|
143
|
+
async prompt(message) {
|
|
144
|
+
this.guardInteraction('prompt');
|
|
145
|
+
if (this._messages.length > 0) {
|
|
146
|
+
throw new Error('prompt() can only be called once per agent');
|
|
147
|
+
}
|
|
148
|
+
const ms = this.createSession(this.resolveTimeoutSec('prompt'));
|
|
149
|
+
try {
|
|
150
|
+
return await this.executeLoggedTurn(ms, message, 1, 'agent_start');
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
await ms.dispose?.();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async startChat(firstMessage) {
|
|
157
|
+
this.guardInteraction('startChat');
|
|
158
|
+
if (this._messages.length > 0) {
|
|
159
|
+
throw new Error('startChat() can only be called once per agent');
|
|
160
|
+
}
|
|
161
|
+
const timeoutSec = this.resolveTimeoutSec('startChat');
|
|
162
|
+
const ms = this.createSession(timeoutSec);
|
|
163
|
+
const turnResult = await this.executeLoggedTurnResult(ms, firstMessage, 1, 'agent_start');
|
|
164
|
+
return new ChatSessionImpl(turnResult, {
|
|
165
|
+
messages: this._messages,
|
|
166
|
+
log: this._log,
|
|
167
|
+
exec: (cmd) => this.exec(cmd),
|
|
168
|
+
sendTurn: (message) => ms.executeTurn(message),
|
|
169
|
+
verbose: this.verbose,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
async runConversation(opts) {
|
|
173
|
+
this.guardInteraction('runConversation');
|
|
174
|
+
if (this._messages.length > 0) {
|
|
175
|
+
throw new Error('runConversation() can only be called once per agent');
|
|
176
|
+
}
|
|
177
|
+
const timeoutSec = this.resolveTimeoutSec('runConversation', opts.maxTurns);
|
|
178
|
+
const ms = this.createSession(timeoutSec, opts.askUserTimeoutMs);
|
|
179
|
+
let turnNumber = 0;
|
|
180
|
+
const sendTurn = async (message) => {
|
|
181
|
+
const turnResult = await ms.executeTurn(message);
|
|
182
|
+
turnNumber++;
|
|
183
|
+
// Accumulate agent turn tokens (from CLI stream-json) on the shared tracker
|
|
184
|
+
if (turnResult.inputTokens || turnResult.outputTokens) {
|
|
185
|
+
this.llm.addTokens?.(turnResult.inputTokens ?? 0, turnResult.outputTokens ?? 0);
|
|
186
|
+
}
|
|
187
|
+
// Accumulate agent turn cost on the same shared tracker when
|
|
188
|
+
// the upstream provider reports it (Claude SDK populates
|
|
189
|
+
// `costUsd` from `total_cost_usd`; Codex/Cursor leave it
|
|
190
|
+
// undefined, in which case this is a no-op).
|
|
191
|
+
if (turnResult.costUsd !== undefined) {
|
|
192
|
+
this.llm.addCost?.(turnResult.costUsd);
|
|
193
|
+
}
|
|
194
|
+
for (const toolEvent of turnResult.toolEvents) {
|
|
195
|
+
this._log.push({
|
|
196
|
+
type: 'tool_event',
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
198
|
+
tool_event: toolEvent,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
// Exit-code failures are no longer thrown here. The runConversation
|
|
202
|
+
// loop projects the partial-turn through `pushModelAgentMessage`
|
|
203
|
+
// first (so `model_agent_result`, `ask_batch`, turn timings/details
|
|
204
|
+
// all capture the failed turn) and then throws — preserving
|
|
205
|
+
// observability into what the agent attempted before the failure.
|
|
206
|
+
return turnResult;
|
|
207
|
+
};
|
|
208
|
+
// Set up persona reply callback if persona config provided
|
|
209
|
+
let personaReply;
|
|
210
|
+
if (opts.persona) {
|
|
211
|
+
const personaWindowConfig = opts.persona.conversationWindow !== undefined
|
|
212
|
+
? opts.persona.conversationWindow
|
|
213
|
+
: this.conversationWindowOpt;
|
|
214
|
+
const persona = createPersona({
|
|
215
|
+
...opts.persona,
|
|
216
|
+
llm: opts.persona.llm ?? this.llm,
|
|
217
|
+
conversationWindow: personaWindowConfig,
|
|
218
|
+
});
|
|
219
|
+
personaReply = async () => {
|
|
220
|
+
const fakeChatSession = {
|
|
221
|
+
turn: turnNumber,
|
|
222
|
+
done: false,
|
|
223
|
+
lastMessage: this._messages[this._messages.length - 1]?.content ?? '',
|
|
224
|
+
messages: this._messages,
|
|
225
|
+
reply: async () => { },
|
|
226
|
+
hasFile: async () => false,
|
|
227
|
+
end: () => { },
|
|
228
|
+
};
|
|
229
|
+
return persona.reply(fakeChatSession);
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// Set up step scorer runner — pass this.llm explicitly so step
|
|
233
|
+
// scorer judge calls accumulate on the same tracker.
|
|
234
|
+
const agent = this;
|
|
235
|
+
const runStepScorers = async (scorers) => {
|
|
236
|
+
return evaluate(agent, scorers, { llm: this.llm });
|
|
237
|
+
};
|
|
238
|
+
try {
|
|
239
|
+
const result = await runConversation(opts, {
|
|
240
|
+
sendTurn,
|
|
241
|
+
hasFile: async (pattern) => {
|
|
242
|
+
const result = await this.exec(`ls -d ${pattern} 2>/dev/null`);
|
|
243
|
+
return result.stdout.trim().length > 0;
|
|
244
|
+
},
|
|
245
|
+
workspace: this.workspace,
|
|
246
|
+
messages: this._messages,
|
|
247
|
+
log: this._log,
|
|
248
|
+
personaReply,
|
|
249
|
+
runStepScorers,
|
|
250
|
+
verbose: this.verbose,
|
|
251
|
+
askBus: ms.askBus,
|
|
252
|
+
agentName: this.agentName,
|
|
253
|
+
agentTimeoutSec: timeoutSec,
|
|
254
|
+
...(this.transport !== undefined ? { transport: this.transport } : {}),
|
|
255
|
+
});
|
|
256
|
+
this.lastConversationResult = result;
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
await ms.dispose?.();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async exec(cmd) {
|
|
264
|
+
return this.ws.exec(cmd);
|
|
265
|
+
}
|
|
266
|
+
transcript() {
|
|
267
|
+
const parts = [];
|
|
268
|
+
for (const msg of this._messages) {
|
|
269
|
+
const label = msg.role === 'user' ? 'User' : 'Agent';
|
|
270
|
+
parts.push(`[${label}]\n${msg.content}`);
|
|
271
|
+
}
|
|
272
|
+
return parts.join('\n\n');
|
|
273
|
+
}
|
|
274
|
+
async dispose() {
|
|
275
|
+
if (this.disposed)
|
|
276
|
+
return;
|
|
277
|
+
this.disposed = true;
|
|
278
|
+
// Do NOT call lifecycle.untrackAgent here — flush() in afterEach needs
|
|
279
|
+
// the agent to still be in pendingAgents so it can collect results
|
|
280
|
+
// (token usage, scorers, diagnostics) into task.meta.pathgrade.
|
|
281
|
+
if (this.debugOpt) {
|
|
282
|
+
const dest = typeof this.debugOpt === 'string'
|
|
283
|
+
? this.debugOpt
|
|
284
|
+
: path.join(this.debugBaseDir, 'pathgrade-debug', this.debugName);
|
|
285
|
+
await fs.remove(dest);
|
|
286
|
+
await fs.copy(this.ws.path, dest);
|
|
287
|
+
if (this.interactionMode === 'runConversation' && this.lastConversationResult) {
|
|
288
|
+
const snapshot = buildRunSnapshot({
|
|
289
|
+
agent: this.agentName,
|
|
290
|
+
messages: this._messages,
|
|
291
|
+
log: this._log,
|
|
292
|
+
conversationResult: this.lastConversationResult,
|
|
293
|
+
workspace: dest,
|
|
294
|
+
});
|
|
295
|
+
await fs.writeJSON(path.join(dest, 'run-snapshot.json'), snapshot, { spaces: 2 });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
await this.ws.dispose();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Create an isolated trial for agent evaluation.
|
|
303
|
+
*
|
|
304
|
+
* Workspace is set up immediately (files copied, skills staged, MCP configured).
|
|
305
|
+
* The agent process is NOT started — it's spawned lazily on first instruct()/chat()/converse().
|
|
306
|
+
*/
|
|
307
|
+
function slugify(s) {
|
|
308
|
+
return s
|
|
309
|
+
.replace(/[^a-zA-Z0-9_-]/g, '-')
|
|
310
|
+
.replace(/-+/g, '-')
|
|
311
|
+
.replace(/^-|-$/g, '')
|
|
312
|
+
.toLowerCase();
|
|
313
|
+
}
|
|
314
|
+
function resolveTestContext() {
|
|
315
|
+
try {
|
|
316
|
+
const worker = globalThis.__vitest_worker__;
|
|
317
|
+
const name = worker?.current?.name ? slugify(worker.current.name) : '';
|
|
318
|
+
const dir = worker?.filepath ? path.dirname(worker.filepath) : '';
|
|
319
|
+
return { name, dir };
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return { name: '', dir: '' };
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
export async function createAgent(opts) {
|
|
326
|
+
const agentName = resolveAgentName(opts, process.env);
|
|
327
|
+
const transport = agentName === 'codex'
|
|
328
|
+
? resolveCodexTransport(opts, process.env)
|
|
329
|
+
: undefined;
|
|
330
|
+
const timeoutSetting = opts.timeout ?? 300;
|
|
331
|
+
// Capture test context now, while vitest state is available
|
|
332
|
+
const testCtx = opts.debug ? resolveTestContext() : { name: '', dir: '' };
|
|
333
|
+
const { timeout: _, mcpMock, agent: __, debug: ___, model: ____, transport: _____, ...rest } = opts;
|
|
334
|
+
const workspace = await prepareWorkspace({
|
|
335
|
+
...rest,
|
|
336
|
+
agent: agentName,
|
|
337
|
+
mcp: mcpMock ? { mock: mcpMock } : undefined,
|
|
338
|
+
});
|
|
339
|
+
// Create agent LLM once, using the fully-resolved sandbox env (includes
|
|
340
|
+
// keychain OAuth tokens, API keys, safe host vars).
|
|
341
|
+
const llm = createAgentLLM(agentName, workspace.env);
|
|
342
|
+
// Fall back to sandbox dir name if no test name resolved
|
|
343
|
+
const debugName = testCtx.name || path.basename(path.dirname(workspace.path));
|
|
344
|
+
// Default debug dir is next to the eval file, fallback to cwd
|
|
345
|
+
const debugBaseDir = testCtx.dir || process.cwd();
|
|
346
|
+
const verbose = createVerboseEmitter({
|
|
347
|
+
enabled: process.env.PATHGRADE_VERBOSE === '1',
|
|
348
|
+
sink: verboseSinkOverride ?? undefined,
|
|
349
|
+
testName: testCtx.name || undefined,
|
|
350
|
+
});
|
|
351
|
+
const agent = new AgentImpl(workspace, agentName, llm, timeoutSetting, opts.conversationWindow, opts.model, opts.debug, debugName, debugBaseDir, verbose, transport);
|
|
352
|
+
lifecycle.trackAgent(agent);
|
|
353
|
+
return agent;
|
|
354
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AskBus } from './types.js';
|
|
2
|
+
interface AskBusConfig {
|
|
3
|
+
askUserTimeoutMs: number;
|
|
4
|
+
}
|
|
5
|
+
export declare class AskBusTimeoutError extends Error {
|
|
6
|
+
readonly batchId: string;
|
|
7
|
+
readonly turnNumber: number;
|
|
8
|
+
constructor(batchId: string, turnNumber: number, timeoutMs: number);
|
|
9
|
+
}
|
|
10
|
+
export declare function createAskBus(config: AskBusConfig): AskBus;
|
|
11
|
+
export interface AgentSessionOptionsWithBus {
|
|
12
|
+
askBus?: AskBus;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Helper for drivers that MUST have a real subscriber because they emit
|
|
16
|
+
* `lifecycle: 'live'` batches. Throws at driver construction when the bus is
|
|
17
|
+
* missing — silently resolving to `null` would send an empty answer map on the
|
|
18
|
+
* wire, which is a worse failure mode than refusing to start.
|
|
19
|
+
*/
|
|
20
|
+
export declare function requireAskBusForLiveBatches(sessionOptions: AgentSessionOptionsWithBus | undefined, driverName: string): AskBus;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export class AskBusTimeoutError extends Error {
|
|
2
|
+
batchId;
|
|
3
|
+
turnNumber;
|
|
4
|
+
constructor(batchId, turnNumber, timeoutMs) {
|
|
5
|
+
super(`ask_user batch ${batchId} on turn ${turnNumber} did not resolve within ${timeoutMs}ms`);
|
|
6
|
+
this.name = 'AskBusTimeoutError';
|
|
7
|
+
this.batchId = batchId;
|
|
8
|
+
this.turnNumber = turnNumber;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function createAskBus(config) {
|
|
12
|
+
const { askUserTimeoutMs } = config;
|
|
13
|
+
const handlers = new Set();
|
|
14
|
+
const pending = [];
|
|
15
|
+
const redactAnswers = (answers, questions) => {
|
|
16
|
+
const secretIds = new Set(questions.filter((q) => q.isSecret).map((q) => q.id));
|
|
17
|
+
return answers.map((answer) => {
|
|
18
|
+
const isSecret = secretIds.has(answer.questionId);
|
|
19
|
+
const shouldRedact = isSecret && (answer.source === 'reaction' || answer.source === 'fallback');
|
|
20
|
+
return {
|
|
21
|
+
questionId: answer.questionId,
|
|
22
|
+
values: shouldRedact ? ['<redacted>'] : [...answer.values],
|
|
23
|
+
source: answer.source,
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const toSnapshot = (entry) => {
|
|
28
|
+
const resolution = entry.resolution
|
|
29
|
+
? { answers: redactAnswers(entry.resolution.answers, entry.batch.questions) }
|
|
30
|
+
: null;
|
|
31
|
+
return { ...entry.batch, resolution };
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
emit(batch) {
|
|
35
|
+
const entry = { batch, resolution: null };
|
|
36
|
+
pending.push(entry);
|
|
37
|
+
if (batch.lifecycle === 'post-hoc') {
|
|
38
|
+
let warned = false;
|
|
39
|
+
let captured = false;
|
|
40
|
+
for (const handler of handlers) {
|
|
41
|
+
try {
|
|
42
|
+
handler(batch, (resolution) => {
|
|
43
|
+
if (!captured) {
|
|
44
|
+
entry.resolution = resolution;
|
|
45
|
+
captured = true;
|
|
46
|
+
}
|
|
47
|
+
if (!warned) {
|
|
48
|
+
warned = true;
|
|
49
|
+
console.warn(`AskBus: responded to post-hoc batch ${batch.batchId} — ignored at the wire; use reactions for live batches`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.error('AskBus handler threw', err);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
batchId: batch.batchId,
|
|
59
|
+
resolution: Promise.resolve(null),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// lifecycle === 'live'
|
|
63
|
+
let resolveFn;
|
|
64
|
+
let rejectFn;
|
|
65
|
+
const resolutionPromise = new Promise((resolve, reject) => {
|
|
66
|
+
resolveFn = resolve;
|
|
67
|
+
rejectFn = reject;
|
|
68
|
+
});
|
|
69
|
+
let settled = false;
|
|
70
|
+
const timer = setTimeout(() => {
|
|
71
|
+
if (settled)
|
|
72
|
+
return;
|
|
73
|
+
settled = true;
|
|
74
|
+
rejectFn(new AskBusTimeoutError(batch.batchId, batch.turnNumber, askUserTimeoutMs));
|
|
75
|
+
}, askUserTimeoutMs);
|
|
76
|
+
if (typeof timer.unref === 'function') {
|
|
77
|
+
timer.unref();
|
|
78
|
+
}
|
|
79
|
+
const respond = (resolution) => {
|
|
80
|
+
if (settled) {
|
|
81
|
+
console.debug(`AskBus: late respond() on batch ${batch.batchId} — first-wins, ignored`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
settled = true;
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
entry.resolution = resolution;
|
|
87
|
+
resolveFn(resolution);
|
|
88
|
+
};
|
|
89
|
+
for (const handler of handlers) {
|
|
90
|
+
try {
|
|
91
|
+
handler(batch, respond);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
console.error('AskBus handler threw', err);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
batchId: batch.batchId,
|
|
99
|
+
resolution: resolutionPromise,
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
onAsk(handler) {
|
|
103
|
+
handlers.add(handler);
|
|
104
|
+
return () => {
|
|
105
|
+
handlers.delete(handler);
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
snapshot(turnNumber) {
|
|
109
|
+
const filtered = turnNumber === undefined
|
|
110
|
+
? pending
|
|
111
|
+
: pending.filter((entry) => entry.batch.turnNumber === turnNumber);
|
|
112
|
+
return filtered.map(toSnapshot);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Helper for drivers that MUST have a real subscriber because they emit
|
|
118
|
+
* `lifecycle: 'live'` batches. Throws at driver construction when the bus is
|
|
119
|
+
* missing — silently resolving to `null` would send an empty answer map on the
|
|
120
|
+
* wire, which is a worse failure mode than refusing to start.
|
|
121
|
+
*/
|
|
122
|
+
export function requireAskBusForLiveBatches(sessionOptions, driverName) {
|
|
123
|
+
const bus = sessionOptions?.askBus;
|
|
124
|
+
if (!bus) {
|
|
125
|
+
throw new Error(`${driverName} requires AgentSessionOptions.askBus for live ask_user batches. ` +
|
|
126
|
+
`Construct the session via createManagedSession or pass an AskBus explicitly.`);
|
|
127
|
+
}
|
|
128
|
+
return bus;
|
|
129
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AskHandler } from './types.js';
|
|
2
|
+
import type { AskUserReactionPreviewEntry, Reaction } from '../types.js';
|
|
3
|
+
export type UnmatchedAskUserDisposition = 'error' | 'first-option' | 'decline';
|
|
4
|
+
export interface AskUserHandlerContext {
|
|
5
|
+
reactions: readonly Reaction[];
|
|
6
|
+
onUnmatchedAskUser: UnmatchedAskUserDisposition;
|
|
7
|
+
firedOnce: Set<number>;
|
|
8
|
+
}
|
|
9
|
+
export interface AskUserUnmatchedSignal {
|
|
10
|
+
batchId: string;
|
|
11
|
+
turnNumber: number;
|
|
12
|
+
/**
|
|
13
|
+
* The question text(s) on the live batch that triggered the unmatched
|
|
14
|
+
* signal — populated whether the trigger is `'error'` outright or a
|
|
15
|
+
* `'first-option'` degradation (free-text / isSecret). Consumers compose
|
|
16
|
+
* the user-facing completion detail from at least the first entry.
|
|
17
|
+
*/
|
|
18
|
+
questionTexts: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface AskUserHandlerApi {
|
|
21
|
+
readonly handler: AskHandler;
|
|
22
|
+
/** Preview entries recorded per batchId (live AND post-hoc). */
|
|
23
|
+
readonly previewByBatch: Map<string, AskUserReactionPreviewEntry[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Set when a live batch falls through to `onUnmatchedAskUser: 'error'`.
|
|
26
|
+
* Caller inspects after each turn and ends the conversation with
|
|
27
|
+
* `completionReason: 'error'`.
|
|
28
|
+
*/
|
|
29
|
+
getUnmatchedError(): AskUserUnmatchedSignal | null;
|
|
30
|
+
}
|
|
31
|
+
export declare function createAskUserHandler(ctx: AskUserHandlerContext): AskUserHandlerApi;
|