eve 0.16.0 → 0.16.1

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 (67) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/src/channel/compiled-channel.d.ts +2 -0
  3. package/dist/src/channel/cors.d.ts +43 -0
  4. package/dist/src/channel/cors.js +1 -0
  5. package/dist/src/cli/dev/tui/agent-info-probe.d.ts +16 -0
  6. package/dist/src/cli/dev/tui/agent-info-probe.js +1 -0
  7. package/dist/src/cli/dev/tui/mcp-connection-status.d.ts +19 -0
  8. package/dist/src/cli/dev/tui/mcp-connection-status.js +1 -0
  9. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  10. package/dist/src/cli/dev/tui/remote-connection-probe.js +1 -1
  11. package/dist/src/cli/dev/tui/runner.d.ts +4 -0
  12. package/dist/src/cli/dev/tui/runner.js +1 -1
  13. package/dist/src/cli/dev/tui/setup-commands.d.ts +1 -0
  14. package/dist/src/cli/dev/tui/setup-commands.js +1 -1
  15. package/dist/src/cli/dev/tui/setup-panel.js +1 -1
  16. package/dist/src/cli/dev/tui/tui.js +1 -1
  17. package/dist/src/compiler/manifest.d.ts +8 -2
  18. package/dist/src/compiler/manifest.js +1 -1
  19. package/dist/src/compiler/normalize-channel.js +1 -1
  20. package/dist/src/context/providers/connection-key.d.ts +9 -0
  21. package/dist/src/context/providers/connection-key.js +1 -0
  22. package/dist/src/context/providers/connection.d.ts +1 -9
  23. package/dist/src/context/providers/connection.js +1 -1
  24. package/dist/src/harness/input-requests.d.ts +1 -0
  25. package/dist/src/harness/input-requests.js +1 -1
  26. package/dist/src/harness/tool-loop.js +1 -1
  27. package/dist/src/internal/application/package.js +1 -1
  28. package/dist/src/internal/nitro/host/channel-routes.d.ts +2 -0
  29. package/dist/src/internal/nitro/host/channel-routes.js +5 -3
  30. package/dist/src/internal/nitro/host/start-development-server.js +2 -2
  31. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  32. package/dist/src/public/channels/eve.d.ts +31 -0
  33. package/dist/src/public/channels/eve.js +1 -1
  34. package/dist/src/public/channels/github/githubChannel.js +1 -1
  35. package/dist/src/public/channels/github/inbound-types.d.ts +189 -0
  36. package/dist/src/public/channels/github/inbound-types.js +1 -0
  37. package/dist/src/public/channels/github/inbound.d.ts +2 -190
  38. package/dist/src/public/channels/github/inbound.js +2 -2
  39. package/dist/src/public/channels/index.d.ts +1 -1
  40. package/dist/src/public/definitions/defineChannel.d.ts +10 -0
  41. package/dist/src/public/definitions/defineChannel.js +1 -1
  42. package/dist/src/runtime/connections/scoped-authorization.d.ts +9 -0
  43. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  44. package/dist/src/runtime/connections/types.d.ts +4 -4
  45. package/dist/src/runtime/framework-channels/index.js +1 -1
  46. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
  47. package/dist/src/runtime/framework-tools/index.d.ts +1 -1
  48. package/dist/src/runtime/framework-tools/index.js +1 -1
  49. package/dist/src/runtime/framework-tools/skill.js +1 -1
  50. package/dist/src/runtime/resolve-channel.js +1 -1
  51. package/dist/src/runtime/types.d.ts +2 -0
  52. package/dist/src/setup/boxes/select-connections.js +1 -1
  53. package/dist/src/setup/cli/option-row.d.ts +2 -0
  54. package/dist/src/setup/cli/option-row.js +1 -1
  55. package/dist/src/setup/flows/connections.d.ts +1 -0
  56. package/dist/src/setup/flows/connections.js +2 -2
  57. package/dist/src/setup/scaffold/create/project.js +1 -1
  58. package/docs/channels/custom.mdx +19 -0
  59. package/docs/channels/eve.mdx +23 -0
  60. package/docs/concepts/sessions-runs-and-streaming.md +2 -0
  61. package/docs/connections/mcp.mdx +161 -19
  62. package/docs/connections/openapi.mdx +133 -36
  63. package/docs/connections/overview.mdx +4 -4
  64. package/docs/patterns/multi-tenant-approvals.md +4 -0
  65. package/docs/patterns/multi-tenant-auth.md +156 -37
  66. package/docs/tools/human-in-the-loop.md +3 -1
  67. package/package.json +1 -1
