eve 0.10.0 → 0.11.0

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 (91) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +14 -0
  3. package/dist/src/cli/banner.d.ts +6 -3
  4. package/dist/src/cli/banner.js +1 -1
  5. package/dist/src/cli/commands/init.js +1 -1
  6. package/dist/src/cli/dev/tui/agent-header.js +1 -1
  7. package/dist/src/cli/run.js +1 -1
  8. package/dist/src/evals/runner/run-evals.d.ts +1 -1
  9. package/dist/src/harness/input-requests.d.ts +20 -1
  10. package/dist/src/harness/input-requests.js +1 -1
  11. package/dist/src/harness/tool-loop.js +1 -1
  12. package/dist/src/internal/application/package.js +1 -1
  13. package/dist/src/protocol/message.d.ts +11 -2
  14. package/dist/src/protocol/message.js +2 -2
  15. package/dist/src/public/next/index.d.ts +1 -7
  16. package/dist/src/public/next/index.js +1 -1
  17. package/dist/src/public/next/vercel-output-config.js +1 -1
  18. package/dist/src/setup/scaffold/create/project.js +1 -1
  19. package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
  20. package/dist/src/setup/scaffold/create/web-template.js +27 -4
  21. package/dist/src/setup/scaffold/update/channels.js +1 -1
  22. package/docs/README.md +19 -5
  23. package/docs/agent-config.md +16 -19
  24. package/docs/channels/custom.mdx +24 -18
  25. package/docs/channels/discord.mdx +25 -13
  26. package/docs/channels/eve.mdx +24 -6
  27. package/docs/channels/github.mdx +41 -10
  28. package/docs/channels/linear.mdx +29 -36
  29. package/docs/channels/overview.mdx +16 -4
  30. package/docs/channels/slack.mdx +59 -32
  31. package/docs/channels/teams.mdx +19 -7
  32. package/docs/channels/telegram.mdx +23 -9
  33. package/docs/channels/twilio.mdx +28 -6
  34. package/docs/concepts/context-control.md +24 -41
  35. package/docs/concepts/default-harness.md +19 -9
  36. package/docs/concepts/execution-model-and-durability.md +14 -14
  37. package/docs/concepts/security-model.md +32 -8
  38. package/docs/concepts/sessions-runs-and-streaming.md +11 -9
  39. package/docs/connections.mdx +49 -29
  40. package/docs/evals/assertions.mdx +30 -29
  41. package/docs/evals/cases.mdx +10 -10
  42. package/docs/evals/judge.mdx +14 -14
  43. package/docs/evals/overview.mdx +15 -17
  44. package/docs/evals/reporters.mdx +19 -7
  45. package/docs/evals/running.mdx +2 -2
  46. package/docs/evals/targets.mdx +15 -4
  47. package/docs/getting-started.mdx +70 -31
  48. package/docs/guides/auth-and-route-protection.md +18 -12
  49. package/docs/guides/client/continuations.mdx +5 -13
  50. package/docs/guides/client/messages.mdx +2 -2
  51. package/docs/guides/client/output-schema.mdx +2 -2
  52. package/docs/guides/client/overview.mdx +4 -4
  53. package/docs/guides/client/streaming.mdx +6 -5
  54. package/docs/guides/deployment.md +22 -20
  55. package/docs/guides/dev-tui.md +85 -13
  56. package/docs/guides/dynamic-capabilities.md +24 -8
  57. package/docs/guides/dynamic-workflows.md +33 -12
  58. package/docs/guides/frontend/nextjs.mdx +28 -14
  59. package/docs/guides/frontend/nuxt.mdx +21 -4
  60. package/docs/guides/frontend/overview.mdx +11 -11
  61. package/docs/guides/frontend/sveltekit.mdx +20 -3
  62. package/docs/guides/frontend/use-eve-agent-svelte.mdx +106 -14
  63. package/docs/guides/frontend/use-eve-agent-vue.mdx +108 -15
  64. package/docs/guides/hooks.md +14 -60
  65. package/docs/guides/instrumentation.md +24 -18
  66. package/docs/guides/remote-agents.md +19 -15
  67. package/docs/guides/session-context.md +12 -12
  68. package/docs/guides/state.md +25 -9
  69. package/docs/instructions.mdx +18 -10
  70. package/docs/introduction.md +12 -12
  71. package/docs/legal.md +16 -0
  72. package/docs/meta.json +1 -0
  73. package/docs/reference/cli.md +101 -53
  74. package/docs/reference/meta.json +1 -1
  75. package/docs/reference/project-layout.md +39 -32
  76. package/docs/reference/typescript-api.md +1 -1
  77. package/docs/sandbox.mdx +25 -19
  78. package/docs/schedules.mdx +16 -14
  79. package/docs/skills.mdx +8 -6
  80. package/docs/subagents.mdx +39 -13
  81. package/docs/tools.mdx +13 -9
  82. package/docs/tutorial/connect-a-warehouse.mdx +16 -7
  83. package/docs/tutorial/first-agent.mdx +21 -11
  84. package/docs/tutorial/guard-the-spend.mdx +11 -9
  85. package/docs/tutorial/how-it-runs.mdx +10 -8
  86. package/docs/tutorial/query-sample-data.mdx +8 -8
  87. package/docs/tutorial/remember-definitions.mdx +5 -5
  88. package/docs/tutorial/run-analysis.mdx +13 -11
  89. package/docs/tutorial/ship-it.mdx +70 -14
  90. package/docs/tutorial/team-playbooks.mdx +33 -10
  91. package/package.json +1 -1
