eve 0.21.1 → 0.22.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 (138) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/src/channel/adapter.d.ts +3 -7
  3. package/dist/src/channel/types.d.ts +18 -1
  4. package/dist/src/compiler/manifest.d.ts +16 -26
  5. package/dist/src/compiler/manifest.js +1 -1
  6. package/dist/src/compiler/normalize-agent-config.js +1 -1
  7. package/dist/src/compiler/normalize-hook.d.ts +2 -2
  8. package/dist/src/compiler/normalize-instructions.d.ts +3 -3
  9. package/dist/src/compiler/normalize-instructions.js +1 -1
  10. package/dist/src/compiler/normalize-skill.js +1 -1
  11. package/dist/src/compiler/normalize-tool.d.ts +2 -2
  12. package/dist/src/context/container.d.ts +2 -0
  13. package/dist/src/context/dynamic-model-lifecycle.d.ts +19 -0
  14. package/dist/src/context/dynamic-model-lifecycle.js +1 -0
  15. package/dist/src/context/keys.d.ts +13 -1
  16. package/dist/src/context/keys.js +1 -1
  17. package/dist/src/discover/manifest.d.ts +8 -0
  18. package/dist/src/evals/cli/eval.js +2 -2
  19. package/dist/src/evals/reporters/index.d.ts +1 -0
  20. package/dist/src/evals/reporters/index.js +1 -1
  21. package/dist/src/evals/runner/reporters/console.d.ts +10 -11
  22. package/dist/src/evals/runner/reporters/console.js +1 -1
  23. package/dist/src/execution/node-step.js +1 -1
  24. package/dist/src/execution/reconcile-session-continuation-token.d.ts +4 -0
  25. package/dist/src/execution/reconcile-session-continuation-token.js +1 -0
  26. package/dist/src/execution/sandbox/bindings/microsandbox-options.d.ts +2 -2
  27. package/dist/src/execution/sandbox/bindings/microsandbox.d.ts +2 -2
  28. package/dist/src/execution/sandbox/bindings/vercel-credentials.js +1 -1
  29. package/dist/src/execution/sandbox/ensure.js +1 -1
  30. package/dist/src/execution/session.js +2 -2
  31. package/dist/src/execution/subagent-adapter.js +1 -1
  32. package/dist/src/execution/subagent-event-proxy-step.d.ts +23 -0
  33. package/dist/src/execution/subagent-event-proxy-step.js +1 -0
  34. package/dist/src/execution/turn-workflow.js +1 -1
  35. package/dist/src/execution/workflow-steps.d.ts +1 -22
  36. package/dist/src/execution/workflow-steps.js +1 -1
  37. package/dist/src/harness/emission.d.ts +1 -0
  38. package/dist/src/harness/emission.js +1 -1
  39. package/dist/src/harness/input-extraction.d.ts +1 -0
  40. package/dist/src/harness/input-extraction.js +1 -1
  41. package/dist/src/harness/input-requests.js +1 -1
  42. package/dist/src/harness/step-hooks.d.ts +4 -1
  43. package/dist/src/harness/step-hooks.js +1 -1
  44. package/dist/src/harness/tool-call-input-errors.d.ts +8 -0
  45. package/dist/src/harness/tool-call-input-errors.js +1 -0
  46. package/dist/src/harness/tool-loop.js +1 -1
  47. package/dist/src/harness/types.d.ts +9 -0
  48. package/dist/src/internal/application/package.js +1 -1
  49. package/dist/src/internal/authored-definition/core.js +1 -1
  50. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  51. package/dist/src/internal/nitro/host/types.d.ts +2 -2
  52. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.d.ts +2 -2
  53. package/dist/src/internal/nitro/routes/agent-info/load-agent-info-data.d.ts +4 -4
  54. package/dist/src/internal/runtime-model.d.ts +1 -1
  55. package/dist/src/protocol/message.d.ts +1 -0
  56. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +1 -1
  57. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
  58. package/dist/src/public/channels/discord/discordChannel.d.ts +2 -2
  59. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  60. package/dist/src/public/channels/eve.d.ts +1 -1
  61. package/dist/src/public/channels/eve.js +1 -1
  62. package/dist/src/public/channels/github/dispatch.d.ts +1 -1
  63. package/dist/src/public/channels/github/githubChannel.d.ts +2 -2
  64. package/dist/src/public/channels/github/githubChannel.js +1 -1
  65. package/dist/src/public/channels/index.d.ts +2 -2
  66. package/dist/src/public/channels/index.js +1 -1
  67. package/dist/src/public/channels/linear/linearChannel.d.ts +1 -1
  68. package/dist/src/public/channels/linear/linearChannel.js +1 -1
  69. package/dist/src/public/channels/slack/hitl.d.ts +3 -2
  70. package/dist/src/public/channels/slack/hitl.js +3 -3
  71. package/dist/src/public/channels/slack/index.d.ts +1 -1
  72. package/dist/src/public/channels/slack/index.js +1 -1
  73. package/dist/src/public/channels/slack/interactions.d.ts +1 -1
  74. package/dist/src/public/channels/slack/interactions.js +1 -1
  75. package/dist/src/public/channels/slack/limits.d.ts +18 -0
  76. package/dist/src/public/channels/slack/limits.js +1 -1
  77. package/dist/src/public/channels/slack/slackChannel.d.ts +2 -2
  78. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  79. package/dist/src/public/channels/teams/teamsChannel.d.ts +2 -2
  80. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  81. package/dist/src/public/channels/telegram/telegramChannel.d.ts +2 -2
  82. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  83. package/dist/src/public/channels/twilio/twilioChannel.d.ts +2 -2
  84. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  85. package/dist/src/public/definitions/agent.d.ts +1 -1
  86. package/dist/src/public/definitions/channel.d.ts +99 -0
  87. package/dist/src/public/definitions/channel.js +1 -1
  88. package/dist/src/public/definitions/hook.d.ts +3 -10
  89. package/dist/src/public/definitions/instructions.d.ts +2 -18
  90. package/dist/src/public/definitions/schedule.d.ts +4 -17
  91. package/dist/src/public/definitions/tool.d.ts +10 -4
  92. package/dist/src/public/definitions/tool.js +1 -1
  93. package/dist/src/public/hooks/index.d.ts +1 -1
  94. package/dist/src/public/hooks/index.js +1 -1
  95. package/dist/src/public/index.d.ts +2 -0
  96. package/dist/src/public/index.js +1 -1
  97. package/dist/src/public/sandbox/backends/default.d.ts +2 -2
  98. package/dist/src/public/sandbox/backends/microsandbox.d.ts +2 -2
  99. package/dist/src/public/sandbox/microsandbox-sandbox.d.ts +1 -1
  100. package/dist/src/public/sandbox/microsandbox.d.ts +1 -1
  101. package/dist/src/runtime/agent/bootstrap.d.ts +8 -0
  102. package/dist/src/runtime/agent/bootstrap.js +1 -1
  103. package/dist/src/runtime/agent/resolve-model.d.ts +16 -1
  104. package/dist/src/runtime/agent/resolve-model.js +1 -1
  105. package/dist/src/runtime/hooks/registry.d.ts +2 -1
  106. package/dist/src/runtime/prompt/compose.d.ts +2 -2
  107. package/dist/src/runtime/resolve-agent.js +1 -1
  108. package/dist/src/runtime/sandbox/keys.d.ts +4 -0
  109. package/dist/src/runtime/sandbox/keys.js +1 -1
  110. package/dist/src/runtime/sandbox/template-plan.d.ts +0 -2
  111. package/dist/src/runtime/schedules/register.d.ts +2 -2
  112. package/dist/src/runtime/schedules/resolve-schedule.d.ts +4 -4
  113. package/dist/src/runtime/types.d.ts +7 -4
  114. package/dist/src/setup/scaffold/create/project.js +1 -1
  115. package/dist/src/shared/agent-definition.d.ts +30 -7
  116. package/dist/src/shared/agent-definition.js +1 -1
  117. package/dist/src/shared/channel-definition.d.ts +92 -0
  118. package/dist/src/shared/channel-definition.js +1 -0
  119. package/dist/src/shared/dynamic-tool-definition.d.ts +18 -8
  120. package/dist/src/shared/dynamic-tool-definition.js +1 -1
  121. package/dist/src/shared/hook-definition.d.ts +36 -0
  122. package/dist/src/shared/hook-definition.js +1 -0
  123. package/dist/src/shared/instructions-definition.d.ts +26 -0
  124. package/dist/src/shared/instructions-definition.js +1 -0
  125. package/dist/src/shared/schedule-definition.d.ts +34 -0
  126. package/dist/src/shared/schedule-definition.js +1 -0
  127. package/dist/src/shared/vercel-project.d.ts +25 -0
  128. package/dist/src/shared/vercel-project.js +1 -0
  129. package/dist/src/source-change/apply-model-name.js +1 -1
  130. package/docs/agent-config.md +44 -0
  131. package/docs/channels/slack.mdx +27 -0
  132. package/docs/connections/overview.mdx +2 -0
  133. package/docs/guides/dynamic-capabilities.md +18 -2
  134. package/docs/sandbox.mdx +3 -1
  135. package/docs/subagents.mdx +1 -1
  136. package/package.json +1 -1
  137. package/dist/src/public/definitions/defineChannel.d.ts +0 -157
  138. package/dist/src/public/definitions/defineChannel.js +0 -1
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Public definition for an instructions prompt authored in markdown or
3
+ * TypeScript.
4
+ *
5
+ * Authored at the agent root as either `instructions.md` or
6
+ * `instructions.{ts,cts,mts,js,cjs,mjs}`, or inside the
7
+ * `agent/instructions/` directory for multi-file setups. Module-backed
8
+ * static instructions execute once at build time. The compiler captures
9
+ * the resulting markdown into the compiled manifest.
10
+ *
11
+ * When used inside a `defineDynamic` handler, the runtime lowers the
12
+ * returned markdown to `{ role: "system", content: markdown }`.
13
+ * Instructions produce system messages only. Use channel `context` for
14
+ * user-role messages.
15
+ */
16
+ export interface PublicInstructionsDefinition {
17
+ markdown: string;
18
+ }
19
+ /**
20
+ * Internal definition for an instructions prompt authored in markdown or
21
+ * TypeScript.
22
+ */
23
+ export interface InternalInstructionsDefinition {
24
+ name: string;
25
+ markdown: string;
26
+ }
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The `run` form of {@link ScheduleDefinition} invokes this handler when a
3
+ * schedule's cron fires. It receives {@link ScheduleHandlerArgs} (`receive`,
4
+ * `waitUntil`, `appAuth`) and may return synchronously or as a promise.
5
+ */
6
+ export type GenericScheduleRunHandler<TArgs> = (args: TArgs) => Promise<void> | void;
7
+ /** Constraint shape that bounds the authored keys accepted by {@link defineSchedule}. */
8
+ export interface GenericScheduleDefinitionFields<TArgs> {
9
+ readonly cron: string;
10
+ readonly markdown?: string;
11
+ readonly run?: GenericScheduleRunHandler<TArgs>;
12
+ }
13
+ /**
14
+ * Public definition for a schedule authored in TypeScript. Provide a required
15
+ * `cron` expression plus exactly one of `markdown` or `run`:
16
+ *
17
+ * - `markdown`: fire-and-forget agent invocation. The framework runs the agent
18
+ * on the prompt and discards the output (equivalent to the `<name>.md`
19
+ * markdown form).
20
+ * - `run`: full handler ({@link GenericScheduleRunHandler}). Receives
21
+ * `{ receive, waitUntil, appAuth }` and decides what to do.
22
+ *
23
+ * Identity is derived from the file path under `agent/schedules/`; authored
24
+ * definitions do not carry a `name` field.
25
+ */
26
+ export type GenericScheduleDefinition<TArgs> = {
27
+ readonly cron: string;
28
+ readonly markdown: string;
29
+ readonly run?: never;
30
+ } | {
31
+ readonly cron: string;
32
+ readonly markdown?: never;
33
+ readonly run: GenericScheduleRunHandler<TArgs>;
34
+ };
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Claims eve reads from a Vercel OIDC token payload.
3
+ *
4
+ * Decoded locally without signature verification: callers use the values
5
+ * as stable identifiers (sandbox key derivation, credential routing),
6
+ * never to make authentication decisions.
7
+ */
8
+ export interface VercelOidcTokenClaims {
9
+ readonly ownerId: string | undefined;
10
+ readonly projectId: string | undefined;
11
+ }
12
+ /**
13
+ * Decodes the payload claims of a Vercel OIDC token. Values that are not
14
+ * decodable JWTs, or that carry no recognized claims, decode to empty
15
+ * claims — callers only ever check individual fields.
16
+ */
17
+ export declare function decodeVercelOidcTokenClaims(token: string): VercelOidcTokenClaims;
18
+ /**
19
+ * Resolves the Vercel project id visible to this process: the
20
+ * `VERCEL_PROJECT_ID` env var when exposed, otherwise the `project_id`
21
+ * claim of `VERCEL_OIDC_TOKEN`. Both sources exist at build time and at
22
+ * deployed runtime and name the same project, so identifiers derived
23
+ * from this value agree across the two phases.
24
+ */
25
+ export declare function resolveVercelProjectIdFromEnvironment(): string | undefined;
@@ -0,0 +1 @@
1
+ const EMPTY_CLAIMS={ownerId:void 0,projectId:void 0};function decodeVercelOidcTokenClaims(t){let n=t.split(`.`)[1];if(n===void 0||n.length===0)return EMPTY_CLAIMS;let r;try{r=JSON.parse(Buffer.from(n,`base64url`).toString(`utf8`))}catch{return EMPTY_CLAIMS}if(typeof r!=`object`||!r)return EMPTY_CLAIMS;let i=r;return{ownerId:typeof i.owner_id==`string`?i.owner_id:void 0,projectId:typeof i.project_id==`string`?i.project_id:void 0}}function resolveVercelProjectIdFromEnvironment(){let e=process.env.VERCEL_PROJECT_ID?.trim();if(e!==void 0&&e.length>0)return e;let t=process.env.VERCEL_OIDC_TOKEN?.trim();if(!(t===void 0||t.length===0))return decodeVercelOidcTokenClaims(t).projectId}export{decodeVercelOidcTokenClaims,resolveVercelProjectIdFromEnvironment};
@@ -1,2 +1,2 @@
1
- import{parseWithNitroRolldownAst}from"#internal/bundler/nitro-rolldown.js";const AGENT_FACTORY=`defineAgent`;async function applyModelNameToSource(e,n){let r=await parseAgentSource(e);if(`kind`in r)return r;if((r.errors?.length??0)>0){let t=r.errors?.[0];return{kind:`bail`,reason:`agent.ts does not parse: ${t?.message??`unknown parse error`}`,line:parseErrorLine(e,t)}}let i=findDefineAgentObject(r.program??r,AGENT_FACTORY);if(i===void 0)return{kind:`bail`,reason:`no \`export default ${AGENT_FACTORY}({ ... })\` call found`,line:1};let a=findStringLiteralProperty(i,`model`);if(a===void 0)return{kind:`bail`,reason:"`model` is absent or is not a string literal (e.g. an env reference, a template, or an inlined SDK model)",line:lineAt(e,i.start??0)};let o=a.value;if(o===n)return{kind:`applied`,from:o,to:n,nextSource:e};let s=a.raw?.[0]===`'`?`'`:`"`,c=`${s}${escapeForQuote(n,s)}${s}`;return{kind:`applied`,from:o,to:n,nextSource:e.slice(0,a.start)+c+e.slice(a.end)}}async function parseAgentSource(t){try{return await parseWithNitroRolldownAst(`agent.ts`,t)}catch(e){let n=e;return{kind:`bail`,reason:`agent.ts does not parse: ${n.message??`unknown parse error`}`,line:parseErrorLine(t,n)}}}function unwrapExpression(e){let t=e;for(;t.type===`ParenthesizedExpression`||t.type===`TSAsExpression`||t.type===`TSSatisfiesExpression`;){if(t.expression===void 0||t.expression===null)return t;t=t.expression}return t}function findDefineAgentObject(e,t){for(let n of e.body??[]){if(n.type!==`ExportDefaultDeclaration`)continue;let e=n.declaration;if(e==null||e.type!==`CallExpression`&&e.type!==`ParenthesizedExpression`&&e.type!==`TSAsExpression`&&e.type!==`TSSatisfiesExpression`)continue;let r=unwrapExpression(e);if(r.type!==`CallExpression`||!isFactoryCallee(r.callee,t))continue;let i=r.arguments?.[0];if(i===void 0||i.type===`SpreadElement`)continue;let a=unwrapExpression(i);if(a.type===`ObjectExpression`)return a}}function isFactoryCallee(e,t){return e?.type===`Identifier`&&e.name===t}function findStringLiteralProperty(e,t){for(let n of e.properties){if(n.type!==`Property`||n.computed||!keyMatches(n.key,t))continue;let e=n.value;if(!isAstNode(e))continue;let r=unwrapExpression(e);return r.type===`Literal`&&typeof r.value==`string`&&r.start!==void 0&&r.end!==void 0?r:void 0}}function keyMatches(e,t){return e===void 0?!1:e.type===`Identifier`?e.name===t:e.type===`Literal`?typeof e.value==`string`&&e.value===t:!1}function isAstNode(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}function parseErrorLine(e,t){return typeof t?.loc?.line==`number`?t.loc.line:lineAt(e,t?.labels?.[0]?.start??t?.start??0)}function escapeForQuote(e,t){return e.replaceAll(`\\`,`\\\\`).replaceAll(t,`\\${t}`)}function lineAt(e,t){let n=1;for(let r=0;r<t&&r<e.length;r+=1)e[r]===`
1
+ import{parseWithNitroRolldownAst}from"#internal/bundler/nitro-rolldown.js";const AGENT_FACTORY=`defineAgent`;async function applyModelNameToSource(e,n){let r=await parseAgentSource(e);if(`kind`in r)return r;if((r.errors?.length??0)>0){let t=r.errors?.[0];return{kind:`bail`,reason:`agent.ts does not parse: ${t?.message??`unknown parse error`}`,line:parseErrorLine(e,t)}}let i=findDefineAgentObject(r.program??r,AGENT_FACTORY);if(i===void 0)return{kind:`bail`,reason:`no \`export default ${AGENT_FACTORY}({ ... })\` call found`,line:1};let a=findStringLiteralProperty(i,`model`);if(a===void 0)return{kind:`bail`,reason:"`model` is absent or is not a string literal (e.g. an env reference, a template, an inlined SDK model, or a defineDynamic() dynamic model)",line:lineAt(e,i.start??0)};let o=a.value;if(o===n)return{kind:`applied`,from:o,to:n,nextSource:e};let s=a.raw?.[0]===`'`?`'`:`"`,c=`${s}${escapeForQuote(n,s)}${s}`;return{kind:`applied`,from:o,to:n,nextSource:e.slice(0,a.start)+c+e.slice(a.end)}}async function parseAgentSource(t){try{return await parseWithNitroRolldownAst(`agent.ts`,t)}catch(e){let n=e;return{kind:`bail`,reason:`agent.ts does not parse: ${n.message??`unknown parse error`}`,line:parseErrorLine(t,n)}}}function unwrapExpression(e){let t=e;for(;t.type===`ParenthesizedExpression`||t.type===`TSAsExpression`||t.type===`TSSatisfiesExpression`;){if(t.expression===void 0||t.expression===null)return t;t=t.expression}return t}function findDefineAgentObject(e,t){for(let n of e.body??[]){if(n.type!==`ExportDefaultDeclaration`)continue;let e=n.declaration;if(e==null||e.type!==`CallExpression`&&e.type!==`ParenthesizedExpression`&&e.type!==`TSAsExpression`&&e.type!==`TSSatisfiesExpression`)continue;let r=unwrapExpression(e);if(r.type!==`CallExpression`||!isFactoryCallee(r.callee,t))continue;let i=r.arguments?.[0];if(i===void 0||i.type===`SpreadElement`)continue;let a=unwrapExpression(i);if(a.type===`ObjectExpression`)return a}}function isFactoryCallee(e,t){return e?.type===`Identifier`&&e.name===t}function findStringLiteralProperty(e,t){for(let n of e.properties){if(n.type!==`Property`||n.computed||!keyMatches(n.key,t))continue;let e=n.value;if(!isAstNode(e))continue;let r=unwrapExpression(e);return r.type===`Literal`&&typeof r.value==`string`&&r.start!==void 0&&r.end!==void 0?r:void 0}}function keyMatches(e,t){return e===void 0?!1:e.type===`Identifier`?e.name===t:e.type===`Literal`?typeof e.value==`string`&&e.value===t:!1}function isAstNode(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}function parseErrorLine(e,t){return typeof t?.loc?.line==`number`?t.loc.line:lineAt(e,t?.labels?.[0]?.start??t?.start??0)}function escapeForQuote(e,t){return e.replaceAll(`\\`,`\\\\`).replaceAll(t,`\\${t}`)}function lineAt(e,t){let n=1;for(let r=0;r<t&&r<e.length;r+=1)e[r]===`
2
2
  `&&(n+=1);return n}export{applyModelNameToSource};
