Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { getGatewayModel } from "@db/services/settings";
import { scopeFromPrincipal } from "@agent/lib/principal-scope";

export default defineAgent({
experimental: {
tasks: true,
},
defaultTools: false,
model: defineDynamic({
events: {
"step.started": async (_event, ctx) => {
Expand Down
126 changes: 80 additions & 46 deletions agent/channels/eve.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { defineChannel } from "eve/channels";
import { eveChannel } from "eve/channels/eve";
import {
ForbiddenError,
localDev,
routeAuth,
UnauthenticatedError,
} from "eve/channels/auth";
import { z } from "zod";
Expand All @@ -20,52 +22,54 @@ import {

const authenticateLocalDev = localDev();

export default eveChannel({
auth: [
async (request) => {
const identity = await requestIdentityFromRequest(request);
if (!identity) return null;
const { phoneNumber, scope } = identity;

await requireOwnedRouteSubject(scope, request);

return {
attributes: {
conversationChannel: "eve",
phoneNumber,
workspaceId: scope.workspaceId,
},
authenticator: "authjs",
principalId: scope.userId,
principalType: "user",
};
},
async (request) => {
const local = await authenticateLocalDev(request);
if (!local) return null;

const scope = accessScopeForUser("better-auth:browser-benchmark");
await requireOwnedRouteSubject(scope, request);

return {
...local,
attributes: {
...local.attributes,
conversationChannel: "eve",
phoneNumber: "+15555550100",
workspaceId: scope.workspaceId,
},
principalId: scope.userId,
principalType: "user" as const,
};
},
() => {
throw new UnauthenticatedError({
code: "authentication_required",
message: "Sign in to continue.",
});
},
],
const authenticate: Parameters<typeof routeAuth>[1] = [
async (request) => {
const identity = await requestIdentityFromRequest(request);
if (!identity) return null;
const { phoneNumber, scope } = identity;

await requireOwnedRouteSubject(scope, request);

return {
attributes: {
conversationChannel: "eve",
phoneNumber,
workspaceId: scope.workspaceId,
},
authenticator: "authjs",
principalId: scope.userId,
principalType: "user",
};
},
async (request) => {
const local = await authenticateLocalDev(request);
if (!local) return null;

const scope = accessScopeForUser("better-auth:browser-benchmark");
await requireOwnedRouteSubject(scope, request);

return {
...local,
attributes: {
...local.attributes,
conversationChannel: "eve",
phoneNumber: "+15555550100",
workspaceId: scope.workspaceId,
},
principalId: scope.userId,
principalType: "user" as const,
};
},
() => {
throw new UnauthenticatedError({
code: "authentication_required",
message: "Sign in to continue.",
});
},
];

const channel = eveChannel({
auth: authenticate,
events: {
async "action.result"(event, _channel, session) {
if (
Expand Down Expand Up @@ -98,6 +102,36 @@ export default eveChannel({
},
});

// Eve callback handlers authenticate their capability tokens internally. Apply
// this app's caller and workspace ownership policy at the public route boundary.
const ownedCallbackRoutes = new Set([
"/eve/v1/connections/:name/callback/:attemptId/:token",
"/eve/v1/connections/:name/callback/:token",
"/eve/v1/callback/:token",
"/eve/v1/task-input/:token",
]);

export default defineChannel({
...channel,
// oxlint-disable-next-line oxc/no-map-spread -- Keep Eve's original route definitions intact when adding the app authorization boundary.
routes: channel.routes.map((route) => {
if (
route.transport === "websocket" ||
!ownedCallbackRoutes.has(route.path)
) {
return route;
}
return {
...route,
async handler(request, context) {
const principal = await routeAuth(request, authenticate);
if (principal instanceof Response) return principal;
return route.handler(request, context);
},
};
}),
});

// Routes without a session subject. Every other eve route must name a session
// this caller owns, either in the path or inside a hook token.
const subjectFreeRoutes = new Set(["/eve/v1/info", "/eve/v1/session"]);
Expand Down
2 changes: 1 addition & 1 deletion agent/subagents/browser-agent/lib/trace/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function traceTimelineRows(event: HookEvent): TraceTimelineRow[] {
detail: compactJson(action.input),
id: `${id}:${String(index)}`,
label:
action.kind === "tool-call"
action.kind === "tool-call" || action.kind === "workflow-tool-call"
? action.toolName
: action.kind === "load-skill"
? "Load skill"
Expand Down
3 changes: 0 additions & 3 deletions agent/tools/agent.ts

This file was deleted.

1 change: 1 addition & 0 deletions agent/tools/ask_question.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "eve/tools/ask_question";
3 changes: 0 additions & 3 deletions agent/tools/bash.ts

This file was deleted.

3 changes: 0 additions & 3 deletions agent/tools/connection_search.ts

This file was deleted.

3 changes: 0 additions & 3 deletions agent/tools/load_skill.ts

This file was deleted.

3 changes: 0 additions & 3 deletions agent/tools/read_file.ts

This file was deleted.

1 change: 1 addition & 0 deletions agent/tools/task_cancel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "eve/tools/task_cancel";
1 change: 1 addition & 0 deletions agent/tools/task_update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "eve/tools/task_update";
3 changes: 0 additions & 3 deletions agent/tools/todo.ts

This file was deleted.

1 change: 1 addition & 0 deletions agent/tools/web_fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "eve/tools/web_fetch";
1 change: 1 addition & 0 deletions agent/tools/web_search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "eve/tools/web_search";
3 changes: 0 additions & 3 deletions agent/tools/write_file.ts

This file was deleted.

12 changes: 11 additions & 1 deletion evals/browser/benchmark-activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,17 @@ export function browserBenchmarkActivity(
) {
for (const event of events.toReversed()) {
if (event.type === "message.appended") {
const message = activityLine(event.data.messageSoFar);
const message = activityLine(
events
.flatMap((candidate) =>
candidate.type === "message.appended" &&
candidate.data.turnId === event.data.turnId &&
candidate.data.stepIndex === event.data.stepIndex
? [candidate.data.messageDelta]
: []
)
.join("")
);
if (message) return message;
}
if (event.type === "message.completed") {
Expand Down
13 changes: 11 additions & 2 deletions evals/browser/tests/browser-benchmark-activity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,24 @@ describe("browser benchmark live activity", () => {
} satisfies MessageStreamEvent,
{
data: {
messageDelta: "Searching",
messageSoFar: "Searching current Brooklyn showtimes",
messageDelta: "Searching ",
sequence: 0,
stepIndex: 0,
turnId: "turn_1",
},
meta: { at: "2026-08-31T17:00:01.000Z", id: "evt_message" },
type: "message.appended",
} satisfies MessageStreamEvent,
{
data: {
messageDelta: "current Brooklyn showtimes",
sequence: 0,
stepIndex: 0,
turnId: "turn_1",
},
meta: { at: "2026-08-31T17:00:02.000Z", id: "evt_message_2" },
type: "message.appended",
} satisfies MessageStreamEvent,
])
).toBe("Searching current Brooklyn showtimes");
});
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
"@vercel/blob": "2.8.0",
"@vercel/connect": "^2.0.0",
"@vercel/oidc": "3.8.5",
"ai": "^7.0.79",
"ai": "^7.0.93",
"better-auth": "1.7.2",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"cmdk": "1.1.1",
"credit-card-type": "^10.3.0",
"drizzle-orm": "^0.45.2",
"eve": "^0.49.0",
"evlog": "2.27.1",
"eve": "https://pkg.eve.dev/59ec96cc99f65a80f7a2daf4ca5e2a0ad95455f2/eve.tgz",
"evlog": "2.28.1",
"lucide-react": "1.34.0",
"motion": "13.1.1",
"nanoid": "6.0.1",
Expand All @@ -43,7 +43,7 @@
"tailwind-merge": "3.6.0",
"tailwindcss": "4.3.3",
"use-stick-to-bottom": "1.1.6",
"zod": "4.4.3"
"zod": "4.5.4"
},
"devDependencies": {
"@electric-sql/pglite": "^0.5.8",
Expand Down
37 changes: 37 additions & 0 deletions patches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Eve and Linq patches

Eve is pinned to the official, immutable `pkg.eve.dev` build at
`59ec96cc99f65a80f7a2daf4ca5e2a0ad95455f2` (`0.52.2+main.59ec96cc99f65a80`).
It includes the merged turn-context placement fix in
[vercel/eve#3089](https://github.com/vercel/eve/pull/3089), which is absent from
npm's `0.52.2` release. Return to a registry version once a release contains this
commit and the patches below have been checked against it.

The tarball SHA-256 is
`633c0d9ebf5d0d5733d8cc2fdc5315952a7ccca8cb7902c31f550dc8ab0750a9`.
The lockfile also records its package integrity. pnpm matches URL dependency
patches by package name, so keep the immutable dependency pin when changing the
Eve patch.

## Remaining patches

- `@linqapp__chat-sdk-adapter@0.5.1.patch` adds `replyToMessageId` to native
message delivery, preserving attachments and idempotency keys.
- `eve@0.52.2+main.59ec96cc99f65a80.patch` applies that same reply option to the
adapter Eve actually bundles. It also redirects incomplete bundled Linq and
Chat SDK declaration exports to the explicitly installed packages. Eve's
runtime still uses its bundled adapter and Chat SDK.

Remove reply changes when upstream Linq and Eve's bundled adapter both support
native replies. Remove declaration bridges when the published declaration
files resolve without them. `linq-bundled-adapter.test.ts` exercises Eve's actual
bundled runtime; `linq-message-delivery.test.ts` covers application delivery and
the separately installed adapter.

The old Eve patches for `ask_question` and `task_cancel` exports are no longer
needed: both now have public entry points. Callback authorization is composed
in `agent/channels/eve.ts` using public `defineChannel` and `routeAuth` APIs.
The Linq webhook verifier already converts an unsuccessful OIDC verification
into `false`, so the extra bundled null-verifier patch was redundant.

No task-loop or prompt-placement patch is applied locally.
67 changes: 0 additions & 67 deletions patches/eve@0.49.0.patch

This file was deleted.

37 changes: 37 additions & 0 deletions patches/eve@0.52.2+main.59ec96cc99f65a80.patch

Large diffs are not rendered by default.

Loading