@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,416 @@
|
|
|
1
|
+
import type { CommandResult, LogEntry } from '../types.js';
|
|
2
|
+
import type { ToolAction } from '../tool-events.js';
|
|
3
|
+
import type { MockMcpServerDescriptor } from '../core/mcp-mock.types.js';
|
|
4
|
+
import type { TrialResult } from '../types.js';
|
|
5
|
+
import type { DiagnosticsReport } from '../reporters/diagnostics.js';
|
|
6
|
+
import type { LLMPort } from '../utils/llm-types.js';
|
|
7
|
+
export type AgentName = 'claude' | 'codex' | 'cursor';
|
|
8
|
+
export interface AgentOptions {
|
|
9
|
+
agent?: AgentName;
|
|
10
|
+
model?: string;
|
|
11
|
+
timeout?: number | 'auto';
|
|
12
|
+
workspace?: string;
|
|
13
|
+
skillDir?: string;
|
|
14
|
+
copyFromHome?: string[];
|
|
15
|
+
env?: Record<string, string>;
|
|
16
|
+
mcpMock?: MockMcpServerDescriptor | MockMcpServerDescriptor[];
|
|
17
|
+
/** Configure the conversation window for transcript-based agents. Set false to disable. */
|
|
18
|
+
conversationWindow?: ConversationWindowConfig | false;
|
|
19
|
+
/** Copy workspace to a persistent location before cleanup. true = ./pathgrade-debug/{test-name}/, string = custom path. */
|
|
20
|
+
debug?: boolean | string;
|
|
21
|
+
/**
|
|
22
|
+
* Glob patterns to ignore when copying workspace and skill directories.
|
|
23
|
+
* Replaces the default ignore list entirely. Pass `[]` to disable filtering.
|
|
24
|
+
* When omitted, DEFAULT_COPY_IGNORE is used.
|
|
25
|
+
*/
|
|
26
|
+
copyIgnore?: string[];
|
|
27
|
+
/**
|
|
28
|
+
* Codex-only transport override. Precedence:
|
|
29
|
+
* `opts.transport` > `PATHGRADE_CODEX_TRANSPORT` env > `'app-server'`
|
|
30
|
+
* Invalid env values throw at `createAgent` time. Ignored for other agents
|
|
31
|
+
* in v1 (documented but not enforced).
|
|
32
|
+
*/
|
|
33
|
+
transport?: AgentTransport;
|
|
34
|
+
}
|
|
35
|
+
export interface ConversationWindowConfig {
|
|
36
|
+
/** Number of recent messages to keep verbatim. Default: 4 */
|
|
37
|
+
windowSize?: number;
|
|
38
|
+
/** Model for summarization. Default: 'claude-haiku-4-5-20251001' */
|
|
39
|
+
model?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface Message {
|
|
42
|
+
role: 'user' | 'agent';
|
|
43
|
+
content: string;
|
|
44
|
+
}
|
|
45
|
+
export interface Agent {
|
|
46
|
+
prompt(message: string): Promise<string>;
|
|
47
|
+
runConversation(opts: ConverseOptions): Promise<ConversationResult>;
|
|
48
|
+
startChat(firstMessage: string): Promise<ChatSession>;
|
|
49
|
+
exec(cmd: string): Promise<CommandResult>;
|
|
50
|
+
transcript(): string;
|
|
51
|
+
readonly llm: LLMPort;
|
|
52
|
+
readonly messages: Message[];
|
|
53
|
+
readonly log: LogEntry[];
|
|
54
|
+
readonly workspace: string;
|
|
55
|
+
dispose(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
export interface ConverseOptions {
|
|
58
|
+
firstMessage: string;
|
|
59
|
+
maxTurns?: number;
|
|
60
|
+
until?: UntilPredicate;
|
|
61
|
+
reactions?: Reaction[];
|
|
62
|
+
persona?: PersonaConfig;
|
|
63
|
+
stepScorers?: StepScorer[];
|
|
64
|
+
/**
|
|
65
|
+
* Conversation-level ceiling (ms) for a single live `ask_user` batch to
|
|
66
|
+
* resolve. When the reaction engine can't produce an answer within this
|
|
67
|
+
* window, `handle.resolution` rejects and the turn ends with
|
|
68
|
+
* `completionReason: 'error'`. Default `30_000`.
|
|
69
|
+
*/
|
|
70
|
+
askUserTimeoutMs?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Disposition when a live `ask_user` batch has no matching
|
|
73
|
+
* `AskUserReaction` (or a matching reaction's `answer` returns `undefined`):
|
|
74
|
+
* - `'error'` (default): the bus is responded to with `source: 'declined'`
|
|
75
|
+
* so the handle resolves; the turn then ends with `completionReason:
|
|
76
|
+
* 'error'` and `completionDetail: 'unmatched ask_user on turn N: <id>'`.
|
|
77
|
+
* - `'first-option'`: pick `options[0].label`. Free-text (`options: null`)
|
|
78
|
+
* and `isSecret: true` questions degrade to `'error'`.
|
|
79
|
+
* - `'decline'`: emit `{ values: [], source: 'declined' }` for the
|
|
80
|
+
* question. The turn continues normally.
|
|
81
|
+
*/
|
|
82
|
+
onUnmatchedAskUser?: 'error' | 'first-option' | 'decline';
|
|
83
|
+
/**
|
|
84
|
+
* When the configured transport cannot deliver `AskUserReaction`s mid-turn
|
|
85
|
+
* (e.g. `codex` transport: `'exec'`), pathgrade's runtime-policy guard
|
|
86
|
+
* refuses to start the session. Set to `true` to silence that guard —
|
|
87
|
+
* reactions will simply never fire for that agent/transport combo.
|
|
88
|
+
* Validation lives in runtime-policy (slices #3/#7); this field is the
|
|
89
|
+
* ConverseOptions plumbing point.
|
|
90
|
+
*/
|
|
91
|
+
allowUnreachableReactions?: boolean;
|
|
92
|
+
}
|
|
93
|
+
export type UntilPredicate = (ctx: UntilContext) => boolean | Promise<boolean>;
|
|
94
|
+
export interface UntilContext {
|
|
95
|
+
turn: number;
|
|
96
|
+
lastMessage: string;
|
|
97
|
+
workspace: string;
|
|
98
|
+
messages: Message[];
|
|
99
|
+
hasFile: (glob: string) => Promise<boolean>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Free-text reaction. When the agent's assistant message matches `when`
|
|
103
|
+
* (and does not match `unless`), `reply` is sent as the next user message.
|
|
104
|
+
* Also kept as a compatibility answer for structured ask_user questions:
|
|
105
|
+
* when no `AskUserReaction` answers a matching question, `reply` is used as
|
|
106
|
+
* that question's answer.
|
|
107
|
+
* Shape is byte-identical to the pre-discriminated-union Reaction.
|
|
108
|
+
*/
|
|
109
|
+
export interface TextReaction {
|
|
110
|
+
when: RegExp;
|
|
111
|
+
unless?: RegExp;
|
|
112
|
+
reply: string;
|
|
113
|
+
once?: boolean;
|
|
114
|
+
}
|
|
115
|
+
/** Structured-question option (mirrors upstream `Option` shape; no `id`). */
|
|
116
|
+
export interface AskUserOption {
|
|
117
|
+
label: string;
|
|
118
|
+
description?: string;
|
|
119
|
+
}
|
|
120
|
+
/** Pathgrade-local mirror of upstream AskUserQuestion shape. */
|
|
121
|
+
export interface AskUserQuestion {
|
|
122
|
+
id: string;
|
|
123
|
+
header?: string;
|
|
124
|
+
question: string;
|
|
125
|
+
isOther: boolean;
|
|
126
|
+
isSecret: boolean;
|
|
127
|
+
options: AskUserOption[] | null;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Structured ask_user reaction. Fires only on ask_user handshake turns
|
|
131
|
+
* (post-hoc Claude denials and live Codex app-server requests). Never
|
|
132
|
+
* evaluated against assistant free text. `answer` returning `undefined`
|
|
133
|
+
* falls through to the next matching reaction, then to `onUnmatchedAskUser`.
|
|
134
|
+
*/
|
|
135
|
+
export interface AskUserReaction {
|
|
136
|
+
whenAsked: RegExp | ((question: AskUserQuestion) => boolean);
|
|
137
|
+
answer: string | string[] | ((question: AskUserQuestion) => string | string[] | undefined);
|
|
138
|
+
once?: boolean;
|
|
139
|
+
}
|
|
140
|
+
export type Reaction = TextReaction | AskUserReaction;
|
|
141
|
+
export type ReactionPreviewStatus = 'fired' | 'vetoed' | 'no-match' | 'shadowed';
|
|
142
|
+
export interface TextReactionPreviewEntry {
|
|
143
|
+
kind: 'text';
|
|
144
|
+
reactionIndex: number;
|
|
145
|
+
whenMatched: boolean;
|
|
146
|
+
unlessMatched: boolean;
|
|
147
|
+
fired: boolean;
|
|
148
|
+
status: ReactionPreviewStatus;
|
|
149
|
+
reply?: string;
|
|
150
|
+
}
|
|
151
|
+
export interface AskUserReactionPreviewEntry {
|
|
152
|
+
kind: 'ask_user';
|
|
153
|
+
reactionIndex: number;
|
|
154
|
+
whenAskedMatched: boolean;
|
|
155
|
+
resolvedAnswers?: string[];
|
|
156
|
+
fired: boolean;
|
|
157
|
+
status: ReactionPreviewStatus;
|
|
158
|
+
}
|
|
159
|
+
export type ReactionPreviewEntry = TextReactionPreviewEntry | AskUserReactionPreviewEntry;
|
|
160
|
+
export interface ReactionPreviewTurn {
|
|
161
|
+
turn: number;
|
|
162
|
+
agentMessage: string;
|
|
163
|
+
reactions: ReactionPreviewEntry[];
|
|
164
|
+
}
|
|
165
|
+
export interface ReactionPreviewResult {
|
|
166
|
+
turns: ReactionPreviewTurn[];
|
|
167
|
+
}
|
|
168
|
+
export interface StepScorer {
|
|
169
|
+
afterTurn: number;
|
|
170
|
+
scorers: Scorer[];
|
|
171
|
+
}
|
|
172
|
+
export interface ChatSession {
|
|
173
|
+
readonly turn: number;
|
|
174
|
+
readonly done: boolean;
|
|
175
|
+
readonly lastMessage: string;
|
|
176
|
+
readonly messages: Message[];
|
|
177
|
+
reply(message: string): Promise<void>;
|
|
178
|
+
hasFile(glob: string): Promise<boolean>;
|
|
179
|
+
end(): void;
|
|
180
|
+
}
|
|
181
|
+
export interface TurnTiming {
|
|
182
|
+
turn: number;
|
|
183
|
+
durationMs: number;
|
|
184
|
+
}
|
|
185
|
+
export interface TurnDetail {
|
|
186
|
+
turn: number;
|
|
187
|
+
durationMs: number;
|
|
188
|
+
outputLines: number;
|
|
189
|
+
outputChars: number;
|
|
190
|
+
}
|
|
191
|
+
export interface ReactionFiredEntry {
|
|
192
|
+
turn: number;
|
|
193
|
+
reactionIndex: number;
|
|
194
|
+
pattern: string;
|
|
195
|
+
reply: string;
|
|
196
|
+
}
|
|
197
|
+
export interface ConversationResult {
|
|
198
|
+
turns: number;
|
|
199
|
+
completionReason: 'until' | 'maxTurns' | 'noReply' | 'timeout' | 'error' | 'agent_crashed';
|
|
200
|
+
completionDetail?: string;
|
|
201
|
+
turnTimings: TurnTiming[];
|
|
202
|
+
turnDetails?: TurnDetail[];
|
|
203
|
+
reactionsFired?: ReactionFiredEntry[];
|
|
204
|
+
stepResults: Array<{
|
|
205
|
+
afterTurn: number;
|
|
206
|
+
result: EvalResult;
|
|
207
|
+
}>;
|
|
208
|
+
/**
|
|
209
|
+
* Subprocess-crash diagnostic, present only when `completionReason` is
|
|
210
|
+
* `'agent_crashed'`. `partialAsks` is sourced from `askBus.snapshot()` so
|
|
211
|
+
* `isSecret` answers are already bus-redacted. `partialToolEvents`
|
|
212
|
+
* narrows to non-ask-user events — ask-user events derive from
|
|
213
|
+
* `partialAsks` via `toAskUserToolEvent` at reporter/log time.
|
|
214
|
+
*/
|
|
215
|
+
crashDiagnostic?: {
|
|
216
|
+
pid?: number;
|
|
217
|
+
signal?: NodeJS.Signals | null;
|
|
218
|
+
exitCode?: number | null;
|
|
219
|
+
lastTurnNumber: number;
|
|
220
|
+
partialAsks: readonly import('./ask-bus/types.js').AskBatchSnapshot[];
|
|
221
|
+
partialToolEvents: import('../tool-events.js').ToolEvent[];
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
export interface Persona {
|
|
225
|
+
reply(chat: ChatSession): Promise<string>;
|
|
226
|
+
}
|
|
227
|
+
export interface PersonaConfig {
|
|
228
|
+
description: string;
|
|
229
|
+
facts: string[];
|
|
230
|
+
model?: string;
|
|
231
|
+
/** LLM port for persona and summarization calls. */
|
|
232
|
+
llm?: LLMPort;
|
|
233
|
+
/** Configure the conversation window for persona history. Set false to disable. */
|
|
234
|
+
conversationWindow?: ConversationWindowConfig | false;
|
|
235
|
+
}
|
|
236
|
+
export type Scorer = CheckScorer | ScoreScorer | JudgeScorer | ToolUsageScorer;
|
|
237
|
+
export interface CheckScorer {
|
|
238
|
+
type: 'check';
|
|
239
|
+
name: string;
|
|
240
|
+
weight: number;
|
|
241
|
+
fn: (ctx: ScorerContext) => boolean | Promise<boolean>;
|
|
242
|
+
}
|
|
243
|
+
export interface ScoreScorer {
|
|
244
|
+
type: 'score';
|
|
245
|
+
name: string;
|
|
246
|
+
weight: number;
|
|
247
|
+
fn: (ctx: ScorerContext) => number | ScoreResult | Promise<number | ScoreResult>;
|
|
248
|
+
}
|
|
249
|
+
export interface ScoreResult {
|
|
250
|
+
score: number;
|
|
251
|
+
details?: string;
|
|
252
|
+
}
|
|
253
|
+
export type JudgeInput = Record<string, unknown> | ((ctx: ScorerContext) => Record<string, unknown> | Promise<Record<string, unknown>>);
|
|
254
|
+
export type CodeJudgeToolName = 'readFile' | 'listDir' | 'grep' | 'getToolEvents';
|
|
255
|
+
export interface JudgeScorer {
|
|
256
|
+
type: 'judge';
|
|
257
|
+
name: string;
|
|
258
|
+
weight: number;
|
|
259
|
+
rubric: string;
|
|
260
|
+
model?: string;
|
|
261
|
+
retry?: boolean | number;
|
|
262
|
+
includeToolEvents?: boolean;
|
|
263
|
+
input?: JudgeInput;
|
|
264
|
+
/** Opt-in allowlist of tools the judge LLM may call. Non-empty = tool-use loop. */
|
|
265
|
+
tools?: CodeJudgeToolName[];
|
|
266
|
+
/** Cap on LLM calls per judge; default 10. */
|
|
267
|
+
maxRounds?: number;
|
|
268
|
+
/** Enable Anthropic prompt caching for system + tool schemas. Default: true when tools is set. */
|
|
269
|
+
cacheControl?: boolean;
|
|
270
|
+
}
|
|
271
|
+
export interface ToolExpectation {
|
|
272
|
+
action: ToolAction;
|
|
273
|
+
min?: number;
|
|
274
|
+
max?: number;
|
|
275
|
+
path?: string;
|
|
276
|
+
commandContains?: string;
|
|
277
|
+
argumentPattern?: string;
|
|
278
|
+
toolName?: string;
|
|
279
|
+
weight?: number;
|
|
280
|
+
}
|
|
281
|
+
export interface ToolUsageScorer {
|
|
282
|
+
type: 'tool_usage';
|
|
283
|
+
name: string;
|
|
284
|
+
weight: number;
|
|
285
|
+
expectations: ToolExpectation[];
|
|
286
|
+
}
|
|
287
|
+
export interface SessionArtifactMatchOptions {
|
|
288
|
+
actions?: import('../tool-events.js').ToolAction[];
|
|
289
|
+
pattern?: string | RegExp;
|
|
290
|
+
}
|
|
291
|
+
export interface SessionArtifactContent {
|
|
292
|
+
path: string;
|
|
293
|
+
content: string;
|
|
294
|
+
}
|
|
295
|
+
export interface SessionArtifacts {
|
|
296
|
+
list: (opts?: SessionArtifactMatchOptions) => string[];
|
|
297
|
+
read: (path: string) => Promise<string>;
|
|
298
|
+
latest: (opts?: SessionArtifactMatchOptions) => Promise<SessionArtifactContent | null>;
|
|
299
|
+
}
|
|
300
|
+
export interface ScorerContext {
|
|
301
|
+
workspace: string;
|
|
302
|
+
log: LogEntry[];
|
|
303
|
+
transcript: string;
|
|
304
|
+
toolEvents: import('../tool-events.js').ToolEvent[];
|
|
305
|
+
runCommand: (cmd: string) => Promise<CommandResult>;
|
|
306
|
+
artifacts: SessionArtifacts;
|
|
307
|
+
}
|
|
308
|
+
export interface EvaluateOptions {
|
|
309
|
+
failFast?: boolean;
|
|
310
|
+
llm?: LLMPort;
|
|
311
|
+
onScorerError?: 'skip' | 'zero' | 'fail';
|
|
312
|
+
}
|
|
313
|
+
export interface TokenUsage {
|
|
314
|
+
inputTokens: number;
|
|
315
|
+
outputTokens: number;
|
|
316
|
+
}
|
|
317
|
+
export type ScorerStatus = 'ok' | 'error' | 'skipped';
|
|
318
|
+
export interface EvalResult {
|
|
319
|
+
score: number;
|
|
320
|
+
scorers: ScorerResultEntry[];
|
|
321
|
+
tokenUsage?: TokenUsage;
|
|
322
|
+
}
|
|
323
|
+
export interface RecordedEvalResult extends EvalResult {
|
|
324
|
+
trial?: TrialResult;
|
|
325
|
+
}
|
|
326
|
+
export interface ScorerResultEntry {
|
|
327
|
+
name: string;
|
|
328
|
+
type: 'check' | 'score' | 'judge' | 'tool_usage';
|
|
329
|
+
score: number;
|
|
330
|
+
weight: number;
|
|
331
|
+
details?: string;
|
|
332
|
+
status?: ScorerStatus;
|
|
333
|
+
/**
|
|
334
|
+
* Machine-readable failure code for judge scorers that support a
|
|
335
|
+
* failure taxonomy (currently only tool-using judges).
|
|
336
|
+
* One of: llm_refused | max_rounds | invalid_score | tool_error_unrecoverable | provider_not_supported
|
|
337
|
+
*/
|
|
338
|
+
errorCode?: string;
|
|
339
|
+
}
|
|
340
|
+
export interface PathgradeTestMeta {
|
|
341
|
+
score: number;
|
|
342
|
+
scorers: ScorerResultEntry[];
|
|
343
|
+
trial?: TrialResult;
|
|
344
|
+
diagnostics?: DiagnosticsReport;
|
|
345
|
+
}
|
|
346
|
+
export interface AgentCapabilities {
|
|
347
|
+
mcp: boolean;
|
|
348
|
+
nativeSession: boolean;
|
|
349
|
+
interactiveQuestionTransport: 'reliable' | 'noninteractive';
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Codex-specific transport. Claude and Cursor ignore this.
|
|
353
|
+
* `app-server` unlocks `interactiveQuestionTransport: 'reliable'` for Codex;
|
|
354
|
+
* `exec` (the default) keeps the `'noninteractive'` channel.
|
|
355
|
+
*/
|
|
356
|
+
export type AgentTransport = 'exec' | 'app-server';
|
|
357
|
+
export declare function getAgentCapabilities(agent: AgentName, transport?: AgentTransport): AgentCapabilities;
|
|
358
|
+
/**
|
|
359
|
+
* @deprecated Use `getAgentCapabilities(agent, transport?)` instead. Kept as
|
|
360
|
+
* an internal read only during the transport-swap migration.
|
|
361
|
+
*/
|
|
362
|
+
export declare const AGENT_CAPABILITIES: Record<AgentName, AgentCapabilities>;
|
|
363
|
+
export interface PathgradePluginOptions {
|
|
364
|
+
include?: string[];
|
|
365
|
+
exclude?: string[];
|
|
366
|
+
timeout?: number;
|
|
367
|
+
reporter?: 'cli' | 'browser' | 'json';
|
|
368
|
+
diagnostics?: boolean;
|
|
369
|
+
/**
|
|
370
|
+
* Stream live per-turn events (turn start/end, tool calls, reactions,
|
|
371
|
+
* blocked prompts, conversation end) to stderr while evals run. Off by
|
|
372
|
+
* default. CLI `--verbose` / `PATHGRADE_VERBOSE=1` take precedence over
|
|
373
|
+
* this option: the plugin only sets `PATHGRADE_VERBOSE=1` if it is not
|
|
374
|
+
* already set by the user's shell or the CLI wrapper. See
|
|
375
|
+
* `docs/prds/PRD_VERBOSE_LIVE_STREAMING.md`.
|
|
376
|
+
*/
|
|
377
|
+
verbose?: boolean;
|
|
378
|
+
scorerModel?: string;
|
|
379
|
+
ci?: {
|
|
380
|
+
threshold?: number;
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* Affected-eval-selection configuration. Consumed by the `pathgrade affected`
|
|
384
|
+
* and `pathgrade run --changed` CLI commands (not by the plugin itself at
|
|
385
|
+
* runtime — selection is a pre-vitest filter).
|
|
386
|
+
*/
|
|
387
|
+
affected?: {
|
|
388
|
+
/**
|
|
389
|
+
* Repo-level "rerun everything" triggers. When any changed file
|
|
390
|
+
* matches any glob here, selection short-circuits and every discovered
|
|
391
|
+
* eval is selected. Keep small — every match defeats the feature's
|
|
392
|
+
* purpose. See PRD §"Root-level `global` config".
|
|
393
|
+
*/
|
|
394
|
+
global?: string[];
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Per-eval selection metadata. Export from an `.eval.ts` as a named
|
|
399
|
+
* top-level constant to declare dependency globs or opt into always-run
|
|
400
|
+
* behavior. Pathgrade extracts this via AST — the eval module is NOT
|
|
401
|
+
* executed during selection, so the value must be a literal expression.
|
|
402
|
+
*
|
|
403
|
+
* See PRD §"Overrides and extras: `__pathgradeMeta`".
|
|
404
|
+
*
|
|
405
|
+
* Note: `onMissing` is intentionally *not* a field here — it names the
|
|
406
|
+
* pathgrade runtime's behavior when `__pathgradeMeta` is entirely absent
|
|
407
|
+
* AND there is no SKILL.md ancestor (fail-closed rerun with warning).
|
|
408
|
+
*/
|
|
409
|
+
export interface PathgradeMeta {
|
|
410
|
+
/** Full override: replaces auto-detected `<skillRoot>/**`. */
|
|
411
|
+
deps?: string[];
|
|
412
|
+
/** Unioned with the auto-detected skill root (or with `deps`). */
|
|
413
|
+
extraDeps?: string[];
|
|
414
|
+
/** Unconditionally include this eval in every `pathgrade run --changed`. */
|
|
415
|
+
alwaysRun?: boolean;
|
|
416
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const BASE_CAPABILITIES = {
|
|
2
|
+
claude: { mcp: true, nativeSession: true, interactiveQuestionTransport: 'reliable' },
|
|
3
|
+
codex: { mcp: false, nativeSession: true, interactiveQuestionTransport: 'noninteractive' },
|
|
4
|
+
cursor: { mcp: true, nativeSession: true, interactiveQuestionTransport: 'noninteractive' },
|
|
5
|
+
};
|
|
6
|
+
export function getAgentCapabilities(agent, transport) {
|
|
7
|
+
const base = BASE_CAPABILITIES[agent];
|
|
8
|
+
if (agent === 'codex' && transport === 'app-server') {
|
|
9
|
+
return { ...base, interactiveQuestionTransport: 'reliable' };
|
|
10
|
+
}
|
|
11
|
+
return base;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated Use `getAgentCapabilities(agent, transport?)` instead. Kept as
|
|
15
|
+
* an internal read only during the transport-swap migration.
|
|
16
|
+
*/
|
|
17
|
+
export const AGENT_CAPABILITIES = BASE_CAPABILITIES;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AgentTurnResult } from '../types.js';
|
|
2
|
+
export declare function getVisibleAssistantMessage(turnResult: AgentTurnResult): string;
|
|
3
|
+
export declare function normalizeTurnResult(result: string | AgentTurnResult): AgentTurnResult;
|
|
4
|
+
export declare function getTurnResultLogMetadata(turnResult: AgentTurnResult): Record<string, unknown>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getRuntimePolicyLogMetadata } from './runtime-policy.js';
|
|
2
|
+
export function getVisibleAssistantMessage(turnResult) {
|
|
3
|
+
return turnResult.visibleAssistantMessage || turnResult.assistantMessage || turnResult.rawOutput;
|
|
4
|
+
}
|
|
5
|
+
export function normalizeTurnResult(result) {
|
|
6
|
+
if (typeof result !== 'string') {
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
rawOutput: result,
|
|
11
|
+
assistantMessage: result,
|
|
12
|
+
visibleAssistantMessage: result,
|
|
13
|
+
visibleAssistantMessageSource: 'assistant_message',
|
|
14
|
+
exitCode: 0,
|
|
15
|
+
toolEvents: [],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function getTurnResultLogMetadata(turnResult) {
|
|
19
|
+
return {
|
|
20
|
+
assistant_message_source: turnResult.visibleAssistantMessageSource,
|
|
21
|
+
...getRuntimePolicyLogMetadata(turnResult),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type ToolAction = 'run_shell' | 'read_file' | 'write_file' | 'edit_file' | 'search_code' | 'list_files' | 'ask_user' | 'web_fetch' | 'use_skill' | 'update_todos' | 'unknown';
|
|
2
|
+
export interface ToolEvent {
|
|
3
|
+
action: ToolAction;
|
|
4
|
+
provider: 'claude' | 'codex' | 'cursor';
|
|
5
|
+
providerToolName: string;
|
|
6
|
+
turnNumber?: number;
|
|
7
|
+
arguments?: Record<string, unknown>;
|
|
8
|
+
summary: string;
|
|
9
|
+
confidence: 'high' | 'medium' | 'low';
|
|
10
|
+
rawSnippet: string;
|
|
11
|
+
skillName?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function summarizeToolEvents(events: ToolEvent[]): string;
|
|
14
|
+
/**
|
|
15
|
+
* Map from provider-specific tool names to normalized Pathgrade actions.
|
|
16
|
+
* Conservative: only map names we're confident about.
|
|
17
|
+
*/
|
|
18
|
+
export declare const TOOL_NAME_MAP: Record<string, ToolAction>;
|
|
19
|
+
export declare function buildSummary(action: ToolAction, toolName: string, args?: Record<string, unknown>): string;
|
|
20
|
+
/**
|
|
21
|
+
* Extract deduplicated skill names from a session log (LogEntry[]).
|
|
22
|
+
* Filters for tool_event entries, then delegates to extractSkillsFromToolEvents.
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractSkillsFromLog(log: ReadonlyArray<{
|
|
25
|
+
type: string;
|
|
26
|
+
tool_event?: ToolEvent;
|
|
27
|
+
}>): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Extract deduplicated skill names from tool events that have action 'use_skill'.
|
|
30
|
+
*/
|
|
31
|
+
export declare function extractSkillsFromToolEvents(events: ToolEvent[]): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Post-processing pass: detect skill usage from tool events.
|
|
34
|
+
* - Skill tool calls: action is already 'use_skill', extract skillName from args.skill
|
|
35
|
+
* - Read on SKILL.md: override action to 'use_skill', extract skillName from path
|
|
36
|
+
*
|
|
37
|
+
* This is agent-agnostic — works for any provider that produces ToolEvent[].
|
|
38
|
+
*/
|
|
39
|
+
export declare function enrichSkillEvents(events: ToolEvent[]): ToolEvent[];
|
|
40
|
+
/**
|
|
41
|
+
* If a file path points to a SKILL.md file, return the parent directory name as the skill name.
|
|
42
|
+
* Returns undefined for non-SKILL.md paths.
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractSkillNameFromPath(filePath: string): string | undefined;
|
|
45
|
+
export declare function inferCodexExecAction(command: string): ToolAction;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export function summarizeToolEvents(events) {
|
|
2
|
+
return events.map((event) => `${event.action}:${event.providerToolName}`).join(', ');
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Map from provider-specific tool names to normalized Pathgrade actions.
|
|
6
|
+
* Conservative: only map names we're confident about.
|
|
7
|
+
*/
|
|
8
|
+
export const TOOL_NAME_MAP = {
|
|
9
|
+
// Shell execution
|
|
10
|
+
exec_command: 'run_shell',
|
|
11
|
+
shell: 'run_shell',
|
|
12
|
+
bash: 'run_shell',
|
|
13
|
+
run_command: 'run_shell',
|
|
14
|
+
// File reading
|
|
15
|
+
read_file: 'read_file',
|
|
16
|
+
localGetFileContent: 'read_file',
|
|
17
|
+
cat: 'read_file',
|
|
18
|
+
// File writing
|
|
19
|
+
write_file: 'write_file',
|
|
20
|
+
create_file: 'write_file',
|
|
21
|
+
// File editing
|
|
22
|
+
edit_file: 'edit_file',
|
|
23
|
+
patch_file: 'edit_file',
|
|
24
|
+
// Code search
|
|
25
|
+
localSearchCode: 'search_code',
|
|
26
|
+
search_code: 'search_code',
|
|
27
|
+
grep: 'search_code',
|
|
28
|
+
rg: 'search_code',
|
|
29
|
+
// File listing
|
|
30
|
+
list_files: 'list_files',
|
|
31
|
+
localViewStructure: 'list_files',
|
|
32
|
+
ls: 'list_files',
|
|
33
|
+
// User interaction
|
|
34
|
+
ask_user: 'ask_user',
|
|
35
|
+
AskUserQuestion: 'ask_user',
|
|
36
|
+
request_user_input: 'ask_user', // Codex
|
|
37
|
+
AskQuestion: 'ask_user', // Cursor (pre-requisite for cursor driver)
|
|
38
|
+
// Web
|
|
39
|
+
web_fetch: 'web_fetch',
|
|
40
|
+
fetch: 'web_fetch',
|
|
41
|
+
WebFetch: 'web_fetch',
|
|
42
|
+
// Claude Code tool names (PascalCase)
|
|
43
|
+
Skill: 'use_skill',
|
|
44
|
+
Read: 'read_file',
|
|
45
|
+
Write: 'write_file',
|
|
46
|
+
Edit: 'edit_file',
|
|
47
|
+
Bash: 'run_shell',
|
|
48
|
+
Grep: 'search_code',
|
|
49
|
+
Glob: 'list_files',
|
|
50
|
+
Agent: 'unknown',
|
|
51
|
+
NotebookEdit: 'edit_file',
|
|
52
|
+
TodoWrite: 'update_todos',
|
|
53
|
+
// Cursor stream-json tool_call discriminants
|
|
54
|
+
readToolCall: 'read_file',
|
|
55
|
+
editToolCall: 'edit_file',
|
|
56
|
+
globToolCall: 'list_files',
|
|
57
|
+
grepToolCall: 'search_code',
|
|
58
|
+
shellToolCall: 'run_shell',
|
|
59
|
+
webFetchToolCall: 'web_fetch',
|
|
60
|
+
updateTodosToolCall: 'update_todos',
|
|
61
|
+
};
|
|
62
|
+
export function buildSummary(action, toolName, args) {
|
|
63
|
+
if (!args)
|
|
64
|
+
return `${action} via ${toolName}`;
|
|
65
|
+
// ask_user: render strictly from question count + first header. Never reach
|
|
66
|
+
// into `values`/`answer` — the bus redacts isSecret answers, but this is the
|
|
67
|
+
// only seam where a future refactor could accidentally read answer data and
|
|
68
|
+
// bypass redaction. Belt-and-braces.
|
|
69
|
+
if (action === 'ask_user') {
|
|
70
|
+
const questions = args.questions;
|
|
71
|
+
if (Array.isArray(questions)) {
|
|
72
|
+
const count = questions.length;
|
|
73
|
+
const firstHeader = questions.find((q) => !!q && typeof q === 'object' && typeof q.header === 'string')?.header;
|
|
74
|
+
return firstHeader
|
|
75
|
+
? `asked ${count} question${count === 1 ? '' : 's'}: ${firstHeader}`
|
|
76
|
+
: `asked ${count} question${count === 1 ? '' : 's'}`;
|
|
77
|
+
}
|
|
78
|
+
return `${action} via ${toolName}`;
|
|
79
|
+
}
|
|
80
|
+
const cmd = args.cmd ?? args.command;
|
|
81
|
+
if (cmd && typeof cmd === 'string')
|
|
82
|
+
return cmd.slice(0, 100);
|
|
83
|
+
const filePath = args.path ?? args.file ?? args.file_path;
|
|
84
|
+
if (filePath && typeof filePath === 'string')
|
|
85
|
+
return `${action} ${filePath}`;
|
|
86
|
+
const pattern = args.pattern ?? args.query;
|
|
87
|
+
if (pattern && typeof pattern === 'string')
|
|
88
|
+
return `${action} "${pattern}"`;
|
|
89
|
+
return `${action} via ${toolName}`;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extract deduplicated skill names from a session log (LogEntry[]).
|
|
93
|
+
* Filters for tool_event entries, then delegates to extractSkillsFromToolEvents.
|
|
94
|
+
*/
|
|
95
|
+
export function extractSkillsFromLog(log) {
|
|
96
|
+
const toolEvents = log
|
|
97
|
+
.filter((e) => e.type === 'tool_event' && e.tool_event != null)
|
|
98
|
+
.map((e) => e.tool_event);
|
|
99
|
+
return extractSkillsFromToolEvents(toolEvents);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Extract deduplicated skill names from tool events that have action 'use_skill'.
|
|
103
|
+
*/
|
|
104
|
+
export function extractSkillsFromToolEvents(events) {
|
|
105
|
+
const seen = new Set();
|
|
106
|
+
for (const event of events) {
|
|
107
|
+
if (event.action === 'use_skill' && event.skillName) {
|
|
108
|
+
seen.add(event.skillName);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...seen];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Post-processing pass: detect skill usage from tool events.
|
|
115
|
+
* - Skill tool calls: action is already 'use_skill', extract skillName from args.skill
|
|
116
|
+
* - Read on SKILL.md: override action to 'use_skill', extract skillName from path
|
|
117
|
+
*
|
|
118
|
+
* This is agent-agnostic — works for any provider that produces ToolEvent[].
|
|
119
|
+
*/
|
|
120
|
+
export function enrichSkillEvents(events) {
|
|
121
|
+
return events.map((event) => {
|
|
122
|
+
// Skill tool call — already mapped to use_skill by TOOL_NAME_MAP
|
|
123
|
+
if (event.action === 'use_skill' && !event.skillName) {
|
|
124
|
+
const skill = event.arguments?.skill;
|
|
125
|
+
if (typeof skill === 'string') {
|
|
126
|
+
return { ...event, skillName: skill };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Read on SKILL.md — reclassify as use_skill
|
|
130
|
+
if (event.action === 'read_file') {
|
|
131
|
+
const filePath = event.arguments?.file_path ?? event.arguments?.path;
|
|
132
|
+
if (typeof filePath === 'string') {
|
|
133
|
+
const skillName = extractSkillNameFromPath(filePath);
|
|
134
|
+
if (skillName) {
|
|
135
|
+
return { ...event, action: 'use_skill', skillName };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return event;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* If a file path points to a SKILL.md file, return the parent directory name as the skill name.
|
|
144
|
+
* Returns undefined for non-SKILL.md paths.
|
|
145
|
+
*/
|
|
146
|
+
export function extractSkillNameFromPath(filePath) {
|
|
147
|
+
if (!filePath.endsWith('/SKILL.md'))
|
|
148
|
+
return undefined;
|
|
149
|
+
const parts = filePath.split('/').filter(Boolean);
|
|
150
|
+
// Need at least [skillName, 'SKILL.md']
|
|
151
|
+
if (parts.length < 2)
|
|
152
|
+
return undefined;
|
|
153
|
+
return parts[parts.length - 2];
|
|
154
|
+
}
|
|
155
|
+
export function inferCodexExecAction(command) {
|
|
156
|
+
const normalized = command.trim();
|
|
157
|
+
if (/\b(cat|sed|head|tail|less|more)\b/.test(normalized)) {
|
|
158
|
+
return 'read_file';
|
|
159
|
+
}
|
|
160
|
+
if (/\b(rg|grep)\b/.test(normalized)) {
|
|
161
|
+
return 'search_code';
|
|
162
|
+
}
|
|
163
|
+
if (/\b(ls|find)\b/.test(normalized)) {
|
|
164
|
+
return 'list_files';
|
|
165
|
+
}
|
|
166
|
+
return 'run_shell';
|
|
167
|
+
}
|