Skip to content

Commit 39ab957

Browse files
committed
feat: Bump support for eve
1 parent 753d396 commit 39ab957

11 files changed

Lines changed: 677 additions & 87 deletions

File tree

‎.changeset/bump-eve-support.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"braintrust": minor
3+
---
4+
5+
feat: Bump support for eve

‎e2e/scenarios/eve-instrumentation/__snapshots__/eve-v0-latest.span-tree.json‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎e2e/scenarios/eve-instrumentation/__snapshots__/eve-v0-latest.span-tree.txt‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ span_tree:
8484
│ │ input: {
8585
│ │ "message": "Run the Braintrust Eve instrumentation e2e scenario"
8686
│ │ }
87-
│ │ output: "Started task researcher-<id>. Its result will arrive in a <task_result> message. To send it another message, call researcher again with taskId researcher-<id>."
87+
│ │ output: "Researcher result: Eve instrumentation https://eve.dev/docs/guides/instrumentation"
8888
│ │ metadata: {
8989
│ │ "eve.action_kind": "subagent-call",
9090
│ │ "eve.session_id": "<eve.session_id:1>",
@@ -750,7 +750,7 @@ span_tree:
750750
│ │ input: {
751751
│ │ "message": "Run the Braintrust Eve instrumentation e2e scenario again"
752752
│ │ }
753-
│ │ output: "Started task researcher-<id>. Its result will arrive in a <task_result> message. To send it another message, call researcher again with taskId researcher-<id>."
753+
│ │ output: "Researcher result: Eve instrumentation https://eve.dev/docs/guides/instrumentation"
754754
│ │ metadata: {
755755
│ │ "eve.action_kind": "subagent-call",
756756
│ │ "eve.session_id": "<eve.session_id:1>",
@@ -1537,7 +1537,7 @@ span_tree:
15371537
│ input: {
15381538
│ "message": "Run the Braintrust Eve instrumentation e2e scenario from a traced caller"
15391539
│ }
1540-
│ output: "Started task researcher-<id>. Its result will arrive in a <task_result> message. To send it another message, call researcher again with taskId researcher-<id>."
1540+
│ output: "Researcher result: Eve instrumentation https://eve.dev/docs/guides/instrumentation"
15411541
│ metadata: {
15421542
│ "eve.action_kind": "subagent-call",
15431543
│ "eve.session_id": "<eve.session_id:1>",

‎e2e/scenarios/eve-instrumentation/agent/agent.ts‎

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,19 @@ export default defineAgent({
2424
...(process.env.EVE_EXPERIMENTAL_INSTRUMENTATION_PROVIDERS === "1"
2525
? { experimental: { instrumentationProviders: true } }
2626
: {}),
27-
model: defineDynamic({
28-
...(providerInstrumentation ? {} : { fallback: dynamicModel }),
29-
events: {
30-
"step.started": () =>
31-
providerInstrumentation
32-
? { model: dynamicModel, modelContextWindowTokens: 32_768 }
33-
: dynamicModel,
34-
},
35-
} as never),
27+
model: defineDynamic(
28+
// Eve 0.91 resolves dynamic values from a function of the session.
29+
(Number(process.env.EVE_VERSION?.split(".")[1]) >= 91
30+
? () => ({ model: dynamicModel, modelContextWindowTokens: 32_768 })
31+
: {
32+
...(providerInstrumentation ? {} : { fallback: dynamicModel }),
33+
events: {
34+
"step.started": () =>
35+
providerInstrumentation
36+
? { model: dynamicModel, modelContextWindowTokens: 32_768 }
37+
: dynamicModel,
38+
},
39+
}) as never,
40+
),
3641
...(providerInstrumentation ? {} : { modelContextWindowTokens: 32_768 }),
3742
} as never);

‎e2e/scenarios/eve-instrumentation/package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"dependencies": {
88
"eve-v0-latest": {
99
"package": "eve",
10-
"range": ">=0.72 <0.84"
10+
"range": ">=0.72 <0.92"
1111
}
1212
}
1313
}
@@ -21,7 +21,7 @@
2121
"eve-v0-provider": "npm:eve@0.34.0",
2222
"eve-v0-70": "npm:eve@0.70.0",
2323
"eve-v0-72": "npm:eve@0.72.0",
24-
"eve-v0-latest": "npm:eve@0.83.3",
24+
"eve-v0-latest": "npm:eve@0.91.0",
2525
"zod": "4.3.6"
2626
},
2727
"pnpm": {

‎e2e/scenarios/eve-instrumentation/pnpm-lock.yaml‎

Lines changed: 37 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎e2e/scenarios/eve-instrumentation/scenario.test.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,12 @@ describe.sequential("eve instrumentation variants", () => {
292292
scenario: "eve-instrumentation",
293293
testRunId: expect.any(String),
294294
});
295-
// Eve 0.70+ runs subagents as tasks, so the call returns a receipt and
296-
// the result reaches the parent in a later step.
295+
// Earlier task APIs settle the call with a receipt. Eve 0.91's
296+
// call.settled event carries the completed child's result instead.
297297
expect(researcher?.output).toContain(
298-
scenario.tasks ? "Started task researcher-" : "Researcher result",
298+
scenario.tasks && Number(scenario.version.split(".")[1]) < 91
299+
? "Started task researcher-"
300+
: "Researcher result",
299301
);
300302
expect(researcher?.error).toBeUndefined();
301303

‎e2e/scenarios/eve-instrumentation/scenario.ts‎

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ async function main() {
3737
const evePackage = JSON.parse(
3838
await readFile(path.join(evePackageDir, "package.json"), "utf8"),
3939
) as { bin?: string | Record<string, string>; version: string };
40+
process.env.EVE_VERSION = evePackage.version;
4041
const eveBinPath =
4142
typeof evePackage.bin === "string" ? evePackage.bin : evePackage.bin?.eve;
4243
if (!eveBinPath) {
@@ -100,7 +101,14 @@ async function main() {
100101
[eveBin, "start", "--host", "127.0.0.1", "--port", String(port)],
101102
{
102103
cwd: process.cwd(),
103-
env: process.env,
104+
env: {
105+
...process.env,
106+
// The callback receiver is local. Eve 0.91 requires explicit dev mode
107+
// before accepting loopback callback URLs.
108+
...(Number(process.env.EVE_VERSION?.split(".")[1]) >= 91
109+
? { EVE_DEV: "1" }
110+
: {}),
111+
},
104112
stdio: ["ignore", "pipe", "pipe"],
105113
},
106114
);
@@ -171,6 +179,9 @@ async function main() {
171179
await runTracedCallerSession(baseUrl);
172180
}
173181
await new Promise((resolve) => setTimeout(resolve, 5000));
182+
} catch (error) {
183+
console.error(output());
184+
throw error;
174185
} finally {
175186
await stopServer(server);
176187
}
@@ -204,6 +215,9 @@ async function runTracedCallerSession(baseUrl: string): Promise<void> {
204215
async (span) => {
205216
const response = await fetch(`${baseUrl}/eve/v1/session`, {
206217
body: JSON.stringify({
218+
...(Number(process.env.EVE_VERSION?.split(".")[1]) >= 91
219+
? { protocolVersion: 3 }
220+
: {}),
207221
callback: {
208222
callId: "call-traced-caller",
209223
subagentName: "traced-caller",
@@ -366,38 +380,63 @@ async function streamUntil(
366380
if (!trimmed) {
367381
continue;
368382
}
369-
const event = JSON.parse(trimmed) as {
370-
data?: { childSessionId?: string; message?: string };
383+
type StreamEvent = {
384+
data?: {
385+
childSessionId?: string;
386+
sessionId?: string;
387+
message?: string;
388+
outcome?: string;
389+
error?: { message?: string };
390+
};
371391
type?: string;
372392
};
373-
nextIndex++;
374-
if (
375-
event.type === "step.failed" ||
376-
event.type === "turn.failed" ||
377-
event.type === "session.failed"
378-
) {
379-
throw new Error(
380-
`Eve emitted ${event.type}: ${event.data?.message ?? trimmed}`,
381-
);
382-
}
383-
if (
384-
event.type === "subagent.called" &&
385-
typeof event.data?.childSessionId === "string" &&
386-
!seenSessionIds.has(event.data.childSessionId)
387-
) {
388-
seenSessionIds.add(event.data.childSessionId);
389-
childStreams.push(
390-
streamUntil(
391-
baseUrl,
392-
event.data.childSessionId,
393-
seenSessionIds,
394-
"turn.completed",
395-
).then(() => undefined),
396-
);
393+
const record = JSON.parse(trimmed) as StreamEvent & {
394+
facts?: StreamEvent[];
395+
$eve?: string;
396+
next?: number;
397+
};
398+
if (record.$eve) {
399+
if (record.$eve === "position" && record.next !== undefined) {
400+
nextIndex = record.next;
401+
}
402+
continue;
397403
}
398-
if (event.type === until) {
399-
await Promise.all(childStreams);
400-
return nextIndex;
404+
nextIndex++;
405+
// Eve 0.91 streams commits containing multiple session facts.
406+
for (const event of record.facts ?? [record]) {
407+
if (
408+
event.type === "step.failed" ||
409+
event.type === "turn.failed" ||
410+
event.type === "session.failed" ||
411+
((event.type === "turn.settled" ||
412+
event.type === "session.ended") &&
413+
event.data?.outcome === "failed")
414+
) {
415+
throw new Error(
416+
`Eve emitted ${event.type}: ${event.data?.error?.message ?? event.data?.message ?? trimmed}`,
417+
);
418+
}
419+
const childSessionId =
420+
event.type === "child.opened"
421+
? event.data?.sessionId
422+
: event.type === "subagent.called"
423+
? event.data?.childSessionId
424+
: undefined;
425+
if (childSessionId && !seenSessionIds.has(childSessionId)) {
426+
seenSessionIds.add(childSessionId);
427+
childStreams.push(
428+
streamUntil(
429+
baseUrl,
430+
childSessionId,
431+
seenSessionIds,
432+
"turn.completed",
433+
).then(() => undefined),
434+
);
435+
}
436+
if (event.type === until || event.type === "turn.settled") {
437+
await Promise.all(childStreams);
438+
return nextIndex;
439+
}
401440
}
402441
}
403442
}

0 commit comments

Comments
 (0)