eve 0.27.13 → 0.28.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 (144) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/src/channel/adapter.d.ts +5 -5
  3. package/dist/src/channel/session.d.ts +2 -2
  4. package/dist/src/channel/types.d.ts +10 -6
  5. package/dist/src/chunks/{use-eve-agent-CsSQOKnp.js → use-eve-agent-CZkX_ZTT.js} +90 -1
  6. package/dist/src/chunks/{use-eve-agent-CWYe-vqE.js → use-eve-agent-hdPX68_i.js} +90 -1
  7. package/dist/src/cli/dev/tui/runner.js +1 -1
  8. package/dist/src/cli/dev/tui/subagent-pump.d.ts +8 -2
  9. package/dist/src/cli/dev/tui/subagent-pump.js +1 -1
  10. package/dist/src/cli/invoke/result.d.ts +15 -0
  11. package/dist/src/cli/invoke/result.js +1 -1
  12. package/dist/src/client/eve-agent-store.d.ts +7 -4
  13. package/dist/src/client/eve-agent-store.js +1 -1
  14. package/dist/src/client/index.d.ts +3 -3
  15. package/dist/src/client/index.js +1 -1
  16. package/dist/src/client/message-action-parts.js +1 -1
  17. package/dist/src/client/message-reducer-types.d.ts +5 -3
  18. package/dist/src/client/message-response.d.ts +4 -4
  19. package/dist/src/client/ndjson.d.ts +3 -3
  20. package/dist/src/client/open-stream.d.ts +2 -2
  21. package/dist/src/client/output-schema.d.ts +2 -2
  22. package/dist/src/client/reducer.d.ts +2 -2
  23. package/dist/src/client/session-utils.d.ts +5 -5
  24. package/dist/src/client/session.d.ts +2 -2
  25. package/dist/src/client/types.d.ts +2 -2
  26. package/dist/src/compiler/compile-from-memory.d.ts +1 -0
  27. package/dist/src/compiler/extension-compatibility.d.ts +8 -8
  28. package/dist/src/compiler/extension-compatibility.js +1 -1
  29. package/dist/src/compiler/manifest.d.ts +2 -2
  30. package/dist/src/compiler/manifest.js +1 -1
  31. package/dist/src/compiler/normalize-agent-config.js +1 -1
  32. package/dist/src/context/dynamic-instruction-lifecycle.d.ts +2 -2
  33. package/dist/src/context/dynamic-model-lifecycle.d.ts +2 -2
  34. package/dist/src/context/dynamic-skill-lifecycle.d.ts +2 -2
  35. package/dist/src/context/dynamic-tool-lifecycle.d.ts +2 -2
  36. package/dist/src/context/hook-lifecycle.d.ts +2 -2
  37. package/dist/src/evals/assertions/run.d.ts +3 -3
  38. package/dist/src/evals/match.d.ts +4 -4
  39. package/dist/src/evals/runner/derive-run-facts.d.ts +2 -2
  40. package/dist/src/evals/session.d.ts +3 -3
  41. package/dist/src/evals/types.d.ts +12 -12
  42. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  43. package/dist/src/execution/route-child-delivery.d.ts +4 -3
  44. package/dist/src/execution/route-child-delivery.js +1 -1
  45. package/dist/src/execution/session-delivery-hook.d.ts +1 -0
  46. package/dist/src/execution/session-delivery-hook.js +1 -1
  47. package/dist/src/execution/session-timeout-control.d.ts +9 -0
  48. package/dist/src/execution/session-timeout-control.js +1 -0
  49. package/dist/src/execution/session-timeout-steps.d.ts +13 -0
  50. package/dist/src/execution/session-timeout-steps.js +1 -0
  51. package/dist/src/execution/session-timeout-workflow.d.ts +6 -0
  52. package/dist/src/execution/session-timeout-workflow.js +1 -0
  53. package/dist/src/execution/session-timeout.d.ts +2 -0
  54. package/dist/src/execution/session-timeout.js +1 -0
  55. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  56. package/dist/src/execution/subagent-event-proxy-step.js +1 -1
  57. package/dist/src/execution/subagent-hitl-proxy.d.ts +5 -1
  58. package/dist/src/execution/subagent-hitl-proxy.js +1 -1
  59. package/dist/src/execution/terminal-session-completion-step.d.ts +5 -0
  60. package/dist/src/execution/terminal-session-completion-step.js +1 -0
  61. package/dist/src/execution/terminal-session-failure-step.js +1 -1
  62. package/dist/src/execution/turn-workflow.js +1 -1
  63. package/dist/src/execution/workflow-entry.d.ts +1 -0
  64. package/dist/src/execution/workflow-entry.js +1 -1
  65. package/dist/src/execution/workflow-runtime.d.ts +4 -0
  66. package/dist/src/execution/workflow-runtime.js +1 -1
  67. package/dist/src/execution/workflow-steps.d.ts +5 -2
  68. package/dist/src/execution/workflow-steps.js +1 -1
  69. package/dist/src/harness/agent-otel-provider.js +1 -1
  70. package/dist/src/harness/ai-sdk-hook-bridge.js +1 -1
  71. package/dist/src/harness/input-extraction.js +1 -1
  72. package/dist/src/harness/input-request-class.d.ts +23 -0
  73. package/dist/src/harness/input-request-class.js +1 -0
  74. package/dist/src/harness/input-requests.d.ts +20 -9
  75. package/dist/src/harness/input-requests.js +1 -1
  76. package/dist/src/harness/instrumentation-lifecycle.d.ts +12 -1
  77. package/dist/src/harness/proxy-input-requests.d.ts +10 -4
  78. package/dist/src/harness/proxy-input-requests.js +1 -1
  79. package/dist/src/harness/session-limit-continuation.d.ts +0 -11
  80. package/dist/src/harness/session-limit-continuation.js +1 -1
  81. package/dist/src/harness/session-limit-enforcement.d.ts +3 -2
  82. package/dist/src/harness/session-limit-enforcement.js +1 -1
  83. package/dist/src/harness/session-token-limits.d.ts +41 -0
  84. package/dist/src/harness/session-token-limits.js +1 -0
  85. package/dist/src/harness/stale-input-responses.js +1 -1
  86. package/dist/src/harness/subagent-token-budget.d.ts +6 -4
  87. package/dist/src/harness/subagent-token-budget.js +1 -1
  88. package/dist/src/harness/tool-loop.js +1 -1
  89. package/dist/src/harness/turn-tag-state.d.ts +26 -17
  90. package/dist/src/harness/turn-tag-state.js +1 -1
  91. package/dist/src/harness/types.d.ts +4 -4
  92. package/dist/src/harness/workflow-lifecycle.d.ts +2 -2
  93. package/dist/src/internal/application/package.js +1 -1
  94. package/dist/src/internal/authored-definition/core.js +1 -1
  95. package/dist/src/internal/workflow-bundle/workflow-core-shim.d.ts +1 -1
  96. package/dist/src/internal/workflow-bundle/workflow-core-shim.js +1 -1
  97. package/dist/src/protocol/event-dedupe.d.ts +25 -0
  98. package/dist/src/protocol/event-dedupe.js +1 -0
  99. package/dist/src/protocol/event-id.d.ts +16 -0
  100. package/dist/src/protocol/event-id.js +1 -0
  101. package/dist/src/protocol/message.d.ts +39 -23
  102. package/dist/src/protocol/message.js +2 -2
  103. package/dist/src/public/channels/discord/discordChannel.d.ts +3 -3
  104. package/dist/src/public/channels/github/githubChannel.d.ts +3 -3
  105. package/dist/src/public/channels/linear/linearChannel.d.ts +3 -3
  106. package/dist/src/public/channels/slack/hitl.js +1 -1
  107. package/dist/src/public/channels/slack/slackChannel.d.ts +3 -3
  108. package/dist/src/public/channels/teams/hitl.js +1 -1
  109. package/dist/src/public/channels/teams/teamsChannel.d.ts +3 -3
  110. package/dist/src/public/channels/telegram/telegramChannel.d.ts +3 -3
  111. package/dist/src/public/channels/twilio/twilioChannel.d.ts +3 -3
  112. package/dist/src/public/definitions/channel.d.ts +4 -4
  113. package/dist/src/public/definitions/hook.d.ts +2 -2
  114. package/dist/src/react/use-eve-agent.d.ts +3 -2
  115. package/dist/src/runtime/framework-tools/ask-question.js +1 -1
  116. package/dist/src/runtime/hooks/registry.d.ts +2 -2
  117. package/dist/src/runtime/input/types.d.ts +19 -4
  118. package/dist/src/runtime/input/types.js +1 -1
  119. package/dist/src/runtime/resolve-agent.js +1 -1
  120. package/dist/src/runtime/types.d.ts +2 -2
  121. package/dist/src/setup/scaffold/create/project.js +1 -1
  122. package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
  123. package/dist/src/setup/scaffold/create/web-template.js +2 -2
  124. package/dist/src/shared/agent-definition.d.ts +12 -0
  125. package/dist/src/shared/dynamic-tool-definition.d.ts +2 -2
  126. package/dist/src/shared/ulid.d.ts +39 -0
  127. package/dist/src/shared/ulid.js +1 -0
  128. package/dist/src/svelte/index.js +1 -1
  129. package/dist/src/svelte/use-eve-agent.d.ts +4 -4
  130. package/dist/src/svelte/use-eve-agent.js +1 -1
  131. package/dist/src/vue/index.js +1 -1
  132. package/dist/src/vue/use-eve-agent.d.ts +4 -4
  133. package/dist/src/vue/use-eve-agent.js +1 -1
  134. package/docs/agent-config.md +28 -16
  135. package/docs/concepts/execution-model-and-durability.mdx +1 -1
  136. package/docs/concepts/sessions-runs-and-streaming.md +61 -0
  137. package/docs/guides/client/streaming.mdx +9 -3
  138. package/docs/guides/frontend/overview.mdx +4 -2
  139. package/docs/guides/frontend/use-eve-agent-svelte.mdx +1 -1
  140. package/docs/guides/frontend/use-eve-agent-vue.mdx +10 -10
  141. package/docs/guides/hooks.md +40 -2
  142. package/docs/guides/instrumentation.md +4 -0
  143. package/docs/tools/human-in-the-loop.md +5 -0
  144. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
