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.
Files changed (195) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/src/channel/types.d.ts +1 -0
  3. package/dist/src/chunks/{use-eve-agent-1NAM6ftx.js → use-eve-agent-BR8EpIOX.js} +27 -10
  4. package/dist/src/chunks/{use-eve-agent-BEjdhhhK.js → use-eve-agent-XnfjQt1Z.js} +27 -10
  5. package/dist/src/cli/commands/deploy.js +1 -1
  6. package/dist/src/cli/commands/extension-init.d.ts +2 -1
  7. package/dist/src/cli/commands/extension-init.js +1 -1
  8. package/dist/src/cli/commands/init.d.ts +2 -13
  9. package/dist/src/cli/commands/init.js +1 -1
  10. package/dist/src/cli/dev/run-interactive-ui.d.ts +3 -0
  11. package/dist/src/cli/dev/run-interactive-ui.js +1 -1
  12. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  13. package/dist/src/cli/dev/tui/runner.d.ts +6 -0
  14. package/dist/src/cli/dev/tui/runner.js +1 -1
  15. package/dist/src/cli/dev/tui/setup-commands.d.ts +5 -3
  16. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  17. package/dist/src/cli/dev/tui/status-line.js +1 -1
  18. package/dist/src/cli/dev/tui/tui.d.ts +4 -0
  19. package/dist/src/cli/dev/tui/tui.js +1 -1
  20. package/dist/src/cli/run.d.ts +1 -1
  21. package/dist/src/cli/run.js +2 -2
  22. package/dist/src/cli/telemetry/index.d.ts +15 -0
  23. package/dist/src/cli/telemetry/index.js +2 -2
  24. package/dist/src/client/session.js +1 -1
  25. package/dist/src/compiled/.vendor-stamp.json +1 -1
  26. package/dist/src/compiler/compile-from-memory.d.ts +1 -0
  27. package/dist/src/compiler/compile-from-memory.js +1 -1
  28. package/dist/src/compiler/extension-compatibility.d.ts +18 -18
  29. package/dist/src/compiler/extension-compatibility.js +1 -1
  30. package/dist/src/context/build-dynamic-tools.d.ts +2 -1
  31. package/dist/src/context/build-dynamic-tools.js +1 -1
  32. package/dist/src/context/dynamic-tool-lifecycle.d.ts +3 -3
  33. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  34. package/dist/src/context/dynamic-tool-metadata.d.ts +1 -1
  35. package/dist/src/context/hook-lifecycle.js +1 -1
  36. package/dist/src/context/keys.d.ts +2 -0
  37. package/dist/src/context/keys.js +1 -1
  38. package/dist/src/eve-channel/index.js +1 -1
  39. package/dist/src/execution/activity-events.d.ts +1 -0
  40. package/dist/src/execution/activity-events.js +1 -1
  41. package/dist/src/execution/node-step.js +1 -1
  42. package/dist/src/execution/sandbox/prewarm.js +1 -1
  43. package/dist/src/execution/session-activity-projection.js +1 -1
  44. package/dist/src/execution/session-activity.js +1 -1
  45. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  46. package/dist/src/execution/tasks/parent/dispatch.js +1 -1
  47. package/dist/src/execution/tasks/parent/task-cancel.d.ts +2 -2
  48. package/dist/src/execution/tasks/parent/task-cancel.js +1 -1
  49. package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
  50. package/dist/src/execution/tools/sleep-workflow.js +1 -1
  51. package/dist/src/execution/workflow-runtime.js +1 -1
  52. package/dist/src/execution/workflow-steps.js +1 -1
  53. package/dist/src/harness/action-presentation.d.ts +13 -0
  54. package/dist/src/harness/action-presentation.js +1 -0
  55. package/dist/src/harness/compaction.js +1 -1
  56. package/dist/src/harness/coordination.js +1 -1
  57. package/dist/src/harness/emission.d.ts +1 -6
  58. package/dist/src/harness/emission.js +1 -1
  59. package/dist/src/harness/execute-tool.d.ts +2 -1
  60. package/dist/src/harness/finish-reason.d.ts +3 -0
  61. package/dist/src/harness/finish-reason.js +1 -0
  62. package/dist/src/harness/provider-tool-schemas.d.ts +1 -0
  63. package/dist/src/harness/provider-tool-schemas.js +1 -1
  64. package/dist/src/harness/step-hooks.js +1 -1
  65. package/dist/src/harness/stream-actions.d.ts +2 -2
  66. package/dist/src/harness/stream-actions.js +1 -1
  67. package/dist/src/harness/tool-call-input-errors.d.ts +4 -3
  68. package/dist/src/harness/tool-call-input-errors.js +1 -1
  69. package/dist/src/harness/tool-loop.js +2 -2
  70. package/dist/src/harness/tool-presentation.d.ts +5 -0
  71. package/dist/src/harness/tool-presentation.js +1 -0
  72. package/dist/src/harness/turn-client-context.d.ts +1 -0
  73. package/dist/src/harness/workflow-lifecycle.d.ts +2 -3
  74. package/dist/src/harness/workflow-lifecycle.js +1 -1
  75. package/dist/src/internal/application/compiled-artifacts.d.ts +3 -0
  76. package/dist/src/internal/application/compiled-artifacts.js +1 -1
  77. package/dist/src/internal/application/package.js +1 -1
  78. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  79. package/dist/src/internal/authored-module-conditions.d.ts +2 -0
  80. package/dist/src/internal/authored-module-conditions.js +1 -0
  81. package/dist/src/internal/authored-module-loader.d.ts +3 -0
  82. package/dist/src/internal/authored-module-loader.js +3 -3
  83. package/dist/src/internal/authored-module-map-loader.d.ts +3 -1
  84. package/dist/src/internal/authored-module-map-loader.js +1 -1
  85. package/dist/src/internal/authored-runtime-modules.d.ts +2 -0
  86. package/dist/src/internal/authored-runtime-modules.js +1 -1
  87. package/dist/src/internal/bundler/nitro-rolldown.d.ts +1 -0
  88. package/dist/src/internal/bundler/nitro-rolldown.js +1 -1
  89. package/dist/src/internal/nitro/development-generation.d.ts +2 -0
  90. package/dist/src/internal/nitro/development-generation.js +1 -1
  91. package/dist/src/internal/nitro/host/configure-nitro-routes.js +1 -1
  92. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
  93. package/dist/src/internal/nitro/host/prepare-application-host.js +1 -1
  94. package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
  95. package/dist/src/internal/workflow-bundle/builder-support.d.ts +5 -0
  96. package/dist/src/internal/workflow-bundle/builder.js +2 -2
  97. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +1 -1
  98. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
  99. package/dist/src/protocol/activity.d.ts +6 -0
  100. package/dist/src/protocol/activity.js +1 -1
  101. package/dist/src/protocol/message.d.ts +13 -1
  102. package/dist/src/protocol/message.js +1 -1
  103. package/dist/src/public/channels/slack/activity-plan.js +1 -1
  104. package/dist/src/public/channels/slack/activity.js +2 -2
  105. package/dist/src/public/models/openai/chatgpt/credential-store.d.ts +6 -0
  106. package/dist/src/public/models/openai/chatgpt/credential-store.js +1 -0
  107. package/dist/src/public/models/openai/chatgpt/model.d.ts +1 -1
  108. package/dist/src/public/models/openai/chatgpt/oauth.d.ts +20 -0
  109. package/dist/src/public/models/openai/chatgpt/oauth.js +1 -0
  110. package/dist/src/public/models/openai/chatgpt/token-broker.d.ts +4 -3
  111. package/dist/src/public/models/openai/chatgpt/token-broker.js +1 -1
  112. package/dist/src/public/models/openai/chatgpt/transport.d.ts +1 -2
  113. package/dist/src/public/models/openai/index.d.ts +3 -4
  114. package/dist/src/public/tools/index.d.ts +1 -1
  115. package/dist/src/runtime/prompt/compose.js +1 -1
  116. package/dist/src/runtime/resolve-tool.js +1 -1
  117. package/dist/src/runtime/types.d.ts +2 -0
  118. package/dist/src/self-modification/agent.js +1 -1
  119. package/dist/src/self-modification/extension/instructions.d.ts +1 -3
  120. package/dist/src/self-modification/extension/instructions.js +4 -2
  121. package/dist/src/self-modification/filesystem.js +1 -1
  122. package/dist/src/setup/flows/chatgpt-auth.d.ts +14 -2
  123. package/dist/src/setup/flows/chatgpt-auth.js +1 -1
  124. package/dist/src/setup/flows/model.d.ts +3 -2
  125. package/dist/src/setup/flows/model.js +1 -1
  126. package/dist/src/setup/flows/provider.js +1 -1
  127. package/dist/src/setup/flows/registry.d.ts +4 -0
  128. package/dist/src/setup/flows/registry.js +1 -1
  129. package/dist/src/setup/scaffold/create/project.js +1 -1
  130. package/dist/src/shared/boundary-hook-error.d.ts +4 -0
  131. package/dist/src/shared/boundary-hook-error.js +1 -0
  132. package/dist/src/{execution/activity-text.d.ts → shared/presentation-text.d.ts} +1 -1
  133. package/dist/src/shared/presentation-text.js +1 -0
  134. package/dist/src/svelte/index.js +1 -1
  135. package/dist/src/svelte/use-eve-agent.js +1 -1
  136. package/dist/src/tools/definition.d.ts +21 -0
  137. package/dist/src/tools/definition.js +1 -1
  138. package/dist/src/tools/durable-callbacks.d.ts +37 -9
  139. package/dist/src/tools/durable-callbacks.js +1 -1
  140. package/dist/src/tools/dynamic.d.ts +2 -1
  141. package/dist/src/tools/framework/agent-contract.js +1 -1
  142. package/dist/src/tools/provided/bash.js +1 -1
  143. package/dist/src/tools/provided/glob.js +1 -1
  144. package/dist/src/tools/provided/grep.js +1 -1
  145. package/dist/src/tools/provided/load-skill.js +1 -1
  146. package/dist/src/tools/provided/read-file.js +1 -1
  147. package/dist/src/tools/provided/web-fetch.js +1 -1
  148. package/dist/src/tools/provided/write-file.js +1 -1
  149. package/dist/src/tools/tool-label.d.ts +1 -0
  150. package/dist/src/tools/tool-label.js +1 -0
  151. package/dist/src/tracing/agent-action-instrumentation.js +1 -1
  152. package/dist/src/tracing/agent-approval-instrumentation.js +1 -1
  153. package/dist/src/tracing/agent-channel-delivery-instrumentation.js +1 -1
  154. package/dist/src/tracing/agent-otel-provider.js +2 -2
  155. package/dist/src/tracing/agent-otel-session-context.js +1 -1
  156. package/dist/src/tracing/agent-otel-usage.d.ts +10 -0
  157. package/dist/src/tracing/agent-otel-usage.js +1 -1
  158. package/dist/src/tracing/agent-span-naming.d.ts +1 -0
  159. package/dist/src/tracing/agent-span-naming.js +1 -0
  160. package/dist/src/tracing/agent-tool-instrumentation.js +1 -1
  161. package/dist/src/vue/index.js +1 -1
  162. package/dist/src/vue/use-eve-agent.js +1 -1
  163. package/docs/concepts/default-harness.md +6 -0
  164. package/docs/concepts/sessions-runs-and-streaming.md +4 -0
  165. package/docs/connections/overview.mdx +1 -1
  166. package/docs/guides/client/continuations.mdx +11 -0
  167. package/docs/guides/dynamic-capabilities.md +4 -4
  168. package/docs/guides/hooks.md +1 -1
  169. package/docs/guides/instrumentation.md +40 -1
  170. package/docs/guides/remote-agents.md +2 -0
  171. package/docs/instructions.mdx +1 -1
  172. package/docs/memory/overview.mdx +45 -13
  173. package/docs/reference/cli.md +6 -0
  174. package/docs/reference/telemetry.md +2 -2
  175. package/docs/reference/typescript-api.md +13 -9
  176. package/docs/sandbox.mdx +14 -2
  177. package/docs/subagents/index.mdx +8 -4
  178. package/docs/tools/overview.mdx +44 -2
  179. package/docs/tools/workflows.mdx +6 -0
  180. package/docs/tutorial/connect-a-warehouse.mdx +49 -26
  181. package/docs/tutorial/first-agent.mdx +20 -19
  182. package/docs/tutorial/guard-the-spend.mdx +4 -4
  183. package/docs/tutorial/how-it-runs.mdx +2 -3
  184. package/docs/tutorial/meta.json +2 -2
  185. package/docs/tutorial/query-sample-data.mdx +30 -19
  186. package/docs/tutorial/remember-definitions.mdx +5 -5
  187. package/docs/tutorial/run-analysis.mdx +155 -18
  188. package/docs/tutorial/ship-it.mdx +71 -81
  189. package/docs/tutorial/team-playbooks.mdx +10 -10
  190. package/package.json +1 -1
  191. package/dist/src/execution/activity-text.js +0 -1
  192. package/dist/src/internal/workflow-bundle/authored-workflow-modules.d.ts +0 -9
  193. package/dist/src/internal/workflow-bundle/authored-workflow-modules.js +0 -1
  194. package/dist/src/public/models/openai/chatgpt/codex-app-server.d.ts +0 -40
  195. package/dist/src/public/models/openai/chatgpt/codex-app-server.js +0 -1
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: "Ship It"
3
- description: "Part 9 of the Build an Agent tutorial. Put a web dashboard on the agent with useEveAgent, replace placeholderAuth, and deploy to Vercel."
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 fine in the TUI. Now ship it for real, as a web dashboard your team logs into, behind actual auth, deployed on Vercel. There are three pieces to wire up. A React UI, the channel's auth, and the deploy itself.
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
- ## A dashboard with `useEveAgent`
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
- ```tsx title="app/page.tsx"
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
- export default function Page() {
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
- `agent.data.messages` and `agent.status` cover most chat UIs. The generated Web Chat renders HITL prompts directly in the conversation: approvals get action controls, and `ask_question` gets a visible form with vertical choices, a text field, or both. The spend approval from [Step 8](./guard-the-spend) uses the same response path. For the full API, see [Frontend](../guides/frontend/overview).
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 fails closed. It rejects production traffic so an unauthenticated app can't go live by accident. Swap it for your app's real auth before you deploy.
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
- Your auth lives in one module that turns a request into a user. Create `agent/lib/auth.ts` and wire your real provider (a cookie session, Auth.js, Clerk) in here. The stub below returns a fixed user so the page compiles and runs end to end:
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
- export interface AppUser {
86
- id: string;
87
- team: string;
88
- }
89
-
90
- // Replace with your real session/provider lookup.
91
- export async function authenticate(_request: Request): Promise<AppUser | null> {
92
- return { id: "demo-user", team: "growth" };
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
- Now point the channel at it. Replace the contents of `agent/channels/eve.ts`, which Step 7 left with a dev-only `devTeam` entry and `placeholderAuth()`. List your app auth first, ahead of the catch-all helpers, so any entry that doesn't recognize the caller falls through to the next one:
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, type AuthFn } from "eve/channels/auth";
101
- import { authenticate } from "../lib/auth";
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
- That `team` attribute is exactly what the dynamic playbook in [Step 7](./team-playbooks) reads from `ctx.session.auth`. Identity is set in this one place and flows out to every capability from there.
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 deploy
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
- On Vercel, the web app stays public and the eve runtime sits behind it on the same origin, with the sandbox running on Vercel Sandbox. You can smoke-test the deployment without leaving the CLI:
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
- That's the full assistant, deployed and authed. It queries the warehouse, runs analysis in a sandbox, charts the results, remembers your team's definitions, loads the right playbook per team, and asks before it spends.
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 nine steps you built and shipped one agent, and along the way you used:
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 7 of the Build an Agent tutorial. Load the caller's team playbook with a dynamic skill keyed on the principal."
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](./remember-definitions) is per-session. But your teams have standing analysis conventions for the analytics assistant (Growth runs cohort retention a particular way, Finance has its own revenue-recognition rules), and those shouldn't bleed across tenants. Load the right team's playbook for whoever is asking.
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 retention, use weekly cohorts anchored on signup week, " +
22
- "report curves not point estimates, and exclude trial accounts.",
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
- "Report revenue net of refunds and recognized over the subscription term. " +
28
- "Always reconcile against the close-of-month snapshot.",
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 9](./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 9 wires real auth:
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 7's playbook resolver has something to read.
62
- // Remove before Step 9.
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 "what's our 8-week retention?" The model sees the Growth playbook fits, calls `load_skill`, and applies the Growth conventions to that turn (weekly cohorts, no trial accounts). Switch `team` to `"finance"`, restart, and the same question routes to Finance's playbook instead.
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.2",
3
+ "version": "0.52.3",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [
@@ -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 {};
@@ -1 +0,0 @@
1
- import{spawn}from"node:child_process";import{isObject}from"#shared/guards.js";import{createInterface}from"node:readline";var CodexAppServerClient=class{#e;#t;#n;#r;constructor(e={}){this.#e=e.command??`codex`,this.#t=e.env,this.#n=e.spawnProcess??spawnCodexAppServer}async getAuthStatus(e){let t=await this.#i();try{return await t.getAuthStatus(e)}catch(e){throw t.exited&&(this.#r=void 0),e}}restart(){let e=this.#r;this.#r=void 0,e?.then(e=>e.close()).catch(()=>void 0)}#i(){return this.#r===void 0&&(this.#r=CodexAppServerConnection.start({command:this.#e,env:this.#t,spawnProcess:this.#n}).catch(e=>{throw this.#r=void 0,e})),this.#r}},CodexAppServerConnection=class CodexAppServerConnection{#e;#t;#n=new Map;#r=1;#i=!1;#a;constructor(e){this.#e=e,e.unref(),unrefStream(e.stdin),unrefStream(e.stdout),unrefStream(e.stderr),e.stderr.resume(),this.#t=createInterface({input:e.stdout}),this.#t.on(`line`,e=>this.#l(e)),e.once(`error`,e=>this.#u(e)),e.once(`exit`,(e,t)=>{let n=t===null?`code ${e??`unknown`}`:`signal ${t}`;this.#u(Error(`Codex app-server exited with ${n}.`))})}get exited(){return this.#i}close(){this.#e.kill(),this.#u(Error(`Codex app-server connection was restarted.`))}static async start(e){let t;try{t=e.spawnProcess(e.command,[`app-server`,`--stdio`],{env:e.env,stdio:[`pipe`,`pipe`,`pipe`]})}catch(e){throw codexUnavailableError(e)}let n=new CodexAppServerConnection(t);try{return await n.#o(`initialize`,{capabilities:null,clientInfo:{name:`eve`,title:`eve`,version:`0.35.0`}}),n.#s(`initialized`,{}),n}catch(e){throw t.kill(),e}}async getAuthStatus(e){let t=await this.#o(`getAuthStatus`,{includeToken:!0,refreshToken:e.refreshToken});if(!isObject(t))throw Error(`Codex app-server returned an invalid authentication response.`);return{...typeof t.authMethod==`string`&&{authMethod:t.authMethod},...typeof t.authToken==`string`&&{authToken:t.authToken},...typeof t.requiresOpenaiAuth==`boolean`&&{requiresOpenaiAuth:t.requiresOpenaiAuth}}}#o(e,t){if(this.#i)return Promise.reject(this.#a??Error(`Codex app-server is not running.`));let n=this.#r++,r=new Promise((e,t)=>{this.#n.set(n,{reject:t,resolve:e})});return this.#c({id:n,method:e,params:t}),r}#s(e,t){this.#c({method:e,params:t})}#c(e){this.#e.stdin.write(`${JSON.stringify(e)}\n`)}#l(e){let t;try{t=JSON.parse(e)}catch{return}if(typeof t.id!=`number`)return;let n=this.#n.get(t.id);if(n!==void 0){if(this.#n.delete(t.id),t.error!==void 0){n.reject(Error(t.error.message??`Codex app-server request failed (${t.error.code}).`));return}n.resolve(t.result)}}#u(e){if(!this.#i){this.#i=!0,this.#a=codexUnavailableError(e),this.#t.close();for(let e of this.#n.values())e.reject(this.#a);this.#n.clear()}}};function spawnCodexAppServer(e,t,n){return spawn(e,[...t],n)}function unrefStream(e){`unref`in e&&typeof e.unref==`function`&&e.unref()}function codexUnavailableError(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return Error("ChatGPT subscription authentication requires the Codex CLI. Install or upgrade `codex`, then run `codex login`.");let t=e instanceof Error?e.message:String(e);return Error(`Codex app-server is unavailable: ${t}`)}export{CodexAppServerClient};