@prismatic-io/lux 0.0.2-preview.4 → 0.0.2-preview.7
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/lib/assertions/authoring.d.ts +2 -2
- package/lib/assertions/authoring.d.ts.map +1 -1
- package/lib/assertions/authoring.js.map +1 -1
- package/lib/assertions/core/index.d.ts.map +1 -1
- package/lib/assertions/core/index.js +2 -1
- package/lib/assertions/core/index.js.map +1 -1
- package/lib/assertions/core/resource-checks.d.ts +19 -0
- package/lib/assertions/core/resource-checks.d.ts.map +1 -1
- package/lib/assertions/core/resource-checks.js +32 -0
- package/lib/assertions/core/resource-checks.js.map +1 -1
- package/lib/cli/program.d.ts.map +1 -1
- package/lib/cli/program.js +6 -65
- package/lib/cli/program.js.map +1 -1
- package/lib/cli/view.d.ts +13 -0
- package/lib/cli/view.d.ts.map +1 -0
- package/lib/cli/view.js +126 -0
- package/lib/cli/view.js.map +1 -0
- package/lib/core/action-events.d.ts +29 -0
- package/lib/core/action-events.d.ts.map +1 -0
- package/lib/core/action-events.js +51 -0
- package/lib/core/action-events.js.map +1 -0
- package/lib/core/agent-activity.d.ts +49 -0
- package/lib/core/agent-activity.d.ts.map +1 -0
- package/lib/core/agent-activity.js +206 -0
- package/lib/core/agent-activity.js.map +1 -0
- package/lib/core/agent-events.d.ts +26 -0
- package/lib/core/agent-events.d.ts.map +1 -0
- package/lib/core/agent-events.js +13 -0
- package/lib/core/agent-events.js.map +1 -0
- package/lib/core/driver-metrics.d.ts +98 -0
- package/lib/core/driver-metrics.d.ts.map +1 -0
- package/lib/core/driver-metrics.js +36 -0
- package/lib/core/driver-metrics.js.map +1 -0
- package/lib/core/events.d.ts +34 -0
- package/lib/core/events.d.ts.map +1 -1
- package/lib/core/events.js +11 -2
- package/lib/core/events.js.map +1 -1
- package/lib/core/experiment.d.ts +19 -0
- package/lib/core/experiment.d.ts.map +1 -1
- package/lib/core/experiment.js +2 -0
- package/lib/core/experiment.js.map +1 -1
- package/lib/core/index.d.ts +4 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +4 -0
- package/lib/core/index.js.map +1 -1
- package/lib/core/run.d.ts +149 -0
- package/lib/core/run.d.ts.map +1 -1
- package/lib/core/run.js +9 -0
- package/lib/core/run.js.map +1 -1
- package/lib/core/wire.d.ts +11 -0
- package/lib/core/wire.d.ts.map +1 -1
- package/lib/drivers/claude-code/parse-events.d.ts.map +1 -1
- package/lib/drivers/claude-code/parse-events.js +73 -8
- package/lib/drivers/claude-code/parse-events.js.map +1 -1
- package/lib/drivers/codex/app-events.d.ts +9 -2
- package/lib/drivers/codex/app-events.d.ts.map +1 -1
- package/lib/drivers/codex/app-events.js +93 -12
- package/lib/drivers/codex/app-events.js.map +1 -1
- package/lib/drivers/codex/index.d.ts.map +1 -1
- package/lib/drivers/codex/index.js +10 -14
- package/lib/drivers/codex/index.js.map +1 -1
- package/lib/index.d.ts +6 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +6 -2
- package/lib/index.js.map +1 -1
- package/lib/orchestrator/compare.d.ts +13 -1
- package/lib/orchestrator/compare.d.ts.map +1 -1
- package/lib/orchestrator/compare.js +12 -0
- package/lib/orchestrator/compare.js.map +1 -1
- package/lib/orchestrator/experiment-contracts.d.ts +19 -0
- package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
- package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -1
- package/lib/orchestrator/experiment-evaluation.js +17 -0
- package/lib/orchestrator/experiment-evaluation.js.map +1 -1
- package/lib/orchestrator/experiment-report.d.ts +19 -0
- package/lib/orchestrator/experiment-report.d.ts.map +1 -1
- package/lib/orchestrator/experiment-runs.d.ts +38 -0
- package/lib/orchestrator/experiment-runs.d.ts.map +1 -1
- package/lib/orchestrator/index.d.ts +1 -1
- package/lib/orchestrator/index.d.ts.map +1 -1
- package/lib/orchestrator/index.js +1 -1
- package/lib/orchestrator/index.js.map +1 -1
- package/lib/orchestrator/list-runs.d.ts +7 -1
- package/lib/orchestrator/list-runs.d.ts.map +1 -1
- package/lib/orchestrator/list-runs.js +50 -6
- package/lib/orchestrator/list-runs.js.map +1 -1
- package/lib/orchestrator/run-execution.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.js +17 -3
- package/lib/orchestrator/run-execution.js.map +1 -1
- package/lib/orchestrator/suite-compare.d.ts +34 -0
- package/lib/orchestrator/suite-compare.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.d.ts +68 -0
- package/lib/orchestrator/suite-summary.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.js +45 -7
- package/lib/orchestrator/suite-summary.js.map +1 -1
- package/lib/previewer/artifacts.d.ts +25 -0
- package/lib/previewer/artifacts.d.ts.map +1 -0
- package/lib/previewer/artifacts.js +180 -0
- package/lib/previewer/artifacts.js.map +1 -0
- package/lib/previewer/server.d.ts +105 -0
- package/lib/previewer/server.d.ts.map +1 -0
- package/lib/previewer/server.js +542 -0
- package/lib/previewer/server.js.map +1 -0
- package/package.json +2 -1
- package/skills/lux/references/eval-authoring.md +4 -2
- package/skills/lux-answerer/SKILL.md +1 -1
- package/src/assertions/authoring.ts +2 -0
- package/src/assertions/core/index.ts +2 -0
- package/src/assertions/core/resource-checks.ts +35 -0
- package/src/cli/program.ts +5 -91
- package/src/cli/view.ts +168 -0
- package/src/core/action-events.ts +59 -0
- package/src/core/agent-activity.ts +236 -0
- package/src/core/agent-events.ts +17 -0
- package/src/core/driver-metrics.ts +51 -0
- package/src/core/events.ts +13 -2
- package/src/core/experiment.ts +2 -0
- package/src/core/index.ts +4 -0
- package/src/core/run.ts +9 -0
- package/src/drivers/claude-code/parse-events.ts +98 -12
- package/src/drivers/codex/app-events.ts +116 -16
- package/src/drivers/codex/index.ts +16 -14
- package/src/index.ts +40 -0
- package/src/orchestrator/compare.ts +18 -0
- package/src/orchestrator/experiment-evaluation.ts +23 -0
- package/src/orchestrator/index.ts +1 -0
- package/src/orchestrator/list-runs.ts +66 -5
- package/src/orchestrator/run-execution.ts +19 -2
- package/src/orchestrator/suite-summary.ts +101 -8
- package/src/previewer/artifacts.ts +221 -0
- package/src/previewer/server.ts +765 -0
- package/viewer/app.css +2075 -0
- package/viewer/app.js +606 -0
- package/viewer/artifact-view.js +48 -0
- package/viewer/charts.js +86 -0
- package/viewer/detail.js +1214 -0
- package/viewer/format.js +129 -0
- package/viewer/index.html +151 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ActionProgressPayloadSchema } from "./action-events.js";
|
|
3
|
+
import { AgentCompletedPayloadSchema, AgentSpawnPayloadSchema } from "./agent-events.js";
|
|
4
|
+
import type { AgentAttribution, ReadyState, RunEvent } from "./events.js";
|
|
5
|
+
import { ToolCallPayloadSchema, ToolResultPayloadSchema } from "./tool-events.js";
|
|
6
|
+
import { TokenUsageSchema } from "./usage.js";
|
|
7
|
+
|
|
8
|
+
export const AgentActivitySchema = z.object({
|
|
9
|
+
agent: z.object({
|
|
10
|
+
id: z.string(),
|
|
11
|
+
role: z.enum(["main", "subagent"]),
|
|
12
|
+
parentId: z.exactOptional(z.string()),
|
|
13
|
+
spawnToolUseId: z.exactOptional(z.string()),
|
|
14
|
+
name: z.exactOptional(z.string()),
|
|
15
|
+
model: z.exactOptional(z.string()),
|
|
16
|
+
}),
|
|
17
|
+
startedAt: z.number(),
|
|
18
|
+
endedAt: z.number(),
|
|
19
|
+
durationMs: z.number().nonnegative(),
|
|
20
|
+
status: z.enum(["completed", "failed", "cancelled", "unknown"]),
|
|
21
|
+
eventCount: z.number().int().nonnegative(),
|
|
22
|
+
usage: TokenUsageSchema.extend({ total: z.number().int().nonnegative() }),
|
|
23
|
+
toolCalls: z.object({
|
|
24
|
+
total: z.number().int().nonnegative(),
|
|
25
|
+
succeeded: z.number().int().nonnegative(),
|
|
26
|
+
failed: z.number().int().nonnegative(),
|
|
27
|
+
unresolved: z.number().int().nonnegative(),
|
|
28
|
+
}),
|
|
29
|
+
actions: z.object({
|
|
30
|
+
total: z.number().int().nonnegative(),
|
|
31
|
+
mutations: z.number().int().nonnegative(),
|
|
32
|
+
durableMutations: z.number().int().nonnegative(),
|
|
33
|
+
timeToFirstActionMs: z.exactOptional(z.number().nonnegative()),
|
|
34
|
+
timeToFirstMutationMs: z.exactOptional(z.number().nonnegative()),
|
|
35
|
+
timeToFirstDurableMutationMs: z.exactOptional(z.number().nonnegative()),
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
export type AgentActivity = z.infer<typeof AgentActivitySchema>;
|
|
39
|
+
|
|
40
|
+
const legacyAttribution = (
|
|
41
|
+
event: Extract<RunEvent, { type: "progress" }>,
|
|
42
|
+
ready: ReadyState,
|
|
43
|
+
): AgentAttribution => {
|
|
44
|
+
const payload = event.payload;
|
|
45
|
+
const parentToolUseId =
|
|
46
|
+
typeof payload === "object" && payload !== null && "parentToolUseId" in payload
|
|
47
|
+
? payload.parentToolUseId
|
|
48
|
+
: undefined;
|
|
49
|
+
const spawnToolUseId =
|
|
50
|
+
typeof parentToolUseId === "string" && parentToolUseId.length > 0 ? parentToolUseId : undefined;
|
|
51
|
+
return spawnToolUseId
|
|
52
|
+
? {
|
|
53
|
+
id: `subagent:${spawnToolUseId}`,
|
|
54
|
+
role: "subagent" as const,
|
|
55
|
+
parentId: ready.agentId,
|
|
56
|
+
spawnToolUseId,
|
|
57
|
+
}
|
|
58
|
+
: { id: ready.agentId, role: "main" as const, ...(ready.model ? { model: ready.model } : {}) };
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
type MutableActivity = AgentActivity & { calls: Map<string, "pending" | "succeeded" | "failed"> };
|
|
62
|
+
type ProgressEvent = Extract<RunEvent, { type: "progress" }>;
|
|
63
|
+
type SpawnInfo = { ts: number; name?: string; model?: string };
|
|
64
|
+
|
|
65
|
+
const initialActivity = (agent: AgentAttribution, startedAt: number): MutableActivity => ({
|
|
66
|
+
agent,
|
|
67
|
+
startedAt,
|
|
68
|
+
endedAt: startedAt,
|
|
69
|
+
durationMs: 0,
|
|
70
|
+
status: "unknown",
|
|
71
|
+
eventCount: 0,
|
|
72
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheCreation: 0, total: 0 },
|
|
73
|
+
toolCalls: { total: 0, succeeded: 0, failed: 0, unresolved: 0 },
|
|
74
|
+
actions: { total: 0, mutations: 0, durableMutations: 0 },
|
|
75
|
+
calls: new Map(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const collectSpawnInfo = (events: RunEvent[]): Map<string, SpawnInfo> => {
|
|
79
|
+
const spawns = new Map<string, SpawnInfo>();
|
|
80
|
+
for (const event of events) {
|
|
81
|
+
if (event.type !== "progress" || event.kind !== "tool-call") continue;
|
|
82
|
+
const call = ToolCallPayloadSchema.safeParse(event.payload).data;
|
|
83
|
+
if (!call) continue;
|
|
84
|
+
const input = z
|
|
85
|
+
.object({
|
|
86
|
+
name: z.string().min(1).optional(),
|
|
87
|
+
subagent_type: z.string().min(1).optional(),
|
|
88
|
+
agent: z.string().min(1).optional(),
|
|
89
|
+
role: z.string().min(1).optional(),
|
|
90
|
+
model: z.string().min(1).optional(),
|
|
91
|
+
})
|
|
92
|
+
.loose()
|
|
93
|
+
.safeParse(call.input).data;
|
|
94
|
+
const name = input?.name ?? input?.subagent_type ?? input?.agent ?? input?.role;
|
|
95
|
+
spawns.set(call.toolUseId, {
|
|
96
|
+
ts: event.ts,
|
|
97
|
+
...(name ? { name } : {}),
|
|
98
|
+
...(input?.model ? { model: input.model } : {}),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return spawns;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const applyLifecycle = (
|
|
105
|
+
event: ProgressEvent,
|
|
106
|
+
activities: Map<string, MutableActivity>,
|
|
107
|
+
): boolean => {
|
|
108
|
+
if (event.kind === "agent-spawn") {
|
|
109
|
+
const spawn = AgentSpawnPayloadSchema.safeParse(event.payload).data;
|
|
110
|
+
if (spawn && !activities.has(spawn.agent.id)) {
|
|
111
|
+
activities.set(spawn.agent.id, initialActivity(spawn.agent, event.ts));
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (event.kind !== "agent-completed") return false;
|
|
116
|
+
const completed = AgentCompletedPayloadSchema.safeParse(event.payload).data;
|
|
117
|
+
const activity = completed ? activities.get(completed.agentId) : undefined;
|
|
118
|
+
if (activity && completed) {
|
|
119
|
+
activity.endedAt = Math.max(activity.endedAt, event.ts);
|
|
120
|
+
activity.durationMs = Math.max(0, activity.endedAt - activity.startedAt);
|
|
121
|
+
activity.status = completed.status;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const activityFor = (
|
|
127
|
+
event: ProgressEvent,
|
|
128
|
+
ready: ReadyState,
|
|
129
|
+
driverStartedAt: number,
|
|
130
|
+
spawns: Map<string, SpawnInfo>,
|
|
131
|
+
activities: Map<string, MutableActivity>,
|
|
132
|
+
): MutableActivity => {
|
|
133
|
+
const agent = event.agent ?? legacyAttribution(event, ready);
|
|
134
|
+
const existing = activities.get(agent.id);
|
|
135
|
+
if (existing) return existing;
|
|
136
|
+
const spawn = agent.spawnToolUseId ? spawns.get(agent.spawnToolUseId) : undefined;
|
|
137
|
+
const name = agent.name ?? spawn?.name;
|
|
138
|
+
const model = agent.model ?? spawn?.model;
|
|
139
|
+
const attributedAgent = {
|
|
140
|
+
...agent,
|
|
141
|
+
...(name ? { name } : {}),
|
|
142
|
+
...(model ? { model } : {}),
|
|
143
|
+
};
|
|
144
|
+
const startedAt =
|
|
145
|
+
agent.role === "subagent" && agent.spawnToolUseId
|
|
146
|
+
? (spawn?.ts ?? event.ts)
|
|
147
|
+
: driverStartedAt || event.ts;
|
|
148
|
+
return activities.get(agent.id) ?? initialActivity(attributedAgent, startedAt);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const addAction = (event: ProgressEvent, activity: MutableActivity): void => {
|
|
152
|
+
const action = ActionProgressPayloadSchema.safeParse(event.payload).data;
|
|
153
|
+
if (!action) return;
|
|
154
|
+
const elapsed = Math.max(0, event.ts - activity.startedAt);
|
|
155
|
+
activity.actions.total++;
|
|
156
|
+
activity.actions.timeToFirstActionMs ??= elapsed;
|
|
157
|
+
if (action.category === "mutation" || action.category === "durable-mutation") {
|
|
158
|
+
activity.actions.mutations++;
|
|
159
|
+
activity.actions.timeToFirstMutationMs ??= elapsed;
|
|
160
|
+
}
|
|
161
|
+
if (action.category === "durable-mutation") {
|
|
162
|
+
activity.actions.durableMutations++;
|
|
163
|
+
activity.actions.timeToFirstDurableMutationMs ??= elapsed;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const addUsage = (event: ProgressEvent, activity: MutableActivity): void => {
|
|
168
|
+
const usage = TokenUsageSchema.safeParse(event.payload).data;
|
|
169
|
+
if (!usage) return;
|
|
170
|
+
activity.usage.input += usage.input;
|
|
171
|
+
activity.usage.output += usage.output;
|
|
172
|
+
activity.usage.cacheRead += usage.cacheRead;
|
|
173
|
+
activity.usage.cacheCreation += usage.cacheCreation;
|
|
174
|
+
activity.usage.total += usage.input + usage.output + usage.cacheRead + usage.cacheCreation;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const addWork = (event: ProgressEvent, activity: MutableActivity): void => {
|
|
178
|
+
activity.eventCount++;
|
|
179
|
+
activity.endedAt = Math.max(activity.endedAt, event.ts);
|
|
180
|
+
activity.durationMs = Math.max(0, activity.endedAt - activity.startedAt);
|
|
181
|
+
if (event.kind === "tool-call") {
|
|
182
|
+
const call = ToolCallPayloadSchema.safeParse(event.payload).data;
|
|
183
|
+
if (call && !activity.calls.has(call.toolUseId)) {
|
|
184
|
+
activity.calls.set(call.toolUseId, "pending");
|
|
185
|
+
activity.toolCalls.total++;
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (event.kind === "tool-result") {
|
|
190
|
+
const result = ToolResultPayloadSchema.safeParse(event.payload).data;
|
|
191
|
+
if (result && activity.calls.has(result.toolUseId)) {
|
|
192
|
+
activity.calls.set(result.toolUseId, result.isError ? "failed" : "succeeded");
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (event.kind === "action") addAction(event, activity);
|
|
197
|
+
if (event.kind === "usage") addUsage(event, activity);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export const summarizeAgentActivity = (events: RunEvent[], ready: ReadyState): AgentActivity[] => {
|
|
201
|
+
const driverStart = events.find(
|
|
202
|
+
(event): event is Extract<RunEvent, { type: "lifecycle" }> =>
|
|
203
|
+
event.type === "lifecycle" && event.stage === "start-driver",
|
|
204
|
+
);
|
|
205
|
+
const driverStartedAt = driverStart?.ts ?? 0;
|
|
206
|
+
const spawns = collectSpawnInfo(events);
|
|
207
|
+
const activities = new Map<string, MutableActivity>();
|
|
208
|
+
for (const event of events) {
|
|
209
|
+
if (event.type !== "progress") continue;
|
|
210
|
+
if (applyLifecycle(event, activities)) continue;
|
|
211
|
+
const activity = activityFor(event, ready, driverStartedAt, spawns, activities);
|
|
212
|
+
addWork(event, activity);
|
|
213
|
+
activities.set(activity.agent.id, activity);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!activities.has(ready.agentId)) {
|
|
217
|
+
activities.set(
|
|
218
|
+
ready.agentId,
|
|
219
|
+
initialActivity(
|
|
220
|
+
{
|
|
221
|
+
id: ready.agentId,
|
|
222
|
+
role: "main",
|
|
223
|
+
...(ready.model ? { model: ready.model } : {}),
|
|
224
|
+
},
|
|
225
|
+
driverStartedAt,
|
|
226
|
+
),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
return [...activities.values()]
|
|
230
|
+
.map(({ calls, ...activity }) => {
|
|
231
|
+
for (const outcome of calls.values())
|
|
232
|
+
activity.toolCalls[outcome === "pending" ? "unresolved" : outcome]++;
|
|
233
|
+
return AgentActivitySchema.parse(activity);
|
|
234
|
+
})
|
|
235
|
+
.sort((a, b) => a.startedAt - b.startedAt || a.agent.id.localeCompare(b.agent.id));
|
|
236
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AgentAttributionSchema } from "./events.js";
|
|
3
|
+
|
|
4
|
+
export const AGENT_SPAWN_KIND = "agent-spawn" as const;
|
|
5
|
+
export const AGENT_COMPLETED_KIND = "agent-completed" as const;
|
|
6
|
+
|
|
7
|
+
export const AgentSpawnPayloadSchema = z.object({
|
|
8
|
+
agent: AgentAttributionSchema.extend({ role: z.literal("subagent") }),
|
|
9
|
+
prompt: z.exactOptional(z.string()),
|
|
10
|
+
});
|
|
11
|
+
export type AgentSpawnPayload = z.infer<typeof AgentSpawnPayloadSchema>;
|
|
12
|
+
|
|
13
|
+
export const AgentCompletedPayloadSchema = z.object({
|
|
14
|
+
agentId: z.string().min(1),
|
|
15
|
+
status: z.enum(["completed", "failed", "cancelled", "unknown"]),
|
|
16
|
+
});
|
|
17
|
+
export type AgentCompletedPayload = z.infer<typeof AgentCompletedPayloadSchema>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const DriverMetricUnitSchema = z.enum([
|
|
4
|
+
"number",
|
|
5
|
+
"count",
|
|
6
|
+
"milliseconds",
|
|
7
|
+
"bytes",
|
|
8
|
+
"tokens",
|
|
9
|
+
"usd",
|
|
10
|
+
"ratio",
|
|
11
|
+
"percent",
|
|
12
|
+
"score",
|
|
13
|
+
]);
|
|
14
|
+
export type DriverMetricUnit = z.infer<typeof DriverMetricUnitSchema>;
|
|
15
|
+
|
|
16
|
+
export const DriverMetricDescriptorSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
unit: DriverMetricUnitSchema.default("number"),
|
|
19
|
+
label: z.string().min(1).max(120).optional(),
|
|
20
|
+
better: z.enum(["lower", "higher", "neutral"]).default("neutral"),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
export type DriverMetricDescriptor = z.infer<typeof DriverMetricDescriptorSchema>;
|
|
24
|
+
|
|
25
|
+
export const DriverMetricSchema = DriverMetricDescriptorSchema.extend({
|
|
26
|
+
value: z.number().finite(),
|
|
27
|
+
}).strict();
|
|
28
|
+
export type DriverMetric = z.infer<typeof DriverMetricSchema>;
|
|
29
|
+
|
|
30
|
+
export const DriverMetricsSchema = z.record(z.string().min(1).max(120), DriverMetricSchema);
|
|
31
|
+
export type DriverMetrics = z.infer<typeof DriverMetricsSchema>;
|
|
32
|
+
|
|
33
|
+
const ReportedDriverMetricSchema = z.union([z.number().finite(), DriverMetricSchema]);
|
|
34
|
+
export const ReportedDriverMetricsSchema = z.record(
|
|
35
|
+
z.string().min(1).max(120),
|
|
36
|
+
ReportedDriverMetricSchema,
|
|
37
|
+
);
|
|
38
|
+
export type ReportedDriverMetrics = z.input<typeof ReportedDriverMetricsSchema>;
|
|
39
|
+
|
|
40
|
+
export const normalizeDriverMetrics = (value: unknown): DriverMetrics => {
|
|
41
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
42
|
+
return Object.fromEntries(
|
|
43
|
+
Object.entries(value).flatMap(([name, metric]) => {
|
|
44
|
+
if (name.length === 0 || name.length > 120) return [];
|
|
45
|
+
const parsed = DriverMetricSchema.safeParse(
|
|
46
|
+
typeof metric === "number" ? { value: metric } : metric,
|
|
47
|
+
).data;
|
|
48
|
+
return parsed ? [[name, parsed]] : [];
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
};
|
package/src/core/events.ts
CHANGED
|
@@ -57,11 +57,22 @@ export const AnswerSchema = z.discriminatedUnion("kind", [
|
|
|
57
57
|
]);
|
|
58
58
|
export type Answer = z.infer<typeof AnswerSchema>;
|
|
59
59
|
|
|
60
|
+
export const AgentAttributionSchema = z.object({
|
|
61
|
+
id: z.string().min(1),
|
|
62
|
+
role: z.enum(["main", "subagent"]),
|
|
63
|
+
parentId: z.exactOptional(z.string().min(1)),
|
|
64
|
+
spawnToolUseId: z.exactOptional(z.string().min(1)),
|
|
65
|
+
name: z.exactOptional(z.string().min(1)),
|
|
66
|
+
model: z.exactOptional(z.string().min(1)),
|
|
67
|
+
});
|
|
68
|
+
export type AgentAttribution = z.infer<typeof AgentAttributionSchema>;
|
|
69
|
+
|
|
60
70
|
export const ProgressSchema = z.object({
|
|
61
71
|
id: z.string(),
|
|
62
72
|
kind: z.string(),
|
|
63
73
|
payload: z.unknown(),
|
|
64
74
|
ts: z.number(),
|
|
75
|
+
agent: z.exactOptional(AgentAttributionSchema),
|
|
65
76
|
});
|
|
66
77
|
export type Progress = z.infer<typeof ProgressSchema>;
|
|
67
78
|
|
|
@@ -69,8 +80,8 @@ export const DoneSchema = z.object({
|
|
|
69
80
|
exitReason: z.literal("done"),
|
|
70
81
|
/**
|
|
71
82
|
* Driver-defined summary. The orchestrator recognizes `usage`, `cost`,
|
|
72
|
-
* `modelCalls`, and `
|
|
73
|
-
* for domain-specific
|
|
83
|
+
* `modelCalls`, `unpricedModelCalls`, and scalar `metrics` when present while
|
|
84
|
+
* preserving room for other domain-specific evidence.
|
|
74
85
|
*/
|
|
75
86
|
summary: z.unknown().optional(),
|
|
76
87
|
});
|
package/src/core/experiment.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { DriverMetricDescriptorSchema } from "./driver-metrics.js";
|
|
2
3
|
import { ConfidenceConfigSchema } from "./statistics.js";
|
|
3
4
|
import { TokenUsageSchema } from "./usage.js";
|
|
4
5
|
import { formatZodIssues } from "./zod.js";
|
|
@@ -415,6 +416,7 @@ export const CandidateEvaluationSchema = z.object({
|
|
|
415
416
|
passRate: z.number().min(0).max(1),
|
|
416
417
|
casePassRates: z.record(z.string(), z.number().min(0).max(1)),
|
|
417
418
|
metrics: z.record(z.string(), z.number()),
|
|
419
|
+
metricDefinitions: z.exactOptional(z.record(z.string(), DriverMetricDescriptorSchema)),
|
|
418
420
|
metricCalls: z.exactOptional(z.number().int().nonnegative()),
|
|
419
421
|
judgeCalls: z.exactOptional(z.number().int().nonnegative()),
|
|
420
422
|
unpricedModelCalls: z.exactOptional(z.number().int().nonnegative()),
|
package/src/core/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export * from "./action-events.js";
|
|
2
|
+
export * from "./agent-activity.js";
|
|
3
|
+
export * from "./agent-events.js";
|
|
1
4
|
export * from "./annotation.js";
|
|
2
5
|
export * from "./answerer.js";
|
|
3
6
|
export * from "./artifact-evidence.js";
|
|
@@ -6,6 +9,7 @@ export * from "./assertion.js";
|
|
|
6
9
|
export * from "./case.js";
|
|
7
10
|
export * from "./conversation-turn.js";
|
|
8
11
|
export * from "./driver.js";
|
|
12
|
+
export * from "./driver-metrics.js";
|
|
9
13
|
export * from "./emitter.js";
|
|
10
14
|
export * from "./events.js";
|
|
11
15
|
export * from "./exec.js";
|
package/src/core/run.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { ActionTimingSchema } from "./action-events.js";
|
|
3
|
+
import { AgentActivitySchema } from "./agent-activity.js";
|
|
2
4
|
import { EvalCaseSchema } from "./case.js";
|
|
3
5
|
import { ArtifactSchema } from "./driver.js";
|
|
6
|
+
import { DriverMetricsSchema } from "./driver-metrics.js";
|
|
4
7
|
import { ErrorEventReasonSchema, ReadyStateSchema, RunEventSchema } from "./events.js";
|
|
5
8
|
import { ToolOutcomeSummarySchema } from "./tool-events.js";
|
|
6
9
|
import { RunUsageSchema } from "./usage.js";
|
|
@@ -112,6 +115,8 @@ export const RunMetadataSchema = z.object({
|
|
|
112
115
|
}),
|
|
113
116
|
),
|
|
114
117
|
cost: z.exactOptional(z.object({ usd: z.exactOptional(z.number()) })),
|
|
118
|
+
/** Driver-defined scalar measurements with display and optimization semantics. */
|
|
119
|
+
driverMetrics: z.exactOptional(DriverMetricsSchema),
|
|
115
120
|
interruptCounts: z.object({ ask: z.number(), approve: z.number(), custom: z.number() }),
|
|
116
121
|
/** Legacy raw count of progress events whose kind is `tool-call`. */
|
|
117
122
|
toolCallCount: z.exactOptional(z.number()),
|
|
@@ -124,6 +129,10 @@ export const RunMetadataSchema = z.object({
|
|
|
124
129
|
),
|
|
125
130
|
/** Wall-clock per phase, derived from the lifecycle event stream. */
|
|
126
131
|
phaseDurationMs: z.exactOptional(PhaseDurationsSchema),
|
|
132
|
+
/** Driver-reported semantic action latency, measured from `start-driver`. */
|
|
133
|
+
actionTiming: z.exactOptional(ActionTimingSchema),
|
|
134
|
+
/** Per-agent lifecycle and activity derived from the attributed event stream. */
|
|
135
|
+
agentActivity: z.exactOptional(z.array(AgentActivitySchema)),
|
|
127
136
|
/**
|
|
128
137
|
* Harness/infrastructure failure that invalidates this run as candidate
|
|
129
138
|
* evidence. A driver-reported subject failure intentionally leaves this
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import {
|
|
3
|
+
type AgentAttribution,
|
|
3
4
|
type Interrupt,
|
|
4
5
|
type ParsedDriverEvent,
|
|
5
6
|
parseAgentUsage,
|
|
@@ -8,6 +9,39 @@ import {
|
|
|
8
9
|
toolResultProgress,
|
|
9
10
|
} from "../../core/index.js";
|
|
10
11
|
|
|
12
|
+
const claudeAgent = (parentToolUseId?: string): AgentAttribution =>
|
|
13
|
+
parentToolUseId
|
|
14
|
+
? {
|
|
15
|
+
id: `claude-code:${parentToolUseId}`,
|
|
16
|
+
role: "subagent",
|
|
17
|
+
parentId: "claude-code",
|
|
18
|
+
spawnToolUseId: parentToolUseId,
|
|
19
|
+
}
|
|
20
|
+
: { id: "claude-code", role: "main" };
|
|
21
|
+
|
|
22
|
+
const isSubagentTool = (name: string): boolean => name === "Agent" || name === "Task";
|
|
23
|
+
|
|
24
|
+
const spawnedClaudeAgent = (
|
|
25
|
+
toolUseId: string,
|
|
26
|
+
input: unknown,
|
|
27
|
+
parentToolUseId?: string,
|
|
28
|
+
): AgentAttribution => {
|
|
29
|
+
const details =
|
|
30
|
+
typeof input === "object" && input !== null ? (input as Record<string, unknown>) : {};
|
|
31
|
+
const name = [details.name, details.subagent_type, details.agent, details.role].find(
|
|
32
|
+
(value): value is string => typeof value === "string" && value.length > 0,
|
|
33
|
+
);
|
|
34
|
+
const model = typeof details.model === "string" && details.model ? details.model : undefined;
|
|
35
|
+
return {
|
|
36
|
+
id: `claude-code:${toolUseId}`,
|
|
37
|
+
role: "subagent",
|
|
38
|
+
parentId: claudeAgent(parentToolUseId).id,
|
|
39
|
+
spawnToolUseId: toolUseId,
|
|
40
|
+
...(name ? { name } : {}),
|
|
41
|
+
...(model ? { model } : {}),
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
11
45
|
const ContentBlockSchema = z.discriminatedUnion("type", [
|
|
12
46
|
z.object({ type: z.literal("text"), text: z.string() }),
|
|
13
47
|
z.object({ type: z.literal("tool_use"), id: z.string(), name: z.string(), input: z.unknown() }),
|
|
@@ -88,6 +122,7 @@ type PendingTurn = {
|
|
|
88
122
|
asked: boolean;
|
|
89
123
|
thought: boolean;
|
|
90
124
|
text?: string;
|
|
125
|
+
agent: AgentAttribution;
|
|
91
126
|
};
|
|
92
127
|
|
|
93
128
|
type AssistantContent = {
|
|
@@ -96,6 +131,7 @@ type AssistantContent = {
|
|
|
96
131
|
asked: boolean;
|
|
97
132
|
thought: boolean;
|
|
98
133
|
firstText?: string;
|
|
134
|
+
agent: AgentAttribution;
|
|
99
135
|
};
|
|
100
136
|
|
|
101
137
|
const questionTextOf = (raw: unknown): string =>
|
|
@@ -225,6 +261,30 @@ class ClaudeEventParser {
|
|
|
225
261
|
if (!block) continue;
|
|
226
262
|
events.push(block);
|
|
227
263
|
if (block.tag !== "progress") continue;
|
|
264
|
+
const resultPayload = block.progress.payload as {
|
|
265
|
+
name?: unknown;
|
|
266
|
+
toolUseId?: unknown;
|
|
267
|
+
isError?: unknown;
|
|
268
|
+
};
|
|
269
|
+
if (
|
|
270
|
+
typeof resultPayload.name === "string" &&
|
|
271
|
+
isSubagentTool(resultPayload.name) &&
|
|
272
|
+
typeof resultPayload.toolUseId === "string"
|
|
273
|
+
) {
|
|
274
|
+
events.push({
|
|
275
|
+
tag: "progress",
|
|
276
|
+
progress: {
|
|
277
|
+
id: `agent-completed-${resultPayload.toolUseId}`,
|
|
278
|
+
kind: "agent-completed",
|
|
279
|
+
payload: {
|
|
280
|
+
agentId: `claude-code:${resultPayload.toolUseId}`,
|
|
281
|
+
status: resultPayload.isError === true ? "failed" : "completed",
|
|
282
|
+
},
|
|
283
|
+
agent: claudeAgent(),
|
|
284
|
+
ts: block.progress.ts,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
228
288
|
const text = (block.progress.payload as { text?: unknown }).text;
|
|
229
289
|
const failure = typeof text === "string" ? toolInfrastructureFailure(text) : null;
|
|
230
290
|
if (failure) {
|
|
@@ -244,20 +304,21 @@ class ClaudeEventParser {
|
|
|
244
304
|
|
|
245
305
|
const toolUseId = block.tool_use_id;
|
|
246
306
|
const content = block.content ?? [];
|
|
307
|
+
const progress = toolResultProgress(
|
|
308
|
+
this.eventSequence++,
|
|
309
|
+
{
|
|
310
|
+
name: this.toolNames.get(toolUseId) ?? "unknown",
|
|
311
|
+
toolUseId,
|
|
312
|
+
content,
|
|
313
|
+
isError: block.is_error === true,
|
|
314
|
+
text: toolResultText(content),
|
|
315
|
+
...(parentToolUseId ? { parentToolUseId } : {}),
|
|
316
|
+
},
|
|
317
|
+
Date.now(),
|
|
318
|
+
);
|
|
247
319
|
return {
|
|
248
320
|
tag: "progress",
|
|
249
|
-
progress:
|
|
250
|
-
this.eventSequence++,
|
|
251
|
-
{
|
|
252
|
-
name: this.toolNames.get(toolUseId) ?? "unknown",
|
|
253
|
-
toolUseId,
|
|
254
|
-
content,
|
|
255
|
-
isError: block.is_error === true,
|
|
256
|
-
text: toolResultText(content),
|
|
257
|
-
...(parentToolUseId ? { parentToolUseId } : {}),
|
|
258
|
-
},
|
|
259
|
-
Date.now(),
|
|
260
|
-
),
|
|
321
|
+
progress: { ...progress, agent: claudeAgent(parentToolUseId) },
|
|
261
322
|
};
|
|
262
323
|
}
|
|
263
324
|
|
|
@@ -284,6 +345,7 @@ class ClaudeEventParser {
|
|
|
284
345
|
tools: [],
|
|
285
346
|
asked: false,
|
|
286
347
|
thought: false,
|
|
348
|
+
agent: claudeAgent(event.parent_tool_use_id),
|
|
287
349
|
};
|
|
288
350
|
for (const rawBlock of event.message.content) {
|
|
289
351
|
const block = ContentBlockSchema.safeParse(rawBlock).data;
|
|
@@ -323,6 +385,7 @@ class ClaudeEventParser {
|
|
|
323
385
|
kind: "agent-message",
|
|
324
386
|
payload: { text, ...(parentToolUseId ? { parentToolUseId } : {}) },
|
|
325
387
|
ts: Date.now(),
|
|
388
|
+
agent: claudeAgent(parentToolUseId),
|
|
326
389
|
},
|
|
327
390
|
});
|
|
328
391
|
}
|
|
@@ -354,8 +417,29 @@ class ClaudeEventParser {
|
|
|
354
417
|
...(parentToolUseId ? { parentToolUseId } : {}),
|
|
355
418
|
},
|
|
356
419
|
ts: Date.now(),
|
|
420
|
+
agent: claudeAgent(parentToolUseId),
|
|
357
421
|
},
|
|
358
422
|
});
|
|
423
|
+
if (isSubagentTool(block.name)) {
|
|
424
|
+
const agent = spawnedClaudeAgent(block.id, block.input, parentToolUseId);
|
|
425
|
+
const details =
|
|
426
|
+
typeof block.input === "object" && block.input !== null
|
|
427
|
+
? (block.input as Record<string, unknown>)
|
|
428
|
+
: {};
|
|
429
|
+
content.events.push({
|
|
430
|
+
tag: "progress",
|
|
431
|
+
progress: {
|
|
432
|
+
id: `agent-spawn-${block.id}`,
|
|
433
|
+
kind: "agent-spawn",
|
|
434
|
+
payload: {
|
|
435
|
+
agent,
|
|
436
|
+
...(typeof details.prompt === "string" ? { prompt: details.prompt } : {}),
|
|
437
|
+
},
|
|
438
|
+
agent: claudeAgent(parentToolUseId),
|
|
439
|
+
ts: Date.now(),
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
}
|
|
359
443
|
}
|
|
360
444
|
|
|
361
445
|
private recordTurn(
|
|
@@ -376,6 +460,7 @@ class ClaudeEventParser {
|
|
|
376
460
|
tools: [],
|
|
377
461
|
asked: false,
|
|
378
462
|
thought: false,
|
|
463
|
+
agent: content.agent,
|
|
379
464
|
};
|
|
380
465
|
this.pendingTurn.usage = usage;
|
|
381
466
|
this.pendingTurn.asked ||= content.asked;
|
|
@@ -451,6 +536,7 @@ class ClaudeEventParser {
|
|
|
451
536
|
kind: "usage",
|
|
452
537
|
payload: { ...this.pendingTurn.usage, ...(label ? { label } : {}) },
|
|
453
538
|
ts: Date.now(),
|
|
539
|
+
agent: this.pendingTurn.agent,
|
|
454
540
|
},
|
|
455
541
|
};
|
|
456
542
|
this.pendingTurn = null;
|