@@ -32,6 +32,7 @@ import { cn } from "@/lib/utils";
32
32
  import { AgentMessage } from "./agent-message";
33
33
 
34
34
  const AGENT_NAME = "__EVE_INIT_APP_NAME__";
35
+ const BETA_TERMS_HREF = "https://vercel.com/docs/release-phases/public-beta-agreement";
35
36
 
36
37
  type AgentStatus = ReturnType<typeof useEveAgent>["status"];
37
38
 
@@ -57,9 +58,19 @@ export function AgentChat() {
57
58
  return (
58
59
  <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">
59
60
  {isEmpty ? null : (
60
- <header className="flex h-14 shrink-0 items-center justify-center gap-2 pl-4 pr-2">
61
- <span className="text-muted-foreground text-sm">{AGENT_NAME}</span>
62
- <StatusDot status={agent.status} />
61
+ <header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">
62
+ <span className="flex min-w-0 items-center gap-2">
63
+ <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>
64
+ <StatusDot status={agent.status} />
65
+ </span>
66
+ <a
67
+ className="rounded-full border border-amber-500/30 px-2 py-0.5 font-medium text-amber-700 text-xs transition-colors hover:bg-amber-500/10 dark:text-amber-300"
68
+ href={BETA_TERMS_HREF}
69
+ rel="noreferrer"
70
+ target="_blank"
71
+ >
72
+ Public preview
73
+ </a>
63
74
  </header>
64
75
  )}
65
76
 
@@ -102,7 +113,19 @@ export function AgentChat() {
102
113
  : "max-w-3xl shrink-0 pb-6",
103
114
  )}
104
115
  >
105
- {isEmpty ? <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1> : null}
116
+ {isEmpty ? (
117
+ <div className="flex flex-col items-center gap-3 text-center">
118
+ <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>
119
+ <a
120
+ className="rounded-full border border-amber-500/30 px-2 py-0.5 font-medium text-amber-700 text-xs transition-colors hover:bg-amber-500/10 dark:text-amber-300"
121
+ href={BETA_TERMS_HREF}
122
+ rel="noreferrer"
123
+ target="_blank"
124
+ >
125
+ Public preview
126
+ </a>
127
+ </div>
128
+ ) : null}
106
129
  <div className="w-full">{composer}</div>
107
130
  </div>
108
131
  </main>
@@ -4,4 +4,4 @@ import { slackChannel } from "eve/channels/slack";
4
4
  export default slackChannel({
5
5
  credentials: connectSlackCredentials(${JSON.stringify(e)}),
6
6
  });
7
- `}function renderWebAppTemplate(e,t,n){let r=n?``:`, { configureVercelOutput: false }`;return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,r)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,eve:n?.eve??WEB_DEFAULT_VERCEL_SERVICES.eve}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let r of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,r);await pathExists(i)&&t.push(i)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(t){let i=join(t.projectRoot,`package.json`),a=await pathExists(join(t.projectRoot,`app/page.tsx`));if(!t.force&&await isNextJsProject(t.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[i],packageJsonUpdated:[]};let o=await patchWebPackageJson(i,resolveWebPackageVersions(t.webPackageVersions)),s=[],l=[],u=[],d=[],m=basename(resolve(t.projectRoot)),h=t.configureVercelServices??!0;if(h){let e=join(t.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(e)===`written`?s.push(e):d.push(e)}let g=await getPackageManagerStrategy(t.packageManager??`pnpm`).applyProjectConfiguration(t.projectRoot);s.push(...g.filesWritten),d.push(...g.filesSkipped);for(let[e,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(t.projectRoot,e);if(e===`agent/channels/eve.ts`&&!t.force&&await pathExists(a)){d.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(i,m,h),{force:!0}),s.push(a),o&&l.push(a)}u.push(...await findCompetingNextConfigFiles(t.projectRoot));let _={kind:`web`,action:a?`overwritten`:`created`,filesWritten:s,filesSkipped:d,packageJsonUpdated:o.mutations};return l.length>0&&(_.filesOverwritten=l),u.length>0&&(_.competingNextConfigFiles=u),o.nodeEngineOverride!==void 0&&(_.nodeEngineOverride=o.nodeEngineOverride),_}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let o=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.2.2`),s=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,o),c=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(e.slackConnectorUid??`slack/${c}`),{force:e.force});let l={kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:s,slackConnectorSlug:c};return i&&(l.filesOverwritten=[t]),l}async function listAuthoredChannels(e){let t=join(e,`agent/channels`),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug};
7
+ `}function renderWebAppTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t).replaceAll(`__EVE_INIT_WITH_EVE_OPTIONS__`,``)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,eve:n?.eve??WEB_DEFAULT_VERCEL_SERVICES.eve}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}async function findCompetingNextConfigFiles(e){let t=[];for(let r of WEB_COMPETING_NEXT_CONFIG_PATHS){let i=join(e,r);await pathExists(i)&&t.push(i)}return t}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(t){let i=join(t.projectRoot,`package.json`),a=await pathExists(join(t.projectRoot,`app/page.tsx`));if(!t.force&&await isNextJsProject(t.projectRoot))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[i],packageJsonUpdated:[]};let o=await patchWebPackageJson(i,resolveWebPackageVersions(t.webPackageVersions)),s=[],l=[],u=[],d=[],m=basename(resolve(t.projectRoot));if(t.configureVercelServices??!0){let e=join(t.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(e)===`written`?s.push(e):d.push(e)}let h=await getPackageManagerStrategy(t.packageManager??`pnpm`).applyProjectConfiguration(t.projectRoot);s.push(...h.filesWritten),d.push(...h.filesSkipped);for(let[e,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(t.projectRoot,e);if(e===`agent/channels/eve.ts`&&!t.force&&await pathExists(a)){d.push(a);continue}let o=await pathExists(a);await writeTextFile(a,renderWebAppTemplate(i,m),{force:!0}),s.push(a),o&&l.push(a)}u.push(...await findCompetingNextConfigFiles(t.projectRoot));let g={kind:`web`,action:a?`overwritten`:`created`,filesWritten:s,filesSkipped:d,packageJsonUpdated:o.mutations};return l.length>0&&(g.filesOverwritten=l),u.length>0&&(g.competingNextConfigFiles=u),o.nodeEngineOverride!==void 0&&(g.nodeEngineOverride=o.nodeEngineOverride),g}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let o=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`0.2.2`),s=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,o),c=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);await writeTextFile(t,buildSlackTemplate(e.slackConnectorUid??`slack/${c}`),{force:e.force});let l={kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:s,slackConnectorSlug:c};return i&&(l.filesOverwritten=[t]),l}async function listAuthoredChannels(e){let t=join(e,`agent/channels`),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,isNextJsProject,listAuthoredChannels,normalizeSlackConnectorSlug};
package/docs/README.md CHANGED
@@ -10,6 +10,20 @@ Important naming note:
10
10
  - The current published package name is `eve`.
11
11
  - The CLI binary is `eve`.
12
12
 
13
+ ## Legal and safeguards
14
+
15
+ Eve is currently a preview and subject to the Vercel beta terms; the framework, APIs, documentation, and behavior may change before general availability.
16
+
17
+ As the deployer, it is your responsibility to ensure your agent complies with applicable laws.
18
+
19
+ You are responsible for configuring approval policies, tool restrictions, connection scopes, route/session authorization, sandbox controls, telemetry exports, and other safeguards appropriate for your use case.
20
+
21
+ Before using Eve with non-public, sensitive, regulated, or production data, review which default tools, custom tools, MCP tools, shell/file/web tools, connected services, subagents, schedules, and external actions are available to the agent.
22
+
23
+ Require human approval or other safeguards for sensitive, irreversible, regulated, financial, healthcare, employment, housing, legal, safety-impacting, user-impacting, or external side-effecting actions.
24
+
25
+ Unless you configure stricter controls, Eve agents may operate with permissive settings, including tool execution without human approval where approval is omitted and sandbox network egress that is not deny-all. Do not rely on model behavior alone to prevent sensitive or irreversible actions.
26
+
13
27
  Casing convention:
14
28
 
15
29
  - Use Title Case for page `title` frontmatter and `meta.json` section titles (Fumadocs renders the page `title` as both the sidebar entry and the `<h1>`, so one casing covers both) — e.g. `Execution Model & Durability`, `Dynamic Capabilities`, `Build an Agent`.
@@ -30,14 +44,14 @@ Read in this order:
30
44
  9. [Connections](./connections.mdx)
31
45
  10. [Sandboxes](./sandbox.mdx)
32
46
  11. [Channels](./channels/overview.mdx)
33
- 12. [Session Context](./guides/session-context.md)
47
+ 12. [Session Context](./reference/typescript-api.md)
34
48
  13. [Sessions And Streaming](./concepts/sessions-runs-and-streaming.md)
35
- 14. [TypeScript SDK](./guides/client/overview.mdx)
49
+ 14. [TypeScript SDK](./clients/typescript-sdk/overview.mdx)
36
50
  15. [Subagents](./subagents.mdx)
37
51
  16. [Schedules](./schedules.mdx)
38
52
  17. [Evals](./evals/overview.mdx)
39
- 18. [Auth And Route Protection](./guides/auth-and-route-protection.md)
40
- 19. [Vercel Deployment](./guides/deployment.md)
53
+ 18. [Auth And Route Protection](./develop/auth-and-route-protection.md)
54
+ 19. [Vercel Deployment](./develop/deployment.md)
41
55
  20. [CLI, Build, And Debugging](./reference/cli.md)
42
56
 
43
57
  ## The public mental model
@@ -90,5 +104,5 @@ That is why Eve exposes two identifiers:
90
104
 
91
105
  ## Good companions in this repo
92
106
 
93
- - Weather-focused smoke/dev fixture: [`../../apps/fixtures/weather-agent`](../../apps/fixtures/weather-agent)
107
+ - Weather-focused smoke/dev fixture: [`../../apps/fixtures/weather-fixture`](../../apps/fixtures/weather-fixture)
94
108
  - Public API source of truth: [`../../packages/eve/src/public/index.ts`](../../packages/eve/src/public/index.ts)
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: "agent.ts"
3
- description: "The agent's runtime config: defineAgent, the model, and compaction."
3
+ description: "Set the agent's runtime config in agent.ts with defineAgent, including the model and compaction."
4
4
  ---
5
5
 
6
6
  An agent's `agent.ts` calls `defineAgent` (from `eve`) to set its runtime config.
7
7
 
8
- ## A good default
8
+ ## Set the model
9
9
 
10
10
  A typical config selects a model:
11
11
 
@@ -20,7 +20,7 @@ export default defineAgent({
20
20
  The root `agent.ts` can be omitted when no runtime config is needed. In that case, Eve defaults
21
21
  to `anthropic/claude-sonnet-4.6`. When `agent.ts` is present, `model` is required.
22
22
 
23
- `model` can be a gateway model id string (which routes through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway)) or a provider-authored `LanguageModel`, when you want to call the provider directly, bypassing the gateway and configuring the model in code:
23
+ `model` accepts a gateway model id string, which routes through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). To call a provider directly and configure the model in code, pass a provider-authored `LanguageModel`:
24
24
 
25
25
  ```ts title="agent/agent.ts"
26
26
  import { anthropic } from "@ai-sdk/anthropic";
@@ -31,9 +31,11 @@ export default defineAgent({
31
31
  });
32
32
  ```
33
33
 
34
+ 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`.
35
+
34
36
  ## Compaction
35
37
 
36
- Compaction summarizes older turns as you approach the context window. It is on by default, so you only touch it to tune when it kicks in. Lower `thresholdPercent` to compact sooner:
38
+ Compaction summarizes older turns as you approach the context window. It's on by default, so you only tune when it kicks in. Lower `thresholdPercent` to compact sooner:
37
39
 
38
40
  ```ts title="agent/agent.ts"
39
41
  export default defineAgent({
@@ -46,25 +48,20 @@ export default defineAgent({
46
48
 
47
49
  See [Default harness](./concepts/default-harness#compaction) for how the loop applies it.
48
50
 
49
- ## Other fields
50
-
51
- `defineAgent` takes a few more fields. For every field and its type, see the [TypeScript API](./reference/typescript-api).
52
-
53
- ### `modelOptions`
54
-
55
- Provider option overrides forwarded to the model call.
56
-
57
- ### `experimental`
58
-
59
- Opt-in flags that can change or disappear in any release, so treat them as unstable. The main one is `codeMode`, which routes executable tools through a sandboxed code-execution wrapper.
51
+ ## Other defineAgent fields
60
52
 
61
- ### `outputSchema`
53
+ `defineAgent` takes a few more fields, all optional. For the exported types, see the [TypeScript API](./reference/typescript-api).
62
54
 
63
- A structured return type for task-mode runs: a subagent, schedule, or remote job.
55
+ | Field | Type | Default | Description |
56
+ | -------------- | --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
57
+ | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
58
+ | `experimental` | `{ codeMode?: boolean }` | flags unset | Opt-in flags that can change or disappear in any release. Treat them as unstable. `codeMode` routes executable tools through a sandboxed code-execution wrapper, where the model writes JavaScript that calls the tools inside the [sandbox](./sandbox). |
59
+ | `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for task-mode runs (a subagent, schedule, or remote job). Interactive conversation turns ignore it unless the client supplies a per-message schema. |
60
+ | `build` | `{ externalDependencies?: string[] }` | none | Hosted-build packaging controls. `externalDependencies` keeps listed packages external while Eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
64
61
 
