@rudderhq/run-intelligence-core 0.1.0-canary.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.
Files changed (78) hide show
  1. package/LICENSE +186 -0
  2. package/dist/cli/analyze.d.ts +2 -0
  3. package/dist/cli/analyze.d.ts.map +1 -0
  4. package/dist/cli/analyze.js +35 -0
  5. package/dist/cli/analyze.js.map +1 -0
  6. package/dist/cli/common.d.ts +9 -0
  7. package/dist/cli/common.d.ts.map +1 -0
  8. package/dist/cli/common.js +28 -0
  9. package/dist/cli/common.js.map +1 -0
  10. package/dist/cli/compare.d.ts +2 -0
  11. package/dist/cli/compare.d.ts.map +1 -0
  12. package/dist/cli/compare.js +24 -0
  13. package/dist/cli/compare.js.map +1 -0
  14. package/dist/cli/trace-entry.d.ts +2 -0
  15. package/dist/cli/trace-entry.d.ts.map +1 -0
  16. package/dist/cli/trace-entry.js +53 -0
  17. package/dist/cli/trace-entry.js.map +1 -0
  18. package/dist/cli/trace-outline.d.ts +2 -0
  19. package/dist/cli/trace-outline.d.ts.map +1 -0
  20. package/dist/cli/trace-outline.js +43 -0
  21. package/dist/cli/trace-outline.js.map +1 -0
  22. package/dist/create-agent-benchmark.d.ts +113 -0
  23. package/dist/create-agent-benchmark.d.ts.map +1 -0
  24. package/dist/create-agent-benchmark.js +451 -0
  25. package/dist/create-agent-benchmark.js.map +1 -0
  26. package/dist/create-agent-benchmark.test.d.ts +2 -0
  27. package/dist/create-agent-benchmark.test.d.ts.map +1 -0
  28. package/dist/create-agent-benchmark.test.js +289 -0
  29. package/dist/create-agent-benchmark.test.js.map +1 -0
  30. package/dist/diagnosis.d.ts +4 -0
  31. package/dist/diagnosis.d.ts.map +1 -0
  32. package/dist/diagnosis.js +360 -0
  33. package/dist/diagnosis.js.map +1 -0
  34. package/dist/diagnosis.test.d.ts +2 -0
  35. package/dist/diagnosis.test.d.ts.map +1 -0
  36. package/dist/diagnosis.test.js +85 -0
  37. package/dist/diagnosis.test.js.map +1 -0
  38. package/dist/index.d.ts +10 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +10 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/langfuse-scores.d.ts +13 -0
  43. package/dist/langfuse-scores.d.ts.map +1 -0
  44. package/dist/langfuse-scores.js +95 -0
  45. package/dist/langfuse-scores.js.map +1 -0
  46. package/dist/langfuse-scores.test.d.ts +2 -0
  47. package/dist/langfuse-scores.test.d.ts.map +1 -0
  48. package/dist/langfuse-scores.test.js +121 -0
  49. package/dist/langfuse-scores.test.js.map +1 -0
  50. package/dist/loaders/filesystem.d.ts +26 -0
  51. package/dist/loaders/filesystem.d.ts.map +1 -0
  52. package/dist/loaders/filesystem.js +97 -0
  53. package/dist/loaders/filesystem.js.map +1 -0
  54. package/dist/loaders/rudder.d.ts +28 -0
  55. package/dist/loaders/rudder.d.ts.map +1 -0
  56. package/dist/loaders/rudder.js +81 -0
  57. package/dist/loaders/rudder.js.map +1 -0
  58. package/dist/parsers.d.ts +3 -0
  59. package/dist/parsers.d.ts.map +1 -0
  60. package/dist/parsers.js +23 -0
  61. package/dist/parsers.js.map +1 -0
  62. package/dist/trace.d.ts +42 -0
  63. package/dist/trace.d.ts.map +1 -0
  64. package/dist/trace.js +167 -0
  65. package/dist/trace.js.map +1 -0
  66. package/dist/trace.test.d.ts +2 -0
  67. package/dist/trace.test.d.ts.map +1 -0
  68. package/dist/trace.test.js +92 -0
  69. package/dist/trace.test.js.map +1 -0
  70. package/dist/transcript.d.ts +7 -0
  71. package/dist/transcript.d.ts.map +1 -0
  72. package/dist/transcript.js +70 -0
  73. package/dist/transcript.js.map +1 -0
  74. package/dist/types.d.ts +122 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/types.js +2 -0
  77. package/dist/types.js.map +1 -0
  78. package/package.json +59 -0