1
  import type { SessionAuthContext } from "#channel/types.js";
2
2
  import type { SessionContext } from "#public/definitions/callback-context.js";
3
3
  import type { ChannelSessionOps } from "#public/definitions/channel.js";
4
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
4
+ import type { UnstampedMessageStreamEvent } from "#protocol/message.js";
5
5
  import { type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials } from "#public/channels/twilio/api.js";
6
6
  import { type TwilioTextMessage, type TwilioVoiceCall, type TwilioVoiceTranscription } from "#public/channels/twilio/inbound.js";
7
7
  import { type TwilioAuthToken, type TwilioWebhookUrl } from "#public/channels/twilio/verify.js";
8
8
  import { type Channel } from "#public/definitions/channel.js";
9
- type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
9
+ type EventData<T extends UnstampedMessageStreamEvent["type"]> = Extract<UnstampedMessageStreamEvent, {
10
10
  type: T;
11
11
  }> extends {
12
12
  data: infer D;
@@ -82,7 +82,7 @@ export interface TwilioVoiceResult {
82
82
  }
83
83
  /** Sync or async {@link TwilioVoiceResult}. */
84
84
  export type TwilioVoiceResultOrPromise = TwilioVoiceResult | null | undefined | Promise<TwilioVoiceResult | null | undefined>;
85
- type TwilioEventHandler<T extends HandleMessageStreamEvent["type"]> = (data: EventData<T>, channel: TwilioEventContext, ctx: SessionContext) => void | Promise<void>;
85
+ type TwilioEventHandler<T extends UnstampedMessageStreamEvent["type"]> = (data: EventData<T>, channel: TwilioEventContext, ctx: SessionContext) => void | Promise<void>;
86
86
  type TwilioSessionFailedHandler = (data: EventData<"session.failed">, channel: TwilioEventContext) => void | Promise<void>;
87
87
  /** Event handlers supported by `twilioChannel({ events })`. */
88
88
  export interface TwilioChannelEvents {
@@ -4,7 +4,7 @@ import type { TypedReceiveTarget } from "#channel/receive-target.js";
4
4
  import type { RouteDefinition, SendFn } from "#channel/routes.js";
5
5
  import type { Session } from "#channel/session.js";
6
6
  import type { CancelTurnInput, CancelTurnResult, DeliverInput, GetEventStreamOptions, RunHandle, RunInput } from "#channel/types.js";
7
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
7
+ import type { UnstampedMessageStreamEvent, MessageStreamEvent } from "#protocol/message.js";
8
8
  import type { SessionContext } from "#public/definitions/callback-context.js";
9
9
  import type { GenericChannelDefinition, GenericReceiveInput } from "#shared/channel-definition.js";
10
10
  declare const CHANNEL_METADATA_TYPE: unique symbol;
@@ -120,7 +120,7 @@ export interface Agent {
120
120
  * at the latest event). The framework HTTP session-stream route forwards
121
121
  * the `startIndex` query parameter unchanged.
122
122
  */
123
- getEventStream(sessionId: string, options?: GetEventStreamOptions): Promise<ReadableStream<HandleMessageStreamEvent>>;
123
+ getEventStream(sessionId: string, options?: GetEventStreamOptions): Promise<ReadableStream<MessageStreamEvent>>;
124
124
  }
125
125
  /**
126
126
  * Marker discriminator written into every {@link DisabledRouteSentinel}.
@@ -146,7 +146,7 @@ export declare function disableRoute(): DisabledRouteSentinel;
146
146
  * produced by {@link disableRoute}.
147
147
  */
148
148
  export declare function isDisabledRouteSentinel(value: unknown): value is DisabledRouteSentinel;
149
- type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
149
+ type EventData<T extends UnstampedMessageStreamEvent["type"]> = Extract<UnstampedMessageStreamEvent, {
150
150
  type: T;
151
151
  }> extends {
152
152
  data: infer D;
@@ -163,7 +163,7 @@ export interface ChannelSessionOps {
163
163
  * {@link ChannelSessionOps}.
164
164
  */
165
165
  export type ChannelContext<TCtx> = TCtx & ChannelSessionOps;
166
- type ChannelEventHandler<T extends HandleMessageStreamEvent["type"], TCtx> = (data: EventData<T>, channel: ChannelContext<TCtx>, ctx: SessionContext) => void | Promise<void>;
166
+ type ChannelEventHandler<T extends UnstampedMessageStreamEvent["type"], TCtx> = (data: EventData<T>, channel: ChannelContext<TCtx>, ctx: SessionContext) => void | Promise<void>;
167
167
  type ChannelSessionFailedHandler<TCtx> = (data: EventData<"session.failed">, channel: ChannelContext<TCtx>) => void | Promise<void>;
168
168
  /**
169
169
  * Optional handlers keyed by session lifecycle event name. Each handler receives
@@ -1,7 +1,7 @@
1
- import type { HandleMessageStreamEvent } from "../../protocol/message.js";
1
+ import type { MessageStreamEvent } from "../../protocol/message.js";
2
2
  import type { SessionContext } from "./callback-context.js";
3
3
  import type { ExactDefinition } from "./exact.js";
4
- type ProtocolEvent<TType extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
4
+ type ProtocolEvent<TType extends MessageStreamEvent["type"]> = Extract<MessageStreamEvent, {
5
5
  type: TType;
6
6
  }>;
7
7
  /**
@@ -2,7 +2,7 @@ import { type EveAgentStoreCallbacks, type EveAgentStoreSnapshot, type EveAgentS
2
2
  import type { EveAgentReducer } from "#client/reducer.js";
3
3
  import type { ClientSession } from "#client/session.js";
4
4
  import { type EveMessageData } from "#client/message-reducer.js";
5
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
5
+ import type { MessageStreamEvent } from "#protocol/message.js";
6
6
  import type { ClientAuth, HeadersValue, SendTurnPayload, SessionState } from "#client/types.js";
7
7
  export type { PrepareSend };
8
8
  /**
@@ -61,7 +61,8 @@ export interface UseEveAgentOptions<TData> extends EveAgentStoreCallbacks<TData>
61
61
  * @default ""
62
62
  */
63
63
  readonly host?: string;
64
- readonly initialEvents?: readonly HandleMessageStreamEvent[];
64
+ /** Ordered prefix of the session stream used to rehydrate projected state. */
65
+ readonly initialEvents?: readonly MessageStreamEvent[];
65
66
  readonly initialSession?: SessionState;
66
67
  /**
67
68
  * Project submitted user messages before eve confirms them with a
@@ -1 +1 @@
1
- import{z}from"#compiled/zod/index.js";import{inputRequestSchema}from"#runtime/input/types.js";const ASK_QUESTION_TOOL_NAME=`ask_question`,ASK_QUESTION_INPUT_SCHEMA=inputRequestSchema.omit({action:!0,display:!0,requestId:!0}),ASK_QUESTION_OUTPUT_SCHEMA=z.object({optionId:z.string().optional(),status:z.enum([`answered`,`ignored`]),text:z.string().optional()}).strict(),ASK_QUESTION_TOOL_DEFINITION={description:`Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.`,inputSchema:ASK_QUESTION_INPUT_SCHEMA,logicalPath:`eve:framework/ask-question`,name:ASK_QUESTION_TOOL_NAME,outputSchema:ASK_QUESTION_OUTPUT_SCHEMA,sourceId:`eve:ask-question-tool`,sourceKind:`module`};export{ASK_QUESTION_INPUT_SCHEMA,ASK_QUESTION_OUTPUT_SCHEMA,ASK_QUESTION_TOOL_DEFINITION,ASK_QUESTION_TOOL_NAME};
1
+ import{z}from"#compiled/zod/index.js";import{inputRequestSchema}from"#runtime/input/types.js";const ASK_QUESTION_TOOL_NAME=`ask_question`,ASK_QUESTION_INPUT_SCHEMA=inputRequestSchema.omit({action:!0,display:!0,kind:!0,requestId:!0}),ASK_QUESTION_OUTPUT_SCHEMA=z.object({optionId:z.string().optional(),status:z.enum([`answered`,`ignored`]),text:z.string().optional()}).strict(),ASK_QUESTION_TOOL_DEFINITION={description:`Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.`,inputSchema:ASK_QUESTION_INPUT_SCHEMA,logicalPath:`eve:framework/ask-question`,name:ASK_QUESTION_TOOL_NAME,outputSchema:ASK_QUESTION_OUTPUT_SCHEMA,sourceId:`eve:ask-question-tool`,sourceKind:`module`};export{ASK_QUESTION_INPUT_SCHEMA,ASK_QUESTION_OUTPUT_SCHEMA,ASK_QUESTION_TOOL_DEFINITION,ASK_QUESTION_TOOL_NAME};
@@ -1,4 +1,4 @@
1
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
1
+ import type { MessageStreamEvent } from "#protocol/message.js";
2
2
  import type { StreamEventHook } from "../../public/definitions/hook.js";
3
3
  import type { ResolvedHookDefinition } from "../types.js";
4
4
  /**
@@ -9,7 +9,7 @@ import type { ResolvedHookDefinition } from "../types.js";
9
9
  */
10
10
  interface RuntimeStreamEventHookEntry {
11
11
  readonly slug: string;
12
- readonly handler: StreamEventHook<HandleMessageStreamEvent>;
12
+ readonly handler: StreamEventHook<MessageStreamEvent>;
13
13
  readonly eventType: string;
14
14
  }
15
15
  /**
@@ -20,11 +20,21 @@ export declare const inputOptionSchema: z.ZodObject<{
20
20
  }>>;
21
21
  }, z.core.$strict>;
22
22
  /**
23
- * Unified input request surfaced to the client when the agent needs
24
- * user input before continuing.
23
+ * Framework-owned source of an input request.
25
24
  *
26
- * Tool approvals and questions share this shape. Approvals are requests
27
- * with two options (`"approve"` / `"deny"`) and `display: "confirmation"`.
25
+ * Consumers use this discriminator for behavior. The action tool name and
26
+ * request id are presentation and identity fields, not request semantics.
27
+ */
28
+ export type InputRequestKind = z.infer<typeof inputRequestKindSchema>;
29
+ /** Zod schema for the framework-owned source of an input request. */
30
+ export declare const inputRequestKindSchema: z.ZodEnum<{
31
+ question: "question";
32
+ "session-limit": "session-limit";
33
+ "tool-approval": "tool-approval";
34
+ }>;
35
+ /**
36
+ * Unified input request surfaced to the client when the agent needs user
37
+ * input before continuing.
28
38
  */
29
39
  export type InputRequest = z.infer<typeof inputRequestSchema>;
30
40
  /**
@@ -43,6 +53,11 @@ export declare const inputRequestSchema: z.ZodObject<{
43
53
  select: "select";
44
54
  text: "text";
45
55
  }>>;
56
+ kind: z.ZodEnum<{
57
+ question: "question";
58
+ "session-limit": "session-limit";
59
+ "tool-approval": "tool-approval";
60
+ }>;
46
61
  options: z.ZodOptional<z.ZodArray<z.ZodObject<{
47
62
  description: z.ZodOptional<z.ZodString>;
48
63
  id: z.ZodString;
@@ -1 +1 @@
1
- import{z}from"#compiled/zod/index.js";import{runtimeToolCallActionRequestSchema}from"#runtime/actions/types.js";const inputOptionSchema=z.object({description:z.string().describe(`Optional additional context for this option.`).optional(),id:z.string().describe(`Stable identifier for the option.`),label:z.string().describe(`User-facing label for the option.`),style:z.enum([`primary`,`danger`,`default`]).describe(`Visual treatment hint for the option.`).optional()}).strict(),inputRequestSchema=z.object({action:runtimeToolCallActionRequestSchema,allowFreeform:z.boolean().describe(`Whether the user may answer with freeform text instead of selecting one of the provided options.`).optional(),display:z.enum([`confirmation`,`select`,`text`]).describe(`Rendering hint: the channel uses this to pick a UX treatment.`).optional(),options:z.array(inputOptionSchema).describe(`Selectable answer options to present to the user.`).optional(),prompt:z.string().describe(`The prompt to present to the user.`),requestId:z.string().describe(`Stable identifier for this request.`)}).strict(),inputResponseSchema=z.object({optionId:z.string().optional(),requestId:z.string(),text:z.string().optional()}).strict();function isInputRequest(e){return inputRequestSchema.safeParse(e).success}function isInputResponse(e){return inputResponseSchema.safeParse(e).success}export{inputOptionSchema,inputRequestSchema,inputResponseSchema,isInputRequest,isInputResponse};
1
+ import{z}from"#compiled/zod/index.js";import{runtimeToolCallActionRequestSchema}from"#runtime/actions/types.js";const inputOptionSchema=z.object({description:z.string().describe(`Optional additional context for this option.`).optional(),id:z.string().describe(`Stable identifier for the option.`),label:z.string().describe(`User-facing label for the option.`),style:z.enum([`primary`,`danger`,`default`]).describe(`Visual treatment hint for the option.`).optional()}).strict(),inputRequestKindSchema=z.enum([`question`,`session-limit`,`tool-approval`]),inputRequestSchema=z.object({action:runtimeToolCallActionRequestSchema,allowFreeform:z.boolean().describe(`Whether the user may answer with freeform text instead of selecting one of the provided options.`).optional(),display:z.enum([`confirmation`,`select`,`text`]).describe(`Rendering hint: the channel uses this to pick a UX treatment.`).optional(),kind:inputRequestKindSchema.describe(`Framework-owned request source used to resolve, route, and render the response.`),options:z.array(inputOptionSchema).describe(`Selectable answer options to present to the user.`).optional(),prompt:z.string().describe(`The prompt to present to the user.`),requestId:z.string().describe(`Stable identifier for this request.`)}).strict(),inputResponseSchema=z.object({optionId:z.string().optional(),requestId:z.string(),text:z.string().optional()}).strict();function isInputRequest(e){return inputRequestSchema.safeParse(e).success}function isInputResponse(e){return inputResponseSchema.safeParse(e).success}export{inputOptionSchema,inputRequestKindSchema,inputRequestSchema,inputResponseSchema,isInputRequest,isInputResponse};
@@ -1 +1 @@
1
- import{ResolveAgentError,createResolvedModuleSourceRef}from"#runtime/resolve-helpers.js";import{resolveChannelDefinition}from"#runtime/resolve-channel.js";import{resolveConnectionDefinition}from"#runtime/resolve-connection.js";import{resolveHookDefinition}from"#runtime/resolve-hook.js";import{resolveSandboxDefinition}from"#runtime/resolve-sandbox.js";import{resolveDynamicInstructionsDefinition}from"#runtime/resolve-dynamic-instructions.js";import{resolveDynamicSkillDefinition}from"#runtime/resolve-dynamic-skill.js";import{resolveDynamicToolDefinition}from"#runtime/resolve-dynamic-tool.js";import{resolveToolDefinition}from"#runtime/resolve-tool.js";async function resolveAgent(e){let t=e.manifest.skills.map(e=>({...e,metadata:e.metadata===void 0?void 0:{...e.metadata}})),r=[],i=[];for(let t of e.manifest.channels){if(t.kind===`disabled`){i.push(t.name);continue}r.push(await resolveChannelDefinition(t,e.moduleMap,e.nodeId))}let a=await Promise.all(e.manifest.tools.map(t=>resolveToolDefinition(t,e.moduleMap,e.nodeId))),o=await Promise.all((e.manifest.dynamicInstructions??[]).map(t=>resolveDynamicInstructionsDefinition(t,e.moduleMap,e.nodeId))),s=await Promise.all((e.manifest.dynamicSkills??[]).map(t=>resolveDynamicSkillDefinition(t,e.moduleMap,e.nodeId))),c=await Promise.all(e.manifest.dynamicTools.map(t=>resolveDynamicToolDefinition(t,e.moduleMap,e.nodeId))),l=await Promise.all(e.manifest.hooks.map(t=>resolveHookDefinition(t,e.moduleMap,e.nodeId))),u=await Promise.all(e.manifest.connections.map(t=>resolveConnectionDefinition(t,e.moduleMap,e.nodeId))),d=e.manifest.sandbox===null?null:await resolveSandboxDefinition(e.manifest.sandbox,e.moduleMap,e.nodeId),f=createResolvedInstructionsDefinition(e.manifest.instructions),p=e.manifest.workspaceResourceRoot,m={channels:r,config:createResolvedAgentConfig(e.manifest),connections:u,disabledFrameworkChannels:i,disabledFrameworkTools:[...e.manifest.disabledFrameworkTools],workflowTool:e.manifest.workflowTool===void 0?void 0:{maxSubagents:e.manifest.workflowTool.maxSubagents},dynamicInstructionsResolvers:o,dynamicSkillResolvers:s,dynamicToolResolvers:c,hooks:l,metadata:{agentRoot:e.manifest.agentRoot,appRoot:e.manifest.appRoot,diagnosticsSummary:e.manifest.diagnosticsSummary},sandbox:d,workspaceResourceRoot:p,skills:t,tools:a,workspaceSpec:{rootEntries:[...p.rootEntries]}};return f===void 0?m:{...m,instructions:f}}function createResolvedInstructionsDefinition(e){if(e!==void 0)return{name:e.name,logicalPath:e.logicalPath,markdown:e.markdown,sourceId:e.sourceId,sourceKind:e.sourceKind}}function createResolvedAgentConfig(e){let n={model:e.config.model.source===void 0?{id:e.config.model.id,contextWindowTokens:e.config.model.contextWindowTokens,providerOptions:e.config.model.providerOptions}:{contextWindowTokens:e.config.model.contextWindowTokens,id:e.config.model.id,providerOptions:e.config.model.providerOptions,source:{exportName:e.config.model.source.exportName,sourceKind:`module`,logicalPath:e.config.model.source.logicalPath,sourceId:e.config.model.source.sourceId}},name:e.config.name};if(e.config.compaction!==void 0){let t={};e.config.compaction.model!==void 0&&(t.model=e.config.compaction.model.source===void 0?{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,providerOptions:e.config.compaction.model.providerOptions}:{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,providerOptions:e.config.compaction.model.providerOptions,source:{exportName:e.config.compaction.model.source.exportName,sourceKind:`module`,logicalPath:e.config.compaction.model.source.logicalPath,sourceId:e.config.compaction.model.source.sourceId}}),e.config.compaction.thresholdPercent!==void 0&&(t.thresholdPercent=e.config.compaction.thresholdPercent),n.compaction=t}return e.config.dynamicModel!==void 0&&(n.dynamicModel={...createResolvedModuleSourceRef(e.config.dynamicModel),eventNames:[...e.config.dynamicModel.eventNames]}),e.config.experimental!==void 0&&(n.experimental={workflow:e.config.experimental.workflow===void 0?void 0:{world:e.config.experimental.workflow.world}}),e.config.outputSchema!==void 0&&(n.outputSchema=e.config.outputSchema),e.config.reasoning!==void 0&&(n.reasoning=e.config.reasoning),e.config.source!==void 0&&(n.source=createResolvedModuleSourceRef(e.config.source)),e.config.limits!==void 0&&(n.limits={maxInputTokensPerSession:e.config.limits.maxInputTokensPerSession,maxOutputTokensPerSession:e.config.limits.maxOutputTokensPerSession}),n}export{ResolveAgentError,resolveAgent};
1
+ import{ResolveAgentError,createResolvedModuleSourceRef}from"#runtime/resolve-helpers.js";import{resolveChannelDefinition}from"#runtime/resolve-channel.js";import{resolveConnectionDefinition}from"#runtime/resolve-connection.js";import{resolveHookDefinition}from"#runtime/resolve-hook.js";import{resolveSandboxDefinition}from"#runtime/resolve-sandbox.js";import{resolveDynamicInstructionsDefinition}from"#runtime/resolve-dynamic-instructions.js";import{resolveDynamicSkillDefinition}from"#runtime/resolve-dynamic-skill.js";import{resolveDynamicToolDefinition}from"#runtime/resolve-dynamic-tool.js";import{resolveToolDefinition}from"#runtime/resolve-tool.js";async function resolveAgent(e){let t=e.manifest.skills.map(e=>({...e,metadata:e.metadata===void 0?void 0:{...e.metadata}})),r=[],i=[];for(let t of e.manifest.channels){if(t.kind===`disabled`){i.push(t.name);continue}r.push(await resolveChannelDefinition(t,e.moduleMap,e.nodeId))}let a=await Promise.all(e.manifest.tools.map(t=>resolveToolDefinition(t,e.moduleMap,e.nodeId))),o=await Promise.all((e.manifest.dynamicInstructions??[]).map(t=>resolveDynamicInstructionsDefinition(t,e.moduleMap,e.nodeId))),s=await Promise.all((e.manifest.dynamicSkills??[]).map(t=>resolveDynamicSkillDefinition(t,e.moduleMap,e.nodeId))),c=await Promise.all(e.manifest.dynamicTools.map(t=>resolveDynamicToolDefinition(t,e.moduleMap,e.nodeId))),l=await Promise.all(e.manifest.hooks.map(t=>resolveHookDefinition(t,e.moduleMap,e.nodeId))),u=await Promise.all(e.manifest.connections.map(t=>resolveConnectionDefinition(t,e.moduleMap,e.nodeId))),d=e.manifest.sandbox===null?null:await resolveSandboxDefinition(e.manifest.sandbox,e.moduleMap,e.nodeId),f=createResolvedInstructionsDefinition(e.manifest.instructions),p=e.manifest.workspaceResourceRoot,m={channels:r,config:createResolvedAgentConfig(e.manifest),connections:u,disabledFrameworkChannels:i,disabledFrameworkTools:[...e.manifest.disabledFrameworkTools],workflowTool:e.manifest.workflowTool===void 0?void 0:{maxSubagents:e.manifest.workflowTool.maxSubagents},dynamicInstructionsResolvers:o,dynamicSkillResolvers:s,dynamicToolResolvers:c,hooks:l,metadata:{agentRoot:e.manifest.agentRoot,appRoot:e.manifest.appRoot,diagnosticsSummary:e.manifest.diagnosticsSummary},sandbox:d,workspaceResourceRoot:p,skills:t,tools:a,workspaceSpec:{rootEntries:[...p.rootEntries]}};return f===void 0?m:{...m,instructions:f}}function createResolvedInstructionsDefinition(e){if(e!==void 0)return{name:e.name,logicalPath:e.logicalPath,markdown:e.markdown,sourceId:e.sourceId,sourceKind:e.sourceKind}}function createResolvedAgentConfig(e){let n={model:e.config.model.source===void 0?{id:e.config.model.id,contextWindowTokens:e.config.model.contextWindowTokens,providerOptions:e.config.model.providerOptions}:{contextWindowTokens:e.config.model.contextWindowTokens,id:e.config.model.id,providerOptions:e.config.model.providerOptions,source:{exportName:e.config.model.source.exportName,sourceKind:`module`,logicalPath:e.config.model.source.logicalPath,sourceId:e.config.model.source.sourceId}},name:e.config.name};if(e.config.compaction!==void 0){let t={};e.config.compaction.model!==void 0&&(t.model=e.config.compaction.model.source===void 0?{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,providerOptions:e.config.compaction.model.providerOptions}:{contextWindowTokens:e.config.compaction.model.contextWindowTokens,id:e.config.compaction.model.id,providerOptions:e.config.compaction.model.providerOptions,source:{exportName:e.config.compaction.model.source.exportName,sourceKind:`module`,logicalPath:e.config.compaction.model.source.logicalPath,sourceId:e.config.compaction.model.source.sourceId}}),e.config.compaction.thresholdPercent!==void 0&&(t.thresholdPercent=e.config.compaction.thresholdPercent),n.compaction=t}return e.config.dynamicModel!==void 0&&(n.dynamicModel={...createResolvedModuleSourceRef(e.config.dynamicModel),eventNames:[...e.config.dynamicModel.eventNames]}),e.config.experimental!==void 0&&(n.experimental={workflow:e.config.experimental.workflow===void 0?void 0:{world:e.config.experimental.workflow.world}}),e.config.outputSchema!==void 0&&(n.outputSchema=e.config.outputSchema),e.config.reasoning!==void 0&&(n.reasoning=e.config.reasoning),e.config.source!==void 0&&(n.source=createResolvedModuleSourceRef(e.config.source)),e.config.limits!==void 0&&(n.limits={maxInputTokensPerSession:e.config.limits.maxInputTokensPerSession,maxOutputTokensPerSession:e.config.limits.maxOutputTokensPerSession,sessionTimeoutMs:e.config.limits.sessionTimeoutMs}),n}export{ResolveAgentError,resolveAgent};
@@ -3,7 +3,7 @@ import type { CompiledChannel } from "#channel/compiled-channel.js";
3
3
  import type { NormalizedChannelCorsOptions } from "#channel/cors.js";
4
4
  import type { HeadersValue } from "#client/types.js";
5
5
  import type { DiscoverDiagnosticsSummary } from "#discover/diagnostics.js";
6
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
6
+ import type { MessageStreamEvent } from "#protocol/message.js";
7
7
  import type { ChannelRouteMethod, RouteContext } from "#public/definitions/channel.js";
8
8
  import type { RouteHandler, WebSocketRouteHandler } from "#channel/routes.js";
9
9
  import type { OutboundAuthFn } from "#public/agents/auth.js";
@@ -178,7 +178,7 @@ export interface ResolvedHookDefinition extends ResolvedModuleSourceRef {
178
178
  * wildcard if declared. Unknown keys are accepted at resolve time
179
179
  * and ignored at dispatch time.
180
180
  */
181
- readonly events: Readonly<Record<string, StreamEventHook<HandleMessageStreamEvent>>>;
181
+ readonly events: Readonly<Record<string, StreamEventHook<MessageStreamEvent>>>;
182
182
  }
183
183
  /**
184
184
  * Runtime-owned authored channel definition resolved from the compiled
@@ -1,4 +1,4 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.38`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.27.13`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.38`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.28.0`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
2
2
 
3
3
  export default defineAgent({
4
4
  model: "__EVE_INIT_MODEL__",
@@ -1,6 +1,6 @@
1
1
  export declare const WEB_APP_TEMPLATE_FILES: {
2
2
  readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n auth: [\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\n // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // This placeholder will not allow browser requests in production.\n // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n // or use none() for a public demo.\n placeholderAuth(),\n ],\n});\n';
3
- readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport type { UserContent } from "ai";\nimport { Client, type HandleMessageStreamEvent } from "eve/client";\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-react";\nimport { useCallback, useRef, useState } from "react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n} from "@/components/ai-elements/conversation";\nimport {\n PromptInput,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n} from "@/components/ai-elements/prompt-input";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\ntype AgentStatus = ReturnType<typeof useEveAgent>["status"];\ntype CancellationState = "idle" | "requested" | "cancelling";\n\ntype Cancellation = {\n requested: boolean;\n sentTurnId?: string;\n turnId?: string;\n};\n\nexport function AgentChat() {\n const [session] = useState(() =>\n new Client({ host: "", preserveCompletedSessions: true }).session(),\n );\n const cancellationRef = useRef<Cancellation>({ requested: false });\n const [cancellationError, setCancellationError] = useState<string>();\n const [cancellationState, setCancellationState] = useState<CancellationState>("idle");\n\n const cancelTurn = useCallback(\n (turnId: string) => {\n const cancellation = cancellationRef.current;\n if (!cancellation.requested || cancellation.sentTurnId === turnId) {\n return;\n }\n\n cancellation.sentTurnId = turnId;\n setCancellationState("cancelling");\n\n void session.cancel({ turnId }).catch((error: unknown) => {\n if (cancellationRef.current !== cancellation) {\n return;\n }\n\n cancellation.requested = false;\n cancellation.sentTurnId = undefined;\n setCancellationError(toErrorMessage(error));\n setCancellationState("idle");\n });\n },\n [session],\n );\n\n const handleEvent = useCallback(\n (event: HandleMessageStreamEvent) => {\n if (event.type !== "turn.started") {\n return;\n }\n\n const cancellation = cancellationRef.current;\n cancellation.turnId = event.data.turnId;\n cancelTurn(event.data.turnId);\n },\n [cancelTurn],\n );\n\n const agent = useEveAgent({ onEvent: handleEvent, session });\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isEmpty = agent.data.messages.length === 0;\n const errorMessage = cancellationError ?? agent.error?.message;\n const submitStatus = isBusy && cancellationState !== "idle" ? "submitted" : agent.status;\n\n const prepareTurn = () => {\n cancellationRef.current = { requested: false };\n setCancellationError(undefined);\n setCancellationState("idle");\n };\n\n const requestCancellation = () => {\n if (!isBusy || cancellationState !== "idle") {\n return;\n }\n\n const cancellation = cancellationRef.current;\n cancellation.requested = true;\n setCancellationError(undefined);\n setCancellationState("requested");\n\n if (cancellation.turnId !== undefined) {\n cancelTurn(cancellation.turnId);\n }\n };\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if ((text.length === 0 && message.files.length === 0) || isBusy) return;\n\n prepareTurn();\n\n if (message.files.length === 0) {\n await agent.send({ message: text });\n return;\n }\n\n const parts: UserContent = [];\n if (text.length > 0) {\n parts.push({ text, type: "text" });\n }\n for (const file of message.files) {\n parts.push({\n data: file.url,\n filename: file.filename,\n mediaType: file.mediaType,\n type: "file",\n });\n }\n\n await agent.send({ message: parts });\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea placeholder="Send a message…" />\n <PromptInputSubmit onStop={requestCancellation} status={submitStatus} />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {isEmpty ? null : (\n <header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">\n <span className="flex min-w-0 items-center gap-2">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n <StatusDot status={agent.status} />\n </span>\n </header>\n )}\n\n {errorMessage ? (\n <div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">\n <div className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm">\n <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n <div>\n <p className="font-medium">Request failed</p>\n <p className="mt-0.5 text-muted-foreground">{errorMessage}</p>\n </div>\n </div>\n </div>\n ) : null}\n\n {isEmpty ? null : (\n <Conversation className="min-h-0 flex-1">\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 py-6 sm:px-6">\n {agent.data.messages.map((message, index) => (\n <AgentMessage\n canRespond={!isBusy}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n onInputResponses={(inputResponses) => {\n prepareTurn();\n return agent.send({ inputResponses });\n }}\n />\n ))}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n )}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n isEmpty\n ? "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]"\n : "max-w-3xl shrink-0 pb-6",\n )}\n >\n {isEmpty ? (\n <div className="flex flex-col items-center gap-3 text-center">\n <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n </div>\n ) : null}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : "Unable to cancel the response.";\n}\n\nfunction StatusDot({ status }: { readonly status: AgentStatus }) {\n const isLive = status === "submitted" || status === "streaming";\n const tone =\n status === "error"\n ? "bg-destructive"\n : isLive\n ? "bg-emerald-500"\n : status === "ready"\n ? "bg-muted-foreground"\n : "bg-muted-foreground/50";\n\n return (\n <span className="relative flex size-1">\n {isLive ? (\n <span\n className={cn(\n "absolute inline-flex size-full animate-ping rounded-full opacity-75",\n tone,\n )}\n />\n ) : null}\n <span className={cn("relative inline-flex size-1 rounded-full transition-colors", tone)} />\n </span>\n );\n}\n';
3
+ readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport type { UserContent } from "ai";\nimport { Client, type MessageStreamEvent } from "eve/client";\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-react";\nimport { useCallback, useRef, useState } from "react";\nimport {\n Conversation,\n ConversationContent,\n ConversationScrollButton,\n} from "@/components/ai-elements/conversation";\nimport {\n PromptInput,\n type PromptInputMessage,\n PromptInputSubmit,\n PromptInputTextarea,\n} from "@/components/ai-elements/prompt-input";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\ntype AgentStatus = ReturnType<typeof useEveAgent>["status"];\ntype CancellationState = "idle" | "requested" | "cancelling";\n\ntype Cancellation = {\n requested: boolean;\n sentTurnId?: string;\n turnId?: string;\n};\n\nexport function AgentChat() {\n const [session] = useState(() =>\n new Client({ host: "", preserveCompletedSessions: true }).session(),\n );\n const cancellationRef = useRef<Cancellation>({ requested: false });\n const [cancellationError, setCancellationError] = useState<string>();\n const [cancellationState, setCancellationState] = useState<CancellationState>("idle");\n\n const cancelTurn = useCallback(\n (turnId: string) => {\n const cancellation = cancellationRef.current;\n if (!cancellation.requested || cancellation.sentTurnId === turnId) {\n return;\n }\n\n cancellation.sentTurnId = turnId;\n setCancellationState("cancelling");\n\n void session.cancel({ turnId }).catch((error: unknown) => {\n if (cancellationRef.current !== cancellation) {\n return;\n }\n\n cancellation.requested = false;\n cancellation.sentTurnId = undefined;\n setCancellationError(toErrorMessage(error));\n setCancellationState("idle");\n });\n },\n [session],\n );\n\n const handleEvent = useCallback(\n (event: MessageStreamEvent) => {\n if (event.type !== "turn.started") {\n return;\n }\n\n const cancellation = cancellationRef.current;\n cancellation.turnId = event.data.turnId;\n cancelTurn(event.data.turnId);\n },\n [cancelTurn],\n );\n\n const agent = useEveAgent({ onEvent: handleEvent, session });\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isEmpty = agent.data.messages.length === 0;\n const errorMessage = cancellationError ?? agent.error?.message;\n const submitStatus = isBusy && cancellationState !== "idle" ? "submitted" : agent.status;\n\n const prepareTurn = () => {\n cancellationRef.current = { requested: false };\n setCancellationError(undefined);\n setCancellationState("idle");\n };\n\n const requestCancellation = () => {\n if (!isBusy || cancellationState !== "idle") {\n return;\n }\n\n const cancellation = cancellationRef.current;\n cancellation.requested = true;\n setCancellationError(undefined);\n setCancellationState("requested");\n\n if (cancellation.turnId !== undefined) {\n cancelTurn(cancellation.turnId);\n }\n };\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if ((text.length === 0 && message.files.length === 0) || isBusy) return;\n\n prepareTurn();\n\n if (message.files.length === 0) {\n await agent.send({ message: text });\n return;\n }\n\n const parts: UserContent = [];\n if (text.length > 0) {\n parts.push({ text, type: "text" });\n }\n for (const file of message.files) {\n parts.push({\n data: file.url,\n filename: file.filename,\n mediaType: file.mediaType,\n type: "file",\n });\n }\n\n await agent.send({ message: parts });\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea placeholder="Send a message…" />\n <PromptInputSubmit onStop={requestCancellation} status={submitStatus} />\n </PromptInput>\n );\n\n return (\n <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n {isEmpty ? null : (\n <header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">\n <span className="flex min-w-0 items-center gap-2">\n <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n <StatusDot status={agent.status} />\n </span>\n </header>\n )}\n\n {errorMessage ? (\n <div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">\n <div className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm">\n <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n <div>\n <p className="font-medium">Request failed</p>\n <p className="mt-0.5 text-muted-foreground">{errorMessage}</p>\n </div>\n </div>\n </div>\n ) : null}\n\n {isEmpty ? null : (\n <Conversation className="min-h-0 flex-1">\n <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 py-6 sm:px-6">\n {agent.data.messages.map((message, index) => (\n <AgentMessage\n canRespond={!isBusy}\n isStreaming={\n agent.status === "streaming" && index === agent.data.messages.length - 1\n }\n key={message.id}\n message={message}\n onInputResponses={(inputResponses) => {\n prepareTurn();\n return agent.send({ inputResponses });\n }}\n />\n ))}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n )}\n\n <div\n className={cn(\n "mx-auto w-full px-4 sm:px-6",\n isEmpty\n ? "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]"\n : "max-w-3xl shrink-0 pb-6",\n )}\n >\n {isEmpty ? (\n <div className="flex flex-col items-center gap-3 text-center">\n <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n </div>\n ) : null}\n <div className="w-full">{composer}</div>\n </div>\n </main>\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : "Unable to cancel the response.";\n}\n\nfunction StatusDot({ status }: { readonly status: AgentStatus }) {\n const isLive = status === "submitted" || status === "streaming";\n const tone =\n status === "error"\n ? "bg-destructive"\n : isLive\n ? "bg-emerald-500"\n : status === "ready"\n ? "bg-muted-foreground"\n : "bg-muted-foreground/50";\n\n return (\n <span className="relative flex size-1">\n {isLive ? (\n <span\n className={cn(\n "absolute inline-flex size-full animate-ping rounded-full opacity-75",\n tone,\n )}\n />\n ) : null}\n <span className={cn("relative inline-flex size-1 rounded-full transition-colors", tone)} />\n </span>\n );\n}\n';
4
4
  readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {\n EveAuthorizationPart,\n EveDynamicToolPart,\n EveMessage,\n EveMessagePart,\n} from "eve/react";\nimport {\n CheckCircleIcon,\n ExternalLinkIcon,\n FileIcon,\n ImageIcon,\n KeyRoundIcon,\n XCircleIcon,\n} from "lucide-react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n Tool,\n ToolContent,\n ToolHeader,\n ToolInput,\n ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\n\ntype EveFilePart = Extract<EveMessagePart, { type: "file" }>;\n\nexport function AgentMessage({\n canRespond,\n isStreaming,\n message,\n onInputResponses,\n}: {\n readonly canRespond: boolean;\n readonly isStreaming: boolean;\n readonly message: EveMessage;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n const lastTextIndex = message.parts.reduce(\n (last, part, index) => (part.type === "text" ? index : last),\n -1,\n );\n\n return (\n <Message\n data-optimistic={message.metadata?.optimistic ? "true" : undefined}\n from={message.role}\n >\n <MessageContent>\n {message.parts.map((part, index) => (\n <AgentMessagePart\n canRespond={canRespond}\n key={partKey(part, index)}\n onInputResponses={onInputResponses}\n part={part}\n showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n />\n ))}\n </MessageContent>\n </Message>\n );\n}\n\nfunction AgentMessagePart({\n canRespond,\n onInputResponses,\n part,\n showCaret,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveMessagePart;\n readonly showCaret: boolean;\n}) {\n switch (part.type) {\n case "step-start":\n return null;\n case "text":\n return (\n <MessageResponse caret="block" isAnimating={showCaret}>\n {part.text}\n </MessageResponse>\n );\n case "reasoning":\n return (\n <Reasoning defaultOpen isStreaming={part.state === "streaming"}>\n <ReasoningTrigger />\n <ReasoningContent>{part.text}</ReasoningContent>\n </Reasoning>\n );\n case "file":\n return <AttachmentPart part={part} />;\n case "authorization":\n return <AuthorizationPrompt part={part} />;\n case "dynamic-tool":\n return (\n <Tool\n defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}\n >\n <ToolHeader\n state={part.state}\n title={part.toolName}\n toolName={part.toolName}\n type="dynamic-tool"\n />\n <ToolContent>\n <ToolInput input={part.input} />\n <InputRequestActions\n canRespond={canRespond}\n part={part}\n onInputResponses={onInputResponses}\n />\n <ToolOutput errorText={part.errorText} output={part.output} />\n </ToolContent>\n </Tool>\n );\n }\n}\n\nfunction AttachmentPart({ part }: { readonly part: EveFilePart }) {\n const label = part.filename ?? "Attachment";\n const detail = [part.mediaType, formatBytes(part.size)].filter(Boolean).join(" - ");\n const isImage = part.mediaType.startsWith("image/") && part.url !== undefined;\n const Icon = isImage ? ImageIcon : FileIcon;\n const body = (\n <span className="flex max-w-sm items-center gap-3 rounded-md border bg-background/60 p-2 text-sm">\n {isImage ? (\n <img alt={label} className="size-12 shrink-0 rounded-sm object-cover" src={part.url} />\n ) : (\n <span className="flex size-10 shrink-0 items-center justify-center rounded-sm bg-muted text-muted-foreground">\n <Icon className="size-4" />\n </span>\n )}\n <span className="min-w-0 flex-1">\n <span className="block truncate font-medium">{label}</span>\n {detail ? <span className="block truncate text-muted-foreground">{detail}</span> : null}\n </span>\n {part.url ? <ExternalLinkIcon className="size-4 shrink-0 text-muted-foreground" /> : null}\n </span>\n );\n\n return part.url ? (\n <a href={part.url} rel="noreferrer" target="_blank">\n {body}\n </a>\n ) : (\n body\n );\n}\n\nfunction AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {\n const isAuthorized = part.state === "completed" && part.outcome === "authorized";\n const isCompleted = part.state === "completed";\n const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;\n const instructions = part.authorization?.instructions;\n const shouldShowInstructions = instructions !== undefined && instructions !== part.description;\n\n return (\n <div\n className={cn(\n "space-y-3 rounded-md border p-3",\n isAuthorized\n ? "border-emerald-500/30 bg-emerald-500/5"\n : isCompleted\n ? "border-destructive/30 bg-destructive/5"\n : "border-blue-500/30 bg-blue-500/5",\n )}\n >\n <div className="flex items-start gap-3">\n <span\n className={cn(\n "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",\n isAuthorized\n ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"\n : isCompleted\n ? "bg-destructive/10 text-destructive"\n : "bg-blue-500/10 text-blue-700 dark:text-blue-300",\n )}\n >\n <Icon className="size-4" />\n </span>\n <div className="min-w-0 flex-1 space-y-2">\n <p className="font-medium text-sm">{authorizationTitle(part)}</p>\n <p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>\n {shouldShowInstructions ? (\n <p className="text-muted-foreground text-sm">{instructions}</p>\n ) : null}\n {part.state === "required" && part.authorization?.userCode ? (\n <div className="flex flex-wrap items-center gap-2 text-sm">\n <span className="text-muted-foreground">Code</span>\n <code className="rounded-md bg-background px-2 py-1 font-mono">\n {part.authorization.userCode}\n </code>\n </div>\n ) : null}\n {part.state === "required" && part.authorization?.url ? (\n <Button asChild size="sm">\n <a href={part.authorization.url} rel="noreferrer" target="_blank">\n <ExternalLinkIcon className="size-4" />\n Sign in with {part.displayName}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n}\n\nfunction authorizationTitle(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return `Connect ${part.displayName}`;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected`;\n }\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}`;\n}\n\nfunction authorizationDescription(part: EveAuthorizationPart): string {\n if (part.state === "required") {\n return part.description;\n }\n if (part.outcome === "authorized") {\n return `${part.displayName} connected.`;\n }\n const tail = part.reason !== undefined ? ` (${part.reason})` : "";\n return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}${tail}.`;\n}\n\nfunction formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {\n switch (outcome) {\n case "authorized":\n return "authorized";\n case "declined":\n return "declined";\n case "failed":\n return "failed";\n case "timed-out":\n return "timed out";\n }\n}\n\nfunction formatBytes(size: number | undefined): string | undefined {\n if (size === undefined) {\n return undefined;\n }\n if (size < 1024) {\n return `${size} B`;\n }\n if (size < 1024 * 1024) {\n return `${(size / 1024).toFixed(1)} KB`;\n }\n return `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction InputRequestActions({\n canRespond,\n onInputResponses,\n part,\n}: {\n readonly canRespond: boolean;\n readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n readonly part: EveDynamicToolPart;\n}) {\n const inputRequest = part.toolMetadata?.eve?.inputRequest;\n if (!inputRequest) {\n return null;\n }\n\n const inputResponse = part.toolMetadata?.eve?.inputResponse;\n const selectedOption = inputRequest.options?.find(\n (option) => option.id === inputResponse?.optionId,\n );\n\n return (\n <div className="space-y-3 rounded-md border border-yellow-500/30 bg-yellow-500/5 p-3">\n <p className="text-muted-foreground text-sm">{inputRequest.prompt}</p>\n {inputResponse ? (\n <p className="font-medium text-sm">\n Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}\n </p>\n ) : (\n <div className="flex flex-wrap gap-2">\n {inputRequest.options?.map((option) => (\n <Button\n disabled={!canRespond}\n key={option.id}\n onClick={() => {\n void onInputResponses([\n {\n optionId: option.id,\n requestId: inputRequest.requestId,\n },\n ]);\n }}\n size="sm"\n type="button"\n variant={option.style === "danger" ? "destructive" : "default"}\n >\n {option.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction partKey(part: EveMessagePart, index: number): string {\n switch (part.type) {\n case "authorization":\n return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;\n case "dynamic-tool":\n return part.toolCallId;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
5
5
  readonly "app/globals.css": '@import "tailwindcss";\n@source "../node_modules/streamdown/dist/*.js";\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;\n --font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;\n}\n\n:root {\n color-scheme: light;\n /* Soft neutral page with white elevated surfaces so cards/composer pop. */\n --background: oklch(0.971 0 0);\n --foreground: oklch(0.16 0 0);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.16 0 0);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.16 0 0);\n --primary: oklch(0.19 0 0);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.94 0 0);\n --secondary-foreground: oklch(0.19 0 0);\n --muted: oklch(0.94 0 0);\n --muted-foreground: oklch(0.6 0 0);\n --accent: oklch(0.94 0 0);\n --accent-foreground: oklch(0.19 0 0);\n --destructive: oklch(0.577 0.245 27.325);\n --border: oklch(0.916 0 0);\n --input: oklch(0.916 0 0);\n --ring: oklch(0.708 0 0);\n --radius: 0.625rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --background: oklch(0.145 0 0);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.205 0 0);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.205 0 0);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.922 0 0);\n --primary-foreground: oklch(0.205 0 0);\n --secondary: oklch(0.269 0 0);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.269 0 0);\n --muted-foreground: oklch(0.708 0 0);\n --accent: oklch(0.269 0 0);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.704 0.191 22.216);\n --border: oklch(1 0 0 / 10%);\n --input: oklch(1 0 0 / 15%);\n --ring: oklch(0.556 0 0);\n }\n}\n\n* {\n border-color: var(--border);\n}\n\nhtml {\n height: 100%;\n}\n\nbody {\n min-height: 100%;\n margin: 0;\n background: var(--background);\n font-family: var(--font-sans);\n}\n\nbutton,\ninput,\ntextarea {\n font: inherit;\n}\n';
6
6
  readonly "app/layout.tsx": 'import type { Metadata } from "next";\nimport { Geist, Geist_Mono } from "next/font/google";\nimport type { ReactNode } from "react";\nimport { TooltipProvider } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport "./globals.css";\n\nconst sans = Geist({\n variable: "--font-sans",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nconst mono = Geist_Mono({\n variable: "--font-mono",\n subsets: ["latin"],\n weight: "variable",\n display: "swap",\n});\n\nexport const metadata: Metadata = {\n title: "__EVE_INIT_APP_NAME__",\n description: "A Next.js starter for eve agents with AI Elements.",\n};\n\nexport default function RootLayout({ children }: { readonly children: ReactNode }) {\n return (\n <html className={cn(sans.variable, mono.variable)} lang="en">\n <body>\n <TooltipProvider>{children}</TooltipProvider>\n </body>\n </html>\n );\n}\n';
@@ -16,7 +16,7 @@ export default eveChannel({
16
16
  `,"app/_components/agent-chat.tsx":`"use client";
17
17
 
18
18
  import type { UserContent } from "ai";
19
- import { Client, type HandleMessageStreamEvent } from "eve/client";
19
+ import { Client, type MessageStreamEvent } from "eve/client";
20
20
  import { useEveAgent } from "eve/react";
21
21
  import { AlertCircleIcon } from "lucide-react";
22
22
  import { useCallback, useRef, useState } from "react";
@@ -78,7 +78,7 @@ export function AgentChat() {
78
78
  );
79
79
 
80
80
  const handleEvent = useCallback(
81
- (event: HandleMessageStreamEvent) => {
81
+ (event: MessageStreamEvent) => {
82
82
  if (event.type !== "turn.started") {
83
83
  return;
84
84
  }
@@ -119,6 +119,18 @@ export interface PublicAgentCompactionDefinition {
119
119
  * Configures framework-owned runtime limits for this agent's runs.
120
120
  */
121
121
  export interface AgentLimitsDefinition {
122
+ /**
123
+ * Maximum lifetime of one durable session, in milliseconds.
124
+ *
125
+ * The deadline starts when the session is created and survives process
126
+ * restarts and redeployments. If it elapses during an active turn, eve lets
127
+ * that turn settle before completing the session normally.
128
+ *
129
+ * `false` disables the timeout.
130
+ *
131
+ * @default 2_592_000_000 (30 days)
132
+ */
133
+ readonly sessionTimeoutMs?: number | false;
122
134
  /**
123
135
  * Maximum provider-reported input tokens accumulated by one durable session.
124
136
  *
@@ -3,13 +3,13 @@ import type { PublicToolInputSchema, PublicToolOutputSchema, ToolModelOutput } f
3
3
  import type { Approval } from "#public/definitions/approval.js";
4
4
  import type { ToolContext } from "#public/definitions/tool.js";
5
5
  import type { SessionAuth } from "#context/keys.js";
6
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
6
+ import type { UnstampedMessageStreamEvent } from "#protocol/message.js";
7
7
  /**
8
8
  * Stream event types allowed for dynamic tool resolvers. Dispatch
9
9
  * supports any event; this extract restricts the public surface until
10
10
  * more events are validated.
11
11
  */
12
- export type DynamicToolEventName = Extract<HandleMessageStreamEvent["type"], "session.started" | "turn.started" | "step.started">;
12
+ export type DynamicToolEventName = Extract<UnstampedMessageStreamEvent["type"], "session.started" | "turn.started" | "step.started">;
13
13
  export declare const ALLOWED_DYNAMIC_TOOL_EVENTS: ReadonlySet<string>;
14
14
  /**
15
15
  * Instructions and skills are restricted to session/turn boundaries.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ULID-compatible generation, vendored rather than taken from npm: the
3
+ * `ulid` package's Node build hard-imports `node:crypto`, and this module is
4
+ * bundled into browser clients, the Nitro server, and the workflow step
5
+ * sandbox. `ulid.test.ts` pins the encoding against the reference
6
+ * implementation's golden vectors.
7
+ */
8
+ /** Character count of a ULID: a 10-char timestamp then a 16-char random tail. */
9
+ export declare const ULID_LENGTH: number;
10
+ /** Mints one ULID. See {@link createUlidFactory}. */
11
+ export type UlidFactory = () => string;
12
+ /**
13
+ * Creates an independent ULID generator with its own monotonic state.
14
+ *
15
+ * Prefer {@link createUlid} unless a caller needs a sequence that unrelated
16
+ * code cannot perturb. Ids sort in the order one generator produced them:
17
+ * those minted within a millisecond increment the random tail, and a
18
+ * backwards clock pins to the last millisecond observed.
19
+ */
20
+ export declare function createUlidFactory(): UlidFactory;
21
+ /**
22
+ * Mints a ULID from the process-wide generator: a 48-bit millisecond
23
+ * timestamp then 80 bits of randomness, in Crockford base32.
24
+ *
25
+ * Ids lead with their timestamp, so they are broadly time-ordered. Ordering
26
+ * holds within one process only — separate processes have independent clocks
27
+ * and random tails — so a ULID is not a lossless pagination cursor.
28
+ *
29
+ * Throws when the clock is outside the 48-bit timestamp range or Web Crypto
30
+ * is unavailable rather than weakening the randomness.
31
+ */
32
+ export declare const createUlid: UlidFactory;
33
+ /**
34
+ * Returns true when `value` is shaped like a ULID: {@link ULID_LENGTH}
35
+ * characters drawn from the Crockford base32 alphabet.
36
+ *
37
+ * Shape-only — this does not prove the value was minted here.
38
+ */
39
+ export declare function isUlid(value: string): boolean;
@@ -0,0 +1 @@
1
+ const ENCODING=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`,TIME_MAX=2**48-1,ULID_LENGTH=26;function createUlidFactory(){let e=-1,n=new Uint8Array(10);return function(){let r=Date.now();if(!Number.isInteger(r)||r<0||r>TIME_MAX)throw Error(`Cannot mint a ULID: timestamp must be an integer from 0 to ${TIME_MAX}.`);if(r>e)e=r,randomFill(n);else if(!incrementRandom(n)){if(e===TIME_MAX)throw Error(`Cannot mint a ULID: random component overflowed at the maximum timestamp.`);e+=1,randomFill(n)}return`${encodeTime(e)}${encodeRandom(n)}`}}const createUlid=createUlidFactory();function isUlid(t){if(t.length!==26||ENCODING.indexOf(t[0]??``)>7)return!1;for(let n of t)if(!ENCODING.includes(n))return!1;return!0}function randomFill(e){let t=globalThis.crypto;if(typeof t?.getRandomValues!=`function`)throw Error(`Cannot mint a ULID: globalThis.crypto.getRandomValues is unavailable.`);t.getRandomValues(e)}function encodeTime(t){let n=t,r=``;for(let t=0;t<10;t+=1)r=ENCODING[n%32]+r,n=Math.floor(n/32);return r}function encodeRandom(t){let n=0,r=0,i=``;for(let a of t){for(n=n<<8|a,r+=8;r>=5;)r-=5,i+=ENCODING[n>>>r&31];n&=(1<<r)-1}return i}function incrementRandom(e){for(let t=e.length-1;t>=0;--t){let n=e[t]??0;if(n<255)return e[t]=n+1,e.fill(0,t+1),!0}return!1}export{ULID_LENGTH,createUlid,createUlidFactory,isUlid};
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CsSQOKnp.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CZkX_ZTT.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -3,7 +3,7 @@ import { type EveMessageData } from "#client/message-reducer.js";
3
3
  import type { EveAgentReducer } from "#client/reducer.js";
4
4
  import type { ClientSession } from "#client/session.js";
5
5
  import type { ClientAuth, HeadersValue, SendTurnPayload, SessionState } from "#client/types.js";
6
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
6
+ import type { MessageStreamEvent } from "#protocol/message.js";
7
7
  export type { PrepareSend };
8
8
  /**
9
9
  * Session lifecycle phase: `"ready"` (idle), `"submitted"` (request sent,
@@ -30,7 +30,7 @@ export interface UseEveAgentReturn<TData> {
30
30
  /** Last transport-level error, or `undefined` when healthy. */
31
31
  readonly error: Error | undefined;
32
32
  /** Raw server events received during this session (authoritative stream). */
33
- readonly events: readonly HandleMessageStreamEvent[];
33
+ readonly events: readonly MessageStreamEvent[];
34
34
  /** Clear all state and start a new session. */
35
35
  readonly reset: () => void;
36
36
  /** Send a turn with full structured input (message, attachments, input responses). */
@@ -76,8 +76,8 @@ export interface UseEveAgentOptions<TData> extends EveAgentStoreCallbacks<TData>
76
76
  * @default ""
77
77
  */
78
78
  readonly host?: string;
79
- /** Seed events for resuming a prior conversation. */
80
- readonly initialEvents?: readonly HandleMessageStreamEvent[];
79
+ /** Ordered prefix of the session stream used to rehydrate projected state. */
80
+ readonly initialEvents?: readonly MessageStreamEvent[];
81
81
  /** Seed session identity and stream cursor for resuming a prior conversation. */
82
82
  readonly initialSession?: SessionState;
83
83
  /**
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-CsSQOKnp.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-CZkX_ZTT.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-CWYe-vqE.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-hdPX68_i.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -3,7 +3,7 @@ import { type EveAgentStoreCallbacks, type EveAgentStoreSnapshot, type EveAgentS
3
3
  import type { EveAgentReducer } from "#client/reducer.js";
4
4
  import type { ClientSession } from "#client/session.js";
5
5
  import { type EveMessageData } from "#client/message-reducer.js";
6
- import type { HandleMessageStreamEvent } from "#protocol/message.js";
6
+ import type { MessageStreamEvent } from "#protocol/message.js";
7
7
  import type { ClientAuth, HeadersValue, SendTurnPayload, SessionState } from "#client/types.js";
8
8
  export type { PrepareSend };
9
9
  /**
@@ -29,7 +29,7 @@ export interface UseEveAgentReturn<TData> {
29
29
  /** Last transport-level error, or `undefined` when healthy. */
30
30
  readonly error: ComputedRef<Error | undefined>;
31
31
  /** Raw server events from this session (authoritative stream). */
32
- readonly events: ComputedRef<readonly HandleMessageStreamEvent[]>;
32
+ readonly events: ComputedRef<readonly MessageStreamEvent[]>;
33
33
  /** Clear all state and start a new session. */
34
34
  readonly reset: () => void;
35
35
  /** Send a turn with full structured input (message, attachments, input responses). */
@@ -75,8 +75,8 @@ export interface UseEveAgentOptions<TData> extends EveAgentStoreCallbacks<TData>
75
75
  * @default ""
76
76
  */
77
77
  readonly host?: string;
78
- /** Prior stream events to rehydrate the projected state from on mount. */
79
- readonly initialEvents?: readonly HandleMessageStreamEvent[];
78
+ /** Ordered prefix of the session stream used to rehydrate projected state. */
79
+ readonly initialEvents?: readonly MessageStreamEvent[];
80
80
  /** Prior session cursor to resume from on mount. */
81
81
  readonly initialSession?: SessionState;
82
82
  /**
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-CWYe-vqE.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-hdPX68_i.js";
2
2
 
3
3
  export { useEveAgent };
@@ -131,10 +131,18 @@ export default defineAgent({
131
131
  limits: {
132
132
  maxInputTokensPerSession: 200_000,
133
133
  maxOutputTokensPerSession: 20_000,
134
+ sessionTimeoutMs: 7 * 24 * 60 * 60 * 1_000,
134
135
  },
135
136
  });
136
137
  ```
137
138
 
139
+ `sessionTimeoutMs` sets an absolute lifetime for every session, including
140
+ delegated sessions. It defaults to 30 days, starts at creation, and survives
141
+ restarts and redeployments. At the deadline, eve lets an active turn settle,
142
+ then emits `session.completed` and releases the continuation; the next
143
+ qualifying channel message starts fresh. Set it to `false` to disable the
144
+ timeout. Expiration does not delete stored session data.
145
+
138
146
  Input and output budgets are checked independently. The model call that crosses
139
147
  either limit is allowed to finish because providers only report exact token
140
148
  usage after a call completes. Before the next model call, eve pauses the
@@ -147,12 +155,14 @@ still over budget, the next message re-raises the prompt. Declining a
147
155
  delegated child's prompt cancels the root turn, which cascades to the whole
148
156
  delegation tree — the delegating parent never receives an error result it
149
157
  could retry against a fresh quota share. A reply that answers neither option
150
- re-raises the prompt; the reply stays in history and is processed once the
151
- budget is granted.
158
+ is queued while the existing prompt stays pending; eve does not raise another
159
+ copy. The reply is processed once the budget is granted.
152
160
 
153
161
  Sessions that cannot reach a human — task-mode runs such as schedules and
154
162
  subagents without input proxying — skip the prompt and fail the next model
155
- call with `SESSION_TOKEN_LIMIT_REACHED`.
163
+ call with `SESSION_TOKEN_LIMIT_REACHED`. A delegated task with no inherited
164
+ quota also fails instead of raising a continuation prompt that could only
165
+ grant another zero-token window.
156
166
 
157
167
  When `maxInputTokensPerSession` is omitted, root sessions apply a default
158
168
  input budget of `40_000_000` provider-reported input tokens.
@@ -161,11 +171,13 @@ to `false` uncaps that axis — the session never stops on it.
161
171
 
162
172
  Delegated subagent sessions have no fixed default. Each child receives a
163
173
  share of the delegating parent's remaining quota at dispatch time — the
164
- remainder (limit minus accumulated usage) split evenly across the batch's
165
- local subagent calls — and a completed child's usage counts against the
166
- parent's quota, so a delegation tree can never outspend the budget configured
167
- at its root. An authored child limit applies only when it is tighter than the
168
- parent's grant; an uncapped parent delegates uncapped children.
174
+ remainder in the current budget window split evenly across the batch's local
175
+ subagent calls — and a completed child's usage counts against the parent's
176
+ quota, so a delegation tree can never outspend the budget configured at its
177
+ root. Approving a continuation opens a fresh parent window for later child
178
+ grants without erasing lifetime usage. An authored child limit applies only
179
+ when it is tighter than the parent's grant; an uncapped parent delegates
180
+ uncapped children.
169
181
 
170
182
  ## Workflow world
171
183
 
@@ -208,14 +220,14 @@ installed package must stay external in hosted output, list it in
208
220
 
209
221
  `defineAgent` takes a few more fields, all optional. For the exported types, see the [TypeScript API](./reference/typescript-api).
210
222
 
211
- | Field | Type | Default | Description |
212
- | -------------- | --------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
213
- | `reasoning` | `AgentReasoningDefinition` | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls. |
214
- | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
215
- | `limits` | `AgentLimitsDefinition` | field-specific | Framework-owned runtime limits. `maxInputTokensPerSession` defaults to `40_000_000` for root sessions, and delegated subagent sessions inherit the parent's remaining quota; `maxOutputTokensPerSession` is unset unless configured; `false` uncaps a session token limit. |
216
- | `experimental` | `{ workflow?: { world?: string } }` | unset | Opt-in settings that can change or disappear in any release. Treat them as unstable. `workflow.world` selects the Workflow world package backing session state, queues, hooks, and streams on the root agent. |
217
- | `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. |
218
- | `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. |
223
+ | Field | Type | Default | Description |
224
+ | -------------- | --------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
225
+ | `reasoning` | `AgentReasoningDefinition` | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls. |
226
+ | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
227
+ | `limits` | `AgentLimitsDefinition` | field-specific | Framework-owned runtime limits. Sessions complete after 30 days by default; token-limit defaults and inheritance are described above. Set a limit to `false` to disable it. |
228
+ | `experimental` | `{ workflow?: { world?: string } }` | unset | Opt-in settings that can change or disappear in any release. Treat them as unstable. `workflow.world` selects the Workflow world package backing session state, queues, hooks, and streams on the root agent. |
229
+ | `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. |
230
+ | `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. |
219
231
 
220
232
  `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.
221
233
 
@@ -56,7 +56,7 @@ Provider keys, tool secrets, and MCP, OpenAPI, and connection credentials stay i
56
56
 
57
57
  ## Resuming after a crash
58
58
 
59
- Crash the process, hit a timeout, or redeploy mid-turn, and the run picks up from the last completed step rather than replaying the whole turn. Completed steps never re-run; eve replays the recorded result. A step interrupted mid-execution re-runs, so make non-idempotent side effects like charges or emails idempotent, or gate them with approval.
59
+ Crash the process, hit a timeout, or redeploy mid-turn, and the run picks up from the last completed step rather than replaying the whole turn. Completed steps never re-run; eve replays the recorded result. A step interrupted mid-execution re-runs, so make non-idempotent side effects like charges or emails idempotent, or gate them with approval. Whatever that step already wrote to the session stream stays there, and the re-run emits its events again under new ids, so a stream consumer sees both attempts — see [the event envelope](./sessions-runs-and-streaming#the-event-envelope).
60
60
 
61
61
  There's nothing to configure. eve owns the workflow lifecycle, and sessions are durable by default.
62
62