eve 0.52.2 → 0.52.4
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 +33 -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/commands/registry-add-command.d.ts +3 -0
- package/dist/src/cli/commands/registry-add-command.js +1 -0
- package/dist/src/cli/commands/registry-metadata.d.ts +4 -1
- package/dist/src/cli/commands/registry-metadata.js +1 -1
- package/dist/src/cli/commands/registry.d.ts +1 -2
- package/dist/src/cli/commands/registry.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/evals/reporters/index.d.ts +1 -0
- package/dist/src/evals/reporters/index.js +1 -1
- package/dist/src/evals/runner/reporters/datadog.d.ts +137 -0
- package/dist/src/evals/runner/reporters/datadog.js +3 -0
- 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/parked-delivery-wait.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-options.d.ts +10 -0
- package/dist/src/execution/sandbox/bindings/vercel-options.js +1 -0
- package/dist/src/execution/sandbox/bindings/vercel.js +2 -1
- package/dist/src/execution/sandbox/prewarm.js +1 -1
- package/dist/src/execution/sandbox/shell-quote.d.ts +1 -12
- package/dist/src/execution/sandbox/shell-quote.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/session-command-inbox.js +1 -1
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/tasks/child/workflow.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/tool-auth.d.ts +4 -32
- package/dist/src/execution/tool-auth.js +1 -1
- package/dist/src/execution/tools/connection-search.js +2 -2
- package/dist/src/execution/tools/sleep-workflow.js +1 -1
- package/dist/src/execution/tools/subagent/accept-event-step.d.ts +1 -1
- package/dist/src/execution/tools/subagent/accept-event-step.js +1 -1
- package/dist/src/execution/tools/subagent/invoke-agent.js +1 -1
- package/dist/src/execution/tools/subagent/invoke-step.js +1 -1
- package/dist/src/execution/tools/workflow/ask.d.ts +3 -2
- package/dist/src/execution/tools/workflow/ask.js +1 -1
- package/dist/src/execution/tools/workflow/authorization-completion.d.ts +3 -0
- package/dist/src/execution/tools/workflow/authorization-completion.js +1 -0
- package/dist/src/execution/tools/workflow/body.d.ts +2 -0
- package/dist/src/execution/tools/workflow/body.js +1 -1
- package/dist/src/execution/tools/workflow/step-context.d.ts +31 -0
- package/dist/src/execution/tools/workflow/step-context.js +1 -0
- package/dist/src/execution/tools/workflow/step-execution.d.ts +3 -0
- package/dist/src/execution/tools/workflow/step-execution.js +1 -0
- package/dist/src/execution/tools/workflow/step.d.ts +3 -0
- package/dist/src/execution/tools/workflow/step.js +1 -0
- package/dist/src/execution/turn-workflow-tool-run.js +1 -1
- package/dist/src/execution/wire/session-inbox-contract.d.ts +7 -0
- package/dist/src/execution/wire/session-inbox-contract.js +1 -1
- package/dist/src/execution/wire/session-inbox-resume.d.ts +2 -0
- package/dist/src/execution/wire/session-inbox-resume.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/authorization.d.ts +5 -2
- package/dist/src/harness/authorization.js +1 -1
- 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/connection.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-package-boundary.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/vercel/build-command.d.ts +2 -1
- package/dist/src/internal/vercel/build-command.js +1 -1
- package/dist/src/internal/workflow-bundle/builder-support.d.ts +5 -0
- package/dist/src/internal/workflow-bundle/builder-support.js +1 -1
- 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/internal/workflow-bundle/workflow-builders.js +1 -1
- package/dist/src/internal/workflow-bundle/workflow-transformer.d.ts +3 -5
- package/dist/src/internal/workflow-bundle/workflow-transformer.js +4 -3
- 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/github/checkout.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/authorization-context.d.ts +14 -0
- package/dist/src/runtime/authorization-context.js +1 -0
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/scoped-authorization.d.ts +18 -2
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/prompt/compose.js +1 -1
- package/dist/src/runtime/resolve-connection.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/sandbox/keys.js +1 -1
- package/dist/src/runtime/types.d.ts +2 -0
- package/dist/src/sandbox/backends/default.d.ts +2 -0
- package/dist/src/sandbox/backends/default.js +1 -1
- package/dist/src/self-modification/agent.d.ts +5 -2
- package/dist/src/self-modification/agent.js +1 -1
- package/dist/src/self-modification/config.d.ts +52 -0
- package/dist/src/self-modification/config.js +1 -0
- package/dist/src/self-modification/credentials.d.ts +18 -0
- package/dist/src/self-modification/credentials.js +1 -0
- package/dist/src/self-modification/extension/eve-add.d.ts +7 -2
- package/dist/src/self-modification/extension/eve-add.js +1 -1
- package/dist/src/self-modification/extension/extension.d.ts +46 -1
- package/dist/src/self-modification/extension/extension.js +1 -1
- package/dist/src/self-modification/extension/instructions.d.ts +1 -3
- package/dist/src/self-modification/extension/instructions.js +24 -8
- package/dist/src/self-modification/extension/production-registry-add.d.ts +42 -0
- package/dist/src/self-modification/extension/production-registry-add.js +1 -0
- package/dist/src/self-modification/extension/tools/edit_file.js +1 -1
- package/dist/src/self-modification/extension/tools/publish.d.ts +7 -0
- package/dist/src/self-modification/extension/tools/publish.js +1 -0
- package/dist/src/self-modification/extension/tools/registry_add.d.ts +39 -7
- package/dist/src/self-modification/extension/tools/registry_add.js +1 -1
- package/dist/src/self-modification/extension/tools/search_registry.d.ts +1 -0
- package/dist/src/self-modification/extension/tools/search_registry.js +1 -1
- package/dist/src/self-modification/filesystem.js +1 -1
- package/dist/src/self-modification/git-workspace.d.ts +31 -0
- package/dist/src/self-modification/git-workspace.js +1 -0
- package/dist/src/self-modification/git.d.ts +6 -0
- package/dist/src/self-modification/git.js +1 -0
- package/dist/src/self-modification/github-publisher.d.ts +27 -0
- package/dist/src/self-modification/github-publisher.js +2 -0
- package/dist/src/self-modification/identifiers.d.ts +4 -0
- package/dist/src/self-modification/identifiers.js +1 -0
- package/dist/src/self-modification/mode.d.ts +4 -0
- package/dist/src/self-modification/mode.js +1 -0
- package/dist/src/self-modification/network-policy.d.ts +1 -0
- package/dist/src/self-modification/network-policy.js +1 -0
- package/dist/src/self-modification/proposal.d.ts +39 -0
- package/dist/src/self-modification/proposal.js +2 -0
- package/dist/src/self-modification/sandbox.d.ts +25 -1
- package/dist/src/self-modification/sandbox.js +1 -1
- package/dist/src/self-modification/setup.d.ts +34 -0
- package/dist/src/self-modification/setup.js +22 -0
- package/dist/src/self-modification/workspace-lock.d.ts +2 -0
- package/dist/src/self-modification/workspace-lock.js +1 -0
- 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/integrations/registry.js +1 -1
- package/dist/src/setup/integrations/runner.js +1 -1
- package/dist/src/setup/integrations/self-modification/setup.d.ts +29 -0
- package/dist/src/setup/integrations/self-modification/setup.js +1 -0
- package/dist/src/setup/integrations/types.d.ts +2 -0
- package/dist/src/setup/integrations/types.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/shared/empty-delivery.js +1 -1
- package/dist/src/shared/git.d.ts +16 -0
- package/dist/src/shared/git.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/shared/shell-quote.d.ts +2 -0
- package/dist/src/shared/shell-quote.js +1 -0
- package/dist/src/shared/vercel-services.js +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tasks/session-index.d.ts +1 -1
- package/dist/src/tasks/session-index.js +1 -1
- package/dist/src/tasks/session-task-cohorts.d.ts +10 -0
- package/dist/src/tasks/session-task-cohorts.js +1 -0
- 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/tools/workflow-definition.d.ts +5 -2
- 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 +6 -0
- package/docs/connections/overview.mdx +1 -1
- package/docs/evals/reporters.mdx +20 -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 +16 -2
- package/docs/subagents/index.mdx +18 -4
- package/docs/tools/overview.mdx +44 -2
- package/docs/tools/workflows.mdx +67 -2
- 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 +17 -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
- package/dist/src/self-modification/extension/source-lock.d.ts +0 -2
- package/dist/src/self-modification/extension/source-lock.js +0 -1
|
@@ -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
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Ship It"
|
|
3
|
-
description: "Part
|
|
3
|
+
description: "Part 8 of the Build an Agent tutorial. Put a web dashboard on the agent with useEveAgent, replace placeholderAuth, and deploy to Vercel."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
The analytics assistant runs
|
|
6
|
+
The analytics assistant runs in the TUI. Now add a web dashboard and deploy a private, single-user version on Vercel. This example protects the sample app with a username and password. It does not require another authentication service.
|
|
7
7
|
|
|
8
8
|
## Add the Web Chat app
|
|
9
9
|
|
|
@@ -24,121 +24,110 @@ const nextConfig: NextConfig = {};
|
|
|
24
24
|
export default withEve(nextConfig);
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
The dashboard talks to the built-in eve HTTP channel (`agent/channels/eve.ts`). On the browser side, `useEveAgent` handles session creation, streaming, and HITL. The scaffold renders its chat from `app/_components/agent-chat.tsx`, mounted by `app/page.tsx`. That component is fuller than you need to start, so replace its contents with this minimal version:
|
|
30
|
-
|
|
31
|
-
```tsx title="app/_components/agent-chat.tsx"
|
|
32
|
-
"use client";
|
|
33
|
-
|
|
34
|
-
import { useEveAgent } from "eve/react";
|
|
35
|
-
|
|
36
|
-
export function AgentChat() {
|
|
37
|
-
const agent = useEveAgent();
|
|
38
|
-
const isBusy = agent.status === "submitted" || agent.status === "streaming";
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<form
|
|
42
|
-
onSubmit={(event) => {
|
|
43
|
-
event.preventDefault();
|
|
44
|
-
const data = new FormData(event.currentTarget);
|
|
45
|
-
const message = String(data.get("q") ?? "").trim();
|
|
46
|
-
if (message) void agent.send(message);
|
|
47
|
-
}}
|
|
48
|
-
>
|
|
49
|
-
{agent.data.messages.map((message) => (
|
|
50
|
-
<article key={message.id}>
|
|
51
|
-
<header>{message.role}</header>
|
|
52
|
-
{message.parts.map((part, index) =>
|
|
53
|
-
part.type === "text" ? <p key={index}>{part.text}</p> : null,
|
|
54
|
-
)}
|
|
55
|
-
</article>
|
|
56
|
-
))}
|
|
57
|
-
<input name="q" disabled={isBusy} placeholder="Ask about the data…" />
|
|
58
|
-
<button type="submit" disabled={isBusy}>
|
|
59
|
-
Ask
|
|
60
|
-
</button>
|
|
61
|
-
</form>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
The generated `app/page.tsx` already imports and renders this `AgentChat` export, so no other wiring is needed:
|
|
27
|
+
## Use the generated chat
|
|
67
28
|
|
|
68
|
-
|
|
69
|
-
import { AgentChat } from "@/app/_components/agent-chat";
|
|
29
|
+
Keep the generated `app/_components/agent-chat.tsx` and `agent-message.tsx` components. `app/page.tsx` already renders the chat, and `useEveAgent` handles session creation and streaming.
|
|
70
30
|
|
|
71
|
-
|
|
72
|
-
return <AgentChat />;
|
|
73
|
-
}
|
|
74
|
-
```
|
|
31
|
+
The generated UI displays tool results and errors, authorization links, approval buttons, and question forms. The spend approval from [Guard the spend](./guard-the-spend) needs those controls to resume a waiting turn. A component that renders only text messages leaves those interactions unavailable.
|
|
75
32
|
|
|
76
|
-
|
|
33
|
+
Run `npm run dev`, open the local web URL printed by the server, and ask for an unfiltered revenue query. Confirm that the approval appears and that approving it lets the query finish. You can customize the UI after this flow works; see [Frontend](../guides/frontend/overview).
|
|
77
34
|
|
|
78
35
|
## Replace `placeholderAuth`
|
|
79
36
|
|
|
80
|
-
The scaffold's channel ships with `placeholderAuth()`, which
|
|
37
|
+
The scaffold's channel ships with `placeholderAuth()`, which rejects unauthenticated production requests. Replace it with a verifier that checks credentials on every request. Never return a fixed user without checking the request.
|
|
81
38
|
|
|
82
|
-
|
|
39
|
+
Create `agent/lib/auth.ts`. This uses eve's HTTP Basic verifier, which compares passwords in constant time. Missing environment variables, missing credentials, and wrong credentials all fail closed:
|
|
83
40
|
|
|
84
41
|
```ts title="agent/lib/auth.ts"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
42
|
+
import { verifyHttpBasic, withAuthChallenges } from "eve/channels/auth";
|
|
43
|
+
|
|
44
|
+
export const appAuth = withAuthChallenges(
|
|
45
|
+
(request: Request) => {
|
|
46
|
+
const username = process.env.ANALYTICS_USERNAME;
|
|
47
|
+
const password = process.env.ANALYTICS_PASSWORD;
|
|
48
|
+
if (!username || !password) return null;
|
|
49
|
+
|
|
50
|
+
if (request.headers.has("origin") && request.headers.get("sec-fetch-site") !== "same-origin")
|
|
51
|
+
return null;
|
|
52
|
+
|
|
53
|
+
const result = verifyHttpBasic(request.headers.get("authorization"), { username, password });
|
|
54
|
+
if (!result.ok) return null;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...result.sessionAuth,
|
|
58
|
+
attributes: { team: "growth" },
|
|
59
|
+
issuer: "analytics-tutorial",
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
[{ scheme: "Basic", parameters: { realm: "analytics", charset: "UTF-8" } }],
|
|
63
|
+
);
|
|
94
64
|
```
|
|
95
65
|
|
|
96
|
-
|
|
66
|
+
Replace `agent/channels/eve.ts`, removing the earlier `devTeam` entry. The eve channel checks `appAuth` for session creation, messages, controls, and streams. The remaining helpers preserve authenticated Vercel CLI access and local development:
|
|
97
67
|
|
|
98
68
|
```ts title="agent/channels/eve.ts"
|
|
99
69
|
import { eveChannel } from "eve/channels/eve";
|
|
100
|
-
import { localDev, vercelOidc
|
|
101
|
-
import {
|
|
102
|
-
|
|
103
|
-
const appAuth: AuthFn<Request> = async (request) => {
|
|
104
|
-
const user = await authenticate(request); // your cookie/session/provider
|
|
105
|
-
if (!user) return null;
|
|
106
|
-
return {
|
|
107
|
-
attributes: { team: user.team }, // the claim Step 7's playbook reads
|
|
108
|
-
principalType: "user",
|
|
109
|
-
principalId: user.id,
|
|
110
|
-
authenticator: "app",
|
|
111
|
-
issuer: "analytics-dashboard",
|
|
112
|
-
};
|
|
113
|
-
};
|
|
70
|
+
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
71
|
+
import { appAuth } from "../lib/auth";
|
|
114
72
|
|
|
115
73
|
export default eveChannel({
|
|
116
74
|
auth: [appAuth, vercelOidc(), localDev()],
|
|
117
75
|
});
|
|
118
76
|
```
|
|
119
77
|
|
|
120
|
-
|
|
78
|
+
The verified username becomes the user principal. The `growth` team selects the sample playbook from [Team playbooks](./team-playbooks). Keep these credentials private to one person. A shared password does not give each person a separate identity or isolated sessions. For a multi-user app, use a real session provider and enforce [session ownership](../guides/auth-and-route-protection#what-reaches-ctxsessionauth).
|
|
79
|
+
|
|
80
|
+
Add `proxy.ts` at the project root so opening the dashboard triggers the browser's native username/password prompt. It protects the generated UI routes; eve API routes keep their channel auth, so an OIDC-authenticated CLI request does not encounter a browser-only gate:
|
|
81
|
+
|
|
82
|
+
```ts title="proxy.ts"
|
|
83
|
+
import { routeAuth } from "eve/channels/auth";
|
|
84
|
+
import { NextResponse } from "next/server";
|
|
85
|
+
import { appAuth } from "./agent/lib/auth";
|
|
86
|
+
|
|
87
|
+
export async function proxy(request: Request) {
|
|
88
|
+
if (process.env.NODE_ENV === "development") return NextResponse.next();
|
|
89
|
+
const result = await routeAuth(request, appAuth);
|
|
90
|
+
return result instanceof Response ? result : NextResponse.next();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const config = { matcher: ["/", "/s/:path*"] };
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
After login, the browser sends the credentials on same-origin requests from the generated chat. When a browser sends an `Origin` header, the Basic verifier also requires [`Sec-Fetch-Site: same-origin`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site). Keep the auth module server-side: never put the password in a React component, a `NEXT_PUBLIC_` variable, or a client-side `useEveAgent` option. Use HTTPS for deployed HTTP Basic authentication.
|
|
121
97
|
|
|
122
98
|
## Deploy to Vercel
|
|
123
99
|
|
|
100
|
+
From `analytics-assistant/`, link a Vercel project and add a username and a long, unique password. These commands prompt for values without putting the password in shell history:
|
|
101
|
+
|
|
124
102
|
```bash
|
|
125
|
-
vercel
|
|
103
|
+
npx vercel@latest link
|
|
104
|
+
npx vercel@latest env add ANALYTICS_USERNAME preview
|
|
105
|
+
npx vercel@latest env add ANALYTICS_PASSWORD preview
|
|
126
106
|
```
|
|
127
107
|
|
|
128
|
-
|
|
108
|
+
Before deploying, configure the model credential your agent uses in the project's Preview environment; local `.env` values are not uploaded by deployment. If you used a local ChatGPT subscription, switch `agent/agent.ts` to an AI Gateway model and configure `AI_GATEWAY_API_KEY` for that model. Subscription credentials stay on your laptop. See [Deployment](../guides/deployment/overview) for model and runtime configuration.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx vercel@latest deploy
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Open the HTTPS preview URL and enter the configured username and password. Create a session and ask a sample-data question. In a private browser window, cancel the login prompt and verify the dashboard is denied. A `POST /eve/v1/session` request without credentials must also return `401`.
|
|
115
|
+
|
|
116
|
+
The authenticated web app and eve runtime share the same origin, and the sandbox runs on Vercel Sandbox. You can also smoke-test the deployment through the authenticated CLI:
|
|
129
117
|
|
|
130
118
|
```bash
|
|
131
119
|
npx eve dev https://your-analytics-app.vercel.app
|
|
132
120
|
```
|
|
133
121
|
|
|
134
|
-
|
|
122
|
+
For a production deployment, add the username, password, and model credential to the Production environment too, then run `npx vercel@latest deploy --prod`. Missing Basic credentials keep browser access closed.
|
|
123
|
+
|
|
124
|
+
The private assistant queries the sample data, runs analysis in a sandbox, charts the results, remembers definitions, loads the Growth playbook, and asks before an expensive query.
|
|
135
125
|
|
|
136
126
|
## What you learned
|
|
137
127
|
|
|
138
|
-
Across the
|
|
128
|
+
Across the eight steps you built and shipped one agent, and along the way you used:
|
|
139
129
|
|
|
140
130
|
- **Tools** to give the model typed actions (`run_sql`, `chart_series`, `define_metric`).
|
|
141
|
-
- **Connections** to reach a warehouse over an OAuth MCP, with per-user tokens eve resolves for you.
|
|
142
131
|
- **The sandbox** to compute and chart beyond SQL in an isolated `/workspace`.
|
|
143
132
|
- **State** (`defineState`) to remember the team's glossary across turns.
|
|
144
133
|
- **Dynamic skills** (`defineDynamic`) to load the right team playbook per caller.
|
|
@@ -148,6 +137,7 @@ Across the nine steps you built and shipped one agent, and along the way you use
|
|
|
148
137
|
|
|
149
138
|
## Next steps
|
|
150
139
|
|
|
140
|
+
- [Connect a warehouse](./connect-a-warehouse) when you have a data service to use in place of the sample dataset.
|
|
151
141
|
- [MCP connections](../connections/mcp) for tool allowlists and per-connection approval.
|
|
152
142
|
- [Sandbox](../sandbox) for backends, lifecycle, and network policy.
|
|
153
143
|
- [Dynamic capabilities](../guides/dynamic-capabilities) for schema-derived dynamic tools, a read-only analyst subagent, and model-authored report workflows on this same example.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Team Playbooks"
|
|
3
|
-
description: "Part
|
|
3
|
+
description: "Part 6 of the Build an Agent tutorial. Load the caller's team playbook with a dynamic skill keyed on the principal."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
The glossary from [Step
|
|
6
|
+
The glossary from [Step 5](./remember-definitions) is per-session. But your teams have standing analysis conventions for the analytics assistant (Growth groups revenue by date, Finance reconciles customer totals), and those shouldn't bleed across tenants. Load the right team's playbook for whoever is asking.
|
|
7
7
|
|
|
8
8
|
A skill is an on-demand procedure. The model pulls it in with `load_skill` only when a turn needs it. Make it dynamic and the skill gets decided at runtime instead of baked in. A `defineDynamic` resolver reads the session and returns a `defineSkill` (or nothing). Here you key that decision on the caller's identity in `ctx.session.auth`.
|
|
9
9
|
|
|
@@ -18,14 +18,14 @@ const PLAYBOOKS: Record<string, { title: string; markdown: string }> = {
|
|
|
18
18
|
growth: {
|
|
19
19
|
title: "Growth analysis playbook",
|
|
20
20
|
markdown:
|
|
21
|
-
"When analyzing
|
|
22
|
-
"
|
|
21
|
+
"When analyzing sample orders, group revenue by order date, report dollars, " +
|
|
22
|
+
"and compare customers by plan.",
|
|
23
23
|
},
|
|
24
24
|
finance: {
|
|
25
25
|
title: "Finance analysis playbook",
|
|
26
26
|
markdown:
|
|
27
|
-
"
|
|
28
|
-
"
|
|
27
|
+
"Reconcile total order revenue against the daily and customer totals. " +
|
|
28
|
+
"Report dollars and label this as gross order revenue; the dataset has no refunds.",
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -52,14 +52,14 @@ export default defineDynamic({
|
|
|
52
52
|
|
|
53
53
|
## See it route
|
|
54
54
|
|
|
55
|
-
The team comes from authenticated claims, which the auth layer stamps on in [Step
|
|
55
|
+
The team comes from authenticated claims, which the auth layer stamps on in [Step 8](./ship-it). Until then `ctx.session.auth.current` has no `team`, so the resolver returns `null` and no playbook loads. To verify routing now, stamp a team in local dev. Add a dev-only entry to `agent/channels/eve.ts` ahead of `localDev()`, and remove it before Step 8 wires real auth:
|
|
56
56
|
|
|
57
57
|
```ts title="agent/channels/eve.ts"
|
|
58
58
|
import { eveChannel } from "eve/channels/eve";
|
|
59
59
|
import { localDev, placeholderAuth, vercelOidc, type AuthFn } from "eve/channels/auth";
|
|
60
60
|
|
|
61
|
-
// Dev-only: stamp a team so Step
|
|
62
|
-
// Remove before Step
|
|
61
|
+
// Dev-only: stamp a team so Step 6's playbook resolver has something to read.
|
|
62
|
+
// Remove before Step 8.
|
|
63
63
|
const devTeam: AuthFn<Request> = () =>
|
|
64
64
|
process.env.NODE_ENV === "production"
|
|
65
65
|
? null
|
|
@@ -75,7 +75,7 @@ export default eveChannel({
|
|
|
75
75
|
});
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
Restart with `npm run dev` and ask "
|
|
78
|
+
Restart with `npm run dev` and ask "Summarize May 2026 revenue using our team's playbook." The model calls `load_skill`, queries the sample data, and applies Growth's daily and plan groupings. Switch `team` to `"finance"`, restart, and start a new session before asking again. The resolver runs at `session.started`, so changing the team does not replace the playbook in an existing session. The same question in the new session uses Finance's reconciliation rules.
|
|
79
79
|
|
|
80
80
|
Because the team comes from authenticated claims, not from the message, one tenant can't borrow another's playbook through the message content.
|
|
81
81
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
|
|
6
6
|
"keywords": [
|
|
@@ -290,6 +290,11 @@
|
|
|
290
290
|
"import": "./dist/src/public/context/index.js",
|
|
291
291
|
"default": "./dist/src/public/context/index.js"
|
|
292
292
|
},
|
|
293
|
+
"./internal/workflow-step-execution": {
|
|
294
|
+
"types": "./dist/src/execution/tools/workflow/step-execution.d.ts",
|
|
295
|
+
"import": "./dist/src/execution/tools/workflow/step-execution.js",
|
|
296
|
+
"default": "./dist/src/execution/tools/workflow/step-execution.js"
|
|
297
|
+
},
|
|
293
298
|
"./internal/programmatic-source-loader": {
|
|
294
299
|
"types": "./dist/src/internal/programmatic-source-loader.d.ts",
|
|
295
300
|
"import": "./dist/src/internal/programmatic-source-loader.js",
|
|
@@ -420,6 +425,12 @@
|
|
|
420
425
|
"types": "./dist/src/setup/index.d.ts",
|
|
421
426
|
"import": "./dist/src/setup/index.js",
|
|
422
427
|
"default": "./dist/src/setup/index.js"
|
|
428
|
+
},
|
|
429
|
+
"./self-modification/config": {
|
|
430
|
+
"eve-source": "./src/self-modification/config.ts",
|
|
431
|
+
"types": "./dist/src/self-modification/config.d.ts",
|
|
432
|
+
"import": "./dist/src/self-modification/config.js",
|
|
433
|
+
"default": "./dist/src/self-modification/config.js"
|
|
423
434
|
}
|
|
424
435
|
},
|
|
425
436
|
"publishConfig": {
|
|
@@ -478,6 +489,7 @@
|
|
|
478
489
|
"chat": "4.34.0",
|
|
479
490
|
"chokidar": "5.0.0",
|
|
480
491
|
"commander": "14.0.3",
|
|
492
|
+
"dd-trace": "6.13.0",
|
|
481
493
|
"emulate": "0.6.0",
|
|
482
494
|
"env-runner": "0.1.16",
|
|
483
495
|
"eventsource-parser": "3.1.0",
|
|
@@ -507,6 +519,7 @@
|
|
|
507
519
|
"@opentelemetry/api": "^1.0.0",
|
|
508
520
|
"ai": "^7.0.82",
|
|
509
521
|
"braintrust": "^3.0.0",
|
|
522
|
+
"dd-trace": "^6.13.0",
|
|
510
523
|
"just-bash": "^3.1.0",
|
|
511
524
|
"microsandbox": "^0.5.0"
|
|
512
525
|
},
|
|
@@ -517,6 +530,9 @@
|
|
|
517
530
|
"braintrust": {
|
|
518
531
|
"optional": true
|
|
519
532
|
},
|
|
533
|
+
"dd-trace": {
|
|
534
|
+
"optional": true
|
|
535
|
+
},
|
|
520
536
|
"just-bash": {
|
|
521
537
|
"optional": true
|
|
522
538
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const MAX_ACTIVITY_TEXT_LENGTH=500;function normalizeActivityText(e){return e.replace(/\p{Cc}/gu,` `).replace(/\s+/g,` `).trim().slice(0,500)}export{MAX_ACTIVITY_TEXT_LENGTH,normalizeActivityText};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export interface AuthoredWorkflowModules {
|
|
2
|
-
readonly directiveModules: readonly string[];
|
|
3
|
-
readonly workflowModules: readonly string[];
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Scans the whole application root, as the SDK's bundler integrations do, so a
|
|
7
|
-
* step helper can live wherever the tool imports it from.
|
|
8
|
-
*/
|
|
9
|
-
export declare function discoverAuthoredWorkflowModules(appRoot: string): Promise<AuthoredWorkflowModules>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{prepareAuthoredWorkflowDirectives}from"./authored-workflow-directives.js";import{isWorkflowSourceFile}from"./builder-support.js";import{isAuthoredApplicationModule,isAuthoredApplicationRoot}from"./workflow-builders.js";import{readFile,readdir}from"node:fs/promises";import{join}from"node:path";import{isGeneratedWorkflowFile}from"#compiled/@workflow/builders/index.js";const IGNORED_DIRECTORIES=new Set([`.cache`,`.eve`,`.git`,`.next`,`.nitro`,`.nuxt`,`.output`,`.pnpm-store`,`.svelte-kit`,`.swc`,`.turbo`,`.vercel`,`.workflow-data`,`.workflow-vitest`,`.yarn`,`coverage`,`dist`,`node_modules`]);function isIgnoredDirectory(e){return IGNORED_DIRECTORIES.has(e)||e.startsWith(`.output.`)}async function discoverAuthoredWorkflowModules(e){let t=[],n=[];if(!isAuthoredApplicationRoot(e))return{directiveModules:t,workflowModules:n};let r=await collectSourceFiles(e);for(let i of r.sort()){if(!isAuthoredApplicationModule(i,e)||isGeneratedWorkflowFile(i))continue;let r=await readFile(i,`utf8`);if(!r.includes(`use workflow`)&&!r.includes(`use step`))continue;let a=await prepareAuthoredWorkflowDirectives({filePath:i,source:r});a.hasDirectives&&(t.push(i),a.hasWorkflowDirective&&n.push(i))}return{directiveModules:t,workflowModules:n}}async function collectSourceFiles(e){let t=[];async function visit(e){let n;try{n=await readdir(e,{withFileTypes:!0})}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}for(let r of n){let n=join(e,r.name);if(r.isDirectory()){isIgnoredDirectory(r.name)||await visit(n);continue}r.isFile()&&isWorkflowSourceFile(r.name)&&t.push(n)}}return await visit(e),t}export{discoverAuthoredWorkflowModules};
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
2
|
-
export interface CodexAuthStatus {
|
|
3
|
-
readonly authMethod?: string;
|
|
4
|
-
readonly authToken?: string;
|
|
5
|
-
readonly requiresOpenaiAuth?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export interface CodexAppServer {
|
|
8
|
-
getAuthStatus(input: {
|
|
9
|
-
readonly refreshToken: boolean;
|
|
10
|
-
}): Promise<CodexAuthStatus>;
|
|
11
|
-
restart?(): void;
|
|
12
|
-
}
|
|
13
|
-
export interface CodexAppServerProcess {
|
|
14
|
-
readonly stderr: ChildProcessWithoutNullStreams["stderr"];
|
|
15
|
-
readonly stdin: ChildProcessWithoutNullStreams["stdin"];
|
|
16
|
-
readonly stdout: ChildProcessWithoutNullStreams["stdout"];
|
|
17
|
-
kill(): boolean;
|
|
18
|
-
once(event: "error", listener: (error: Error) => void): this;
|
|
19
|
-
once(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): this;
|
|
20
|
-
unref(): void;
|
|
21
|
-
}
|
|
22
|
-
type SpawnCodexAppServer = (command: string, args: readonly string[], options: {
|
|
23
|
-
readonly env?: NodeJS.ProcessEnv;
|
|
24
|
-
stdio: ["pipe", "pipe", "pipe"];
|
|
25
|
-
}) => CodexAppServerProcess;
|
|
26
|
-
export interface CodexAppServerOptions {
|
|
27
|
-
readonly command?: string;
|
|
28
|
-
readonly env?: NodeJS.ProcessEnv;
|
|
29
|
-
readonly spawnProcess?: SpawnCodexAppServer;
|
|
30
|
-
}
|
|
31
|
-
/** Minimal authenticated subset of Codex's JSONL app-server protocol. */
|
|
32
|
-
export declare class CodexAppServerClient implements CodexAppServer {
|
|
33
|
-
#private;
|
|
34
|
-
constructor(options?: CodexAppServerOptions);
|
|
35
|
-
getAuthStatus(input: {
|
|
36
|
-
readonly refreshToken: boolean;
|
|
37
|
-
}): Promise<CodexAuthStatus>;
|
|
38
|
-
restart(): void;
|
|
39
|
-
}
|
|
40
|
-
export {};
|