65
- ### `build`
62
+ `codeMode` is experimental and may change or be removed.
66
63
 
67
- Build packaging controls. `externalDependencies` keeps listed packages external while Eve compiles authored modules such as tools and channels, and traces those packages into the hosted output.
64
+ `externalDependencies` is a packaging control only. It keeps selected packages as runtime dependencies in the hosted output; it does not authorize, configure, or review any third-party service those packages may call.
68
65
 
69
66
  ## Where adjacent settings live
70
67
 
@@ -3,13 +3,13 @@ title: "Custom Channels"
3
3
  description: "Author custom HTTP and WebSocket channels with routes, events, metadata, continuation tokens, and file uploads."
4
4
  ---
5
5
 
6
- When Eve doesn't ship a channel for your surface, you build one. Custom channels let you expose HTTP or WebSocket endpoints, parse incoming requests, start or resume sessions, observe runtime events, and own delivery back to your platform.
6
+ When Eve doesn't ship a channel for your surface, you build one. Custom channels expose HTTP or WebSocket endpoints, parse incoming requests, start or resume sessions, observe runtime events, and own delivery back to your platform.
7
7
 
8
8
  ## File location and identity
9
9
 
10
10
  Custom channels live in `agent/channels/` at the root agent. Local subagents do not declare channels today.
11
11
 
12
- The channel file stem becomes the channel id, so `agent/channels/internal-webhook.ts` is addressed as `internal-webhook`. Channel files are module-backed: export the channel definition as the default export.
12
+ The channel file stem becomes the channel id, so `agent/channels/internal-webhook.ts` is addressed as `internal-webhook`. Export the channel definition as the module's default export.
13
13
 
14
14
  ## Define a channel
15
15
 
@@ -32,7 +32,7 @@ export default defineChannel({
32
32
  const stream = await session.getEventStream();
33
33
 
34
34
  return new Response(stream, {
35
- headers: { "content-type": "text/event-stream" },
35
+ headers: { "content-type": "application/x-ndjson; charset=utf-8" },
36
36
  });
37
37
  }),
38
38
  ],
@@ -44,14 +44,16 @@ export default defineChannel({
44
44
  });