@@ -35,6 +35,100 @@ export function requireTenantCaller(ctx: SessionContext): {
35
35
 
36
36
  The tenant comes from verified route auth, never a prompt, tool argument, or remote API response. See [Auth & route protection](../guides/auth-and-route-protection) for custom session and OIDC examples.
37
37
 
38
+ ## Authenticate with your own API key or JWT
39
+
40
+ For production apps with many customer orgs, the inbound credential is often
41
+ your own API key, session cookie, or JWT. Use that credential to authenticate
42
+ the caller before eve starts a run, then stamp the tenant onto the session:
43
+
44
+ ```ts title="agent/channels/eve.ts"
45
+ import { eveChannel } from "eve/channels/eve";
46
+ import { localDev, type AuthFn } from "eve/channels/auth";
47
+ import { verifyAgentCaller } from "../../lib/app-auth.js";
48
+
49
+ function tenantAppAuth(): AuthFn<Request> {
50
+ return async (request) => {
51
+ const caller = await verifyAgentCaller(request);
52
+ if (caller === null) return null;
53
+
54
+ return {
55
+ authenticator: "app",
56
+ issuer: "https://app.example.com",
57
+ principalId: caller.userId,
58
+ principalType: "user",
59
+ subject: caller.userId,
60
+ attributes: {
61
+ tenantId: caller.tenantId,
62
+ roles: caller.roles,
63
+ },
64
+ };
65
+ };
66
+ }
67
+
68
+ export default eveChannel({
69
+ auth: [tenantAppAuth(), localDev()],
70
+ });
71
+ ```
72
+
73
+ `verifyAgentCaller` is application code. It can validate an API key, verify a
74
+ JWT, or read an app session, but it should return only after the user belongs
75
+ to the tenant they are claiming. Keep `principalId` stable for the same user,
76
+ include an `issuer` when ids can come from more than one identity system, and
77
+ put routing facts such as `tenantId` in `attributes`.
78
+
79
+ If one user can switch between orgs, authenticate the selected org on every
80
+ session create or continue request and stamp that selected `tenantId` onto the
81
+ current turn.
82
+
83
+ This is not connection OAuth. The user is already authenticated to your app;
84
+ eve uses that verified principal to pick the correct outbound credential.
85
+
86
+ ## Build tenant connection auth
87
+
88
+ For Bearer tokens or tenant-scoped JWTs, write one non-interactive auth helper
89
+ and reuse it across OpenAPI and MCP connections. `principalType: "user"` tells
90
+ eve to require the authenticated user from route auth, key the step-local token
91
+ cache by that user, and pass the projected principal into `getToken`:
92
+
93
+ ```ts title="agent/lib/tenant-connection-auth.ts"
94
+ import type { ConnectionPrincipal, NonInteractiveAuthorizationDefinition } from "eve/connections";
95
+ import { tenantCredentials, type TenantService } from "./tenant-credentials.js";
96
+
97
+ function requireTenantPrincipal(principal: ConnectionPrincipal): {
98
+ tenantId: string;
99
+ userId: string;
100
+ } {
101
+ const tenantId = principal.type === "user" ? principal.attributes?.tenantId : undefined;
102
+
103
+ if (principal.type !== "user" || typeof tenantId !== "string") {
104
+ throw new Error("An authenticated tenant user is required.");
105
+ }
106
+
107
+ return { tenantId, userId: principal.id };
108
+ }
109
+
110
+ export function tenantBearerAuth(service: TenantService): NonInteractiveAuthorizationDefinition {
111
+ return {
112
+ principalType: "user",
113
+ async getToken({ principal }) {
114
+ const { tenantId, userId } = requireTenantPrincipal(principal);
115
+ const credential = await tenantCredentials.getBearer(tenantId, service, { userId });
116
+
117
+ return {
118
+ token: credential.bearerToken,
119
+ ...(credential.expiresAt ? { expiresAt: credential.expiresAt } : {}),
120
+ };
121
+ },
122
+ };
123
+ }
124
+ ```
125
+
126
+ The model never supplies `tenantId` or sees the returned token. If the remote
127
+ service uses tenant-level credentials shared by multiple users, keep the
128
+ credential lookup keyed by `tenantId` in your provider; user-scoped connection
129
+ auth is still useful because it rejects unauthenticated sessions and keeps
130
+ eve's token cache from crossing caller identities.
131
+
38
132
  ## Authenticate an authored tool call
39
133
 
40
134
  Derive the tenant inside `execute`, fetch its credential from your application provider, and construct the outbound request:
@@ -50,11 +144,11 @@ export default defineTool({
50
144
  inputSchema: z.object({ limit: z.number().int().min(1).max(100).default(20) }),
51
145
  async execute({ limit }, ctx) {
52
146
  const { tenantId } = requireTenantCaller(ctx);
53
- const credential = await tenantCredentials.get(tenantId, "billing");
147
+ const credential = await tenantCredentials.getBearer(tenantId, "billing");
54
148
 
55
149
  const response = await fetch(`https://billing.example.com/v1/invoices?limit=${limit}`, {
56
150
  headers: {
57
- authorization: `Bearer ${credential.token}`,
151
+ authorization: `Bearer ${credential.bearerToken}`,
58
152
  "x-account-id": credential.externalTenantId,
59
153
  },
60
154
  });
@@ -68,40 +162,29 @@ The model controls only `limit`. Even if a prompt asks for another tenant, the e
68
162
 
69
163
  ## Authenticate an OpenAPI connection
70
164
 
71
- An async `auth(ctx)` resolver can select a tenant bearer without exposing it to generated tools:
165
+ Attach the reusable auth helper to the connection. Generated operation tools
166
+ receive the token at call time without exposing it to the model:
72
167
 
73
168
  ```ts title="agent/connections/billing.ts"
74
169
  import { defineOpenAPIConnection } from "eve/connections";
75
170
  import { tenantCredentials } from "../lib/tenant-credentials.js";
171
+ import { tenantBearerAuth } from "../lib/tenant-connection-auth.js";
76
172
  import { requireTenantCaller } from "../lib/tenant.js";
77
173
 
78
174
  export default defineOpenAPIConnection({
79
175
  spec: "https://billing.example.com/openapi.json",
80
176
  description: "Invoices and subscriptions for the current tenant.",
81
177
  operations: { allow: ["listInvoices", "getInvoice", "updateSubscription"] },
82
-
83
- auth: async (ctx) => {
84
- const { tenantId } = requireTenantCaller(ctx);
85
- const credential = await tenantCredentials.get(tenantId, "billing");
86
- return {
87
- principalType: "user",
88
- getToken: async () => ({
89
- token: credential.token,
90
- ...(credential.expiresAt ? { expiresAt: credential.expiresAt } : {}),
91
- }),
92
- };
93
- },
178
+ auth: tenantBearerAuth("billing"),
94
179
 
95
180
  headers: async (ctx) => {
96
181
  const { tenantId } = requireTenantCaller(ctx);
97
- const credential = await tenantCredentials.get(tenantId, "billing");
182
+ const credential = await tenantCredentials.getBearer(tenantId, "billing");
98
183
  return { "X-Account-Id": credential.externalTenantId };
99
184
  },
100
185
  });
101
186
  ```
102
187
 
103
- `principalType: "user"` requires an authenticated user and keeps eve's step-local token handling scoped to that principal. If credentials are per user rather than shared by a tenant, include `userId` in your provider lookup.
104
-
105
188
  Do not return `Authorization` from `headers` when `auth` is present. eve constructs that header from `getToken` and rejects conflicting definitions.
106
189
 
107
190
  ## Authenticate an MCP connection
@@ -111,63 +194,99 @@ MCP connections accept the same callbacks:
111
194
  ```ts title="agent/connections/support.ts"
112
195
  import { defineMcpClientConnection } from "eve/connections";
113
196
  import { tenantCredentials } from "../lib/tenant-credentials.js";
197
+ import { tenantBearerAuth } from "../lib/tenant-connection-auth.js";
114
198
  import { requireTenantCaller } from "../lib/tenant.js";
115
199
 
116
200
  export default defineMcpClientConnection({
117
201
  url: "https://support.example.com/mcp",
118
202
  description: "Support tickets and customers for the current tenant.",
119
203
  tools: { allow: ["search_tickets", "get_ticket", "add_internal_note"] },
120
-
121
- auth: async (ctx) => {
122
- const { tenantId } = requireTenantCaller(ctx);
123
- const credential = await tenantCredentials.get(tenantId, "support");
124
- return {
125
- principalType: "user",
126
- getToken: async () => ({
127
- token: credential.token,
128
- ...(credential.expiresAt ? { expiresAt: credential.expiresAt } : {}),
129
- }),
130
- };
131
- },
204
+ auth: tenantBearerAuth("support"),
132
205
 
133
206
  headers: {
134
207
  "X-Workspace-Id": async (ctx) => {
135
208
  const { tenantId } = requireTenantCaller(ctx);
136
- const credential = await tenantCredentials.get(tenantId, "support");
209
+ const credential = await tenantCredentials.getBearer(tenantId, "support");
137
210
  return credential.externalTenantId;
138
211
  },
139
212
  },
140
213
  });
141
214
  ```
142
215
 
143
- For an API-key-only server, omit `auth` and return both the key and routing metadata from the async `headers` callback instead.
216
+ ## Authenticate an API-key-only connection
217
+
218
+ If the remote server does not accept Bearer auth, omit `auth` and return the
219
+ tenant API key from `headers` instead:
220
+
221
+ ```ts title="agent/connections/support.ts"
222
+ import { defineMcpClientConnection } from "eve/connections";
223
+ import { tenantCredentials } from "../lib/tenant-credentials.js";
224
+ import { requireTenantCaller } from "../lib/tenant.js";
225
+
226
+ export default defineMcpClientConnection({
227
+ url: "https://support.example.com/mcp",
228
+ description: "Support tickets and customers for the current tenant.",
229
+ tools: { allow: ["search_tickets", "get_ticket", "add_internal_note"] },
230
+
231
+ headers: async (ctx) => {
232
+ const { tenantId, userId } = requireTenantCaller(ctx);
233
+ const credential = await tenantCredentials.getApiKey(tenantId, "support", { userId });
234
+
235
+ return {
236
+ "X-Api-Key": credential.apiKey,
237
+ "X-Workspace-Id": credential.externalTenantId,
238
+ };
239
+ },
240
+ });
241
+ ```
242
+
243
+ Use the same shape for OpenAPI connections. API keys resolved in `headers` are
244
+ sent only on outbound requests; they are not model inputs or tool results.
144
245
 
145
246
  ## Supply the credential provider
146
247
 
147
248
  The eve-facing files need only this application contract:
148
249
 
149
250
  ```ts title="agent/lib/tenant-credentials.ts"
150
- export interface TenantCredential {
151
- token: string;
251
+ export type TenantService = "billing" | "support";
252
+
253
+ export interface TenantBearerCredential {
254
+ bearerToken: string;
152
255
  externalTenantId: string;
153
256
  expiresAt?: number;
154
257
  }
155
258
 
259
+ export interface TenantApiKeyCredential {
260
+ apiKey: string;
261
+ externalTenantId: string;
262
+ }
263
+
156
264
  export interface TenantCredentialProvider {
157
- get(tenantId: string, service: "billing" | "support"): Promise<TenantCredential>;
265
+ getBearer(
266
+ tenantId: string,
267
+ service: TenantService,
268
+ options?: { userId?: string },
269
+ ): Promise<TenantBearerCredential>;
270
+ getApiKey(
271
+ tenantId: string,
272
+ service: TenantService,
273
+ options?: { userId?: string },
274
+ ): Promise<TenantApiKeyCredential>;
158
275
  }
159
276
 
160
277
  export { tenantCredentials } from "../../lib/tenant-credentials.js";
161
278
  ```
162
279
 
163
- Implement the provider with the secret system your application already trusts: a cloud secret manager, an encrypted database table, a token broker, or per-user OAuth. eve does not prescribe that choice.
280
+ Implement the provider with the secret system your application already trusts:
281
+ a cloud secret manager, an encrypted database table, a token broker, or an
282
+ out-of-band OAuth flow you own. eve does not prescribe that choice.
164
283
 
165
284
  The provider must fail closed for unknown tenants, avoid returning secrets in logs or errors, and rotate or refresh credentials before `expiresAt`. Prefer credentials that are themselves restricted to one remote tenant; treat workspace headers as routing, not authorization.
166
285
 
167
286
  ## What the model can and cannot see
168
287
 
169
288
  1. Route auth stamps the verified tenant onto the session.
170
- 2. The callback reads `ctx.session.auth.current` inside the active turn.
289
+ 2. Tool code reads `ctx.session.auth.current`, and connection auth receives the projected `principal`.
171
290
  3. The application provider resolves the corresponding credential.
172
291
  4. eve sends the resulting token and headers directly to the remote service.
173
292
  5. Neither becomes a model message or tool result.
@@ -100,10 +100,12 @@ Approvals and questions share one protocol:
100
100
  1. The model requests input (an approval, or an `ask_question`).
101
101
  2. eve emits an `input.requested` stream event carrying the pending requests.
102
102
  3. The turn parks at `session.waiting`, durably, for as long as it takes.
103
- 4. The client answers with `inputResponses` (structured, keyed by `requestId`) or a normal follow-up `message`. A follow-up whose text matches an option label (case-insensitive) resolves automatically.
103
+ 4. The client answers with `inputResponses` (structured, keyed by `requestId`) or a normal follow-up `message`. A follow-up whose text matches an option ID, option label, or numeric option index resolves automatically, including approval options such as `approve` and `deny`.
104
104
 
105
105
  The run picks back up exactly where it parked. Because the pause is durable, nothing is held in memory while it waits — the process can restart and the parked turn survives.
106
106
 
107
+ For approval requests, unrelated follow-up text does not deny the tool call. eve keeps the approval pending and holds that text until the approval is answered, then replays it as the next message in the session.
108
+
107
109
  See [Sessions, runs & streaming](/docs/concepts/sessions-runs-and-streaming) for the full event and resume contract that this builds on.
108
110
 
109
111
  ## Answering from a client or channel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [