Add Vercel Eve as a third agent framework#179
Conversation
| } | ||
|
|
||
| function installDeps(root) { | ||
| const env = { ...process.env }; |
There was a problem hiding this comment.
This env inherits OPENAI_API_KEY and ANTHROPIC_API_KEY, and buildProject falls back to process.env too. That means arbitrary preinstall/postinstall scripts or build-time code from the agent's dependencies can read provider secrets before the turn starts. Scrub provider keys from the env used for install/build, then expose them only when sending the session.
| function installDeps(root) { | ||
| const env = { ...process.env }; | ||
| if (process.env.TAS_EVE_PNPM_STORE) env.PNPM_HOME = process.env.TAS_EVE_PNPM_STORE; | ||
| const args = ["install", "--ignore-workspace", "--prod=false"]; |
There was a problem hiding this comment.
Runtime installs should be locked to the committed pnpm-lock.yaml; otherwise a run can resolve newer transitive deps or rewrite the lockfile in the temp dir and become non-reproducible.
| const args = ["install", "--ignore-workspace", "--prod=false"]; | |
| const args = ["install", "--ignore-workspace", "--prod=false", "--frozen-lockfile"]; |
| const d = ev?.data ?? {}; | ||
| if (t === "message.completed" && typeof d.message === "string") { | ||
| text = d.message; | ||
| } else if (t === "message.appended" && typeof d.messageSoFar === "string" && !text) { |
There was a problem hiding this comment.
If Eve ever omits message.completed, this fallback keeps only the first appended chunk because text becomes truthy after the first event. Keep replacing it with the cumulative messageSoFar so the fallback returns the full reply.
| } else if (t === "message.appended" && typeof d.messageSoFar === "string" && !text) { | |
| } else if (t === "message.appended" && typeof d.messageSoFar === "string") { |
| const files = readFilesEnv(); | ||
| const message = process.env.TAS_MESSAGE && process.env.TAS_MESSAGE.length ? process.env.TAS_MESSAGE : "Hello."; | ||
|
|
||
| const root = materialize(files); |
There was a problem hiding this comment.
Each run leaves the materialized project, installed node_modules, .output, and workflow data under /tmp. In a long-running API container this will steadily consume disk; wrap the run in a finally and remove both temp dirs after emitting the result.
| // in-process, with no Vercel deploy and no AI Gateway — so the single | ||
| // non-negotiable rule is: configure the model with a DIRECT @ai-sdk provider, | ||
| // not the gateway string. Everything else is standard Eve. | ||
| const EVE_GUIDE: string = `# Eve Agent Guide (Tembo Agent Studio) |
There was a problem hiding this comment.
EVE_GUIDE is stamped with TAS_GUIDANCE_VERSION, but that hash is computed above without this string. After the first write, future edits to the Eve guide will keep the same marker and the sync path will treat stale files as current. Move this guide above the hash and include it in the digest.
| const { dir, ext } = FRAMEWORK_PATH[framework]; | ||
| const agentPath = `agents/${dir}/${agentSlug}.${ext}`; | ||
| const agentPath = | ||
| framework === "eve" |
There was a problem hiding this comment.
Now that eve can reach buildCreateAgentPrompt, it still gets the single-file spec instructions for name:, YAML connections:, and model: provider:model, which conflicts with Eve's directory/TypeScript shape. Branch the create prompt for Eve or gate the Pydantic/Cargo-specific text before enabling this option.
Validates that an Eve agent can run a turn one-shot, fully in-process with no listening socket, returning text + token usage — by building the Nitro output, patching out serve() to expose the fetch handler, running the durable workflow via WORKFLOW_LOCAL_* + a globalThis.fetch loopback, public channel auth, and a direct @ai-sdk provider. Captures the recipe (EVE_SPIKE_NOTES.md) and the working driver for the production harness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend half of the Eve framework integration (execution path): - api/scripts/run_eve.mjs: in-process one-shot harness (materialize agent dir, pnpm install, eve build, patch out serve(), local workflow loopback, drive via eve/client, emit __TAS_USAGE__/__TAS_STEPS__) — implements the proven spike. - api/src/runs/eve.rs: spawns the harness, reuses the pydantic sentinel parsers. - runner.rs/handlers.rs/mod.rs: Framework::Eve + dispatch + RunContext.agent_files + CreateRunRequest.agent_files + parse_framework "eve". - api/Dockerfile: Node 24 + pnpm@10 + run_eve.mjs + persistent pnpm store. cargo check passes. Web touch points (framework registry, readEveAgentDir, CAP scaffolding, docs) follow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make Eve a first-class framework in the web layer (tsc + eslint clean): - registry: "eve" in FRAMEWORKS/FRAMEWORK_LABELS, AgentFramework union - agent-format: EveSpec + buildEveSpec/extractEveModel (directory, not a parsed single file) - workspace-agents: FRAMEWORK_DIRS eve, readEveAgentDir, listEveAgents (scan subdirs), getAgentByName + resolveAgentForDispatch eve branches (live dir → agentFiles, no stable snapshot) - runs-api: agentFiles → agent_files; framework union - thread agentFiles through every dispatch consumer (run-now, webhook, slack, api-v1, composio, scheduler); guard chat-run + promote for eve (v1 scope) - FRAMEWORK_PATH eve entries with directory-aware entrypoint path Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- agent-guidance: EVE_GUIDE + GUIDANCE_EVE_PATH, wired into guidanceFilesFor / allGuidanceFiles. The guide's load-bearing rule: use a direct @ai-sdk provider model, not the AI Gateway string (TAS has no Gateway creds). - cap-api: frameworkFromAgentPath detects agents/eve/; frameworkGuidePath helper points create + chat prompts at the Eve guide. - docs: "Eve agents (experimental)" section in authoring-agents. Full branch green: cargo test (38) + tsc + eslint + vitest (101) all pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds Eve (github.com/vercel/eve) as a third agent framework alongside Pydantic AgentSpec and Cargo AI. An Eve agent committed at
agents/eve/<name>/is listed like any other agent and Run (plus scheduler / Slack / webhook / REST / Composio triggers) executes it — one turn, locally, in-process, with no Eve HTTP server — capturing reply + token usage into therun/run_steprows exactly like the other frameworks.Why this was non-trivial
Eve isn't a single-file spec — it's a directory of TypeScript files, and it has no public server-less one-shot execution mode (everything routes through its HTTP app + the Vercel Workflow SDK). A spike proved we can run a turn fully in-process with no listening socket by building the agent, patching out the Nitro
serve()call to expose the fetch handler, pointing the Workflow SDK's local backend at a sentinel host, and looping its callbacks back throughglobalThis.fetch. Seeapi/scripts/EVE_SPIKE_NOTES.md.What's in here
Backend (execution path)
api/scripts/run_eve.mjs— the harness: materialize the agent dir →pnpm install→eve build→ patchserve()→ workflow loopback → drive viaeve/client→ emit__TAS_USAGE__/__TAS_STEPS__.api/src/runs/eve.rs— spawns the harness, reuses the Pydantic sentinel parsers.runner.rs/handlers.rs/mod.rs—Framework::Evedispatch,RunContext.agent_files,CreateRunRequest.agent_files,parse_framework "eve".api/Dockerfile— Node 24 + pnpm@10 + the harness + a persistent pnpm store. (Adds a third language runtime to the API image — see review note.)Web (directory-as-agent modeling)
FRAMEWORKS/labels/AgentFramework),EveSpec+buildEveSpec/extractEveModel.workspace-agents.ts:readEveAgentDir,listEveAgents(scan subdirs),getAgentByName+resolveAgentForDispatcheve branches (live dir →agentFiles, no stable snapshot).runs-api.tsagentFiles→agent_files, threaded through every dispatch consumer.cap-api.ts),agent-guidance.tsEve guide, docs.Critical authoring rule (baked into the Eve guide)
TAS runs with the workspace's Anthropic/OpenAI key and no AI Gateway credentials, so Eve agents must use a direct
@ai-sdkprovider model (model: anthropic("claude-opus-4-8")), not the default gateway string.Validated locally (all green)
cargo test— 38 pass;cargo checkcleantsc --noEmit— 0 errorseslint— 0 errorsvitest— 101 passNOT yet verified — test plan before merge
agents/eve/hello/(direct-provider model), Run it, confirm output + token/step usage + cost render like a Pydantic run; confirm a second run hits the pnpm store cache.tasuser (HOME=/tmp,/opt/eve-store).v1 scope (flagged in code + docs)
just-bash); no Composio/native-MCP credential injection (Eve manages its own).Review note
This adds Node 24 + pnpm to the production API container and runs agent-authored TypeScript at run time (acceptable on TAS's single-tenant, customer-owned deployment, but worth a conscious look). If you'd prefer Eve execution in a separate sidecar/image rather than the main API container, that's a reasonable alternative the web dispatch could target instead.
🤖 Generated with Claude Code