45
45
  ```
46
46
 
47
- `defineChannel({ routes, ... })` defines a channel. Routes are declared with the `POST()` and `GET()` helpers. Each route handler receives the raw `Request` and a helpers object:
47
+ Declare routes with the `POST()` and `GET()` helpers. Each route handler receives the raw `Request` and a helpers object:
48
48
 
49
- - `send(message, { auth, continuationToken, state? })`: start or resume a session. Returns a `Session`.
50
- - `getSession(sessionId)`: look up an existing session. The returned `Session` exposes `getEventStream({ startIndex? })` for streaming.
51
- - `params`: route parameters extracted from the path pattern.
52
- - `waitUntil(promise)`: extend the request lifetime for background work.
49
+ - `send(message, { auth, continuationToken, state? })` starts or resumes a session. Returns a `Session`.
50
+ - `getSession(sessionId)` looks up an existing session. The returned `Session` exposes `getEventStream({ startIndex? })` for streaming.
51
+ - `receive(channel, ...)` hands inbound work to a different channel for cross-channel hand-off.
52
+ - `params` holds route parameters extracted from the path pattern.
53
+ - `waitUntil(promise)` extends the request lifetime for background work.
54
+ - `requestIp` is the client IP, or `null` when the host cannot provide it.
53
55
 
54
- Event handlers like `"message.completed"` are declared under the `events` key. They receive `(eventData, channel, ctx)`, where `channel` carries platform handles and session continuation operations, and `ctx` is the Eve `SessionContext`.
56
+ Event handlers like `"message.completed"` are declared under the `events` key. They receive `(eventData, channel, ctx)`, where `eventData` is the event payload, `channel` carries platform handles and session continuation operations, and `ctx` is the Eve `SessionContext`. Every channel kind shares this signature. The one exception is `session.failed`, which receives only `(eventData, channel)` with no `ctx`.
55
57
 
56
58
  ## WebSocket routes
57
59
 
@@ -92,7 +94,7 @@ export default defineChannel({
92
94
  });
93
95
  ```
94
96
 
95
- The bridge server does not listen on its own port. It receives only upgrade events that matched the Eve route, and only on hosts where Nitro exposes the raw Node upgrade request, socket, and head. Treat it as a compatibility adapter for libraries with server-binding APIs, not as the primary way to build websocket channels in Eve.
97
+ The bridge server does not listen on its own port. It receives only upgrade events that matched the Eve route, and only on hosts where Nitro exposes the raw Node upgrade request, socket, and head. Treat it as a compatibility adapter for libraries with server-binding APIs, not the primary way to build websocket channels in Eve.
96
98
 
97
99
  ## Cross-channel hand-off
98
100
 
@@ -135,7 +137,7 @@ Semantics:
135
137
 
136
138
  ## Channel metadata
137
139
 
138
- Channels can project a subset of their adapter state as metadata available to instrumentation resolvers, dynamic tool resolvers, and dynamic skill or instruction resolvers. Define a `metadata(state)` function on the channel config:
140
+ A channel can project a subset of its adapter state as metadata, available to instrumentation resolvers, dynamic tool resolvers, and dynamic skill or instruction resolvers. Define a `metadata(state)` function on the channel config:
139
141
 
140
142
  ```ts
141
143
  import { defineChannel, POST } from "eve/channels";
@@ -167,8 +169,8 @@ export default defineChannel({
167
169
  }),
168
170
  ],
169
171
  events: {
170
- "turn.started"(_event, ctx) {
171
- ctx.state.internalCounter += 1;
172
+ "turn.started"(eventData, channel) {
173
+ channel.state.internalCounter += 1;
172
174
  },
173
175
  },
174
176
  });
@@ -190,13 +192,17 @@ slackContinuationToken("C0123ABC", "1800000000.001234"); // "C0123ABC:1800000000
190
192
  twilioContinuationToken("+15551234567", "+15557654321"); // "+15551234567:+15557654321"
191
193
  ```
192
194
 
193
- Custom channels write their own function that joins the identity fields. The framework does not derive anything for you: the channel owns its token format.
195
+ Custom channels write their own function that joins the identity fields. The framework derives nothing for you; the channel owns its token format.
194
196
 
195
- When the identity that should address a session is not known until later, the channel can re-key the parked session by calling `ctx.session.setContinuationToken(...)` from a handler. Pass the channel-local raw token; the runtime preserves the current channel namespace.
197
+ When the identity that should address a session is not known until later, the channel can re-key the parked session by calling `session.setContinuationToken(...)`. Pass the channel-local raw token; the runtime preserves the current channel namespace.
198
+
199
+ The `context(state, session)` config option builds the per-step `channel` argument handed to every event handler. It receives the channel's live adapter `state` and a `SessionHandle`, and returns the channel-owned context (thread handles, API clients, late-bound callbacks). The framework injects [`ChannelSessionOps`](#define-a-channel) and passes the result as the second positional argument to each handler. Closing over `session` lets the factory register callbacks that re-key the session later. State mutations made through the returned context are written back to adapter state.
196
200
 
197
201
  ```ts
198
202
  import { defineChannel } from "eve/channels";
199
203
 
204
+ import { mintRef } from "./refs.js";
205
+
200
206
  defineChannel<{ ref: string | null }>({
201
207
  state: { ref: null },
202
208
  context(state, session) {
@@ -209,8 +215,8 @@ defineChannel<{ ref: string | null }>({
209
215
  };
210
216
  },
211
217
  events: {
212
- "message.completed"(_event, ctx) {
213
- if (!ctx.state.ref) ctx.registerAnchor(mintRef());
218
+ "message.completed"(eventData, channel) {
219
+ if (!channel.state.ref) channel.registerAnchor(mintRef());
214
220
  },
215
221
  },
216
222
  routes: [
@@ -264,7 +270,7 @@ defineChannel({
264
270
  });
265
271
  ```
266
272
 