@@ -0,0 +1,289 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { appendCreateAgentBenchmarkMetadata, buildCreateAgentBenchmarkMetadata, createAgentEvalCheckToScoreValue, evaluateCreateAgentBenchmark, extractCreateAgentBenchmarkMetadata, parseCreateAgentCase, } from "./create-agent-benchmark.js";
3
+ function makeRunDetail(status = "succeeded") {
4
+ return {
5
+ run: {
6
+ id: "run-1",
7
+ orgId: "org-1",
8
+ agentId: "agent-bench",
9
+ invocationSource: "assignment",
10
+ triggerDetail: "system",
11
+ status,
12
+ startedAt: new Date("2026-04-14T00:00:00.000Z"),
13
+ finishedAt: new Date("2026-04-14T00:01:00.000Z"),
14
+ error: null,
15
+ wakeupRequestId: null,
16
+ exitCode: 0,
17
+ signal: null,
18
+ usageJson: null,
19
+ resultJson: null,
20
+ sessionIdBefore: null,
21
+ sessionIdAfter: null,
22
+ logStore: null,
23
+ logRef: null,
24
+ logBytes: null,
25
+ logSha256: null,
26
+ logCompressed: false,
27
+ stdoutExcerpt: null,
28
+ stderrExcerpt: null,
29
+ errorCode: null,
30
+ externalRunId: null,
31
+ processPid: null,
32
+ processStartedAt: null,
33
+ retryOfRunId: null,
34
+ processLossRetryCount: 0,
35
+ contextSnapshot: { issueId: "issue-1" },
36
+ createdAt: new Date("2026-04-14T00:00:00.000Z"),
37
+ updatedAt: new Date("2026-04-14T00:01:00.000Z"),
38
+ },
39
+ agentName: "Benchmark Agent",
40
+ orgName: "Rudder",
41
+ issue: {
42
+ id: "issue-1",
43
+ identifier: "RUD-1",
44
+ title: "Create a CTO agent",
45
+ },
46
+ bundle: {
47
+ agentRuntimeType: "codex_local",
48
+ agentConfigRevisionId: null,
49
+ agentConfigRevisionCreatedAt: null,
50
+ agentConfigFingerprint: null,
51
+ runtimeConfigFingerprint: null,
52
+ },
53
+ langfuse: null,
54
+ events: [],
55
+ logContent: null,
56
+ logChunks: [],
57
+ transcript: [
58
+ {
59
+ kind: "assistant",
60
+ ts: "2026-04-14T00:00:10.000Z",
61
+ text: "I created the CTO agent and linked the issue.",
62
+ delta: false,
63
+ },
64
+ {
65
+ kind: "result",
66
+ ts: "2026-04-14T00:00:50.000Z",
67
+ text: "Created CTO",
68
+ errors: [],
69
+ subtype: "success",
70
+ inputTokens: 12,
71
+ outputTokens: 18,
72
+ cachedTokens: 0,
73
+ costUsd: 0.01,
74
+ isError: false,
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ function makeAgent(overrides = {}) {
80
+ return {
81
+ id: "agent-new",
82
+ orgId: "org-1",
83
+ name: "CTO",
84
+ urlKey: "cto",
85
+ role: "cto",
86
+ title: "Chief Technology Officer",
87
+ icon: "crown",
88
+ status: "idle",
89
+ reportsTo: "agent-ceo",
90
+ capabilities: null,
91
+ agentRuntimeType: "codex_local",
92
+ agentRuntimeConfig: {},
93
+ runtimeConfig: {},
94
+ budgetMonthlyCents: 0,
95
+ spentMonthlyCents: 0,
96
+ pauseReason: null,
97
+ pausedAt: null,
98
+ permissions: { canCreateAgents: false },
99
+ lastHeartbeatAt: null,
100
+ metadata: null,
101
+ createdAt: new Date("2026-04-14T00:00:20.000Z"),
102
+ updatedAt: new Date("2026-04-14T00:00:20.000Z"),
103
+ ...overrides,
104
+ };
105
+ }
106
+ function makeApproval(overrides = {}) {
107
+ return {
108
+ id: "approval-1",
109
+ orgId: "org-1",
110
+ type: "hire_agent",
111
+ requestedByAgentId: "agent-bench",
112
+ requestedByUserId: null,
113
+ status: "pending",
114
+ payload: { agentId: "agent-new" },
115
+ decisionNote: null,
116
+ decidedByUserId: null,
117
+ decidedAt: null,
118
+ createdAt: new Date("2026-04-14T00:00:20.000Z"),
119
+ updatedAt: new Date("2026-04-14T00:00:20.000Z"),
120
+ ...overrides,
121
+ };
122
+ }
123
+ describe("create-agent benchmark helpers", () => {
124
+ it("parses valid benchmark cases", () => {
125
+ expect(parseCreateAgentCase({
126
+ id: "approval-cto",
127
+ prompt: "Create a CTO agent that reports to the CEO.",
128
+ expectedPath: "approval_required",
129
+ expectedAgentShape: {
130
+ role: "cto",
131
+ title: "Chief Technology Officer",
132
+ reportsToFixture: "ceo",
133
+ agentRuntimeType: "codex_local",
134
+ desiredSkills: ["rudder/rudder-create-agent"],
135
+ sourceIssueRequired: true,
136
+ },
137
+ fixtures: {
138
+ requiredApproval: true,
139
+ requiredFixtureKeys: ["ceo"],
140
+ },
141
+ judgeFocus: ["governance judgment"],
142
+ })).toMatchObject({
143
+ id: "approval-cto",
144
+ expectedPath: "approval_required",
145
+ expectedAgentShape: {
146
+ role: "cto",
147
+ reportsToFixture: "ceo",
148
+ },
149
+ });
150
+ });
151
+ it("round-trips benchmark metadata in issue descriptions", () => {
152
+ const metadata = buildCreateAgentBenchmarkMetadata({
153
+ testCase: parseCreateAgentCase({
154
+ id: "direct-engineer",
155
+ prompt: "Create an engineer",
156
+ expectedPath: "direct_create",
157
+ expectedAgentShape: {
158
+ role: "engineer",
159
+ },
160
+ }),
161
+ judgeVersion: "langfuse:judge-create-agent@production",
162
+ });
163
+ const description = appendCreateAgentBenchmarkMetadata("Create an engineer", metadata);
164
+ expect(extractCreateAgentBenchmarkMetadata(description)).toEqual(metadata);
165
+ });
166
+ it("evaluates approval-required cases with deterministic pass signals", () => {
167
+ const testCase = parseCreateAgentCase({
168
+ id: "approval-cto",
169
+ prompt: "Create a CTO agent that reports to the CEO.",
170
+ expectedPath: "approval_required",
171
+ expectedAgentShape: {
172
+ name: "CTO",
173
+ role: "cto",
174
+ title: "Chief Technology Officer",
175
+ reportsToFixture: "ceo",
176
+ agentRuntimeType: "codex_local",
177
+ desiredSkills: ["rudder/rudder-create-agent"],
178
+ sourceIssueRequired: true,
179
+ },
180
+ });
181
+ const benchmarkMetadata = buildCreateAgentBenchmarkMetadata({ testCase, judgeVersion: null });
182
+ const result = evaluateCreateAgentBenchmark({
183
+ testCase,
184
+ benchmarkMetadata,
185
+ issueId: "issue-1",
186
+ runDetail: makeRunDetail(),
187
+ createdAgents: [
188
+ {
189
+ agent: makeAgent({ status: "pending_approval" }),
190
+ skills: {
191
+ agentRuntimeType: "codex_local",
192
+ supported: true,
193
+ mode: "persistent",
194
+ desiredSkills: ["rudder/rudder-create-agent"],
195
+ entries: [],
196
+ warnings: [],
197
+ },
198
+ },
199
+ ],
200
+ createdApprovals: [
201
+ {
202
+ approval: makeApproval(),
203
+ issueIds: ["issue-1"],
204
+ },
205
+ ],
206
+ fixtureRefs: { ceo: "agent-ceo" },
207
+ judge: {
208
+ status: "completed",
209
+ version: "judge-v1",
210
+ summary: "Configuration quality is solid.",
211
+ configQuality: 5,
212
+ reasoningQuality: 4,
213
+ governanceJudgmentQuality: 5,
214
+ },
215
+ });
216
+ expect(result.checks.create_agent_path_correct.value).toBe("pass");
217
+ expect(result.checks.create_agent_reports_to_valid.value).toBe("pass");
218
+ expect(result.checks.create_agent_skills_valid.value).toBe("pass");
219
+ expect(result.checks.create_agent_overall_correctness.value).toBe("pass");
220
+ expect(result.finalClassification).toBe("pass");
221
+ expect(createAgentEvalCheckToScoreValue(result.checks.create_agent_overall_correctness)).toBe(true);
222
+ });
223
+ it("flags filesystem fallback and failed overall correctness", () => {
224
+ const testCase = parseCreateAgentCase({
225
+ id: "direct-engineer",
226
+ prompt: "Create an engineer",
227
+ expectedPath: "direct_create",
228
+ expectedAgentShape: {
229
+ role: "engineer",
230
+ },
231
+ });
232
+ const detail = makeRunDetail();
233
+ detail.logContent = "mkdir .agents/agents/Engineer\nwrote SKILL.md";
234
+ const result = evaluateCreateAgentBenchmark({
235
+ testCase,
236
+ benchmarkMetadata: buildCreateAgentBenchmarkMetadata({ testCase, judgeVersion: null }),
237
+ issueId: "issue-1",
238
+ runDetail: detail,
239
+ createdAgents: [],
240
+ createdApprovals: [],
241
+ judge: null,
242
+ });
243
+ expect(result.checks.create_agent_no_filesystem_fallback.value).toBe("fail");
244
+ expect(result.checks.create_agent_overall_correctness.value).toBe("fail");
245
+ expect(result.finalClassification).toBe("fail");
246
+ });
247
+ it("keeps partial side effects observable when the run is still in progress", () => {
248
+ const testCase = parseCreateAgentCase({
249
+ id: "direct-engineer",
250
+ prompt: "Create an engineer",
251
+ expectedPath: "direct_create",
252
+ expectedAgentShape: {
253
+ role: "engineer",
254
+ title: "Senior Engineer",
255
+ agentRuntimeType: "codex_local",
256
+ },
257
+ });
258
+ const detail = makeRunDetail("running");
259
+ const result = evaluateCreateAgentBenchmark({
260
+ testCase,
261
+ benchmarkMetadata: buildCreateAgentBenchmarkMetadata({ testCase, judgeVersion: null }),
262
+ issueId: "issue-1",
263
+ runDetail: detail,
264
+ createdAgents: [
265
+ {
266
+ agent: makeAgent({
267
+ role: "engineer",
268
+ title: "Senior Engineer",
269
+ agentRuntimeType: "codex_local",
270
+ }),
271
+ skills: {
272
+ agentRuntimeType: "codex_local",
273
+ supported: true,
274
+ mode: "persistent",
275
+ desiredSkills: [],
276
+ entries: [],
277
+ warnings: [],
278
+ },
279
+ },
280
+ ],
281
+ createdApprovals: [],
282
+ judge: null,
283
+ });
284
+ expect(result.checks.create_agent_request_completed.value).toBe("pass");
285
+ expect(result.finalClassification).toBe("needs_review");
286
+ expect(result.reviewReasons).toContain("run_incomplete:running");
287
+ });
288
+ });
289
+ //# sourceMappingURL=create-agent-benchmark.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-agent-benchmark.test.js","sourceRoot":"","sources":["../src/create-agent-benchmark.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ObservedRunDetail, RunComparison, RunDiagnosis, RunDiagnosisMode } from "./types.js";
2
+ export declare function diagnoseRun(detail: ObservedRunDetail, requestedMode?: RunDiagnosisMode): RunDiagnosis;
3
+ export declare function compareRunDiagnoses(left: RunDiagnosis, right: RunDiagnosis): RunComparison;
4
+ //# sourceMappingURL=diagnosis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnosis.d.ts","sourceRoot":"","sources":["../src/diagnosis.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAA0B,MAAM,YAAY,CAAC;AAsT3H,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,aAAa,GAAE,gBAAyB,GAAG,YAAY,CAqC7G;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,CAwC1F"}
@@ -0,0 +1,360 @@
1
+ function addFinding(target, finding) {
2
+ if (!finding)
3
+ return;
4
+ if (target.some((existing) => existing.id === finding.id))
5
+ return;
6
+ target.push(finding);
7
+ }
8
+ function countTokens(entries) {
9
+ let inputTokens = 0;
10
+ let outputTokens = 0;
11
+ let cachedTokens = 0;
12
+ for (const entry of entries) {
13
+ if (entry.kind !== "result")
14
+ continue;
15
+ inputTokens += entry.inputTokens;
16
+ outputTokens += entry.outputTokens;
17
+ cachedTokens += entry.cachedTokens;
18
+ }
19
+ return { inputTokens, outputTokens, cachedTokens };
20
+ }
21
+ function summarizeIssue(detail) {
22
+ if (!detail.issue)
23
+ return "No linked issue";
24
+ return detail.issue.identifier ? `${detail.issue.identifier} ${detail.issue.title ?? ""}`.trim() : detail.issue.title ?? detail.issue.id;
25
+ }
26
+ function inferFailureTaxonomy(detail) {
27
+ const errorText = [
28
+ detail.run.error ?? "",
29
+ detail.run.errorCode ?? "",
30
+ detail.run.stderrExcerpt ?? "",
31
+ ...detail.events.map((event) => event.message ?? ""),
32
+ ].join("\n");
33
+ if (detail.run.status === "timed_out")
34
+ return "timeout";
35
+ if (/permission denied/i.test(errorText))
36
+ return "permission_denied";
37
+ if (/could not read username|authentication failed|401|403/i.test(errorText))
38
+ return "auth_or_git_failure";
39
+ if (/cannot find module|module not found|command not found|no such file/i.test(errorText))
40
+ return "dependency_or_boot_failure";
41
+ if (/connection refused|econnrefused|network/i.test(errorText))
42
+ return "network_dependency_failure";
43
+ if (/detached|process_lost|orphan/i.test(errorText))
44
+ return "runtime_process_failure";
45
+ if (detail.run.status === "failed")
46
+ return "run_failed_unknown";
47
+ if (detail.run.status === "cancelled")
48
+ return "cancelled";
49
+ return "healthy_or_unknown";
50
+ }
51
+ function computeMetrics(detail) {
52
+ const { inputTokens: transcriptInputTokens, outputTokens: transcriptOutputTokens, cachedTokens: transcriptCachedTokens } = countTokens(detail.transcript);
53
+ const usage = detail.run.usageJson ?? {};
54
+ const inputTokens = Number(usage.inputTokens ?? transcriptInputTokens ?? 0);
55
+ const outputTokens = Number(usage.outputTokens ?? transcriptOutputTokens ?? 0);
56
+ const cachedTokens = Number(usage.cachedInputTokens ?? usage.cachedTokens ?? transcriptCachedTokens ?? 0);
57
+ const costUsd = Number(usage.costUsd ?? usage.totalCostUsd ?? 0);
58
+ const startedAtMs = detail.run.startedAt ? new Date(detail.run.startedAt).getTime() : null;
59
+ const createdAtMs = new Date(detail.run.createdAt).getTime();
60
+ const baselineMs = startedAtMs ?? createdAtMs;
61
+ let assistantTurns = 0;
62
+ let toolCalls = 0;
63
+ let toolResults = 0;
64
+ let stderrLines = 0;
65
+ let firstToolCallLatencyMs = null;
66
+ let firstAssistantOutputLatencyMs = null;
67
+ const topToolCounts = new Map();
68
+ for (const entry of detail.transcript) {
69
+ const tsMs = Number.isFinite(Date.parse(entry.ts)) ? Date.parse(entry.ts) : baselineMs;
70
+ if (entry.kind === "assistant" || entry.kind === "thinking") {
71
+ assistantTurns += 1;
72
+ if (firstAssistantOutputLatencyMs === null) {
73
+ firstAssistantOutputLatencyMs = Math.max(0, tsMs - baselineMs);
74
+ }
75
+ }
76
+ if (entry.kind === "tool_call") {
77
+ toolCalls += 1;
78
+ if (firstToolCallLatencyMs === null) {
79
+ firstToolCallLatencyMs = Math.max(0, tsMs - baselineMs);
80
+ }
81
+ topToolCounts.set(entry.name, (topToolCounts.get(entry.name) ?? 0) + 1);
82
+ }
83
+ if (entry.kind === "tool_result") {
84
+ toolResults += 1;
85
+ }
86
+ if (entry.kind === "stderr") {
87
+ stderrLines += 1;
88
+ }
89
+ }
90
+ const durationMs = detail.run.finishedAt && detail.run.startedAt
91
+ ? Math.max(0, new Date(detail.run.finishedAt).getTime() - new Date(detail.run.startedAt).getTime())
92
+ : 0;
93
+ return {
94
+ durationMs,
95
+ inputTokens,
96
+ outputTokens,
97
+ cachedTokens,
98
+ costUsd,
99
+ assistantTurns,
100
+ toolCalls,
101
+ toolResults,
102
+ stderrLines,
103
+ firstToolCallLatencyMs,
104
+ firstAssistantOutputLatencyMs,
105
+ topTools: [...topToolCounts.entries()]
106
+ .sort((left, right) => right[1] - left[1])
107
+ .slice(0, 8)
108
+ .map(([name, count]) => ({ name, count })),
109
+ };
110
+ }
111
+ function quickFindings(detail, metrics) {
112
+ const findings = [];
113
+ addFinding(findings, detail.run.status === "failed" ? {
114
+ id: "failed-run",
115
+ severity: "error",
116
+ category: "health",
117
+ title: "Run failed",
118
+ detail: detail.run.error ?? "The run ended in a failed state.",
119
+ evidence: [summarizeIssue(detail)],
120
+ } : null);
121
+ addFinding(findings, detail.run.status === "running" ? {
122
+ id: "running-run",
123
+ severity: "info",
124
+ category: "health",
125
+ title: "Run still in progress",
126
+ detail: "The run is not terminal yet, so findings may still change.",
127
+ evidence: [summarizeIssue(detail)],
128
+ } : null);
129
+ addFinding(findings, metrics.durationMs > 5 * 60 * 1000 ? {
130
+ id: "long-runtime",
131
+ severity: "warn",
132
+ category: "performance",
133
+ title: "Long runtime",
134
+ detail: `Run duration exceeded 5 minutes (${Math.round(metrics.durationMs / 1000)}s).`,
135
+ evidence: [String(metrics.durationMs)],
136
+ } : null);
137
+ addFinding(findings, metrics.costUsd > 5 ? {
138
+ id: "high-cost",
139
+ severity: "warn",
140
+ category: "performance",
141
+ title: "High cost",
142
+ detail: `Run cost exceeded $5 ($${metrics.costUsd.toFixed(2)}).`,
143
+ evidence: [`$${metrics.costUsd.toFixed(2)}`],
144
+ } : null);
145
+ addFinding(findings, detail.run.error ? {
146
+ id: "recorded-error",
147
+ severity: "warn",
148
+ category: "error",
149
+ title: "Recorded error",
150
+ detail: detail.run.error,
151
+ evidence: [detail.run.errorCode ?? "no-error-code"],
152
+ } : null);
153
+ if (findings.length === 0) {
154
+ findings.push({
155
+ id: "healthy-run",
156
+ severity: "info",
157
+ category: "health",
158
+ title: "No obvious health warnings",
159
+ detail: "The run completed without high-signal warning conditions.",
160
+ evidence: [summarizeIssue(detail)],
161
+ });
162
+ }
163
+ return findings;
164
+ }
165
+ function errorFindings(detail, taxonomy) {
166
+ const findings = [];
167
+ const contextLines = detail.events.slice(-5).map((event) => `[${event.seq}] ${event.message ?? event.eventType}`);
168
+ const evidence = contextLines.length > 0 ? contextLines : [detail.run.error ?? "No event context"];
169
+ const knownPatterns = [
170
+ [/could not read username|authentication failed/i, "auth_or_git_failure", "Git or provider authentication is misconfigured."],
171
+ [/timeout|timed out/i, "timeout", "The run exceeded its allowed execution window."],
172
+ [/cannot find module|module not found|command not found/i, "dependency_or_boot_failure", "A required dependency or command is missing."],
173
+ [/permission denied/i, "permission_denied", "The run failed because the runtime lacked required permissions."],
174
+ [/connection refused|econnrefused/i, "network_dependency_failure", "The run depended on a service that was unavailable."],
175
+ ];
176
+ const haystack = [detail.run.error, detail.run.stderrExcerpt, detail.run.stdoutExcerpt].filter(Boolean).join("\n");
177
+ const matched = knownPatterns.find(([pattern]) => pattern.test(haystack));
178
+ addFinding(findings, {
179
+ id: `taxonomy-${taxonomy}`,
180
+ severity: detail.run.status === "failed" || detail.run.status === "timed_out" ? "error" : "warn",
181
+ category: "error",
182
+ title: "Failure taxonomy",
183
+ detail: taxonomy,
184
+ evidence,
185
+ });
186
+ if (matched) {
187
+ findings.push({
188
+ id: `known-pattern-${matched[1]}`,
189
+ severity: "error",
190
+ category: "error",
191
+ title: "Matched known failure signature",
192
+ detail: matched[2],
193
+ evidence,
194
+ });
195
+ }
196
+ else if (detail.run.status === "failed" || detail.run.status === "timed_out") {
197
+ findings.push({
198
+ id: "unknown-failure-pattern",
199
+ severity: "warn",
200
+ category: "error",
201
+ title: "Unknown failure signature",
202
+ detail: "The run failed, but no known signature matched. Inspect the raw log and late event stream.",
203
+ evidence,
204
+ });
205
+ }
206
+ return findings;
207
+ }
208
+ function perfFindings(metrics) {
209
+ const findings = [];
210
+ addFinding(findings, metrics.durationMs > 10 * 60 * 1000 ? {
211
+ id: "duration-over-10m",
212
+ severity: "warn",
213
+ category: "performance",
214
+ title: "Runtime above 10 minutes",
215
+ detail: `Run duration reached ${Math.round(metrics.durationMs / 1000)} seconds.`,
216
+ evidence: [String(metrics.durationMs)],
217
+ } : null);
218
+ addFinding(findings, metrics.inputTokens > 500_000 ? {
219
+ id: "very-high-input-tokens",
220
+ severity: "warn",
221
+ category: "performance",
222
+ title: "Very high input token usage",
223
+ detail: `Input tokens exceeded 500k (${metrics.inputTokens.toLocaleString()}).`,
224
+ evidence: [String(metrics.inputTokens)],
225
+ } : null);
226
+ addFinding(findings, metrics.toolCalls > 100 ? {
227
+ id: "very-high-tool-volume",
228
+ severity: "warn",
229
+ category: "behavior",
230
+ title: "Very high tool call volume",
231
+ detail: `Tool calls exceeded 100 (${metrics.toolCalls}).`,
232
+ evidence: metrics.topTools.map((tool) => `${tool.name}:${tool.count}`),
233
+ } : null);
234
+ addFinding(findings, metrics.firstToolCallLatencyMs !== null && metrics.firstToolCallLatencyMs > 30_000 ? {
235
+ id: "slow-first-tool-call",
236
+ severity: "warn",
237
+ category: "performance",
238
+ title: "Slow first tool call",
239
+ detail: `The agent took ${Math.round(metrics.firstToolCallLatencyMs / 1000)} seconds before its first tool call.`,
240
+ evidence: [String(metrics.firstToolCallLatencyMs)],
241
+ } : null);
242
+ if (findings.length === 0) {
243
+ findings.push({
244
+ id: "no-perf-red-flags",
245
+ severity: "info",
246
+ category: "performance",
247
+ title: "No obvious performance red flags",
248
+ detail: "The run stayed within the default time, cost, and tool-volume thresholds.",
249
+ evidence: [],
250
+ });
251
+ }
252
+ return findings;
253
+ }
254
+ function nextStepsFromFindings(findings, taxonomy, detail) {
255
+ const nextSteps = [];
256
+ const findingIds = new Set(findings.map((finding) => finding.id));
257
+ if (taxonomy === "auth_or_git_failure") {
258
+ nextSteps.push("Verify the runtime's git/provider authentication and rerun after credentials are fixed.");
259
+ }
260
+ if (taxonomy === "dependency_or_boot_failure") {
261
+ nextSteps.push("Check the working directory and required dependencies or CLI commands before retrying.");
262
+ }
263
+ if (taxonomy === "timeout") {
264
+ nextSteps.push("Inspect the late transcript for repeated loops, then decide whether to raise timeout or tighten the prompt.");
265
+ }
266
+ if (findingIds.has("very-high-tool-volume")) {
267
+ nextSteps.push("Inspect bursts of repeated tool calls and batch repeated reads or searches.");
268
+ }
269
+ if (findingIds.has("very-high-input-tokens")) {
270
+ nextSteps.push("Reduce broad context loading and prefer targeted file reads or delta-oriented fetches.");
271
+ }
272
+ if (findingIds.has("unknown-failure-pattern")) {
273
+ nextSteps.push("Open the raw log and final event slice to capture a new failure signature for future taxonomy rules.");
274
+ }
275
+ if (detail.transcript.length > 0) {
276
+ nextSteps.push("Start with a compact trace outline, then expand only the suspicious turn or step instead of loading every payload.");
277
+ }
278
+ if (nextSteps.length === 0) {
279
+ nextSteps.push("Review the transcript and linked issue context to confirm whether the run behavior matched intent.");
280
+ }
281
+ return nextSteps;
282
+ }
283
+ export function diagnoseRun(detail, requestedMode = "auto") {
284
+ const mode = requestedMode === "auto"
285
+ ? (detail.run.status === "failed" || detail.run.status === "timed_out"
286
+ ? "full"
287
+ : "quick")
288
+ : requestedMode;
289
+ const metrics = computeMetrics(detail);
290
+ const taxonomy = inferFailureTaxonomy(detail);
291
+ const findings = [];
292
+ for (const finding of quickFindings(detail, metrics))
293
+ addFinding(findings, finding);
294
+ if (mode === "error" || mode === "full") {
295
+ for (const finding of errorFindings(detail, taxonomy))
296
+ addFinding(findings, finding);
297
+ }
298
+ if (mode === "perf" || mode === "full") {
299
+ for (const finding of perfFindings(metrics))
300
+ addFinding(findings, finding);
301
+ }
302
+ findings.sort((left, right) => {
303
+ const severityOrder = { error: 0, warn: 1, info: 2 };
304
+ return severityOrder[left.severity] - severityOrder[right.severity];
305
+ });
306
+ const headlineFinding = findings[0];
307
+ const summary = headlineFinding
308
+ ? `${headlineFinding.title}: ${headlineFinding.detail}`
309
+ : `Run ${detail.run.status}`;
310
+ return {
311
+ mode,
312
+ status: detail.run.status,
313
+ summary,
314
+ failureTaxonomy: taxonomy,
315
+ findings,
316
+ nextSteps: nextStepsFromFindings(findings, taxonomy, detail),
317
+ metrics,
318
+ };
319
+ }
320
+ export function compareRunDiagnoses(left, right) {
321
+ const deltas = [
322
+ {
323
+ metric: "status",
324
+ left: left.status,
325
+ right: right.status,
326
+ detail: `Status changed from ${left.status} to ${right.status}.`,
327
+ },
328
+ {
329
+ metric: "durationMs",
330
+ left: left.metrics.durationMs,
331
+ right: right.metrics.durationMs,
332
+ detail: `Duration delta: ${right.metrics.durationMs - left.metrics.durationMs}ms.`,
333
+ },
334
+ {
335
+ metric: "inputTokens",
336
+ left: left.metrics.inputTokens,
337
+ right: right.metrics.inputTokens,
338
+ detail: `Input token delta: ${right.metrics.inputTokens - left.metrics.inputTokens}.`,
339
+ },
340
+ {
341
+ metric: "costUsd",
342
+ left: left.metrics.costUsd,
343
+ right: right.metrics.costUsd,
344
+ detail: `Cost delta: ${(right.metrics.costUsd - left.metrics.costUsd).toFixed(2)} USD.`,
345
+ },
346
+ {
347
+ metric: "toolCalls",
348
+ left: left.metrics.toolCalls,
349
+ right: right.metrics.toolCalls,
350
+ detail: `Tool call delta: ${right.metrics.toolCalls - left.metrics.toolCalls}.`,
351
+ },
352
+ ];
353
+ return {
354
+ left,
355
+ right,
356
+ summary: `Compared ${left.status} vs ${right.status}; top headline moved from "${left.summary}" to "${right.summary}".`,
357
+ deltas,
358
+ };
359
+ }
360
+ //# sourceMappingURL=diagnosis.js.map