@@ -43,6 +43,50 @@ version uses hyphens (`claude-opus-4-8`), while the Gateway id above uses a dot
43
43
 
44
44
  Model use is subject to the terms, data-processing commitments, retention behavior, and available controls of the selected provider and routing path. Review the [AI Gateway model catalog](https://vercel.com/ai-gateway/models) for gateway-routed models, and review the provider's terms when you configure a direct `LanguageModel`.
45
45
 
46
+ ### Choose the model dynamically
47
+
48
+ `model` also accepts `defineDynamic({ fallback, events })`. `fallback` is the
49
+ compiled static model: it anchors build-time metadata (routing, credentials,
50
+ context window) and serves whenever no dynamic selection is set.
51
+
52
+ ```ts title="agent/agent.ts"
53
+ import { defineAgent, defineDynamic } from "eve";
54
+
55
+ export default defineAgent({
56
+ model: defineDynamic({
57
+ fallback: "anthropic/claude-sonnet-5",
58
+ events: {
59
+ "session.started": (_event, ctx) =>
60
+ ctx.session.auth.initiator?.attributes.plan === "enterprise"
61
+ ? "anthropic/claude-opus-4.8"
62
+ : null,
63
+ },
64
+ }),
65
+ });
66
+ ```
67
+
68
+ Handlers receive the shared [dynamic resolver
69
+ context](./guides/dynamic-capabilities) (`ctx.session`, `ctx.channel`,
70
+ `ctx.messages`) and return a gateway model id, an AI SDK `LanguageModel`, a
71
+ selection object, or `null` to leave the scope unset.
72
+
73
+ - **Scopes.** `session.started` (once per session), `turn.started` (once per
74
+ turn), `step.started` (every model step). Precedence: step > turn >
75
+ session > `fallback`. Prefer `session.started`: prompt caches are per
76
+ model, so every switch re-ingests the conversation at uncached prices.
77
+ - **Failures degrade, never fail the turn.** A resolver that throws or
78
+ returns an invalid selection logs an error and leaves its scope unset.
79
+ Build-time validation covers only `fallback`; a selected model without
80
+ credentials fails at request time.
81
+ - **Serialization.** Session/turn selections must be model id strings; return
82
+ live `LanguageModel` objects only from `step.started`.
83
+ - **Selection object.** `{ model, modelContextWindowTokens?, modelOptions? }`.
84
+ Set `modelContextWindowTokens` when the selected model's window differs
85
+ from the fallback's — it is never inherited. Omitted `modelOptions` reuses
86
+ the agent-level `modelOptions`.
87
+
88
+ Runtime identity reports a dynamic agent's model as `dynamic:<fallback id>`.
89
+
46
90
  ## Reasoning effort
47
91
 
48
92
  Set `reasoning` to control the model's reasoning effort through AI SDK's
@@ -70,6 +70,33 @@ export default slackChannel({
70
70
 
71
71
  `threadContext` performs one `conversations.replies` request for each triggering thread reply and requires the matching Slack history scope. Omit it when the agent should see only direct mentions. `loadThreadContextMessages` remains available when you need custom filtering or non-model processing of the raw thread messages.
72
72
 
73
+ ### Slack API calls outside a handler
74
+
75
+ Inside webhook-side handlers (`onAppMention`, `onInteraction`, `events`),
76
+ `ctx.slack.request(operation, body)` is the raw-API escape hatch. Outside
77
+ those contexts there is no handle — a schedule resolving reactions on old
78
+ messages, for example, has no inbound Slack request. For that, call the
79
+ same primitive the handle uses directly:
80
+
81
+ ```ts
82
+ import { callSlackApi, resolveSlackBotToken } from "eve/channels/slack";
83
+ import { connectSlackCredentials } from "@vercel/connect/eve";
84
+
85
+ const { botToken } = connectSlackCredentials("slack/my-agent");
86
+ const response = await callSlackApi({
87
+ botToken,
88
+ operation: "reactions.get",
89
+ body: { channel: "C0123456789", timestamp: "1712345678.000100", full: true },
90
+ });
91
+ if (!response.ok) throw new Error(String(response.error));
92
+ ```
93
+
94
+ `callSlackApi` resolves function-form tokens (secret managers, Connect
95
+ rotation) at call time and form-encodes the body — the only safe default,
96
+ since Slack's JSON support is partial (`conversations.replies` rejects
97
+ JSON). `resolveSlackBotToken` materializes a `SlackBotToken` to a string
98
+ when you need the bearer token itself.
99
+
73
100
  ### Delivery
74
101
 
75
102
  The default handlers reply in-thread and show progress. Typing indicators post automatically: `Thinking…` on inbound, `Working…` on `turn.started`, a truncated reasoning snippet on `reasoning.appended`, and tool status on `actions.requested`. Reasoning snippets build progressively: extensions of at least four characters appear immediately, while smaller streamed deltas use the five-second refresh interval to avoid one Slack request per token. Override `events["reasoning.appended"]` if you prefer generic wording. Override `onAppMention` or the `events` handlers to customize.
@@ -138,6 +138,8 @@ export default defineMcpClientConnection({
138
138
 
139
139
  `"linear/myagent"` is the UID you chose when registering the Connect client. `connect("linear/myagent")` is shorthand for a user-scoped interactive OAuth connection: eve resolves a token for the active user before each tool call, emits `authorization.required` when that user has not authorized yet, and resumes the parked turn after the callback completes.
140
140
 
141
+ When a local subagent needs interactive authorization, eve surfaces the authorization lifecycle on the root session's channel, including through nested subagent chains. The challenge still points to the child session's callback, so completing it resumes the child directly while the parent continues waiting for its result.
142
+
141
143
  That means the channel that creates or continues the session must authenticate a real user. For a web app, configure `agent/channels/eve.ts` so your app session maps to `principalType: "user"`; for platform channels, use the built-in channel auth that maps the sender to a user principal. If no authenticated user is attached to the session, the first user-scoped connection call fails with `reason: "principal_required"`.
142
144
 
143
145
  If the remote service should act as the agent itself instead of the end-user, make the Connect connection app-scoped:
@@ -1,9 +1,25 @@
1
1
  ---
2
2
  title: "Dynamic Capabilities"
3
- description: "Resolve tools, skills, and instructions at runtime with defineDynamic: the resolver events, execution order, and how dynamic tools survive step boundaries."
3
+ description: "Resolve models, tools, skills, and instructions at runtime: dynamic model selection, defineDynamic resolver events, execution order, and durable dynamic tools."
4
4
  ---
5
5
 
6
- `defineDynamic` resolves tools, skills, and instructions at runtime from a session event instead of declaring them up front. Reach for it when the right capabilities aren't known until the session starts, because they hinge on who the caller is, what tenant they belong to, feature flags, or external data. The [tools](../tools), [skills](../skills), and [instructions](../instructions) guides each point here for their dynamic form.
6
+ `defineDynamic` resolves the model, tools, skills, and instructions at runtime from a session event instead of declaring them up front. Reach for it when the right capability isn't known until the session starts, because it hinges on who the caller is, what tenant they belong to, feature flags, or external data. The [tools](../tools), [skills](../skills), and [instructions](../instructions) guides each point here for their dynamic form.
7
+
8
+ ## Dynamic models
9
+
10
+ The `model` field in `agent.ts` accepts `defineDynamic({ fallback, events })`.
11
+ Resolvers run at `session.started`, `turn.started`, or `step.started`
12
+ (precedence: step > turn > session > `fallback`); `null` leaves a scope unset
13
+ and failures degrade to the next scope. Prefer `session.started` — prompt
14
+ caches are per model, so switching mid-session re-ingests the conversation at
15
+ uncached prices. See
16
+ [agent configuration](../agent-config#choose-the-model-dynamically) for the
17
+ full contract.
18
+
19
+ `fallback` is model-only: the agent always needs exactly one model, and the
20
+ compiled fallback anchors build-time metadata. Tools, skills, and instructions
21
+ default by authoring a static entry (or returning `null`), so `fallback` on
22
+ their `defineDynamic` export is a build error.
7
23
 
8
24
  ## Dynamic tools
9
25
 
package/docs/sandbox.mdx CHANGED
@@ -164,7 +164,7 @@ You can also write your own backend. A `SandboxBackend` is an adapter object wit
164
164
  There are two hooks, scoped differently:
165
165
 
166
166
  - **`bootstrap({ use })`** is template-scoped and runs once when the template is built. Put reusable setup here that every later session inherits, such as cloning a baseline repo, installing dependencies, or seeding files. Call `use()` to get a `SandboxSession`. Only template filesystem state and supported backend metadata carry into later sessions; config like network policy does not. If external inputs affect what bootstrap produces, set `revalidationKey: () => string` so eve knows when to rebuild the template (authored sandbox source and seed contents are already tracked for you).
167
- - **`onSession({ use, ctx })`** is durable-session-scoped and runs once per session. Put per-session setup here, including network policy, resources, timeout, per-user credentials, and one-time markers. Because it runs inside the active runtime context, it can read `ctx.session` and derive the current principal without baking credentials into the template. Call `use(opts?)` to get a `SandboxSession`; `opts` flow to the backend's update path after create.
167
+ - **`onSession({ use, ctx })`** is durable-session-scoped and runs once per session (and again if a sandbox definition change replaces the session's sandbox). Put per-session setup here, including network policy, resources, timeout, per-user credentials, and one-time markers. Because it runs inside the active runtime context, it can read `ctx.session` and derive the current principal without baking credentials into the template. Call `use(opts?)` to get a `SandboxSession`; `opts` flow to the backend's update path after create.
168
168
 
169
169
  If you require a network policy or other configuration for every session, configure it on the backend factory or in `onSession`; do not rely on bootstrap-only configuration.
170
170
 
@@ -185,6 +185,8 @@ export default defineSandbox({
185
185
 
186
186
  Sessions are persistent, and how the underlying runtime idles out depends on the backend. On the Vercel backend, the VM times out after a period of inactivity (default 30 minutes); eve preserves the filesystem and resumes the sandbox on the next message as if nothing happened, even days later. The Docker backend keeps a long-lived container per durable session and persists `/workspace` across turns without that timeout, and the just-bash backend stores its virtual filesystem under `.eve/sandbox-cache/`. In every case, `/workspace` survives between turns for the same session.
187
187
 
188
+ Session sandboxes are keyed per durable session, not per deployment, so redeploying your app does not discard them. A session gets a replacement sandbox only when the sandbox definition itself changes — the authored sandbox source, workspace seed content, or `revalidationKey` — in which case the next turn starts from the rebuilt template and `onSession` runs again.
189
+
188
190
  When the eve server stops, no sandbox compute outlives it. `eve dev` stops the sandboxes it started when the dev server closes, and a self-hosted production server stops every open sandbox on shutdown (`SIGTERM`/`SIGINT`). Session state persists across the stop — the next server start reattaches each durable session from its stopped container, VM, or snapshot. Custom `SandboxBackend` adapters participate through the handle's `shutdown()` method: stop the underlying compute, keeping the session reattachable from persisted state where the backend supports it.
189
191
 
190
192
  ## Network policy
@@ -109,7 +109,7 @@ Because the name lives in the same runtime tool namespace as authored tools, a s
109
109
 
110
110
  Do not rely on subagent delegation by itself as an approval boundary. Put sensitive tools behind `approval`, connection approval, route/session authorization, or other controls wherever those tools can be called.
111
111
 
112
- Each delegated subagent spins up its own child session and stream. The parent stream carries only the control-plane events `subagent.called` and `subagent.completed`. To follow the child's full progress, read `subagent.called.data.childSessionId` and subscribe at `GET /eve/v1/session/:childSessionId/stream`.
112
+ Each delegated subagent spins up its own child session and stream. The parent stream carries the control-plane events `subagent.called` and `subagent.completed`, plus interactive `input.requested`, `authorization.required`, and `authorization.completed` events proxied from descendants so the root channel can prompt the user. To follow the child's other progress, read `subagent.called.data.childSessionId` and subscribe at `GET /eve/v1/session/:childSessionId/stream`.
113
113
 
114
114
  ## When to split
115
115
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.21.1",
3
+ "version": "0.22.1",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [
@@ -1,157 +0,0 @@
1
- import type { FetchFileResult } from "#channel/adapter.js";
2
- import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
3
- import { type ChannelCors, type ChannelCorsOptions } from "#channel/cors.js";
4
- import type { TypedReceiveTarget } from "#channel/receive-target.js";
5
- import type { SessionAuthContext } from "#channel/types.js";
6
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
7
- import type { SessionContext } from "#public/definitions/callback-context.js";
8
- import type { RouteDefinition, SendFn } from "#channel/routes.js";
9
- import type { Session, SessionHandle } from "#channel/session.js";
10
- declare const CHANNEL_METADATA_TYPE: unique symbol;
11
- export type { Session, SessionHandle } from "#channel/session.js";
12
- export type { ChannelCors, ChannelCorsOptions } from "#channel/cors.js";
13
- export { GET, POST, PUT, PATCH, DELETE, WS } from "#channel/routes.js";
14
- export type { HttpRouteDefinition, RouteDefinition, RouteHandlerArgs, SendFn, SendOptions, SendPayload, GetSessionFn, WebSocketMessage, WebSocketPeer, WebSocketRouteDefinition, WebSocketRouteHandler, WebSocketRouteHooks, WebSocketUpgradeRequest, WebSocketUpgradeResult, } from "#channel/routes.js";
15
- type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
16
- type: T;
17
- }> extends {
18
- data: infer D;
19
- } ? D : undefined;
20
- /**
21
- * Session operations on the `channel` argument of every channel event handler.
22
- */
23
- export interface ChannelSessionOps {
24
- readonly continuationToken: string;
25
- setContinuationToken(token: string): void;
26
- }
27
- /**
28
- * Channel context passed to event handlers: `TCtx` intersected with
29
- * {@link ChannelSessionOps}.
30
- */
31
- export type ChannelContext<TCtx> = TCtx & ChannelSessionOps;
32
- type ChannelEventHandler<T extends HandleMessageStreamEvent["type"], TCtx> = (data: EventData<T>, channel: ChannelContext<TCtx>, ctx: SessionContext) => void | Promise<void>;
33
- type ChannelSessionFailedHandler<TCtx> = (data: EventData<"session.failed">, channel: ChannelContext<TCtx>) => void | Promise<void>;
34
- /**
35
- * Optional handlers keyed by session lifecycle event name. Each handler receives
36
- * the event `data`, the {@link ChannelContext}, and a {@link SessionContext}
37
- * `ctx`. The `session.failed` handler is the exception: it receives only `data`
38
- * and the channel context, with no `ctx`.
39
- */
40
- export interface ChannelEvents<TCtx = void> {
41
- readonly "turn.started"?: ChannelEventHandler<"turn.started", TCtx>;
42
- readonly "actions.requested"?: ChannelEventHandler<"actions.requested", TCtx>;
43
- readonly "action.result"?: ChannelEventHandler<"action.result", TCtx>;
44
- readonly "message.completed"?: ChannelEventHandler<"message.completed", TCtx>;
45
- readonly "message.appended"?: ChannelEventHandler<"message.appended", TCtx>;
46
- readonly "reasoning.appended"?: ChannelEventHandler<"reasoning.appended", TCtx>;
47
- readonly "reasoning.completed"?: ChannelEventHandler<"reasoning.completed", TCtx>;
48
- readonly "input.requested"?: ChannelEventHandler<"input.requested", TCtx>;
49
- readonly "turn.failed"?: ChannelEventHandler<"turn.failed", TCtx>;
50
- readonly "turn.completed"?: ChannelEventHandler<"turn.completed", TCtx>;
51
- readonly "session.failed"?: ChannelSessionFailedHandler<TCtx>;
52
- readonly "session.completed"?: ChannelEventHandler<"session.completed", TCtx>;
53
- readonly "session.waiting"?: ChannelEventHandler<"session.waiting", TCtx>;
54
- readonly "authorization.required"?: ChannelEventHandler<"authorization.required", TCtx>;
55
- readonly "authorization.completed"?: ChannelEventHandler<"authorization.completed", TCtx>;
56
- }
57
- /**
58
- * Input passed to a channel's `receive` callback when another channel or
59
- * schedule proactively routes a message to it.
60
- */
61
- export interface ReceiveInput<TReceiveTarget = Record<string, unknown>> {
62
- readonly message: string;
63
- readonly target: Readonly<TReceiveTarget>;
64
- readonly auth: SessionAuthContext | null;
65
- }
66
- /**
67
- * The object passed to {@link defineChannel}. `routes` is required; `state`
68
- * seeds durable adapter state, `context` builds the per-step `channel` argument
69
- * for `events` and `deliver`, `events` handle session lifecycle, `receive`
70
- * accepts cross-channel handoffs, `fetchFile` stages remote file URLs, and
71
- * `metadata` projects observability data.
72
- *
73
- * Generics: `TState` (adapter state), `TCtx` (context factory return type),
74
- * `TReceiveTarget` (cross-channel target shape), `TMetadata` (instrumentation
75
- * projection).
76
- */
77
- export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> {
78
- readonly state?: TState;
79
- /**
80
- * CORS policy for this channel's HTTP routes. `true` enables H3/Nitro's
81
- * permissive defaults (`origin`, methods, request headers, and exposed
82
- * headers all `"*"`); `false` or omission leaves CORS untouched. Pass an
83
- * object for a serializable subset of H3/Nitro CORS options.
84
- */
85
- readonly cors?: ChannelCors;
86
- /**
87
- * Builds the per-step channel context handed to `events` and `deliver`.
88
- * Receives the live {@link SessionHandle}, so a factory can close over it to
89
- * register late-bound callbacks. eve writes state mutations made inside the
90
- * returned context back through `adapter.state`.
91
- *
92
- * Return the channel-owned context (thread handles, API clients, etc.). The
93
- * framework passes it as the `channel` argument to event handlers (with
94
- * {@link ChannelSessionOps} injected) and passes {@link SessionContext} as a
95
- * separate `ctx` argument.
96
- */
97
- context?(state: NonNullable<TState>, session: SessionHandle): TCtx;
98
- readonly routes: readonly RouteDefinition<TState>[];
99
- receive?(input: ReceiveInput<TReceiveTarget>, args: {
100
- send: SendFn<TState>;
101
- }): Promise<Session>;
102
- readonly events?: ChannelEvents<TCtx>;
103
- /**
104
- * Fetches bytes for a `URL` object encountered on a `FilePart.data` by the
105
- * staging pipeline. Return `null` to pass the URL through to the model
106
- * provider unchanged, or bytes / {@link FetchFileResult} to stage the file to
107
- * the sandbox.
108
- */
109
- readonly fetchFile?: (url: string) => Promise<Buffer | FetchFileResult | null>;
110
- /**
111
- * Channel-owned metadata exposed to instrumentation callbacks. This is the
112
- * channel's public observability surface, not a dump of durable adapter state,
113
- * so keep it small. Return an object of JSON primitives, arrays, and plain
114
- * objects: eve omits `undefined` properties and drops projections containing
115
- * values such as `Date` or `Map`.
116
- */
117
- readonly metadata?: (state: NonNullable<TState>) => TMetadata;
118
- /**
119
- * Identifier of the adapter family this channel belongs to. Set by
120
- * higher-level wrappers (e.g. `slackChannel` passes `"slack"`) so downstream
121
- * consumers can render typed channel chips instead of bucketing everything
122
- * under "unknown".
123
- *
124
- * Authors calling `defineChannel` directly do not need to set this; the
125
- * framework defaults to `"http"` for stateless channels and `"defineChannel"`
126
- * for stateful ones.
127
- */
128
- readonly kindHint?: string;
129
- }
130
- /**
131
- * Opaque channel value produced by {@link defineChannel} and exported from
132
- * `agent/channels/<name>.ts`. Exposes the channel's routes, an optional
133
- * `receive` hook, and (via a phantom property) its metadata shape. Unlike
134
- * {@link ChannelDefinition} it has no `TCtx` parameter: the context type is
135
- * internal to the definition.
136
- */
137
- export interface Channel<TState = undefined, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> extends TypedReceiveTarget<TReceiveTarget> {
138
- readonly __kind: typeof CHANNEL_SENTINEL;
139
- readonly [CHANNEL_METADATA_TYPE]?: TMetadata;
140
- readonly routes: readonly RouteDefinition<TState>[];
141
- readonly cors?: ChannelCorsOptions;
142
- readonly receive?: (input: ReceiveInput<TReceiveTarget>, args: {
143
- send: SendFn<TState>;
144
- }) => Promise<Session>;
145
- }
146
- /**
147
- * Extracts the metadata projection type (`TMetadata`) from a {@link Channel}.
148
- * Resolves to `Record<string, unknown>` when the value is not a Channel.
149
- */
150
- export type InferChannelMetadata<TChannel> = TChannel extends Channel<any, any, infer TMetadata> ? TMetadata : Record<string, unknown>;
151
- /**
152
- * Builds a {@link Channel} from a {@link ChannelDefinition}. Returns a value
153
- * placed at `agent/channels/<name>.ts`; the file path supplies the channel name
154
- * (do not add a `name` field). `TCtx` (the context factory's return type) is
155
- * internal to the definition and is not part of the returned Channel signature.
156
- */
157
- export declare function defineChannel<TState = undefined, TCtx = void, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>>(definition: ChannelDefinition<TState, TCtx, TReceiveTarget, TMetadata>): Channel<TState, TReceiveTarget, TMetadata>;
@@ -1 +0,0 @@
1
- import{CHANNEL_SENTINEL}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{normalizeChannelCors}from"#channel/cors.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";import{DELETE,GET,PATCH,POST,PUT,WS}from"#channel/routes.js";function defineChannel(t){let n=buildAdapter(t),i=normalizeChannelCors(t.cors);return{__kind:CHANNEL_SENTINEL,routes:t.routes,adapter:n,cors:i,receive:t.receive}}function buildAdapter(e){let r=e.state!=null,a=e.context!=null,o=e.fetchFile!==void 0,s=e.metadata,c=r||a||s!==void 0,l={},u=!1,d=[`turn.started`,`actions.requested`,`action.result`,`message.completed`,`message.appended`,`reasoning.appended`,`reasoning.completed`,`input.requested`,`turn.failed`,`turn.completed`,`session.failed`,`session.completed`,`session.waiting`,`authorization.required`,`authorization.completed`],f=e.events;for(let e of d){let t=f?.[e];t&&(u=!0,l[e]=(r,i)=>{let a={...i,continuationToken:i.session?.continuationToken??``,setContinuationToken:e=>i.session?.setContinuationToken(e)};return e===`session.failed`?t(r,a):t(r,a,buildCallbackContext())})}return!c&&!u&&!o?{kind:e.kindHint??HTTP_ADAPTER_KIND}:{kind:e.kindHint??`defineChannel`,state:r?{...e.state}:{},fetchFile:e.fetchFile,instrumentation:s===void 0?void 0:{metadata(e){return s(e)}},createAdapterContext(t){let n=t.state,r=t.session;return{...a?e.context(n,r):{},state:n,ctx:t.ctx,session:r}},deliver(e){return defaultDeliverResult(e)},...l}}export{DELETE,GET,PATCH,POST,PUT,WS,defineChannel};