267
- The `URL` object survives the queue boundary as a string and is reconstituted inside the workflow step. The staging pipeline calls `fetchFile(url)`: return bytes to stage the file to the sandbox, or return `null` to let the URL pass through to the model provider.
273
+ The `URL` object survives the queue boundary as a string and is reconstituted inside the workflow step. The staging pipeline calls `fetchFile` with the URL serialized as a string (the URL's `href`), which is why the example matches on `url.startsWith(...)`. Return bytes to stage the file to the sandbox, or `null` to let the URL pass through to the model provider.
268
274
 
269
275
  The framework handles staging bytes to the sandbox, enforcing upload policy, hydrating files for the model call, and reconstituting `URL` objects after queue serialization.
270
276
 
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  title: "Discord"
3
- description: "Reach your agent from Discord HTTP Interactions: slash commands, components, and modals."
3
+ description: "Reach your agent from Discord HTTP Interactions, including slash commands, components, and modals."
4
4
  type: integration
5
5
  ---
6
6
 
7
- The Discord channel wires your agent into Discord's HTTP Interactions: slash and application commands, message components, and modal submissions. The hard part Discord forces on you is the three-second ACK deadline, so the channel verifies the Ed25519 signature headers, acknowledges the command right away, and runs the actual Eve work in the background. See [Channels](./overview) for the contract this builds on.
7
+ The Discord channel wires your agent into Discord's HTTP Interactions, including slash and application commands, message components, and modal submissions. Discord enforces a three-second ACK deadline, so the channel verifies the Ed25519 signature headers, acknowledges the command right away, and runs the Eve work in the background. See [Channels](./overview) for the contract this builds on.
8
8
 
9
9
  ## Add the channel
10
10
 
@@ -20,11 +20,11 @@ DISCORD_APPLICATION_ID=... # edits the deferred response and sends followups
20
20
  DISCORD_BOT_TOKEN=... # proactive messages + fallback + typing indicators
21
21
  ```
22
22
 
23
- If you'd rather not use env vars, pass the same values through `credentials: { applicationId, botToken, publicKey }`. The route is `POST /eve/v1/discord` by default. Paste that public URL into your Discord application's Interactions Endpoint URL.
23
+ To skip env vars, pass the same values through `credentials: { applicationId, botToken, publicKey }`. The route is `POST /eve/v1/discord` by default. Paste that public URL into your Discord application's Interactions Endpoint URL.
24
24
 
25
25
  ## Register a command
26
26
 
27
- Registering commands is on you, not the channel. Use Discord's API or the Developer Portal. If you give the command a string option named `message`, it lines up with Eve's default prompt extraction:
27
+ Registering commands is on you, not the channel. Use Discord's API or the Developer Portal. A string option named `message` lines up with Eve's default prompt extraction:
28
28
 
29
29
  ```bash
30
30
  curl -X PUT "https://discord.com/api/v10/applications/$DISCORD_APPLICATION_ID/commands" \
@@ -35,9 +35,11 @@ curl -X PUT "https://discord.com/api/v10/applications/$DISCORD_APPLICATION_ID/co
35
35
 
36
36
  Use guild commands during development for faster propagation.
37
37
 
38
- ## Affordances
38
+ ## How the channel handles messages
39
39
 
40
- `onCommand(ctx, interaction)` is where you decide whether to dispatch and under what `auth`. Return `{ auth }` to proceed or `null` to drop the interaction. Out of the box, auth comes from the invoking user.
40
+ ### Dispatch
41
+
42
+ `onCommand(ctx, interaction)` decides whether to dispatch and under what `auth`. Return `{ auth }` to proceed or `null` to drop the interaction. By default, auth comes from the invoking user. Event handlers receive `(eventData, channel, ctx)`, with Discord platform handles on `channel.discord`:
41
43
 
42
44
  ```ts
43
45
  import { discordChannel } from "eve/channels/discord";
@@ -52,21 +54,31 @@ export default discordChannel({
52
54
  },
53
55
  }),
54
56
  events: {
55
- "message.completed"(event, ctx) {
56
- if (event.finishReason === "tool-calls") return;
57
- if (event.message) ctx.discord.post(event.message);
57
+ "message.completed"(eventData, channel, ctx) {
58
+ if (eventData.finishReason === "tool-calls") return;
59
+ if (eventData.message) channel.discord.post(eventData.message);
58
60
  },
59
61
  },
60
62
  });
61
63
  ```
62
64
 
63
- **Delivery.** The default `message.completed` handler edits the deferred response for the first reply and sends followups after that. If the interaction token gets rejected, it falls back to a bot-authenticated channel message. Long text is split to Discord's 2000-char limit, and generated messages default to `allowed_mentions: { parse: [] }`.
65
+ ### Delivery
66
+
67
+ The default `message.completed` handler edits the deferred response for the first reply and sends followups after that. If the interaction token is rejected, it falls back to a bot-authenticated channel message. Long text is split to Discord's 2000-char limit, and generated messages default to `allowed_mentions: { parse: [] }`.
68
+
69
+ Typing fires on `turn.started` and `actions.requested`, but only when a bot token is present. In custom hooks, call `channel.discord.startTyping()` yourself.
70
+
71
+ ### Human-in-the-loop (HITL)
72
+
73
+ HITL renders as Discord components. Confirmations and options become buttons, `display: "select"` becomes a string select, and freeform input becomes a button that opens a modal. When the user responds, the parked session (paused awaiting input) resumes.
74
+
75
+ ### Proactive sessions
64
76
 
65
- **Typing** fires on `turn.started` and `actions.requested`, but only when a bot token is present. In custom hooks, call `ctx.discord.startTyping()` yourself.
77
+ Start a session without an inbound interaction through `receive(discord, { message, target, auth })` from a schedule `run` handler, or `args.receive(discord, ...)` from another channel. The proactive target shape is `{ channelId, conversationId?, initialMessage? }`. Either path needs `DISCORD_BOT_TOKEN`.
66
78
 
67
- **HITL** shows up as Discord components. Confirmations and options become buttons, `display: "select"` becomes a string select, and freeform input becomes a button that opens a modal. When the user responds, the parked session resumes on its own.
79
+ ### Attachments
68
80
 
69
- **Proactive sessions** go through `receive(discord, { message, target, auth })` from a schedule `run` handler, or `args.receive(discord, ...)` from another channel. Either way you need `DISCORD_BOT_TOKEN`.
81
+ Inbound file attachments are not supported on this channel today.
70
82
 
71
83
  ## What to read next
72
84
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: "Eve"
3
- description: "The default HTTP API for an agent: session routes, auth, and customization."
3
+ description: "The default HTTP API for an agent, covering session routes, auth, and customization."
4
4
  ---
5
5
 
6
6
  The Eve channel is the framework's default HTTP API. It's what the terminal UI, [`useEveAgent`](../guides/frontend/overview), `curl`, and any SDK client talk to when they start sessions, send messages, and stream events. `eveChannel()` mounts the canonical session routes under `/eve/v1/session*`, and they are enabled by default even when `agent/channels/eve.ts` does not exist.
7
7
 
8
- Reach for it when something needs HTTP access to your agent: local tooling, a browser frontend, the terminal UI, or another API client. Most apps never write this file. Add `agent/channels/eve.ts` only to override the defaults, usually the route auth policy:
8
+ Reach for it when something needs HTTP access to your agent, including local tooling, a browser frontend, the terminal UI, or another API client. Most apps never write this file. Add `agent/channels/eve.ts` only to override the defaults, usually the route auth policy.
9
9
 
10
10
  ```ts title="agent/channels/eve.ts"
11
11
  import { eveChannel } from "eve/channels/eve";
@@ -18,18 +18,36 @@ export default eveChannel({
18
18
 
19
19
  ## Routes
20
20
 
21
- The channel exposes the routes that create sessions, send follow-ups, and stream events:
21
+ The channel exposes routes that create sessions, send follow-ups, and stream events:
22
22
 
23
23
  - `GET /eve/v1/health`
24
24
  - `POST /eve/v1/session` (start a session)
25
25
  - `POST /eve/v1/session/:sessionId` (send a follow-up)
26
26
  - `GET /eve/v1/session/:sessionId/stream` (stream events, NDJSON)
27
27
 
28
- See [Sessions, runs & streaming](../concepts/sessions-runs-and-streaming) for the request and stream flow.
28
+ Start a session with a minimal body. The response returns `sessionId` and the `continuationToken` you reuse for follow-ups:
29
+
30
+ ```bash
31
+ curl -X POST https://<deployment>/eve/v1/session \
32
+ -H "Content-Type: application/json" \
33
+ -d '{"message":"What is the weather in Paris?"}'
34
+ # {"continuationToken":"eve:7f3c...","ok":true,"sessionId":"ses_01h..."}
35
+ ```
36
+
37
+ Stream that session's events as newline-delimited JSON (`application/x-ndjson; charset=utf-8`), one event object per line:
38
+
39
+ ```bash
40
+ curl -N https://<deployment>/eve/v1/session/ses_01h.../stream
41
+ # {"type":"turn.started",...}
42
+ # {"type":"text.delta","delta":"It is "}
43
+ # {"type":"message.completed",...}
44
+ ```
45
+
46
+ See [Sessions, runs & streaming](../concepts/sessions-runs-and-streaming) for the full request and stream flow, including the complete event set.
29
47
 
30
48
  ## Authentication
31
49
 
32
- The `auth` option decides who can call these routes. The built-in helpers are meant for development and trusted infrastructure:
50
+ The `auth` option decides who can call these routes. The built-in helpers cover development and trusted infrastructure:
33
51
 
34
52
  - `localDev()` accepts requests during local development.
35
53
  - `vercelOidc()` lets the local CLI reach a deployed agent, and lets other internal deployments from your team call it.
@@ -58,7 +76,7 @@ export default eveChannel({
58
76
  };
59
77
  },
60
78
  events: {
61
- "message.completed"(event, channel, ctx) {
79
+ "message.completed"(eventData, channel, ctx) {
62
80
  console.log("Eve response completed", {
63
81
  continuationToken: channel.continuationToken,
64
82
  sessionId: ctx.session.id,
@@ -4,7 +4,7 @@ description: "Reach your agent from GitHub App webhooks, with @mention dispatch,
4
4
  type: integration
5
5
  ---
6
6
 
7
- The GitHub channel lets the agent work directly on a repository. Someone `@mentions` it in an issue, PR, or review comment, and the agent answers right there in the thread, with the PR diff already in context and the repo checked out into the sandbox. Under the hood it takes GitHub App webhooks at `/eve/v1/github`, checks the signature, figures out auth from whoever triggered the event, and replies on the native surface. See [Channels](./overview) for the contract this builds on.
7
+ The GitHub channel lets the agent work directly on a repository. Someone `@mentions` it in an issue, PR, or review comment, and the agent answers right there in the thread, with the PR diff already in context and the repo checked out into the sandbox. It takes GitHub App webhooks at `/eve/v1/github`, checks the signature, derives auth from whoever triggered the event, and replies on the native surface. See [Channels](./overview) for the contract this builds on.
8
8
 
9
9
  ## Add the channel
10
10
 
@@ -21,26 +21,31 @@ export default githubChannel({
21
21
  });
22
22
  ```
23
23
 
24
- Every field falls back to an env var (`GITHUB_APP_ID`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_WEBHOOK_SECRET`; `botName` reads `GITHUB_APP_SLUG`), so you can drop the `credentials` block entirely once those are set. `appId`/`privateKey`/`webhookSecret` will also take a lazy resolver function if you'd rather fetch them on demand.
24
+ Every field falls back to an env var, so you can drop the `credentials` block entirely once these are set:
25
25
 
26
- Point the GitHub App webhook URL at `https://<deployment>/eve/v1/github`. For mention-driven turns, subscribe to `issue_comment` and `pull_request_review_comment`; add `issues` / `pull_request` if you wire up their opt-in hooks. A comment that `@mention`s `botName` starts a turn.
26
+ ```bash
27
+ GITHUB_APP_ID=... # GitHub App id
28
+ GITHUB_APP_PRIVATE_KEY=... # GitHub App private key (PEM)
29
+ GITHUB_WEBHOOK_SECRET=... # verifies the webhook signature
30
+ GITHUB_APP_SLUG=... # supplies botName when it is not set in config
31
+ ```
27
32
 
28
- ## Affordances
33
+ `appId`/`privateKey`/`webhookSecret` also take a lazy resolver function if you'd rather fetch them on demand.
29
34
 
30
- **Acknowledgement & replies.** When a turn starts, the channel drops an `eyes` reaction on the triggering comment (turn this off with `progress: { reactions: false }`). The reply comes back as a comment, on the timeline or in the review thread, and splits across multiple comments when it runs long. If the turn fails, you get a short error comment carrying an error id.
35
+ Point the GitHub App webhook URL at `https://<deployment>/eve/v1/github`. For mention-driven turns, subscribe to `issue_comment` and `pull_request_review_comment`; add `issues` / `pull_request` if you wire up their opt-in hooks. A comment that `@mention`s `botName` starts a turn.
31
36
 
32
- **PR context.** Summon the agent on a PR and it always sees the diff. PR metadata and the changed-file patch land in `context`. Large generated files still show up in the list, but their patch body is dropped; add more paths to the skip list with `pullRequestContext.excludedFiles`.
37
+ ## How the channel handles messages
33
38
 
34
- **Sandbox checkout.** Before the first model call, every triggered turn checks out the relevant ref into the sandbox, so `read_file`/`glob`/`grep`/`bash` all run against the real tree. The trick with the installation token is that it never enters the sandbox: `git` fetches a token-free URL, and the platform injects auth on egress at the firewall. That requires a firewall-capable backend (Vercel); the local backend skips checkout. Within a session, checkout is incremental across turns.
39
+ ### Dispatch
35
40
 
36
- **Inbound hooks** return `{ auth }` to dispatch, or `null` to ignore. Reach for `defaultGitHubAuth(ctx)` to derive auth from the actor.
41
+ Inbound hooks return `{ auth }` to dispatch, or `null` to ignore. Use `defaultGitHubAuth(ctx)` to derive auth from the actor.
37
42
 
38
43
  ```ts
39
44
  import { defaultGitHubAuth, githubChannel } from "eve/channels/github";
40
45
 
41
46
  export default githubChannel({
42
47
  botName: "my-agent",
43
- // Replaces the @mention gate. ctx.conversation.kind: "issue" | "pull_request" | "review_thread".
48
+ // Replaces the @mention gate. ctx.conversation.kind is "issue", "pull_request", or "review_thread".
44
49
  onComment: (ctx, comment) => ({ auth: defaultGitHubAuth(ctx) }),
45
50
  // Opt in; no default dispatch on these events.
46
51
  onIssue: (ctx, issue) => (issue.action === "opened" ? { auth: defaultGitHubAuth(ctx) } : null),
@@ -48,7 +53,33 @@ export default githubChannel({
48
53
  });
49
54
  ```
50
55
 
51
- **Arbitrary API calls.** For anything the channel doesn't wrap, call `ctx.github.request({ method, path, body })`; it carries installation-token auth.
56
+ ### Delivery
57
+
58
+ When a turn starts, the channel adds an `eyes` reaction to the triggering comment (turn this off with `progress: { reactions: false }`). The reply comes back as a comment, on the timeline or in the review thread, and splits across multiple comments when it runs long. If the turn fails, you get a short error comment carrying an error id.
59
+
60
+ ### Human-in-the-loop (HITL)
61
+
62
+ GitHub comments have no interactive button or card affordance. A human-in-the-loop (HITL) `input.requested` event is posted as a comment prompt, and the user's reply comment maps back to the pending input request. Declare an `events["input.requested"]` handler to customize the prompt.
63
+
64
+ ### Proactive sessions
65
+
66
+ Start a session without an inbound mention through `receive(github, { message, target, auth })` from a schedule `run` handler, or `args.receive(github, ...)` from another channel. The target requires `owner`, `repo`, and exactly one of `issueNumber` or `pullRequestNumber`.
67
+
68
+ ### Attachments
69
+
70
+ Inbound file attachments are not supported on this channel today. Repository contents reach the agent through the sandbox checkout below, not as message attachments.
71
+
72
+ ### PR context
73
+
74
+ Summon the agent on a PR and it always sees the diff. PR metadata and the changed-file patch land in `context`. Large generated files still appear in the list, but their patch body is dropped; add more paths to the skip list with `pullRequestContext.excludedFiles`.
75
+
76
+ ### Sandbox checkout
77
+
78
+ Before the first model call, every triggered turn checks out the relevant ref into the sandbox, so `read_file`/`glob`/`grep`/`bash` all run against the real tree. The installation token never enters the sandbox. `git` fetches a token-free URL, and the platform injects auth on egress at the firewall. That requires a firewall-capable backend (Vercel); the local backend skips checkout. Within a session, checkout is incremental across turns.
79
+
80
+ ### Arbitrary API calls
81
+
82
+ For anything the channel doesn't wrap, call `ctx.github.request({ method, path, body })`. It carries installation-token auth.
52
83
 
53
84
  ## What to read next
54
85