@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,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in eval reporters.
|
|
3
|
+
*
|
|
4
|
+
* The CLI already prints a console summary; reporters add destinations on top.
|
|
5
|
+
* Attach them in `evals/evals.config.ts` to observe every case in the run, or
|
|
6
|
+
* on a single `defineEval({ reporters })` to scope a destination to that case.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // evals/evals.config.ts
|
|
10
|
+
* import { defineEvalConfig } from "@cursor/july/evals";
|
|
11
|
+
* import { JUnit } from "@cursor/july/evals/reporters";
|
|
12
|
+
*
|
|
13
|
+
* export default defineEvalConfig({
|
|
14
|
+
* maxConcurrency: 20,
|
|
15
|
+
* reporters: [JUnit({ filePath: ".agent-serve/junit.xml" })],
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* `agentkit eval --junit <path>` and `--artifacts <dir>` attach these same two
|
|
20
|
+
* reporters without touching the eval files, which is usually the better fit:
|
|
21
|
+
* CI owns the output paths, not the eval author.
|
|
22
|
+
*/
|
|
23
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
24
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
25
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
26
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
27
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
28
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
29
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
33
|
+
var t = {};
|
|
34
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
35
|
+
t[p] = s[p];
|
|
36
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
37
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
38
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
39
|
+
t[p[i]] = s[p[i]];
|
|
40
|
+
}
|
|
41
|
+
return t;
|
|
42
|
+
};
|
|
43
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
44
|
+
import { dirname, join, resolve } from "node:path";
|
|
45
|
+
/**
|
|
46
|
+
* Write JUnit XML so CI shows per-case annotations.
|
|
47
|
+
*
|
|
48
|
+
* Each case becomes one `<testcase>` named by its path-derived id. Failed gates
|
|
49
|
+
* and execution errors become `<failure>`, `t.skip(reason)` becomes `<skipped>`,
|
|
50
|
+
* and a soft threshold miss becomes a `<failure>` only under `--strict` (it is
|
|
51
|
+
* reported as a `<system-out>` score line otherwise).
|
|
52
|
+
*/
|
|
53
|
+
export function JUnit(options) {
|
|
54
|
+
return {
|
|
55
|
+
onRunComplete(summary) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
var _a;
|
|
58
|
+
const xml = renderJUnitXml(summary, (_a = options.suiteName) !== null && _a !== void 0 ? _a : "agentkit-evals");
|
|
59
|
+
const target = resolve(options.filePath);
|
|
60
|
+
yield mkdir(dirname(target), { recursive: true });
|
|
61
|
+
yield writeFile(target, xml, "utf8");
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Render a {@link EvalRunSummary} as JUnit XML. Exported for tests. */
|
|
67
|
+
export function renderJUnitXml(summary, suiteName) {
|
|
68
|
+
var _a, _b, _c, _d;
|
|
69
|
+
const lines = [
|
|
70
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
71
|
+
`<testsuites name="${escapeXml(suiteName)}" tests="${summary.total}" failures="${summary.failed + (summary.strict ? summary.scored : 0)}" skipped="${summary.skipped}" time="${(summary.durationMs / 1000).toFixed(3)}">`,
|
|
72
|
+
` <testsuite name="${escapeXml(suiteName)}" tests="${summary.total}" failures="${summary.failed + (summary.strict ? summary.scored : 0)}" skipped="${summary.skipped}" time="${(summary.durationMs / 1000).toFixed(3)}">`,
|
|
73
|
+
];
|
|
74
|
+
for (const result of summary.results) {
|
|
75
|
+
const verdict = (_a = result.verdict) !== null && _a !== void 0 ? _a : (result.ok ? "passed" : "failed");
|
|
76
|
+
const time = (result.durationMs / 1000).toFixed(3);
|
|
77
|
+
lines.push(` <testcase name="${escapeXml(result.id)}" classname="${escapeXml(suiteName)}" time="${time}">`);
|
|
78
|
+
if (verdict === "skipped") {
|
|
79
|
+
lines.push(` <skipped message="${escapeXml((_b = result.skipReason) !== null && _b !== void 0 ? _b : "skipped")}"/>`);
|
|
80
|
+
}
|
|
81
|
+
else if (verdict === "failed" ||
|
|
82
|
+
(verdict === "scored" && summary.strict)) {
|
|
83
|
+
const failures = result.assertions.filter((a) => !a.passed);
|
|
84
|
+
const message = (_d = (_c = result.error) !== null && _c !== void 0 ? _c : failures
|
|
85
|
+
.map((a) => `${a.name}${a.detail === undefined ? "" : ` (${a.detail})`}`)
|
|
86
|
+
.join("; ")) !== null && _d !== void 0 ? _d : verdict;
|
|
87
|
+
lines.push(` <failure message="${escapeXml(message === "" ? verdict : message)}" type="${escapeXml(verdict)}"/>`);
|
|
88
|
+
}
|
|
89
|
+
const systemOut = renderSystemOut(result);
|
|
90
|
+
if (systemOut !== "") {
|
|
91
|
+
lines.push(` <system-out>${escapeXml(systemOut)}</system-out>`);
|
|
92
|
+
}
|
|
93
|
+
lines.push(" </testcase>");
|
|
94
|
+
}
|
|
95
|
+
lines.push(" </testsuite>", "</testsuites>", "");
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
function renderSystemOut(result) {
|
|
99
|
+
const parts = [];
|
|
100
|
+
for (const assertion of result.assertions) {
|
|
101
|
+
if (assertion.score === undefined) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const bar = assertion.threshold === undefined ? "" : ` (>= ${assertion.threshold})`;
|
|
105
|
+
parts.push(`${assertion.name}=${assertion.score.toFixed(3)}${bar}`);
|
|
106
|
+
}
|
|
107
|
+
for (const [name, value] of Object.entries(result.metrics)) {
|
|
108
|
+
parts.push(`${name}=${String(value)}`);
|
|
109
|
+
}
|
|
110
|
+
parts.push(...result.logs);
|
|
111
|
+
return parts.join("\n");
|
|
112
|
+
}
|
|
113
|
+
function escapeXml(text) {
|
|
114
|
+
return stripIllegalXmlChars(text)
|
|
115
|
+
.replace(/&/g, "&")
|
|
116
|
+
.replace(/</g, "<")
|
|
117
|
+
.replace(/>/g, ">")
|
|
118
|
+
.replace(/"/g, """)
|
|
119
|
+
.replace(/'/g, "'");
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Drop characters XML 1.0 forbids outright.
|
|
123
|
+
*
|
|
124
|
+
* Tool output routinely carries stray control bytes, and they are illegal in
|
|
125
|
+
* XML even when escaped — a CI provider would reject the whole report. Tab,
|
|
126
|
+
* newline, and carriage return are the only legal ones below 0x20.
|
|
127
|
+
*/
|
|
128
|
+
function stripIllegalXmlChars(text) {
|
|
129
|
+
let out = "";
|
|
130
|
+
for (const char of text) {
|
|
131
|
+
const code = char.codePointAt(0);
|
|
132
|
+
if (code >= 0x20 || code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
133
|
+
out += char;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Persist the full story of a run to disk.
|
|
140
|
+
*
|
|
141
|
+
* Console output stays tight on purpose; when a case fails, the artifact has
|
|
142
|
+
* everything — every assertion with its score, the captured event stream, the
|
|
143
|
+
* tool calls, and `t.log` lines.
|
|
144
|
+
*
|
|
145
|
+
* ```text
|
|
146
|
+
* <dir>/summary.json run totals + config
|
|
147
|
+
* <dir>/results.jsonl one line per case, in completion order
|
|
148
|
+
* <dir>/evals/<case-id>.json per-case detail
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
export function Artifacts(options) {
|
|
152
|
+
const root = resolve(options.dir);
|
|
153
|
+
const jsonlPath = join(root, "results.jsonl");
|
|
154
|
+
const lines = [];
|
|
155
|
+
let target;
|
|
156
|
+
let discovered = [];
|
|
157
|
+
return {
|
|
158
|
+
onRunStart(evals, runTarget) {
|
|
159
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
discovered = evals;
|
|
161
|
+
target = runTarget;
|
|
162
|
+
yield mkdir(join(root, "evals"), { recursive: true });
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
onEvalComplete(result) {
|
|
166
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
167
|
+
var _a;
|
|
168
|
+
lines.push(JSON.stringify({
|
|
169
|
+
id: result.id,
|
|
170
|
+
verdict: (_a = result.verdict) !== null && _a !== void 0 ? _a : (result.ok ? "passed" : "failed"),
|
|
171
|
+
ok: result.ok,
|
|
172
|
+
durationMs: result.durationMs,
|
|
173
|
+
}));
|
|
174
|
+
const file = join(root, "evals", `${sanitizeCaseId(result.id)}.json`);
|
|
175
|
+
yield mkdir(dirname(file), { recursive: true });
|
|
176
|
+
yield writeFile(file, `${JSON.stringify(result, null, 2)}\n`, "utf8");
|
|
177
|
+
yield writeFile(jsonlPath, `${lines.join("\n")}\n`, "utf8");
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
onRunComplete(summary) {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
yield mkdir(root, { recursive: true });
|
|
183
|
+
const { results } = summary, totals = __rest(summary, ["results"]);
|
|
184
|
+
yield writeFile(join(root, "summary.json"), `${JSON.stringify(Object.assign(Object.assign({}, totals), { caseCount: results.length, target, discovered: discovered.map((e) => e.id) }), null, 2)}\n`, "utf8");
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Turn a case id into a safe relative path.
|
|
191
|
+
*
|
|
192
|
+
* Ids are already path-shaped (`weather/nyc`), and `defineEval` rejects `..` in
|
|
193
|
+
* case ids, but the artifact writer sanitizes anyway so a hand-built result can
|
|
194
|
+
* never escape the run directory.
|
|
195
|
+
*/
|
|
196
|
+
export function sanitizeCaseId(id) {
|
|
197
|
+
return id
|
|
198
|
+
.split("/")
|
|
199
|
+
.map((segment) => segment.replace(/[^A-Za-z0-9._-]/g, "_"))
|
|
200
|
+
.filter((segment) => segment !== "" && segment !== "." && segment !== "..")
|
|
201
|
+
.join("/");
|
|
202
|
+
}
|
|
203
|
+
/** Fan one lifecycle call out to several reporters, isolating failures. */
|
|
204
|
+
export function combineReporters(reporters, onError) {
|
|
205
|
+
const forEach = (run) => __awaiter(this, void 0, void 0, function* () {
|
|
206
|
+
for (const reporter of reporters) {
|
|
207
|
+
try {
|
|
208
|
+
yield run(reporter);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
// A broken destination must not fail the eval run itself.
|
|
212
|
+
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
onRunStart: (evals, target) => forEach((r) => { var _a; return (_a = r.onRunStart) === null || _a === void 0 ? void 0 : _a.call(r, evals, target); }),
|
|
218
|
+
onEvalComplete: (result) => forEach((r) => { var _a; return (_a = r.onEvalComplete) === null || _a === void 0 ? void 0 : _a.call(r, result); }),
|
|
219
|
+
onRunComplete: (summary) => forEach((r) => { var _a; return (_a = r.onRunComplete) === null || _a === void 0 ? void 0 : _a.call(r, summary); }),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
import type { EvalAssertionResult, EvalVerdict } from "./assertions.js";
|
|
9
|
+
/** One finished eval case. */
|
|
10
|
+
export interface EvalRunResult {
|
|
11
|
+
id: string;
|
|
12
|
+
path: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
/**
|
|
15
|
+
* No hard gate failed. Unchanged from before severity existed: a soft
|
|
16
|
+
* threshold miss (`verdict: "scored"`) leaves this `true` and is fatal only
|
|
17
|
+
* under `--strict`.
|
|
18
|
+
*/
|
|
19
|
+
ok: boolean;
|
|
20
|
+
/** Full grade, including `scored` and `skipped`. */
|
|
21
|
+
verdict?: EvalVerdict;
|
|
22
|
+
/** Reason passed to `t.skip(...)`. */
|
|
23
|
+
skipReason?: string;
|
|
24
|
+
assertions: EvalAssertionResult[];
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
/** Messages passed to `t.send` (eval inputs). */
|
|
27
|
+
inputs: string[];
|
|
28
|
+
finalText?: string;
|
|
29
|
+
tools?: string[];
|
|
30
|
+
/** Tool calls with args/results from the trajectory. */
|
|
31
|
+
toolCalls?: Array<{
|
|
32
|
+
toolName: string;
|
|
33
|
+
args?: unknown;
|
|
34
|
+
output?: unknown;
|
|
35
|
+
isError: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
error?: string;
|
|
38
|
+
logs: string[];
|
|
39
|
+
/** Structured scores from `t.metric(...)`. */
|
|
40
|
+
metrics: Record<string, string | number>;
|
|
41
|
+
/** Author-supplied metadata from `defineEval({ metadata })`. */
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
tags?: string[];
|
|
44
|
+
durationMs: number;
|
|
45
|
+
}
|
|
46
|
+
/** Aggregate outcome of one `eval` invocation. */
|
|
47
|
+
export interface EvalRunSummary {
|
|
48
|
+
total: number;
|
|
49
|
+
passed: number;
|
|
50
|
+
failed: number;
|
|
51
|
+
/** Cases that only missed a soft threshold. */
|
|
52
|
+
scored: number;
|
|
53
|
+
skipped: number;
|
|
54
|
+
/** Whether soft threshold misses were treated as failures. */
|
|
55
|
+
strict: boolean;
|
|
56
|
+
startedAt: string;
|
|
57
|
+
finishedAt: string;
|
|
58
|
+
durationMs: number;
|
|
59
|
+
results: readonly EvalRunResult[];
|
|
60
|
+
}
|
|
61
|
+
/** One discovered case, as handed to {@link EvalReporter.onRunStart}. */
|
|
62
|
+
export interface EvalReporterEvalInfo {
|
|
63
|
+
id: string;
|
|
64
|
+
fileId: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
tags?: string[];
|
|
67
|
+
}
|
|
68
|
+
/** Where the run executed. */
|
|
69
|
+
export interface EvalReporterTarget {
|
|
70
|
+
baseUrl: string;
|
|
71
|
+
/** `local` boots an ephemeral server; the others target a running one. */
|
|
72
|
+
mode: "local" | "url" | "prod";
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A destination for eval results.
|
|
76
|
+
*
|
|
77
|
+
* The runner grades everything itself; a reporter only ships results out. All
|
|
78
|
+
* three hooks are optional and may return a promise for async work such as an
|
|
79
|
+
* upload or a file write.
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* // evals/evals.config.ts
|
|
83
|
+
* export default defineEvalConfig({
|
|
84
|
+
* maxConcurrency: 20,
|
|
85
|
+
* reporters: [JUnit({ filePath: ".agent-serve/junit.xml" })],
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export interface EvalReporter {
|
|
90
|
+
/** Fires once before any case runs. */
|
|
91
|
+
onRunStart?(evals: readonly EvalReporterEvalInfo[], target: EvalReporterTarget): void | Promise<void>;
|
|
92
|
+
/** Fires after each observed case, with its assertions and verdict. */
|
|
93
|
+
onEvalComplete?(result: EvalRunResult): void | Promise<void>;
|
|
94
|
+
/** Fires once with the aggregated summary. */
|
|
95
|
+
onRunComplete?(summary: EvalRunSummary): void | Promise<void>;
|
|
96
|
+
}
|
|
97
|
+
/** Tally verdicts for {@link EvalRunSummary}. */
|
|
98
|
+
export declare function summarizeEvalResults(results: readonly EvalRunResult[], options: {
|
|
99
|
+
strict: boolean;
|
|
100
|
+
startedAt: string;
|
|
101
|
+
finishedAt: string;
|
|
102
|
+
}): EvalRunSummary;
|
|
103
|
+
//# sourceMappingURL=results.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/evals/results.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
/** Tally verdicts for {@link EvalRunSummary}. */
|
|
9
|
+
export function summarizeEvalResults(results, options) {
|
|
10
|
+
const verdictOf = (result) => { var _a; return (_a = result.verdict) !== null && _a !== void 0 ? _a : (result.ok ? "passed" : "failed"); };
|
|
11
|
+
const counts = { passed: 0, failed: 0, scored: 0, skipped: 0 };
|
|
12
|
+
for (const result of results) {
|
|
13
|
+
counts[verdictOf(result)]++;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
total: results.length,
|
|
17
|
+
passed: counts.passed,
|
|
18
|
+
failed: counts.failed,
|
|
19
|
+
scored: counts.scored,
|
|
20
|
+
skipped: counts.skipped,
|
|
21
|
+
strict: options.strict,
|
|
22
|
+
startedAt: options.startedAt,
|
|
23
|
+
finishedAt: options.finishedAt,
|
|
24
|
+
durationMs: new Date(options.finishedAt).getTime() -
|
|
25
|
+
new Date(options.startedAt).getTime(),
|
|
26
|
+
results,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
import type { SessionEvent } from "../types.js";
|
|
9
|
+
import type { EvalToolCallStatus } from "./matchers.js";
|
|
10
|
+
/** One tool call, in request order, with its resolved lifecycle state. */
|
|
11
|
+
export interface EvalToolCallFact {
|
|
12
|
+
callId: string;
|
|
13
|
+
toolName: string;
|
|
14
|
+
/** Arguments from `actions.requested` (absent for result-only calls). */
|
|
15
|
+
input?: unknown;
|
|
16
|
+
/** Payload from `action.result`. */
|
|
17
|
+
output?: unknown;
|
|
18
|
+
status: EvalToolCallStatus;
|
|
19
|
+
/** Approval outcome when the tool required a human decision. */
|
|
20
|
+
approvalDecision?: "approve" | "deny";
|
|
21
|
+
/** Set when the call ran inside a subagent. */
|
|
22
|
+
parentCallId?: string;
|
|
23
|
+
turnId?: string;
|
|
24
|
+
/** 0-based position in request order across the whole run. */
|
|
25
|
+
index: number;
|
|
26
|
+
}
|
|
27
|
+
/** One subagent delegation (`task`). */
|
|
28
|
+
export interface EvalSubagentFact {
|
|
29
|
+
callId: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
/** The delegating `task` tool call's result payload, once it settled. */
|
|
33
|
+
output?: unknown;
|
|
34
|
+
status: "completed" | "pending";
|
|
35
|
+
turnId?: string;
|
|
36
|
+
index: number;
|
|
37
|
+
}
|
|
38
|
+
/** One human approval request raised by a `needsApproval` tool. */
|
|
39
|
+
export interface EvalInputRequestFact {
|
|
40
|
+
callId: string;
|
|
41
|
+
toolName: string;
|
|
42
|
+
args?: unknown;
|
|
43
|
+
resolved: boolean;
|
|
44
|
+
decision?: "approve" | "deny";
|
|
45
|
+
}
|
|
46
|
+
export interface EvalRunFacts {
|
|
47
|
+
/** Every tool call in request order, including nested subagent calls. */
|
|
48
|
+
toolCalls: EvalToolCallFact[];
|
|
49
|
+
subagents: EvalSubagentFact[];
|
|
50
|
+
inputRequests: EvalInputRequestFact[];
|
|
51
|
+
/** Approval requests still awaiting a human decision. */
|
|
52
|
+
pendingInputRequests: EvalInputRequestFact[];
|
|
53
|
+
/** True when the run stopped on an unanswered approval request. */
|
|
54
|
+
parked: boolean;
|
|
55
|
+
turns: number;
|
|
56
|
+
turnsFailed: number;
|
|
57
|
+
/** First failure message from `turn.failed` / `session.failed`. */
|
|
58
|
+
failureMessage?: string;
|
|
59
|
+
/** No failed turn and at least one turn. */
|
|
60
|
+
ok: boolean;
|
|
61
|
+
/** Every finished assistant message, in order (excludes subagent text). */
|
|
62
|
+
assistantMessages: string[];
|
|
63
|
+
/** All assistant text joined with newlines. */
|
|
64
|
+
assistantText: string;
|
|
65
|
+
/** Tool calls that reported `isError`. */
|
|
66
|
+
failedToolCalls: EvalToolCallFact[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Collapse a session event list into {@link EvalRunFacts}.
|
|
70
|
+
*
|
|
71
|
+
* Tool calls are keyed by `callId`, so a result event updates the call that
|
|
72
|
+
* `actions.requested` created rather than appending a duplicate. Result-only
|
|
73
|
+
* calls (no matching request) are still recorded, which keeps assertions honest
|
|
74
|
+
* against transports that drop the request event.
|
|
75
|
+
*/
|
|
76
|
+
export declare function deriveRunFacts(events: readonly SessionEvent[]): EvalRunFacts;
|
|
77
|
+
//# sourceMappingURL=run-facts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-facts.d.ts","sourceRoot":"","sources":["../../src/evals/run-facts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oCAAoC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACtC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,yDAAyD;IACzD,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;IAC7C,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,EAAE,EAAE,OAAO,CAAC;IACZ,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,gBAAgB,EAAE,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,CAgM5E"}
|
|
@@ -0,0 +1,187 @@
|
|
|
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
|
+
* Collapse a session event list into {@link EvalRunFacts}.
|
|
10
|
+
*
|
|
11
|
+
* Tool calls are keyed by `callId`, so a result event updates the call that
|
|
12
|
+
* `actions.requested` created rather than appending a duplicate. Result-only
|
|
13
|
+
* calls (no matching request) are still recorded, which keeps assertions honest
|
|
14
|
+
* against transports that drop the request event.
|
|
15
|
+
*/
|
|
16
|
+
export function deriveRunFacts(events) {
|
|
17
|
+
var _a, _b, _c, _d;
|
|
18
|
+
const toolCalls = [];
|
|
19
|
+
const byCallId = new Map();
|
|
20
|
+
const subagents = [];
|
|
21
|
+
const subagentsByCallId = new Map();
|
|
22
|
+
const inputRequests = [];
|
|
23
|
+
const inputRequestsByCallId = new Map();
|
|
24
|
+
const assistantMessages = [];
|
|
25
|
+
const turnIds = new Set();
|
|
26
|
+
const failedTurnIds = new Set();
|
|
27
|
+
let failureMessage;
|
|
28
|
+
let sessionFailed = false;
|
|
29
|
+
const ensureCall = (callId, toolName, turnId, parentCallId) => {
|
|
30
|
+
const existing = byCallId.get(callId);
|
|
31
|
+
if (existing !== undefined) {
|
|
32
|
+
return existing;
|
|
33
|
+
}
|
|
34
|
+
const fact = {
|
|
35
|
+
callId,
|
|
36
|
+
toolName,
|
|
37
|
+
status: "pending",
|
|
38
|
+
parentCallId,
|
|
39
|
+
turnId,
|
|
40
|
+
index: toolCalls.length,
|
|
41
|
+
};
|
|
42
|
+
toolCalls.push(fact);
|
|
43
|
+
byCallId.set(callId, fact);
|
|
44
|
+
return fact;
|
|
45
|
+
};
|
|
46
|
+
for (const event of events) {
|
|
47
|
+
if (event.turnId !== undefined) {
|
|
48
|
+
turnIds.add(event.turnId);
|
|
49
|
+
}
|
|
50
|
+
switch (event.type) {
|
|
51
|
+
case "actions.requested": {
|
|
52
|
+
for (const call of event.data.calls) {
|
|
53
|
+
const fact = ensureCall(call.callId, call.toolName, event.turnId, (_a = call.parentCallId) !== null && _a !== void 0 ? _a : event.data.parentCallId);
|
|
54
|
+
fact.input = call.args;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "action.result": {
|
|
59
|
+
const fact = ensureCall(event.data.callId, event.data.toolName, event.turnId, event.data.parentCallId);
|
|
60
|
+
fact.toolName = event.data.toolName;
|
|
61
|
+
fact.output = event.data.output;
|
|
62
|
+
// A denied approval is a distinct outcome from a tool that ran and
|
|
63
|
+
// errored, so do not let the synthetic error result overwrite it.
|
|
64
|
+
if (fact.status !== "rejected") {
|
|
65
|
+
fact.status = event.data.isError ? "failed" : "completed";
|
|
66
|
+
}
|
|
67
|
+
const subagent = subagentsByCallId.get(event.data.callId);
|
|
68
|
+
if (subagent !== undefined) {
|
|
69
|
+
subagent.output = event.data.output;
|
|
70
|
+
subagent.status = "completed";
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case "action.approval_requested": {
|
|
75
|
+
const request = {
|
|
76
|
+
callId: event.data.callId,
|
|
77
|
+
toolName: event.data.toolName,
|
|
78
|
+
args: event.data.args,
|
|
79
|
+
resolved: false,
|
|
80
|
+
};
|
|
81
|
+
inputRequests.push(request);
|
|
82
|
+
inputRequestsByCallId.set(event.data.callId, request);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case "action.approval_resolved": {
|
|
86
|
+
const request = inputRequestsByCallId.get(event.data.callId);
|
|
87
|
+
if (request !== undefined) {
|
|
88
|
+
request.resolved = true;
|
|
89
|
+
request.decision = event.data.decision;
|
|
90
|
+
}
|
|
91
|
+
const fact = ensureCall(event.data.callId, event.data.toolName, event.turnId, event.data.parentCallId);
|
|
92
|
+
fact.approvalDecision = event.data.decision;
|
|
93
|
+
if (event.data.decision === "deny") {
|
|
94
|
+
fact.status = "rejected";
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case "subagent.called": {
|
|
99
|
+
const existing = subagentsByCallId.get(event.data.callId);
|
|
100
|
+
if (existing === undefined) {
|
|
101
|
+
const fact = {
|
|
102
|
+
callId: event.data.callId,
|
|
103
|
+
name: event.data.name,
|
|
104
|
+
description: event.data.description,
|
|
105
|
+
status: "pending",
|
|
106
|
+
turnId: event.turnId,
|
|
107
|
+
index: subagents.length,
|
|
108
|
+
};
|
|
109
|
+
subagents.push(fact);
|
|
110
|
+
subagentsByCallId.set(event.data.callId, fact);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
existing.name = (_b = event.data.name) !== null && _b !== void 0 ? _b : existing.name;
|
|
114
|
+
existing.description = (_c = event.data.description) !== null && _c !== void 0 ? _c : existing.description;
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case "subagent.completed": {
|
|
119
|
+
const fact = subagentsByCallId.get(event.data.callId);
|
|
120
|
+
if (fact === undefined) {
|
|
121
|
+
const created = {
|
|
122
|
+
callId: event.data.callId,
|
|
123
|
+
name: event.data.name,
|
|
124
|
+
status: "completed",
|
|
125
|
+
turnId: event.turnId,
|
|
126
|
+
index: subagents.length,
|
|
127
|
+
};
|
|
128
|
+
subagents.push(created);
|
|
129
|
+
subagentsByCallId.set(event.data.callId, created);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
fact.status = "completed";
|
|
133
|
+
fact.name = (_d = fact.name) !== null && _d !== void 0 ? _d : event.data.name;
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case "message.completed": {
|
|
138
|
+
// Nested subagent chatter is not part of the agent's reply.
|
|
139
|
+
if (event.data.parentCallId === undefined && event.data.text !== "") {
|
|
140
|
+
assistantMessages.push(event.data.text);
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case "turn.completed": {
|
|
145
|
+
if (event.data.result !== undefined &&
|
|
146
|
+
event.data.result !== "" &&
|
|
147
|
+
!assistantMessages.includes(event.data.result)) {
|
|
148
|
+
assistantMessages.push(event.data.result);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case "turn.failed":
|
|
153
|
+
case "session.failed": {
|
|
154
|
+
if (event.turnId !== undefined) {
|
|
155
|
+
failedTurnIds.add(event.turnId);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// A session-scoped failure carries no turn id, so it would otherwise
|
|
159
|
+
// leave `turnsFailed` at 0 and read as a healthy run.
|
|
160
|
+
sessionFailed = true;
|
|
161
|
+
}
|
|
162
|
+
failureMessage = failureMessage !== null && failureMessage !== void 0 ? failureMessage : event.data.message;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
default:
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const pendingInputRequests = inputRequests.filter((r) => !r.resolved);
|
|
170
|
+
// `turn.*` events all carry a turnId; session-scoped events do not, so the
|
|
171
|
+
// turn count is the number of distinct turn ids seen.
|
|
172
|
+
const turns = turnIds.size;
|
|
173
|
+
return {
|
|
174
|
+
toolCalls,
|
|
175
|
+
subagents,
|
|
176
|
+
inputRequests,
|
|
177
|
+
pendingInputRequests,
|
|
178
|
+
parked: pendingInputRequests.length > 0,
|
|
179
|
+
turns,
|
|
180
|
+
turnsFailed: failedTurnIds.size,
|
|
181
|
+
failureMessage,
|
|
182
|
+
ok: failedTurnIds.size === 0 && !sessionFailed && turns > 0,
|
|
183
|
+
assistantMessages,
|
|
184
|
+
assistantText: assistantMessages.join("\n"),
|
|
185
|
+
failedToolCalls: toolCalls.filter((c) => c.status === "failed"),
|
|
186
|
+
};
|
|
187
|
+
}
|