eve 0.52.2 → 0.52.3
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/CHANGELOG.md +20 -0
- package/dist/src/channel/types.d.ts +1 -0
- package/dist/src/chunks/{use-eve-agent-1NAM6ftx.js → use-eve-agent-BR8EpIOX.js} +27 -10
- package/dist/src/chunks/{use-eve-agent-BEjdhhhK.js → use-eve-agent-XnfjQt1Z.js} +27 -10
- package/dist/src/cli/commands/deploy.js +1 -1
- package/dist/src/cli/commands/extension-init.d.ts +2 -1
- package/dist/src/cli/commands/extension-init.js +1 -1
- package/dist/src/cli/commands/init.d.ts +2 -13
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/dev/run-interactive-ui.d.ts +3 -0
- package/dist/src/cli/dev/run-interactive-ui.js +1 -1
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/runner.d.ts +6 -0
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +5 -3
- package/dist/src/cli/dev/tui/setup-commands.js +2 -2
- package/dist/src/cli/dev/tui/status-line.js +1 -1
- package/dist/src/cli/dev/tui/tui.d.ts +4 -0
- package/dist/src/cli/dev/tui/tui.js +1 -1
- package/dist/src/cli/run.d.ts +1 -1
- package/dist/src/cli/run.js +2 -2
- package/dist/src/cli/telemetry/index.d.ts +15 -0
- package/dist/src/cli/telemetry/index.js +2 -2
- package/dist/src/client/session.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiler/compile-from-memory.d.ts +1 -0
- package/dist/src/compiler/compile-from-memory.js +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +18 -18
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/context/build-dynamic-tools.d.ts +2 -1
- package/dist/src/context/build-dynamic-tools.js +1 -1
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +3 -3
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/dynamic-tool-metadata.d.ts +1 -1
- package/dist/src/context/hook-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +2 -0
- package/dist/src/context/keys.js +1 -1
- package/dist/src/eve-channel/index.js +1 -1
- package/dist/src/execution/activity-events.d.ts +1 -0
- package/dist/src/execution/activity-events.js +1 -1
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/sandbox/prewarm.js +1 -1
- package/dist/src/execution/session-activity-projection.js +1 -1
- package/dist/src/execution/session-activity.js +1 -1
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/tasks/parent/dispatch.js +1 -1
- package/dist/src/execution/tasks/parent/task-cancel.d.ts +2 -2
- package/dist/src/execution/tasks/parent/task-cancel.js +1 -1
- package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
- package/dist/src/execution/tools/sleep-workflow.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/action-presentation.d.ts +13 -0
- package/dist/src/harness/action-presentation.js +1 -0
- package/dist/src/harness/compaction.js +1 -1
- package/dist/src/harness/coordination.js +1 -1
- package/dist/src/harness/emission.d.ts +1 -6
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/execute-tool.d.ts +2 -1
- package/dist/src/harness/finish-reason.d.ts +3 -0
- package/dist/src/harness/finish-reason.js +1 -0
- package/dist/src/harness/provider-tool-schemas.d.ts +1 -0
- package/dist/src/harness/provider-tool-schemas.js +1 -1
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/harness/stream-actions.d.ts +2 -2
- package/dist/src/harness/stream-actions.js +1 -1
- package/dist/src/harness/tool-call-input-errors.d.ts +4 -3
- package/dist/src/harness/tool-call-input-errors.js +1 -1
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/harness/tool-presentation.d.ts +5 -0
- package/dist/src/harness/tool-presentation.js +1 -0
- package/dist/src/harness/turn-client-context.d.ts +1 -0
- package/dist/src/harness/workflow-lifecycle.d.ts +2 -3
- package/dist/src/harness/workflow-lifecycle.js +1 -1
- package/dist/src/internal/application/compiled-artifacts.d.ts +3 -0
- package/dist/src/internal/application/compiled-artifacts.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/internal/authored-module-conditions.d.ts +2 -0
- package/dist/src/internal/authored-module-conditions.js +1 -0
- package/dist/src/internal/authored-module-loader.d.ts +3 -0
- package/dist/src/internal/authored-module-loader.js +3 -3
- package/dist/src/internal/authored-module-map-loader.d.ts +3 -1
- package/dist/src/internal/authored-module-map-loader.js +1 -1
- package/dist/src/internal/authored-runtime-modules.d.ts +2 -0
- package/dist/src/internal/authored-runtime-modules.js +1 -1
- package/dist/src/internal/bundler/nitro-rolldown.d.ts +1 -0
- package/dist/src/internal/bundler/nitro-rolldown.js +1 -1
- package/dist/src/internal/nitro/development-generation.d.ts +2 -0
- package/dist/src/internal/nitro/development-generation.js +1 -1
- package/dist/src/internal/nitro/host/configure-nitro-routes.js +1 -1
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
- package/dist/src/internal/nitro/host/prepare-application-host.js +1 -1
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
- package/dist/src/internal/workflow-bundle/builder-support.d.ts +5 -0
- package/dist/src/internal/workflow-bundle/builder.js +2 -2
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
- package/dist/src/protocol/activity.d.ts +6 -0
- package/dist/src/protocol/activity.js +1 -1
- package/dist/src/protocol/message.d.ts +13 -1
- package/dist/src/protocol/message.js +1 -1
- package/dist/src/public/channels/slack/activity-plan.js +1 -1
- package/dist/src/public/channels/slack/activity.js +2 -2
- package/dist/src/public/models/openai/chatgpt/credential-store.d.ts +6 -0
- package/dist/src/public/models/openai/chatgpt/credential-store.js +1 -0
- package/dist/src/public/models/openai/chatgpt/model.d.ts +1 -1
- package/dist/src/public/models/openai/chatgpt/oauth.d.ts +20 -0
- package/dist/src/public/models/openai/chatgpt/oauth.js +1 -0
- package/dist/src/public/models/openai/chatgpt/token-broker.d.ts +4 -3
- package/dist/src/public/models/openai/chatgpt/token-broker.js +1 -1
- package/dist/src/public/models/openai/chatgpt/transport.d.ts +1 -2
- package/dist/src/public/models/openai/index.d.ts +3 -4
- package/dist/src/public/tools/index.d.ts +1 -1
- package/dist/src/runtime/prompt/compose.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/types.d.ts +2 -0
- package/dist/src/self-modification/agent.js +1 -1
- package/dist/src/self-modification/extension/instructions.d.ts +1 -3
- package/dist/src/self-modification/extension/instructions.js +4 -2
- package/dist/src/self-modification/filesystem.js +1 -1
- package/dist/src/setup/flows/chatgpt-auth.d.ts +14 -2
- package/dist/src/setup/flows/chatgpt-auth.js +1 -1
- package/dist/src/setup/flows/model.d.ts +3 -2
- package/dist/src/setup/flows/model.js +1 -1
- package/dist/src/setup/flows/provider.js +1 -1
- package/dist/src/setup/flows/registry.d.ts +4 -0
- package/dist/src/setup/flows/registry.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/shared/boundary-hook-error.d.ts +4 -0
- package/dist/src/shared/boundary-hook-error.js +1 -0
- package/dist/src/{execution/activity-text.d.ts → shared/presentation-text.d.ts} +1 -1
- package/dist/src/shared/presentation-text.js +1 -0
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tools/definition.d.ts +21 -0
- package/dist/src/tools/definition.js +1 -1
- package/dist/src/tools/durable-callbacks.d.ts +37 -9
- package/dist/src/tools/durable-callbacks.js +1 -1
- package/dist/src/tools/dynamic.d.ts +2 -1
- package/dist/src/tools/framework/agent-contract.js +1 -1
- package/dist/src/tools/provided/bash.js +1 -1
- package/dist/src/tools/provided/glob.js +1 -1
- package/dist/src/tools/provided/grep.js +1 -1
- package/dist/src/tools/provided/load-skill.js +1 -1
- package/dist/src/tools/provided/read-file.js +1 -1
- package/dist/src/tools/provided/web-fetch.js +1 -1
- package/dist/src/tools/provided/write-file.js +1 -1
- package/dist/src/tools/tool-label.d.ts +1 -0
- package/dist/src/tools/tool-label.js +1 -0
- package/dist/src/tracing/agent-action-instrumentation.js +1 -1
- package/dist/src/tracing/agent-approval-instrumentation.js +1 -1
- package/dist/src/tracing/agent-channel-delivery-instrumentation.js +1 -1
- package/dist/src/tracing/agent-otel-provider.js +2 -2
- package/dist/src/tracing/agent-otel-session-context.js +1 -1
- package/dist/src/tracing/agent-otel-usage.d.ts +10 -0
- package/dist/src/tracing/agent-otel-usage.js +1 -1
- package/dist/src/tracing/agent-span-naming.d.ts +1 -0
- package/dist/src/tracing/agent-span-naming.js +1 -0
- package/dist/src/tracing/agent-tool-instrumentation.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/concepts/default-harness.md +6 -0
- package/docs/concepts/sessions-runs-and-streaming.md +4 -0
- package/docs/connections/overview.mdx +1 -1
- package/docs/guides/client/continuations.mdx +11 -0
- package/docs/guides/dynamic-capabilities.md +4 -4
- package/docs/guides/hooks.md +1 -1
- package/docs/guides/instrumentation.md +40 -1
- package/docs/guides/remote-agents.md +2 -0
- package/docs/instructions.mdx +1 -1
- package/docs/memory/overview.mdx +45 -13
- package/docs/reference/cli.md +6 -0
- package/docs/reference/telemetry.md +2 -2
- package/docs/reference/typescript-api.md +13 -9
- package/docs/sandbox.mdx +14 -2
- package/docs/subagents/index.mdx +8 -4
- package/docs/tools/overview.mdx +44 -2
- package/docs/tools/workflows.mdx +6 -0
- package/docs/tutorial/connect-a-warehouse.mdx +49 -26
- package/docs/tutorial/first-agent.mdx +20 -19
- package/docs/tutorial/guard-the-spend.mdx +4 -4
- package/docs/tutorial/how-it-runs.mdx +2 -3
- package/docs/tutorial/meta.json +2 -2
- package/docs/tutorial/query-sample-data.mdx +30 -19
- package/docs/tutorial/remember-definitions.mdx +5 -5
- package/docs/tutorial/run-analysis.mdx +155 -18
- package/docs/tutorial/ship-it.mdx +71 -81
- package/docs/tutorial/team-playbooks.mdx +10 -10
- package/package.json +1 -1
- package/dist/src/execution/activity-text.js +0 -1
- package/dist/src/internal/workflow-bundle/authored-workflow-modules.d.ts +0 -9
- package/dist/src/internal/workflow-bundle/authored-workflow-modules.js +0 -1
- package/dist/src/public/models/openai/chatgpt/codex-app-server.d.ts +0 -40
- package/dist/src/public/models/openai/chatgpt/codex-app-server.js +0 -1
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Your First Agent"
|
|
3
|
-
description: "
|
|
3
|
+
description: "Scaffold the analytics assistant, give it an analyst persona, run it, and ask a question."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
For a bespoke learning experience, start with the [eve template gallery](https://eve.dev/templates) or ask your coding agent to build your use case. For example:
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
- “Build an eve Slack agent that answers questions from our team’s documentation.”
|
|
9
|
+
- “Build an eve incident-response agent that investigates alerts using our observability tools.”
|
|
10
|
+
- “Build an eve GitHub maintainer that triages issues and summarizes pull requests.”
|
|
11
|
+
|
|
12
|
+
If you prefer a step-by-step walkthrough, continue with this tutorial. It constructs one app end to end: a data analytics assistant. You ask in natural language, and over eight steps it learns to query sample data, run analysis in a sandbox, remember your team's metric definitions, and refuse to exceed your query budget without asking.
|
|
13
|
+
|
|
14
|
+
Step 1 gets it talking. In Step 3 you create a small local dataset that you use throughout the tutorial. You can complete the tutorial without a warehouse or Vercel Connect account. [Connect a warehouse](./connect-a-warehouse) is an optional follow-up once your agent works.
|
|
9
15
|
|
|
10
16
|
## Prerequisites
|
|
11
17
|
|
|
12
18
|
- Node 24 or newer and npm.
|
|
13
|
-
- A model credential
|
|
19
|
+
- A working model credential, configured through [Getting Started](../getting-started). Keep the model and credential you already tested there.
|
|
14
20
|
|
|
15
21
|
If you have not run eve before, complete [Getting Started](../getting-started) first. Without a credential, "Run the agent" below fails when the runtime tries to reach the model; the dev TUI's `/model` flow walks you through pasting a key or linking a project.
|
|
16
22
|
|
|
@@ -21,24 +27,19 @@ npx eve@latest init analytics-assistant
|
|
|
21
27
|
cd analytics-assistant
|
|
22
28
|
```
|
|
23
29
|
|
|
24
|
-
The command writes the starter agent
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
below. It does not create a Vercel project or deploy. `init` creates the
|
|
30
|
+
The command writes the starter agent and built-in HTTP API channel
|
|
31
|
+
(`agent/channels/eve.ts`), installs dependencies, initializes Git, and offers
|
|
32
|
+
to start the development server. Stop the server if you started it before
|
|
33
|
+
continuing with the edits below. It does not create a Vercel project or deploy. `init` creates the
|
|
28
34
|
`analytics-assistant/` directory, so `cd` into it before running further
|
|
29
35
|
commands.
|
|
30
36
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
`agent/agent.ts` holds the model and config. Use a capable model for analysis work:
|
|
37
|
+
## Keep your configured model
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
model: "anthropic/claude-opus-4.8",
|
|
40
|
-
});
|
|
41
|
-
```
|
|
39
|
+
`agent/agent.ts` holds the model and config. Use the same model and credentials
|
|
40
|
+
that worked in Getting Started. The tutorial does not require switching providers.
|
|
41
|
+
If you need to configure this new project, use `/model` in the dev TUI before
|
|
42
|
+
sending your first question.
|
|
42
43
|
|
|
43
44
|
## Give it an analyst persona
|
|
44
45
|
|
|
@@ -53,7 +54,7 @@ You are a senior data analyst. You answer questions about the team's data.
|
|
|
53
54
|
the data, say so plainly.
|
|
54
55
|
```
|
|
55
56
|
|
|
56
|
-
Instructions are identity and standing rules. On-demand procedures belong in skills (Step
|
|
57
|
+
Instructions are identity and standing rules. On-demand procedures belong in skills (Step 6), and actions belong in tools (Step 3). See [Instructions](../instructions).
|
|
57
58
|
|
|
58
59
|
## Run the agent
|
|
59
60
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Guard the Spend"
|
|
3
|
-
description: "Part
|
|
3
|
+
description: "Part 7 of the Build an Agent tutorial. Gate expensive queries with cost-based approval. The agent pauses, asks, and resumes."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Use the sample dataset to practice an approval before a tool runs. The example assigns a simulated scan cost to each query; it does not incur warehouse charges. Make the analytics assistant stop and check with you before a query exceeds that threshold. The agent pauses, asks you, and resumes with your answer. That's human-in-the-loop, and you wire it up with one field on the tool.
|
|
7
7
|
|
|
8
8
|
`approval` runs before `execute`. Return `"user-approval"` and the turn parks on an approval request; you answer, and the run picks up from that exact step. The function gets the tool input, so you can make the decision cost-based.
|
|
9
9
|
|
|
10
10
|
## Estimate, then gate
|
|
11
11
|
|
|
12
|
-
This step keeps `run_sql` on the Step 3 sample dataset so you can demo the gate locally.
|
|
12
|
+
This step keeps `run_sql` on the Step 3 sample dataset so you can demo the gate locally. If you later [connect a warehouse](./connect-a-warehouse), use the provider's dry-run estimate to decide when approval is needed.
|
|
13
13
|
|
|
14
14
|
Add a cheap estimator and gate `run_sql` on it:
|
|
15
15
|
|
|
@@ -30,7 +30,7 @@ const THRESHOLD_GB = 50;
|
|
|
30
30
|
|
|
31
31
|
export default defineTool({
|
|
32
32
|
description: "Run a read-only SQL query against the analytics tables.",
|
|
33
|
-
inputSchema: z.object({ sql: z.string() }),
|
|
33
|
+
inputSchema: z.object({ sql: z.string().max(10_000) }),
|
|
34
34
|
// Cost-based gate: only the expensive queries need a human yes.
|
|
35
35
|
approval: ({ toolInput }) =>
|
|
36
36
|
estimateScanGb(toolInput?.sql ?? "") > THRESHOLD_GB ? "user-approval" : "not-applicable",
|
|
@@ -15,9 +15,8 @@ Each turn runs as a durable workflow, and eve saves progress at every step. Comp
|
|
|
15
15
|
|
|
16
16
|
That's why the features in the rest of this tutorial work the way they do:
|
|
17
17
|
|
|
18
|
-
- The
|
|
19
|
-
- The
|
|
20
|
-
- The spend approval in Step 8 pauses the turn on your yes/no, then picks up exactly where it left off.
|
|
18
|
+
- The metric glossary in Step 5 survives across turns. State is checkpointed at step boundaries, so it sticks.
|
|
19
|
+
- The spend approval in Step 7 pauses the turn on your yes/no, then picks up exactly where it left off.
|
|
21
20
|
|
|
22
21
|
You author capabilities, including tools, instructions, channels, and skills. eve drives the model-to-tool loop and decides when a turn continues, waits, or ends. You never write that loop yourself.
|
|
23
22
|
|
package/docs/tutorial/meta.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Query Sample Data"
|
|
3
|
-
description: "Part 3 of the Build an Agent tutorial. Add a run_sql tool over
|
|
3
|
+
description: "Part 3 of the Build an Agent tutorial. Add a run_sql tool over a local sample dataset and watch the tool loop."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
The analytics assistant can hold a conversation, but it can't see a single row of data. Give it a tool. A tool is the action primitive. Typed input goes in, your code runs, structured output comes back. The name the model sees is the filename, so `agent/tools/run_sql.ts` becomes the tool `run_sql`.
|
|
@@ -14,27 +14,21 @@ npm install sql.js
|
|
|
14
14
|
npm install --save-dev @types/sql.js
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
`sql.js` loads its WebAssembly binary from the installed package at runtime. Keep the package external so the binary remains next to its JavaScript:
|
|
17
|
+
`sql.js` loads its WebAssembly binary from the installed package at runtime. Keep the package external so the binary remains next to its JavaScript. Add this `build` property inside your existing `defineAgent({ ... })` in `agent/agent.ts`, keeping your configured `model` and its imports:
|
|
18
18
|
|
|
19
|
-
```ts
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
model: "anthropic/claude-opus-4.8",
|
|
24
|
-
build: {
|
|
25
|
-
externalDependencies: ["sql.js"],
|
|
26
|
-
},
|
|
27
|
-
});
|
|
19
|
+
```ts
|
|
20
|
+
build: {
|
|
21
|
+
externalDependencies: ["sql.js"],
|
|
22
|
+
},
|
|
28
23
|
```
|
|
29
24
|
|
|
30
25
|
Restart the dev server after changing `externalDependencies`. eve reads this setting when the server starts.
|
|
31
26
|
|
|
32
27
|
## A tiny sample dataset
|
|
33
28
|
|
|
34
|
-
|
|
29
|
+
Create `agent/lib/sample-db.ts` with the dataset below. These four orders and three customers supply the data for every remaining tutorial step. No database server, account, or connection string is needed. The dataset is recreated when the app restarts.
|
|
35
30
|
|
|
36
31
|
```ts title="agent/lib/sample-db.ts"
|
|
37
|
-
// A toy SQLite-in-memory stand-in. Swap for your real warehouse in Step 4.
|
|
38
32
|
import initSqlJs from "sql.js";
|
|
39
33
|
|
|
40
34
|
const SEED = `
|
|
@@ -60,12 +54,27 @@ async function db() {
|
|
|
60
54
|
|
|
61
55
|
export async function runReadOnlySql(sql: string) {
|
|
62
56
|
const database = await db();
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
const query = `SELECT * FROM (\n${sql.trim().replace(/;$/, "")}\n) LIMIT 501`;
|
|
58
|
+
const statement = database.prepare(query);
|
|
59
|
+
try {
|
|
60
|
+
// prepare() accepts the first statement; reject any unparsed trailing SQL.
|
|
61
|
+
if (statement.getSQL() !== query) {
|
|
62
|
+
throw new Error("Provide a single SELECT query.");
|
|
63
|
+
}
|
|
64
|
+
const columns = statement.getColumnNames();
|
|
65
|
+
const rows: unknown[][] = [];
|
|
66
|
+
while (rows.length < 501 && statement.step()) rows.push(statement.get());
|
|
67
|
+
return { columns, rows };
|
|
68
|
+
} finally {
|
|
69
|
+
statement.free();
|
|
70
|
+
}
|
|
66
71
|
}
|
|
67
72
|
```
|
|
68
73
|
|
|
74
|
+
The outer `SELECT` lets SQLite accept queries, including `WITH` queries, while rejecting writes and configuration statements. The helper prepares one statement, rejects trailing SQL, and reads at most 501 rows. The extra row lets the tool report that its 500-row output was truncated. It frees the statement even when a query fails.
|
|
75
|
+
|
|
76
|
+
This is an in-memory tutorial database. A row limit does not bound the work needed to compute a query; use database-enforced read-only permissions and query timeouts when adapting the tool to a production database.
|
|
77
|
+
|
|
69
78
|
## Define the run_sql tool
|
|
70
79
|
|
|
71
80
|
```ts title="agent/tools/run_sql.ts"
|
|
@@ -78,7 +87,7 @@ export default defineTool({
|
|
|
78
87
|
"Run a read-only SQL query against the analytics tables (orders, customers) " +
|
|
79
88
|
"and return the columns and rows.",
|
|
80
89
|
inputSchema: z.object({
|
|
81
|
-
sql: z.string().describe("A single read-only SELECT statement."),
|
|
90
|
+
sql: z.string().max(10_000).describe("A single read-only SELECT statement."),
|
|
82
91
|
}),
|
|
83
92
|
async execute({ sql }) {
|
|
84
93
|
const { columns, rows } = await runReadOnlySql(sql);
|
|
@@ -98,8 +107,10 @@ Restart the dev server with `npm run dev` and ask:
|
|
|
98
107
|
Which customer has spent the most, and how much?
|
|
99
108
|
```
|
|
100
109
|
|
|
101
|
-
Watch the loop play out in the TUI. The model emits a `run_sql` call, eve runs your `execute`, and the rows come back as a tool result. The model reads them and answers with a real number. eve drove the whole loop
|
|
110
|
+
Watch the loop play out in the TUI. The model emits a `run_sql` call, eve runs your `execute`, and the rows come back as a tool result. The model reads them and answers with a real number. The result is Globex with 9,900 cents ($99.00). The amounts are stored in cents, so divide by 100 when reporting dollars. eve drove the whole loop; you supplied the tool.
|
|
111
|
+
|
|
112
|
+
Keep `run_sql` and the sample database for the rest of the tutorial. When you have your own data service, [Connect a warehouse](./connect-a-warehouse) explains the optional integration.
|
|
102
113
|
|
|
103
|
-
→ Next: [
|
|
114
|
+
→ Next: [Run analysis](./run-analysis)
|
|
104
115
|
|
|
105
116
|
Learn more: [Tools](../tools)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Remember Definitions"
|
|
3
|
-
description: "Part
|
|
3
|
+
description: "Part 5 of the Build an Agent tutorial. Use defineState to remember the team's metric glossary across turns."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Every team has house definitions for the analytics assistant. "Active" means a purchase in the last 30 days, revenue is net of refunds, a "week" starts Monday. Re-explaining all of that on every turn is a waste. State gives the agent a place to keep them.
|
|
@@ -55,15 +55,15 @@ export default defineTool({
|
|
|
55
55
|
## See it persist
|
|
56
56
|
|
|
57
57
|
```text
|
|
58
|
-
> For
|
|
58
|
+
> For this dataset, a high-value customer has at least $50 in total orders.
|
|
59
59
|
Remember that.
|
|
60
|
-
→ calls define_metric("
|
|
60
|
+
→ calls define_metric("high-value customer", "at least $50 in total orders")
|
|
61
61
|
|
|
62
|
-
> How many
|
|
62
|
+
> How many high-value customers do we have?
|
|
63
63
|
→ recalls the definition, writes the matching SQL, answers
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
The second turn is a separate turn in the same session, yet the definition is still there. State checkpoints at step boundaries, so it's the same durability from [Step 2](./how-it-runs), now applied to your own data.
|
|
66
|
+
The sample data has two high-value customers: Acme ($57) and Globex ($99). This definition works with the same fixed dataset whenever you run the tutorial. The second turn is a separate turn in the same session, yet the definition is still there. State checkpoints at step boundaries, so it's the same durability from [Step 2](./how-it-runs), now applied to your own data.
|
|
67
67
|
|
|
68
68
|
State is scoped to a session and isolated per agent, so a subagent starts with fresh state and never sees the parent's. Need to reset something each turn? Call `update(() => fresh)` in a lifecycle hook. More in [State](../concepts/state).
|
|
69
69
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Run Analysis"
|
|
3
|
-
description: "Part
|
|
3
|
+
description: "Part 4 of the Build an Agent tutorial. Seed the sample database schema into the sandbox workspace, then compute and chart beyond SQL."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
SQL tells the analytics assistant the numbers, but a cohort curve, a forecast, or a chart needs real computation. That's what the sandbox is for. It's an isolated bash environment with a `/workspace` filesystem, and every agent gets exactly one.
|
|
@@ -9,7 +9,7 @@ This takes two pieces. First seed reference files the model can read, then compu
|
|
|
9
9
|
|
|
10
10
|
## Seed the schema into the workspace
|
|
11
11
|
|
|
12
|
-
Mount the
|
|
12
|
+
Mount the sample database schema into the sandbox so the model isn't guessing at table shapes. Seeding uses the folder sandbox layout, where anything under `agent/sandbox/workspace/` lands in the live `/workspace` cwd at session bootstrap.
|
|
13
13
|
|
|
14
14
|
```text
|
|
15
15
|
agent/sandbox/
|
|
@@ -21,65 +21,202 @@ agent/sandbox/
|
|
|
21
21
|
```sql
|
|
22
22
|
-- agent/sandbox/workspace/schema.sql
|
|
23
23
|
-- Reference only: table shapes the analyst can read before writing queries.
|
|
24
|
-
CREATE TABLE orders (id
|
|
25
|
-
CREATE TABLE customers (id
|
|
24
|
+
CREATE TABLE orders (id INTEGER, customer_id INTEGER, amount_cents INTEGER, created_at TEXT);
|
|
25
|
+
CREATE TABLE customers (id INTEGER, name TEXT, plan TEXT);
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Top-level workspace entries get advertised to the model automatically, so it knows `schema.sql` is there to read.
|
|
28
|
+
Top-level workspace entries get advertised to the model automatically, so it knows `schema.sql` is there to read. A `workspace/` folder seeds your files without requiring a sandbox definition. Add one next to install the chart dependencies.
|
|
29
|
+
|
|
30
|
+
## Install the chart dependencies
|
|
31
|
+
|
|
32
|
+
Python needs a sandbox that runs real binaries. For local development, start a Docker-compatible daemon and run `docker info` to verify it is reachable before restarting `npm run dev`. eve also supports [microsandbox](../sandbox#microsandbox) on compatible hosts. The `just-bash` fallback cannot run Python or install packages; installing Python on your laptop does not add it to the sandbox. You can [continue to the next chapter](./remember-definitions) if you do not want to set up a local container or VM yet.
|
|
33
|
+
|
|
34
|
+
The default eve images for Docker, microsandbox, and Vercel Sandbox include Python, but not matplotlib. Add this definition alongside `workspace/` to install matplotlib in a virtual environment. It uses the same environment for installation and chart execution, without changing the system Python:
|
|
35
|
+
|
|
36
|
+
```ts title="agent/sandbox/sandbox.ts"
|
|
37
|
+
import { defineSandbox } from "eve/sandbox";
|
|
38
|
+
|
|
39
|
+
export default defineSandbox({
|
|
40
|
+
async bootstrap({ use }) {
|
|
41
|
+
const sandbox = await use();
|
|
42
|
+
const commands = [
|
|
43
|
+
"sudo apt-get update && sudo apt-get install -y python3 python3-venv",
|
|
44
|
+
"python3 -m venv /workspace/.venv",
|
|
45
|
+
"/workspace/.venv/bin/python -m pip install matplotlib==3.10.8",
|
|
46
|
+
"/workspace/.venv/bin/python -c \"import matplotlib.pyplot; print('Chart dependencies ready')\"",
|
|
47
|
+
];
|
|
48
|
+
for (const command of commands) {
|
|
49
|
+
const result = await sandbox.run({ command });
|
|
50
|
+
if (result.exitCode !== 0) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Chart setup failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Bootstrap runs when eve builds the sandbox template, and sessions inherit the installed environment. The first build needs network access to Ubuntu package repositories and PyPI. Keep the default network policy while following this example; a custom `deny-all` or allow-list policy must permit these downloads during bootstrap. The same definition uses Vercel Sandbox after deployment to Vercel.
|
|
61
|
+
|
|
62
|
+
Restart `npm run dev` after adding the definition. Before asking for a chart, send this message in the TUI:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
Run /workspace/.venv/bin/python -c "import matplotlib; print(matplotlib.__version__)" in the sandbox.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The command should print `3.10.8`. If setup fails, check the dev server's sandbox logs for the failing command. Resolve the package download or backend error before continuing.
|
|
29
69
|
|
|
30
70
|
## Compute and chart in the sandbox
|
|
31
71
|
|
|
32
72
|
The built-in `bash`, `read_file`, and `write_file` tools already target the sandbox. When you write your own analysis steps, grab a live handle with `ctx.getSandbox()`:
|
|
33
73
|
|
|
34
74
|
```ts title="agent/tools/chart_series.ts"
|
|
75
|
+
import { randomUUID } from "node:crypto";
|
|
35
76
|
import { defineTool } from "eve/tools";
|
|
36
77
|
import { z } from "zod";
|
|
37
78
|
|
|
38
79
|
export default defineTool({
|
|
39
80
|
description:
|
|
40
81
|
"Plot a time series to a PNG in the workspace. Pass {date, value} points; " +
|
|
41
|
-
"returns the chart path.",
|
|
82
|
+
"returns the chart path and PNG data for the client to save.",
|
|
42
83
|
inputSchema: z.object({
|
|
43
|
-
title: z.string(),
|
|
44
|
-
points: z
|
|
84
|
+
title: z.string().min(1).max(120),
|
|
85
|
+
points: z
|
|
86
|
+
.array(z.object({ date: z.string().max(40), value: z.number() }))
|
|
87
|
+
.min(1)
|
|
88
|
+
.max(366),
|
|
45
89
|
}),
|
|
46
90
|
async execute({ title, points }, ctx) {
|
|
47
91
|
const sandbox = await ctx.getSandbox();
|
|
92
|
+
const directory = `analysis/${randomUUID()}`;
|
|
48
93
|
await sandbox.writeTextFile({
|
|
49
|
-
path:
|
|
94
|
+
path: `${directory}/series.json`,
|
|
50
95
|
content: JSON.stringify({ title, points }),
|
|
51
96
|
});
|
|
52
97
|
await sandbox.writeTextFile({
|
|
53
|
-
path:
|
|
98
|
+
path: `${directory}/plot.py`,
|
|
54
99
|
content: [
|
|
55
100
|
"import json, matplotlib",
|
|
56
101
|
"matplotlib.use('Agg')",
|
|
57
102
|
"import matplotlib.pyplot as plt",
|
|
58
103
|
"d = json.load(open('series.json'))",
|
|
104
|
+
"plt.figure(figsize=(8, 4), dpi=100)",
|
|
59
105
|
"plt.plot([p['date'] for p in d['points']], [p['value'] for p in d['points']])",
|
|
60
106
|
"plt.title(d['title']); plt.savefig('chart.png')",
|
|
61
107
|
].join("\n"),
|
|
62
108
|
});
|
|
63
|
-
const root = sandbox.resolvePath(
|
|
64
|
-
await sandbox.run({
|
|
65
|
-
|
|
109
|
+
const root = sandbox.resolvePath(directory);
|
|
110
|
+
const result = await sandbox.run({
|
|
111
|
+
command: `cd ${JSON.stringify(root)} && /workspace/.venv/bin/python plot.py`,
|
|
112
|
+
});
|
|
113
|
+
if (result.exitCode !== 0) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Chart generation failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const chart = `${root}/chart.png`;
|
|
119
|
+
const png = await sandbox.readBinaryFile({ path: chart });
|
|
120
|
+
if (!png || png.byteLength === 0) {
|
|
121
|
+
throw new Error("The chart command did not produce a PNG.");
|
|
122
|
+
}
|
|
123
|
+
if (png.byteLength > 1024 * 1024) {
|
|
124
|
+
throw new Error("The chart exceeds the 1 MiB download limit. Plot fewer points.");
|
|
125
|
+
}
|
|
126
|
+
return { chart, pngBase64: Buffer.from(png).toString("base64") };
|
|
127
|
+
},
|
|
128
|
+
toModelOutput({ chart }) {
|
|
129
|
+
return {
|
|
130
|
+
type: "text",
|
|
131
|
+
value: `Created ${chart}. The PNG is available in the tool result for the client to save.`,
|
|
132
|
+
};
|
|
66
133
|
},
|
|
67
134
|
});
|
|
68
135
|
```
|
|
69
136
|
|
|
70
|
-
|
|
137
|
+
The tool uses the Python environment installed in bootstrap. `matplotlib.use('Agg')` selects a renderer that writes PNGs without a desktop display.
|
|
71
138
|
|
|
72
|
-
|
|
139
|
+
`sandbox.run()` returns an exit code even when the command fails. Check `exitCode` before returning a chart path. Throwing an error gives the model the failed command's output, including a missing Python executable or matplotlib import, so it can report the failure instead of claiming a chart exists.
|
|
140
|
+
|
|
141
|
+
Ask for a chart using the sample data from Step 3:
|
|
73
142
|
|
|
74
143
|
```text
|
|
75
|
-
Plot
|
|
144
|
+
Plot daily order revenue in dollars for May 2026.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The model queries `run_sql` for daily totals, divides `amount_cents` by 100, and passes date/value points to `chart_series`. The sample data has four dates with revenue of $42, $15, $99, and $8. The chart uses the same data you queried in Step 3.
|
|
148
|
+
|
|
149
|
+
## Save the chart to your computer
|
|
150
|
+
|
|
151
|
+
A sandbox path is not a download URL or a file on your laptop. `readBinaryFile` copies the generated PNG out of the sandbox into the tool result, where your client can use it. `toModelOutput` sends the model only a short description, so the image's base64 data does not fill its context. The TUI shows tool results as text; it does not save or preview this PNG automatically.
|
|
152
|
+
|
|
153
|
+
Create this script in your project root. It asks for a chart through the same local HTTP API as the TUI, then saves the successful tool result as `chart.png` in the directory where you run the script:
|
|
154
|
+
|
|
155
|
+
```ts title="save-chart.ts"
|
|
156
|
+
import { writeFile } from "node:fs/promises";
|
|
157
|
+
import { resolve } from "node:path";
|
|
158
|
+
import { Client } from "eve/client";
|
|
159
|
+
import { z } from "zod";
|
|
160
|
+
|
|
161
|
+
const host = process.argv[2];
|
|
162
|
+
if (!host) throw new Error("Usage: node save-chart.ts <dev-server-url>");
|
|
163
|
+
|
|
164
|
+
const client = new Client({ host });
|
|
165
|
+
const { response } = await client.sessions.create({
|
|
166
|
+
message: "Use run_sql and chart_series to plot daily order revenue in May 2026.",
|
|
167
|
+
});
|
|
168
|
+
const turn = await response.result();
|
|
169
|
+
if (turn.status === "failed") throw new Error("The agent turn failed. Check the dev server logs.");
|
|
170
|
+
if (turn.inputRequests.length > 0) {
|
|
171
|
+
const prompts = turn.inputRequests.map((request) => request.prompt).join("\n");
|
|
172
|
+
throw new Error(
|
|
173
|
+
`Session ${turn.sessionId} needs your input before it can create the chart:\n${prompts}`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const chartOutput = z.object({ pngBase64: z.string().min(1).max(1_398_104) });
|
|
178
|
+
let saved = false;
|
|
179
|
+
for (const event of turn.events) {
|
|
180
|
+
if (event.type !== "action.result") continue;
|
|
181
|
+
const result = event.data.result;
|
|
182
|
+
if (result.kind !== "tool-result" || result.toolName !== "chart_series" || result.isError)
|
|
183
|
+
continue;
|
|
184
|
+
|
|
185
|
+
const { pngBase64 } = chartOutput.parse(result.output);
|
|
186
|
+
const png = Buffer.from(pngBase64, "base64");
|
|
187
|
+
if (
|
|
188
|
+
png.byteLength > 1024 * 1024 ||
|
|
189
|
+
!png.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))
|
|
190
|
+
) {
|
|
191
|
+
throw new Error("The tool result is not a PNG within the 1 MiB download limit.");
|
|
192
|
+
}
|
|
193
|
+
await writeFile("chart.png", png);
|
|
194
|
+
saved = true;
|
|
195
|
+
}
|
|
196
|
+
if (!saved) {
|
|
197
|
+
console.error(turn.message ?? "The agent returned no explanation.");
|
|
198
|
+
throw new Error("No chart was returned. Check the agent's response above and sandbox setup.");
|
|
199
|
+
}
|
|
200
|
+
console.log(`Saved ${resolve("chart.png")}`);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Keep `npm run dev` running. In a second terminal, run the script with the actual URL printed by the dev server; for example:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
node save-chart.ts http://localhost:2000
|
|
76
207
|
```
|
|
77
208
|
|
|
78
|
-
|
|
209
|
+
Open the saved `chart.png` in your image viewer. This creates a separate session from your TUI conversation and overwrites an existing `chart.png`. A failed tool call or a turn without a chart fails the script instead of claiming a file was saved.
|
|
210
|
+
|
|
211
|
+
This step does not require approvals. If you return after adding the approval gate in [Guard the spend](./guard-the-spend), the script reports any pending request with its session ID. [Respond through the client](../guides/client/messages#answer-human-input-requests) before collecting that session's chart result.
|
|
212
|
+
|
|
213
|
+
The same client can save a chart from a hosted agent: pass the deployment URL and configure [client authentication](../guides/client/overview#authentication) for that deployment. The file is saved on the computer running the script. In a web UI, consume the same `action.result` output to build an image preview or download control. Returning bytes from a tool does not add that UI automatically.
|
|
214
|
+
|
|
215
|
+
This example caps charts at 1 MiB because tool outputs are persisted with session events. For larger files, copy the bytes to your application's file storage and return an authorized download URL instead. Sandbox files themselves are not permanent artifact storage.
|
|
79
216
|
|
|
80
217
|
## Secrets stay out of the sandbox
|
|
81
218
|
|
|
82
|
-
The sandbox has no `process.env` and no access to your app's secrets.
|
|
219
|
+
The sandbox has no `process.env` and no access to your app's secrets. The `run_sql` tool and sample database run in your app. You pass only the query results to the chart tool; the sandbox does not need database credentials.
|
|
83
220
|
|
|
84
221
|
The local backend runs the sandbox on your laptop during `eve dev`; on Vercel it runs on Vercel Sandbox. Lifecycle, backends, and network policy are in [Sandbox](../sandbox).
|
|
85
222
|
|