@cursor/july 0.1.16 → 0.1.17
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 +9 -4
- package/dist/bin/agent-serve.js +60 -2
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +13 -3
- package/dist/channels/slack/auth.d.ts +7 -1
- package/dist/channels/slack/auth.d.ts.map +1 -1
- package/dist/channels/slack/auth.js +14 -0
- package/dist/channels/slack/blocks.d.ts +61 -0
- package/dist/channels/slack/blocks.d.ts.map +1 -0
- package/dist/channels/slack/blocks.js +90 -0
- package/dist/channels/slack/defaults.d.ts +2 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +29 -3
- package/dist/channels/slack/dispatch.d.ts +3 -17
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/interactive.d.ts +39 -3
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +152 -7
- package/dist/channels/slack/manifest.d.ts +3 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/setup.d.ts +4 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +16 -2
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +24 -3
- package/dist/channels/slack/types.d.ts +77 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/connections.d.ts +4 -1
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +4 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.DjHC-Ggf.js → app.BbEXr2KO.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DCBsrfwP.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DwviwmIq.js → VPLocalSearchBox.DBEi4HDp.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BIpeYSon.js → theme.DGfptcHw.js} +2 -2
- package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.js → guides_mcp-oauth.md.Dd8EgSem.js} +3 -2
- package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.lean.js → guides_mcp-oauth.md.Dd8EgSem.lean.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.FWale6Ip.js → guides_slack.md.D_xFKlnF.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.CsSr3rDz.js → reference_connections.md.B9Q3TOve.js} +12 -5
- package/dist/docs/assets/{reference_connections.md.CsSr3rDz.lean.js → reference_connections.md.B9Q3TOve.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +6 -5
- package/dist/docs/guides/slack.html +5 -5
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +14 -7
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/evals/assertions.d.ts +190 -0
- package/dist/evals/assertions.d.ts.map +1 -0
- package/dist/evals/assertions.js +461 -0
- package/dist/evals/expect.d.ts +95 -0
- package/dist/evals/expect.d.ts.map +1 -0
- package/dist/evals/expect.js +194 -0
- package/dist/evals/judge.d.ts +133 -0
- package/dist/evals/judge.d.ts.map +1 -0
- package/dist/evals/judge.js +228 -0
- package/dist/evals/loaders.d.ts +51 -0
- package/dist/evals/loaders.d.ts.map +1 -0
- package/dist/evals/loaders.js +124 -0
- package/dist/evals/matchers.d.ts +95 -0
- package/dist/evals/matchers.d.ts.map +1 -0
- package/dist/evals/matchers.js +145 -0
- package/dist/evals/reporters.d.ts +70 -0
- package/dist/evals/reporters.d.ts.map +1 -0
- package/dist/evals/reporters.js +221 -0
- package/dist/evals/results.d.ts +103 -0
- package/dist/evals/results.d.ts.map +1 -0
- package/dist/evals/results.js +28 -0
- package/dist/evals/run-facts.d.ts +77 -0
- package/dist/evals/run-facts.d.ts.map +1 -0
- package/dist/evals/run-facts.js +187 -0
- package/dist/evals.d.ts +118 -31
- package/dist/evals.d.ts.map +1 -1
- package/dist/evals.js +46 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/cli-ax.d.ts +27 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +90 -12
- package/dist/internal/cli-mcp.d.ts +8 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -1
- package/dist/internal/cli-mcp.js +98 -0
- package/dist/internal/cli-slack.d.ts +29 -1
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +777 -12
- package/dist/internal/deploy-client.d.ts +7 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +9 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +29 -9
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +2 -0
- package/dist/internal/eval-judge-model.d.ts +59 -0
- package/dist/internal/eval-judge-model.d.ts.map +1 -0
- package/dist/internal/eval-judge-model.js +131 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +12 -20
- package/dist/internal/eval-runner.d.ts +31 -25
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +329 -126
- package/dist/internal/evals-client.d.ts.map +1 -1
- package/dist/internal/evals-client.js +4 -1
- package/dist/internal/local-env.d.ts +8 -0
- package/dist/internal/local-env.d.ts.map +1 -0
- package/dist/internal/local-env.js +47 -0
- package/dist/internal/playground/static.d.ts +3 -0
- package/dist/internal/playground/static.d.ts.map +1 -1
- package/dist/internal/playground/static.js +3 -0
- package/dist/internal/run-client.d.ts +15 -0
- package/dist/internal/run-client.d.ts.map +1 -1
- package/dist/internal/run-client.js +3 -2
- package/dist/internal/sdk-runner.d.ts +36 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +65 -25
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +10 -1
- package/dist/internal/session-engine.d.ts +18 -5
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +72 -19
- package/dist/internal/slack-provision-client.d.ts +130 -0
- package/dist/internal/slack-provision-client.d.ts.map +1 -0
- package/dist/internal/slack-provision-client.js +284 -0
- package/dist/playground/assets/cursor-icons-16-f_W_ogc-.woff2 +0 -0
- package/dist/playground/assets/index-CidizGZv.css +1 -0
- package/dist/playground/assets/index-DNqirsrK.js +85 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +70 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +26 -0
- package/docs/guides/mcp-oauth.md +10 -5
- package/docs/guides/slack.md +3 -2
- package/docs/reference/connections.md +28 -18
- package/package.json +20 -2
- package/skills/create-agent/SKILL.md +15 -7
- package/skills/debug/SKILL.md +3 -1
- package/skills/mcp-auth/SKILL.md +1 -1
- package/skills/setup-slack/SKILL.md +173 -11
- package/src/bin/agent-serve.ts +63 -2
- package/src/channels/slack/api.ts +16 -3
- package/src/channels/slack/auth.ts +16 -1
- package/src/channels/slack/blocks.ts +139 -0
- package/src/channels/slack/defaults.ts +32 -4
- package/src/channels/slack/dispatch.ts +3 -18
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/interactive.ts +183 -19
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/setup.ts +16 -2
- package/src/channels/slack/slack-channel.ts +37 -6
- package/src/channels/slack/types.ts +89 -0
- package/src/connections.ts +4 -1
- package/src/evals/assertions.ts +704 -0
- package/src/evals/expect.ts +272 -0
- package/src/evals/judge.ts +346 -0
- package/src/evals/loaders.ts +113 -0
- package/src/evals/matchers.ts +246 -0
- package/src/evals/reporters.ts +274 -0
- package/src/evals/results.ts +132 -0
- package/src/evals/run-facts.ts +274 -0
- package/src/evals.ts +205 -42
- package/src/index.ts +2 -0
- package/src/internal/cli-ax.ts +134 -15
- package/src/internal/cli-mcp.ts +118 -5
- package/src/internal/cli-slack.ts +993 -15
- package/src/internal/deploy-client.ts +10 -0
- package/src/internal/discovery.ts +31 -5
- package/src/internal/distribution.ts +2 -0
- package/src/internal/eval-judge-model.ts +148 -0
- package/src/internal/eval-run-store.ts +10 -14
- package/src/internal/eval-runner.ts +460 -214
- package/src/internal/evals-client.ts +5 -0
- package/src/internal/local-env.ts +53 -0
- package/src/internal/playground/static.ts +3 -0
- package/src/internal/run-client.ts +17 -1
- package/src/internal/sdk-runner.ts +103 -22
- package/src/internal/server.ts +10 -0
- package/src/internal/session-engine.ts +89 -21
- package/src/internal/slack-provision-client.ts +438 -0
- package/src/types.ts +92 -4
- package/dist/channels/slack/post-update-delivery.d.ts +0 -85
- package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DZtu0ZIc.js +0 -1
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/persistence-coordinator.d.ts +0 -127
- package/dist/internal/persistence-coordinator.d.ts.map +0 -1
- package/dist/persistence.d.ts +0 -184
- package/dist/persistence.d.ts.map +0 -1
- package/dist/playground/assets/cursor-icons-16-CQ50JpfO.woff2 +0 -0
- package/dist/playground/assets/index-Bwl4Onx4.js +0 -85
- package/dist/playground/assets/index-ByV4nfch.css +0 -1
- /package/dist/docs/assets/{guides_slack.md.FWale6Ip.lean.js → guides_slack.md.D_xFKlnF.lean.js} +0 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result and reporter types shared by the runner, the CLI, and reporters.
|
|
3
|
+
*
|
|
4
|
+
* Pure types only: reporters that touch the filesystem live in
|
|
5
|
+
* `@cursor/july/evals/reporters`, so the authoring surface stays importable
|
|
6
|
+
* from anywhere.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { EvalAssertionResult, EvalVerdict } from "./assertions.js";
|
|
10
|
+
|
|
11
|
+
/** One finished eval case. */
|
|
12
|
+
export interface EvalRunResult {
|
|
13
|
+
id: string;
|
|
14
|
+
path: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
/**
|
|
17
|
+
* No hard gate failed. Unchanged from before severity existed: a soft
|
|
18
|
+
* threshold miss (`verdict: "scored"`) leaves this `true` and is fatal only
|
|
19
|
+
* under `--strict`.
|
|
20
|
+
*/
|
|
21
|
+
ok: boolean;
|
|
22
|
+
/** Full grade, including `scored` and `skipped`. */
|
|
23
|
+
verdict?: EvalVerdict;
|
|
24
|
+
/** Reason passed to `t.skip(...)`. */
|
|
25
|
+
skipReason?: string;
|
|
26
|
+
assertions: EvalAssertionResult[];
|
|
27
|
+
sessionId?: string;
|
|
28
|
+
/** Messages passed to `t.send` (eval inputs). */
|
|
29
|
+
inputs: string[];
|
|
30
|
+
finalText?: string;
|
|
31
|
+
tools?: string[];
|
|
32
|
+
/** Tool calls with args/results from the trajectory. */
|
|
33
|
+
toolCalls?: Array<{
|
|
34
|
+
toolName: string;
|
|
35
|
+
args?: unknown;
|
|
36
|
+
output?: unknown;
|
|
37
|
+
isError: boolean;
|
|
38
|
+
}>;
|
|
39
|
+
error?: string;
|
|
40
|
+
logs: string[];
|
|
41
|
+
/** Structured scores from `t.metric(...)`. */
|
|
42
|
+
metrics: Record<string, string | number>;
|
|
43
|
+
/** Author-supplied metadata from `defineEval({ metadata })`. */
|
|
44
|
+
metadata?: Record<string, unknown>;
|
|
45
|
+
tags?: string[];
|
|
46
|
+
durationMs: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Aggregate outcome of one `eval` invocation. */
|
|
50
|
+
export interface EvalRunSummary {
|
|
51
|
+
total: number;
|
|
52
|
+
passed: number;
|
|
53
|
+
failed: number;
|
|
54
|
+
/** Cases that only missed a soft threshold. */
|
|
55
|
+
scored: number;
|
|
56
|
+
skipped: number;
|
|
57
|
+
/** Whether soft threshold misses were treated as failures. */
|
|
58
|
+
strict: boolean;
|
|
59
|
+
startedAt: string;
|
|
60
|
+
finishedAt: string;
|
|
61
|
+
durationMs: number;
|
|
62
|
+
results: readonly EvalRunResult[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** One discovered case, as handed to {@link EvalReporter.onRunStart}. */
|
|
66
|
+
export interface EvalReporterEvalInfo {
|
|
67
|
+
id: string;
|
|
68
|
+
fileId: string;
|
|
69
|
+
description?: string;
|
|
70
|
+
tags?: string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Where the run executed. */
|
|
74
|
+
export interface EvalReporterTarget {
|
|
75
|
+
baseUrl: string;
|
|
76
|
+
/** `local` boots an ephemeral server; the others target a running one. */
|
|
77
|
+
mode: "local" | "url" | "prod";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A destination for eval results.
|
|
82
|
+
*
|
|
83
|
+
* The runner grades everything itself; a reporter only ships results out. All
|
|
84
|
+
* three hooks are optional and may return a promise for async work such as an
|
|
85
|
+
* upload or a file write.
|
|
86
|
+
*
|
|
87
|
+
* ```ts
|
|
88
|
+
* // evals/evals.config.ts
|
|
89
|
+
* export default defineEvalConfig({
|
|
90
|
+
* maxConcurrency: 20,
|
|
91
|
+
* reporters: [JUnit({ filePath: ".agent-serve/junit.xml" })],
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export interface EvalReporter {
|
|
96
|
+
/** Fires once before any case runs. */
|
|
97
|
+
onRunStart?(
|
|
98
|
+
evals: readonly EvalReporterEvalInfo[],
|
|
99
|
+
target: EvalReporterTarget
|
|
100
|
+
): void | Promise<void>;
|
|
101
|
+
/** Fires after each observed case, with its assertions and verdict. */
|
|
102
|
+
onEvalComplete?(result: EvalRunResult): void | Promise<void>;
|
|
103
|
+
/** Fires once with the aggregated summary. */
|
|
104
|
+
onRunComplete?(summary: EvalRunSummary): void | Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Tally verdicts for {@link EvalRunSummary}. */
|
|
108
|
+
export function summarizeEvalResults(
|
|
109
|
+
results: readonly EvalRunResult[],
|
|
110
|
+
options: { strict: boolean; startedAt: string; finishedAt: string }
|
|
111
|
+
): EvalRunSummary {
|
|
112
|
+
const verdictOf = (result: EvalRunResult): EvalVerdict =>
|
|
113
|
+
result.verdict ?? (result.ok ? "passed" : "failed");
|
|
114
|
+
const counts = { passed: 0, failed: 0, scored: 0, skipped: 0 };
|
|
115
|
+
for (const result of results) {
|
|
116
|
+
counts[verdictOf(result)]++;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
total: results.length,
|
|
120
|
+
passed: counts.passed,
|
|
121
|
+
failed: counts.failed,
|
|
122
|
+
scored: counts.scored,
|
|
123
|
+
skipped: counts.skipped,
|
|
124
|
+
strict: options.strict,
|
|
125
|
+
startedAt: options.startedAt,
|
|
126
|
+
finishedAt: options.finishedAt,
|
|
127
|
+
durationMs:
|
|
128
|
+
new Date(options.finishedAt).getTime() -
|
|
129
|
+
new Date(options.startedAt).getTime(),
|
|
130
|
+
results,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed facts derived from a captured session event stream.
|
|
3
|
+
*
|
|
4
|
+
* Assertions never walk raw events: they read this normalized view, which
|
|
5
|
+
* tracks each tool call's lifecycle (requested → result / approval decision),
|
|
6
|
+
* subagent delegations, and unanswered human-in-the-loop (HITL) approvals.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { SessionEvent } from "../types.js";
|
|
10
|
+
import type { EvalToolCallStatus } from "./matchers.js";
|
|
11
|
+
|
|
12
|
+
/** One tool call, in request order, with its resolved lifecycle state. */
|
|
13
|
+
export interface EvalToolCallFact {
|
|
14
|
+
callId: string;
|
|
15
|
+
toolName: string;
|
|
16
|
+
/** Arguments from `actions.requested` (absent for result-only calls). */
|
|
17
|
+
input?: unknown;
|
|
18
|
+
/** Payload from `action.result`. */
|
|
19
|
+
output?: unknown;
|
|
20
|
+
status: EvalToolCallStatus;
|
|
21
|
+
/** Approval outcome when the tool required a human decision. */
|
|
22
|
+
approvalDecision?: "approve" | "deny";
|
|
23
|
+
/** Set when the call ran inside a subagent. */
|
|
24
|
+
parentCallId?: string;
|
|
25
|
+
turnId?: string;
|
|
26
|
+
/** 0-based position in request order across the whole run. */
|
|
27
|
+
index: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** One subagent delegation (`task`). */
|
|
31
|
+
export interface EvalSubagentFact {
|
|
32
|
+
callId: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
/** The delegating `task` tool call's result payload, once it settled. */
|
|
36
|
+
output?: unknown;
|
|
37
|
+
status: "completed" | "pending";
|
|
38
|
+
turnId?: string;
|
|
39
|
+
index: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One human approval request raised by a `needsApproval` tool. */
|
|
43
|
+
export interface EvalInputRequestFact {
|
|
44
|
+
callId: string;
|
|
45
|
+
toolName: string;
|
|
46
|
+
args?: unknown;
|
|
47
|
+
resolved: boolean;
|
|
48
|
+
decision?: "approve" | "deny";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface EvalRunFacts {
|
|
52
|
+
/** Every tool call in request order, including nested subagent calls. */
|
|
53
|
+
toolCalls: EvalToolCallFact[];
|
|
54
|
+
subagents: EvalSubagentFact[];
|
|
55
|
+
inputRequests: EvalInputRequestFact[];
|
|
56
|
+
/** Approval requests still awaiting a human decision. */
|
|
57
|
+
pendingInputRequests: EvalInputRequestFact[];
|
|
58
|
+
/** True when the run stopped on an unanswered approval request. */
|
|
59
|
+
parked: boolean;
|
|
60
|
+
turns: number;
|
|
61
|
+
turnsFailed: number;
|
|
62
|
+
/** First failure message from `turn.failed` / `session.failed`. */
|
|
63
|
+
failureMessage?: string;
|
|
64
|
+
/** No failed turn and at least one turn. */
|
|
65
|
+
ok: boolean;
|
|
66
|
+
/** Every finished assistant message, in order (excludes subagent text). */
|
|
67
|
+
assistantMessages: string[];
|
|
68
|
+
/** All assistant text joined with newlines. */
|
|
69
|
+
assistantText: string;
|
|
70
|
+
/** Tool calls that reported `isError`. */
|
|
71
|
+
failedToolCalls: EvalToolCallFact[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Collapse a session event list into {@link EvalRunFacts}.
|
|
76
|
+
*
|
|
77
|
+
* Tool calls are keyed by `callId`, so a result event updates the call that
|
|
78
|
+
* `actions.requested` created rather than appending a duplicate. Result-only
|
|
79
|
+
* calls (no matching request) are still recorded, which keeps assertions honest
|
|
80
|
+
* against transports that drop the request event.
|
|
81
|
+
*/
|
|
82
|
+
export function deriveRunFacts(events: readonly SessionEvent[]): EvalRunFacts {
|
|
83
|
+
const toolCalls: EvalToolCallFact[] = [];
|
|
84
|
+
const byCallId = new Map<string, EvalToolCallFact>();
|
|
85
|
+
const subagents: EvalSubagentFact[] = [];
|
|
86
|
+
const subagentsByCallId = new Map<string, EvalSubagentFact>();
|
|
87
|
+
const inputRequests: EvalInputRequestFact[] = [];
|
|
88
|
+
const inputRequestsByCallId = new Map<string, EvalInputRequestFact>();
|
|
89
|
+
const assistantMessages: string[] = [];
|
|
90
|
+
const turnIds = new Set<string>();
|
|
91
|
+
const failedTurnIds = new Set<string>();
|
|
92
|
+
let failureMessage: string | undefined;
|
|
93
|
+
let sessionFailed = false;
|
|
94
|
+
|
|
95
|
+
const ensureCall = (
|
|
96
|
+
callId: string,
|
|
97
|
+
toolName: string,
|
|
98
|
+
turnId: string | undefined,
|
|
99
|
+
parentCallId: string | undefined
|
|
100
|
+
): EvalToolCallFact => {
|
|
101
|
+
const existing = byCallId.get(callId);
|
|
102
|
+
if (existing !== undefined) {
|
|
103
|
+
return existing;
|
|
104
|
+
}
|
|
105
|
+
const fact: EvalToolCallFact = {
|
|
106
|
+
callId,
|
|
107
|
+
toolName,
|
|
108
|
+
status: "pending",
|
|
109
|
+
parentCallId,
|
|
110
|
+
turnId,
|
|
111
|
+
index: toolCalls.length,
|
|
112
|
+
};
|
|
113
|
+
toolCalls.push(fact);
|
|
114
|
+
byCallId.set(callId, fact);
|
|
115
|
+
return fact;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
for (const event of events) {
|
|
119
|
+
if (event.turnId !== undefined) {
|
|
120
|
+
turnIds.add(event.turnId);
|
|
121
|
+
}
|
|
122
|
+
switch (event.type) {
|
|
123
|
+
case "actions.requested": {
|
|
124
|
+
for (const call of event.data.calls) {
|
|
125
|
+
const fact = ensureCall(
|
|
126
|
+
call.callId,
|
|
127
|
+
call.toolName,
|
|
128
|
+
event.turnId,
|
|
129
|
+
call.parentCallId ?? event.data.parentCallId
|
|
130
|
+
);
|
|
131
|
+
fact.input = call.args;
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "action.result": {
|
|
136
|
+
const fact = ensureCall(
|
|
137
|
+
event.data.callId,
|
|
138
|
+
event.data.toolName,
|
|
139
|
+
event.turnId,
|
|
140
|
+
event.data.parentCallId
|
|
141
|
+
);
|
|
142
|
+
fact.toolName = event.data.toolName;
|
|
143
|
+
fact.output = event.data.output;
|
|
144
|
+
// A denied approval is a distinct outcome from a tool that ran and
|
|
145
|
+
// errored, so do not let the synthetic error result overwrite it.
|
|
146
|
+
if (fact.status !== "rejected") {
|
|
147
|
+
fact.status = event.data.isError ? "failed" : "completed";
|
|
148
|
+
}
|
|
149
|
+
const subagent = subagentsByCallId.get(event.data.callId);
|
|
150
|
+
if (subagent !== undefined) {
|
|
151
|
+
subagent.output = event.data.output;
|
|
152
|
+
subagent.status = "completed";
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case "action.approval_requested": {
|
|
157
|
+
const request: EvalInputRequestFact = {
|
|
158
|
+
callId: event.data.callId,
|
|
159
|
+
toolName: event.data.toolName,
|
|
160
|
+
args: event.data.args,
|
|
161
|
+
resolved: false,
|
|
162
|
+
};
|
|
163
|
+
inputRequests.push(request);
|
|
164
|
+
inputRequestsByCallId.set(event.data.callId, request);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case "action.approval_resolved": {
|
|
168
|
+
const request = inputRequestsByCallId.get(event.data.callId);
|
|
169
|
+
if (request !== undefined) {
|
|
170
|
+
request.resolved = true;
|
|
171
|
+
request.decision = event.data.decision;
|
|
172
|
+
}
|
|
173
|
+
const fact = ensureCall(
|
|
174
|
+
event.data.callId,
|
|
175
|
+
event.data.toolName,
|
|
176
|
+
event.turnId,
|
|
177
|
+
event.data.parentCallId
|
|
178
|
+
);
|
|
179
|
+
fact.approvalDecision = event.data.decision;
|
|
180
|
+
if (event.data.decision === "deny") {
|
|
181
|
+
fact.status = "rejected";
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case "subagent.called": {
|
|
186
|
+
const existing = subagentsByCallId.get(event.data.callId);
|
|
187
|
+
if (existing === undefined) {
|
|
188
|
+
const fact: EvalSubagentFact = {
|
|
189
|
+
callId: event.data.callId,
|
|
190
|
+
name: event.data.name,
|
|
191
|
+
description: event.data.description,
|
|
192
|
+
status: "pending",
|
|
193
|
+
turnId: event.turnId,
|
|
194
|
+
index: subagents.length,
|
|
195
|
+
};
|
|
196
|
+
subagents.push(fact);
|
|
197
|
+
subagentsByCallId.set(event.data.callId, fact);
|
|
198
|
+
} else {
|
|
199
|
+
existing.name = event.data.name ?? existing.name;
|
|
200
|
+
existing.description = event.data.description ?? existing.description;
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case "subagent.completed": {
|
|
205
|
+
const fact = subagentsByCallId.get(event.data.callId);
|
|
206
|
+
if (fact === undefined) {
|
|
207
|
+
const created: EvalSubagentFact = {
|
|
208
|
+
callId: event.data.callId,
|
|
209
|
+
name: event.data.name,
|
|
210
|
+
status: "completed",
|
|
211
|
+
turnId: event.turnId,
|
|
212
|
+
index: subagents.length,
|
|
213
|
+
};
|
|
214
|
+
subagents.push(created);
|
|
215
|
+
subagentsByCallId.set(event.data.callId, created);
|
|
216
|
+
} else {
|
|
217
|
+
fact.status = "completed";
|
|
218
|
+
fact.name = fact.name ?? event.data.name;
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
case "message.completed": {
|
|
223
|
+
// Nested subagent chatter is not part of the agent's reply.
|
|
224
|
+
if (event.data.parentCallId === undefined && event.data.text !== "") {
|
|
225
|
+
assistantMessages.push(event.data.text);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case "turn.completed": {
|
|
230
|
+
if (
|
|
231
|
+
event.data.result !== undefined &&
|
|
232
|
+
event.data.result !== "" &&
|
|
233
|
+
!assistantMessages.includes(event.data.result)
|
|
234
|
+
) {
|
|
235
|
+
assistantMessages.push(event.data.result);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
case "turn.failed":
|
|
240
|
+
case "session.failed": {
|
|
241
|
+
if (event.turnId !== undefined) {
|
|
242
|
+
failedTurnIds.add(event.turnId);
|
|
243
|
+
} else {
|
|
244
|
+
// A session-scoped failure carries no turn id, so it would otherwise
|
|
245
|
+
// leave `turnsFailed` at 0 and read as a healthy run.
|
|
246
|
+
sessionFailed = true;
|
|
247
|
+
}
|
|
248
|
+
failureMessage = failureMessage ?? event.data.message;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
default:
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const pendingInputRequests = inputRequests.filter((r) => !r.resolved);
|
|
257
|
+
// `turn.*` events all carry a turnId; session-scoped events do not, so the
|
|
258
|
+
// turn count is the number of distinct turn ids seen.
|
|
259
|
+
const turns = turnIds.size;
|
|
260
|
+
return {
|
|
261
|
+
toolCalls,
|
|
262
|
+
subagents,
|
|
263
|
+
inputRequests,
|
|
264
|
+
pendingInputRequests,
|
|
265
|
+
parked: pendingInputRequests.length > 0,
|
|
266
|
+
turns,
|
|
267
|
+
turnsFailed: failedTurnIds.size,
|
|
268
|
+
failureMessage,
|
|
269
|
+
ok: failedTurnIds.size === 0 && !sessionFailed && turns > 0,
|
|
270
|
+
assistantMessages,
|
|
271
|
+
assistantText: assistantMessages.join("\n"),
|
|
272
|
+
failedToolCalls: toolCalls.filter((c) => c.status === "failed"),
|
|
273
|
+
};
|
|
274
|
+
}
|