eve 0.15.4 → 0.15.5

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # eve
2
2
 
3
+ ## 0.15.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 8078807: Render authorization prompts in the default web chat projection. Scaffolded web UIs now show OAuth sign-in affordances from `authorization.required` events and update them when authorization completes.
8
+
3
9
  ## 0.15.4
4
10
 
5
11
  ### Patch Changes
@@ -1111,6 +1111,52 @@ function toTerminalStreamFailureError(event) {
1111
1111
  return error;
1112
1112
  }
1113
1113
 
1114
+ //#endregion
1115
+ //#region src/client/authorization-message-parts.ts
1116
+ function createAuthorizationRequiredPart(event) {
1117
+ const displayName = event.data.authorization?.displayName ?? formatAuthorizationDisplayName(event.data.name);
1118
+ return {
1119
+ authorization: event.data.authorization,
1120
+ description: normalizeAuthorizationDescription(event.data.description, event.data.name, displayName),
1121
+ displayName,
1122
+ name: event.data.name,
1123
+ state: "required",
1124
+ stepIndex: event.data.stepIndex,
1125
+ turnId: event.data.turnId,
1126
+ type: "authorization"
1127
+ };
1128
+ }
1129
+ function createAuthorizationCompletedPart(event, existing) {
1130
+ const displayName = event.data.authorization?.displayName ?? existing?.displayName ?? formatAuthorizationDisplayName(event.data.name);
1131
+ return {
1132
+ authorization: existing?.authorization || event.data.authorization ? {
1133
+ ...existing?.authorization,
1134
+ ...event.data.authorization
1135
+ } : void 0,
1136
+ description: existing?.description ?? buildCompletedAuthorizationDescription(displayName, event.data.outcome, event.data.reason),
1137
+ displayName,
1138
+ name: event.data.name,
1139
+ outcome: event.data.outcome,
1140
+ reason: event.data.reason,
1141
+ state: "completed",
1142
+ stepIndex: existing?.stepIndex ?? event.data.stepIndex,
1143
+ turnId: existing?.turnId ?? event.data.turnId,
1144
+ type: "authorization"
1145
+ };
1146
+ }
1147
+ function buildCompletedAuthorizationDescription(displayName, outcome, reason) {
1148
+ if (outcome === "authorized") return `${displayName} connected.`;
1149
+ return `${displayName} authorization ${outcome}${reason !== void 0 ? ` (${reason})` : ""}.`;
1150
+ }
1151
+ function normalizeAuthorizationDescription(description, name, displayName) {
1152
+ if (description === `Authorization required for ${name}`) return `Authorization required for ${displayName}`;
1153
+ return description;
1154
+ }
1155
+ function formatAuthorizationDisplayName(name) {
1156
+ if (name.length === 0) return name;
1157
+ return `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
1158
+ }
1159
+
1114
1160
  //#endregion
1115
1161
  //#region src/client/message-reducer.ts
1116
1162
  function defaultMessageReducer() {
@@ -1253,6 +1299,8 @@ function reduceMessageData(data, event) {
1253
1299
  if (existing !== void 0) return updateToolPart(data, event.data.result.callId, nextPart);
1254
1300
  return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), nextPart));
1255
1301
  }
1302
+ case "authorization.required": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), createAuthorizationRequiredPart(event)));
1303
+ case "authorization.completed": return completeAuthorization(data, event);
1256
1304
  case "message.appended": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
1257
1305
  state: "streaming",
1258
1306
  stepIndex: event.data.stepIndex,
@@ -1360,9 +1408,30 @@ function updateToolPart(data, toolCallId, next) {
1360
1408
  if (!message) return data;
1361
1409
  return upsertMessage(data, upsertPart(message, next));
1362
1410
  }
1411
+ function completeAuthorization(data, event) {
1412
+ const existing = findLatestPendingAuthorizationPart(data, event.data.name);
1413
+ const next = createAuthorizationCompletedPart(event, existing);
1414
+ if (existing !== void 0) return updateAuthorizationPart(data, existing, next);
1415
+ return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), next));
1416
+ }
1417
+ function updateAuthorizationPart(data, existing, next) {
1418
+ const message = data.messages.find((candidate) => candidate.role === "assistant" && candidate.parts.some((part) => part === existing));
1419
+ if (!message) return data;
1420
+ return upsertMessage(data, upsertPart(message, next));
1421
+ }
1363
1422
  function findToolPart(data, toolCallId) {
1364
1423
  for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.toolCallId === toolCallId) return part;
1365
1424
  }
1425
+ function findLatestPendingAuthorizationPart(data, name) {
1426
+ for (let messageIndex = data.messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
1427
+ const message = data.messages[messageIndex];
1428
+ if (message?.role !== "assistant") continue;
1429
+ for (let partIndex = message.parts.length - 1; partIndex >= 0; partIndex -= 1) {
1430
+ const part = message.parts[partIndex];
1431
+ if (part?.type === "authorization" && part.state === "required" && part.name === name) return part;
1432
+ }
1433
+ }
1434
+ }
1366
1435
  function findToolPartByApprovalId(data, approvalId) {
1367
1436
  for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.approval?.id === approvalId) return part;
1368
1437
  }
@@ -1371,6 +1440,7 @@ function partKey(part) {
1371
1440
  case "text": return `text:${part.stepIndex ?? 0}`;
1372
1441
  case "reasoning": return `reasoning:${part.stepIndex ?? 0}`;
1373
1442
  case "step-start": return "step-start";
1443
+ case "authorization": return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;
1374
1444
  case "dynamic-tool": return `dynamic-tool:${part.toolCallId}`;
1375
1445
  }
1376
1446
  }
@@ -1111,6 +1111,52 @@ function toTerminalStreamFailureError(event) {
1111
1111
  return error;
1112
1112
  }
1113
1113
 
1114
+ //#endregion
1115
+ //#region src/client/authorization-message-parts.ts
1116
+ function createAuthorizationRequiredPart(event) {
1117
+ const displayName = event.data.authorization?.displayName ?? formatAuthorizationDisplayName(event.data.name);
1118
+ return {
1119
+ authorization: event.data.authorization,
1120
+ description: normalizeAuthorizationDescription(event.data.description, event.data.name, displayName),
1121
+ displayName,
1122
+ name: event.data.name,
1123
+ state: "required",
1124
+ stepIndex: event.data.stepIndex,
1125
+ turnId: event.data.turnId,
1126
+ type: "authorization"
1127
+ };
1128
+ }
1129
+ function createAuthorizationCompletedPart(event, existing) {
1130
+ const displayName = event.data.authorization?.displayName ?? existing?.displayName ?? formatAuthorizationDisplayName(event.data.name);
1131
+ return {
1132
+ authorization: existing?.authorization || event.data.authorization ? {
1133
+ ...existing?.authorization,
1134
+ ...event.data.authorization
1135
+ } : void 0,
1136
+ description: existing?.description ?? buildCompletedAuthorizationDescription(displayName, event.data.outcome, event.data.reason),
1137
+ displayName,
1138
+ name: event.data.name,
1139
+ outcome: event.data.outcome,
1140
+ reason: event.data.reason,
1141
+ state: "completed",
1142
+ stepIndex: existing?.stepIndex ?? event.data.stepIndex,
1143
+ turnId: existing?.turnId ?? event.data.turnId,
1144
+ type: "authorization"
1145
+ };
1146
+ }
1147
+ function buildCompletedAuthorizationDescription(displayName, outcome, reason) {
1148
+ if (outcome === "authorized") return `${displayName} connected.`;
1149
+ return `${displayName} authorization ${outcome}${reason !== void 0 ? ` (${reason})` : ""}.`;
1150
+ }
1151
+ function normalizeAuthorizationDescription(description, name, displayName) {
1152
+ if (description === `Authorization required for ${name}`) return `Authorization required for ${displayName}`;
1153
+ return description;
1154
+ }
1155
+ function formatAuthorizationDisplayName(name) {
1156
+ if (name.length === 0) return name;
1157
+ return `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
1158
+ }
1159
+
1114
1160
  //#endregion
1115
1161
  //#region src/client/message-reducer.ts
1116
1162
  function defaultMessageReducer() {
@@ -1253,6 +1299,8 @@ function reduceMessageData(data, event) {
1253
1299
  if (existing !== void 0) return updateToolPart(data, event.data.result.callId, nextPart);
1254
1300
  return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), nextPart));
1255
1301
  }
1302
+ case "authorization.required": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), createAuthorizationRequiredPart(event)));
1303
+ case "authorization.completed": return completeAuthorization(data, event);
1256
1304
  case "message.appended": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
1257
1305
  state: "streaming",
1258
1306
  stepIndex: event.data.stepIndex,
@@ -1360,9 +1408,30 @@ function updateToolPart(data, toolCallId, next) {
1360
1408
  if (!message) return data;
1361
1409
  return upsertMessage(data, upsertPart(message, next));
1362
1410
  }
1411
+ function completeAuthorization(data, event) {
1412
+ const existing = findLatestPendingAuthorizationPart(data, event.data.name);
1413
+ const next = createAuthorizationCompletedPart(event, existing);
1414
+ if (existing !== void 0) return updateAuthorizationPart(data, existing, next);
1415
+ return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), next));
1416
+ }
1417
+ function updateAuthorizationPart(data, existing, next) {
1418
+ const message = data.messages.find((candidate) => candidate.role === "assistant" && candidate.parts.some((part) => part === existing));
1419
+ if (!message) return data;
1420
+ return upsertMessage(data, upsertPart(message, next));
1421
+ }
1363
1422
  function findToolPart(data, toolCallId) {
1364
1423
  for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.toolCallId === toolCallId) return part;
1365
1424
  }
1425
+ function findLatestPendingAuthorizationPart(data, name) {
1426
+ for (let messageIndex = data.messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
1427
+ const message = data.messages[messageIndex];
1428
+ if (message?.role !== "assistant") continue;
1429
+ for (let partIndex = message.parts.length - 1; partIndex >= 0; partIndex -= 1) {
1430
+ const part = message.parts[partIndex];
1431
+ if (part?.type === "authorization" && part.state === "required" && part.name === name) return part;
1432
+ }
1433
+ }
1434
+ }
1366
1435
  function findToolPartByApprovalId(data, approvalId) {
1367
1436
  for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.approval?.id === approvalId) return part;
1368
1437
  }
@@ -1371,6 +1440,7 @@ function partKey(part) {
1371
1440
  case "text": return `text:${part.stepIndex ?? 0}`;
1372
1441
  case "reasoning": return `reasoning:${part.stepIndex ?? 0}`;
1373
1442
  case "step-start": return "step-start";
1443
+ case "authorization": return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;
1374
1444
  case "dynamic-tool": return `dynamic-tool:${part.toolCallId}`;
1375
1445
  }
1376
1446
  }
@@ -0,0 +1,4 @@
1
+ import type { AuthorizationCompletedStreamEvent, AuthorizationRequiredStreamEvent } from "#protocol/message.js";
2
+ import type { EveAuthorizationPart } from "#client/message-reducer-types.js";
3
+ export declare function createAuthorizationRequiredPart(event: AuthorizationRequiredStreamEvent): EveAuthorizationPart;
4
+ export declare function createAuthorizationCompletedPart(event: AuthorizationCompletedStreamEvent, existing?: EveAuthorizationPart): EveAuthorizationPart;
@@ -0,0 +1 @@
1
+ function createAuthorizationRequiredPart(e){let t=e.data.authorization?.displayName??formatAuthorizationDisplayName(e.data.name);return{authorization:e.data.authorization,description:normalizeAuthorizationDescription(e.data.description,e.data.name,t),displayName:t,name:e.data.name,state:`required`,stepIndex:e.data.stepIndex,turnId:e.data.turnId,type:`authorization`}}function createAuthorizationCompletedPart(e,t){let n=e.data.authorization?.displayName??t?.displayName??formatAuthorizationDisplayName(e.data.name);return{authorization:t?.authorization||e.data.authorization?{...t?.authorization,...e.data.authorization}:void 0,description:t?.description??buildCompletedAuthorizationDescription(n,e.data.outcome,e.data.reason),displayName:n,name:e.data.name,outcome:e.data.outcome,reason:e.data.reason,state:`completed`,stepIndex:t?.stepIndex??e.data.stepIndex,turnId:t?.turnId??e.data.turnId,type:`authorization`}}function buildCompletedAuthorizationDescription(e,t,n){return t===`authorized`?`${e} connected.`:`${e} authorization ${t}${n===void 0?``:` (${n})`}.`}function normalizeAuthorizationDescription(e,t,n){return e===`Authorization required for ${t}`?`Authorization required for ${n}`:e}function formatAuthorizationDisplayName(e){return e.length===0?e:`${e.charAt(0).toUpperCase()}${e.slice(1)}`}export{createAuthorizationCompletedPart,createAuthorizationRequiredPart};
@@ -9,8 +9,8 @@ export { ClientSession } from "#client/session.js";
9
9
  export type { EveAgentStoreCallbacks, EveAgentStoreInit, EveAgentStoreSnapshot, EveAgentStoreStatus, PrepareSend, } from "#client/eve-agent-store.js";
10
10
  export type { AgentInfoEntry, AgentInfoChannelEntry, AgentInfoChannels, AgentInfoConnectionEntry, AgentInfoDynamicResolverEntry, AgentInfoFrameworkChannelEntry, AgentInfoFrameworkToolEntry, AgentInfoHookEntry, AgentInfoInstructions, AgentInfoInstructionsEntry, AgentInfoResult, AgentInfoSandboxEntry, AgentInfoScheduleEntry, AgentInfoSkillEntry, AgentInfoSource, AgentInfoSubagentEntry, AgentInfoToolEntry, AgentInfoTools, ClientAuth, ClientOptions, ClientRedirectPolicy, HeadersValue, HealthResult, MessageResult, SendTurnInput, SendTurnPayload, SessionState, StreamOptions, TokenValue, } from "#client/types.js";
11
11
  export type { EveAgentReducer, EveAgentReducerEvent, ClientInputRespondedEvent, ClientMessageFailedEvent, ClientMessageSubmittedEvent, } from "#client/reducer.js";
12
- export type { EveMessageData, EveDynamicToolPart, EveMessageInputRequest, EveMessage, EveMessageMetadata, EveMessagePart, EveMessageToolMetadata, } from "#client/message-reducer.js";
13
- export type { ActionResultStreamEvent, ActionsRequestedStreamEvent, AssistantStepFinishReason, CompactionCompletedStreamEvent, CompactionRequestedStreamEvent, AuthorizationCompletedStreamEvent, ConnectionAuthorizationOutcome, AuthorizationRequiredStreamEvent, HandleMessageStreamEvent, InputRequestedStreamEvent, MessageAppendedStreamEvent, MessageCompletedStreamEvent, MessageReceivedStreamEvent, ReasoningAppendedStreamEvent, ReasoningCompletedStreamEvent, ResultCompletedStreamEvent, SessionCompletedStreamEvent, SessionFailedStreamEvent, SessionStartedStreamEvent, SessionWaitingStreamEvent, StepCompletedStreamEvent, StepFailedStreamEvent, StepStartedStreamEvent, SubagentCalledStreamEvent, SubagentChildEventStreamEvent, SubagentCompletedStreamEvent, SubagentStartedStreamEvent, TurnCompletedStreamEvent, TurnFailedStreamEvent, TurnStartedStreamEvent, TurnFailureStreamEvent, } from "#protocol/message.js";
12
+ export type { EveAuthorizationChallenge, EveAuthorizationOutcome, EveAuthorizationPart, EveMessageData, EveDynamicToolPart, EveMessageInputRequest, EveMessage, EveMessageMetadata, EveMessagePart, EveMessageToolMetadata, } from "#client/message-reducer.js";
13
+ export type { ActionResultStreamEvent, ActionsRequestedStreamEvent, AssistantStepFinishReason, AuthorizationOutcome, CompactionCompletedStreamEvent, CompactionRequestedStreamEvent, AuthorizationCompletedStreamEvent, ConnectionAuthorizationOutcome, AuthorizationRequiredStreamEvent, HandleMessageStreamEvent, InputRequestedStreamEvent, MessageAppendedStreamEvent, MessageCompletedStreamEvent, MessageReceivedStreamEvent, ReasoningAppendedStreamEvent, ReasoningCompletedStreamEvent, ResultCompletedStreamEvent, SessionCompletedStreamEvent, SessionFailedStreamEvent, SessionStartedStreamEvent, SessionWaitingStreamEvent, StepCompletedStreamEvent, StepFailedStreamEvent, StepStartedStreamEvent, SubagentCalledStreamEvent, SubagentChildEventStreamEvent, SubagentCompletedStreamEvent, SubagentStartedStreamEvent, TurnCompletedStreamEvent, TurnFailedStreamEvent, TurnStartedStreamEvent, TurnFailureStreamEvent, } from "#protocol/message.js";
14
14
  export { isCurrentTurnBoundaryEvent, isTurnFailureEvent } from "#protocol/message.js";
15
15
  export type { InputOption, InputRequest, InputResponse } from "#runtime/input/types.js";
16
16
  export { inputOptionSchema, inputRequestSchema, inputResponseSchema, isInputRequest, isInputResponse, } from "#runtime/input/types.js";
@@ -1,4 +1,5 @@
1
1
  import type { InputResponse } from "#runtime/input/types.js";
2
+ import type { AuthorizationOutcome } from "#protocol/message.js";
2
3
  /**
3
4
  * UIMessage-compatible eve message projection for chat and agent UIs.
4
5
  */
@@ -52,7 +53,45 @@ export type EveMessagePart = {
52
53
  readonly type: "reasoning";
53
54
  } | {
54
55
  readonly type: "step-start";
55
- } | EveDynamicToolPart;
56
+ } | EveAuthorizationPart | EveDynamicToolPart;
57
+ /**
58
+ * User-facing authorization challenge projected from an
59
+ * `authorization.required` stream event. These fields are safe to render in a
60
+ * browser UI; the model-facing tool output never receives the URL or code.
61
+ */
62
+ export interface EveAuthorizationChallenge {
63
+ readonly displayName?: string;
64
+ readonly expiresAt?: string;
65
+ readonly instructions?: string;
66
+ readonly url?: string;
67
+ readonly userCode?: string;
68
+ }
69
+ /**
70
+ * Outcome of a completed user authorization flow.
71
+ */
72
+ export type EveAuthorizationOutcome = AuthorizationOutcome;
73
+ /**
74
+ * An authorization prompt or result. The default reducer projects
75
+ * `authorization.required` into a pending part so browser chat UIs can render a
76
+ * sign-in affordance, then updates it when `authorization.completed` arrives.
77
+ */
78
+ export type EveAuthorizationPart = {
79
+ readonly authorization?: EveAuthorizationChallenge;
80
+ readonly description: string;
81
+ readonly displayName: string;
82
+ readonly name: string;
83
+ readonly stepIndex: number;
84
+ readonly turnId: string;
85
+ readonly type: "authorization";
86
+ } & ({
87
+ readonly outcome?: never;
88
+ readonly reason?: never;
89
+ readonly state: "required";
90
+ } | {
91
+ readonly outcome: EveAuthorizationOutcome;
92
+ readonly reason?: string;
93
+ readonly state: "completed";
94
+ });
56
95
  /**
57
96
  * A tool-call part of an assistant message, following the AI SDK `dynamic-tool`
58
97
  * convention. `state` advances through the lifecycle: `"input-streaming"` and
@@ -1,14 +1,12 @@
1
1
  import type { EveAgentReducer } from "#client/reducer.js";
2
2
  import type { EveMessageData } from "#client/message-reducer-types.js";
3
- export type { EveMessageData, EveDynamicToolPart, EveMessageInputRequest, EveMessage, EveMessageMetadata, EveMessagePart, EveMessageToolMetadata, } from "#client/message-reducer-types.js";
3
+ export type { EveAuthorizationChallenge, EveAuthorizationOutcome, EveAuthorizationPart, EveMessageData, EveDynamicToolPart, EveMessageInputRequest, EveMessage, EveMessageMetadata, EveMessagePart, EveMessageToolMetadata, } from "#client/message-reducer-types.js";
4
4
  /**
5
5
  * Creates a UIMessage-compatible eve reducer for chat and agent UIs.
6
6
  *
7
7
  * The returned projection keeps eve-owned types while following the AI SDK
8
8
  * `messages[].parts[]` rendering convention used by AI Elements. It projects
9
- * text, reasoning, tool calls, tool results, tool approvals, and submitted
10
- * HITL responses. Connection authorization stream events remain available to
11
- * custom reducers through the reducer event contract until eve has a dedicated
12
- * message-part shape for authorization UI.
9
+ * text, reasoning, tool calls, tool results, tool approvals, submitted HITL
10
+ * responses, and authorization prompts.
13
11
  */
14
12
  export declare function defaultMessageReducer(): EveAgentReducer<EveMessageData>;
@@ -1 +1 @@
1
- function defaultMessageReducer(){return{initial(){return{messages:[]}},reduce(e,t){return reduceMessageData(e,t)}}}function reduceMessageData(e,t){switch(t.type){case`client.message.submitted`:return upsertMessage(e,{id:optimisticUserMessageId(t.data.submissionId),metadata:{optimistic:!0,status:`submitted`},parts:[{type:`text`,text:t.data.message}],role:`user`});case`client.message.failed`:return upsertMessage(e,{id:optimisticUserMessageId(t.data.submissionId),metadata:{optimistic:!0,status:`failed`},parts:[{type:`text`,text:t.data.message}],role:`user`});case`client.input.responded`:{let n=e;for(let e of t.data.responses)n=respondToInputRequest(n,e);return n}case`message.received`:return upsertMessage(e,{id:`${t.data.turnId}:user`,metadata:{status:`complete`,turnId:t.data.turnId},parts:[{type:`text`,text:t.data.message,state:`done`}],role:`user`});case`step.started`:return updateAssistantMessage(e,t.data.turnId,e=>ensureStepStartPart(e,t.data.stepIndex));case`reasoning.appended`:return updateAssistantMessage(e,t.data.turnId,e=>upsertPart(ensureStepStartPart(e,t.data.stepIndex),{state:`streaming`,stepIndex:t.data.stepIndex,text:t.data.reasoningSoFar,type:`reasoning`}));case`reasoning.completed`:return updateAssistantMessage(e,t.data.turnId,e=>upsertPart(ensureStepStartPart(e,t.data.stepIndex),{state:`done`,stepIndex:t.data.stepIndex,text:t.data.reasoning,type:`reasoning`}));case`actions.requested`:{let n=e;for(let e of t.data.actions){let r=normalizeActionRequest(e);n=updateAssistantMessage(n,t.data.turnId,n=>upsertPart(ensureStepStartPart(n,t.data.stepIndex),{input:`input`in e?e.input:void 0,state:`input-available`,stepIndex:t.data.stepIndex,toolCallId:e.callId,toolMetadata:createToolMetadata(r),toolName:r.toolName,type:`dynamic-tool`}))}return n}case`input.requested`:{let n=e;for(let e of t.data.requests){let r=normalizeActionRequest(e.action);n=updateAssistantMessage(n,t.data.turnId,n=>upsertPart(ensureStepStartPart(n,t.data.stepIndex),{approval:{id:e.requestId},input:e.action.input,state:`approval-requested`,stepIndex:t.data.stepIndex,toolCallId:e.action.callId,toolMetadata:createToolMetadata(r,{inputRequest:toMessageInputRequest(e)}),toolName:r.toolName,type:`dynamic-tool`}))}return n}case`action.result`:{let n=normalizeActionResult(t.data.result),r=findToolPart(e,t.data.result.callId),i=t.data.error?.code===`TOOL_EXECUTION_DENIED`,a=t.data.status===`failed`&&!i,o=r?.approval?.id??t.data.result.callId,s=mergeToolMetadata(r?.toolMetadata,createToolMetadata(n)),c={input:r?.input,stepIndex:t.data.stepIndex,toolCallId:t.data.result.callId,toolMetadata:s,toolName:r?.toolName??n.toolName,type:`dynamic-tool`},l;return l=i?{...c,approval:{approved:!1,id:o,reason:t.data.error?.message},state:`output-denied`}:a?{...c,approval:approvedApproval(r),errorText:t.data.error?.message??stringifyUnknown(t.data.result.output),state:`output-error`}:{...c,approval:approvedApproval(r),output:t.data.result.output,state:`output-available`},r===void 0?updateAssistantMessage(e,t.data.turnId,e=>upsertPart(ensureStepStartPart(e,t.data.stepIndex),l)):updateToolPart(e,t.data.result.callId,l)}case`message.appended`:return updateAssistantMessage(e,t.data.turnId,e=>upsertPart(ensureStepStartPart(e,t.data.stepIndex),{state:`streaming`,stepIndex:t.data.stepIndex,text:t.data.messageSoFar,type:`text`}));case`message.completed`:return updateAssistantMessage(e,t.data.turnId,e=>t.data.message===null?removeTextPart(e,t.data.stepIndex):upsertPart(ensureStepStartPart(e,t.data.stepIndex),{state:`done`,stepIndex:t.data.stepIndex,text:t.data.message,type:`text`}));case`result.completed`:return updateAssistantMetadata(e,t.data.turnId,{result:t.data.result});case`turn.completed`:return updateAssistantMetadata(e,t.data.turnId,{status:`complete`});case`turn.failed`:case`session.failed`:return e;default:return e}}function respondToInputRequest(e,t){let n=findToolPartByApprovalId(e,t.requestId);if(!n)return e;let r={id:t.requestId};return t.text!==void 0&&(r.reason=t.text),updateToolPart(e,n.toolCallId,{approval:r,input:n.input,state:`approval-responded`,stepIndex:n.stepIndex,toolCallId:n.toolCallId,toolMetadata:mergeToolMetadata(n.toolMetadata,{eve:{inputResponse:t,kind:n.toolMetadata?.eve?.kind??`unknown`,name:n.toolMetadata?.eve?.name??n.toolName}}),toolName:n.toolName,type:`dynamic-tool`})}function updateAssistantMessage(e,t,n){return upsertMessage(e,n(e.messages.find(e=>e.role===`assistant`&&e.metadata?.turnId===t)??createAssistantMessage(t)))}function updateAssistantMetadata(e,t,n){return updateAssistantMessage(e,t,e=>({...e,metadata:{...e.metadata,...n}}))}function createAssistantMessage(e){return{id:`${e}:assistant`,metadata:{status:`streaming`,turnId:e},parts:[],role:`assistant`}}function ensureStepStartPart(e,t){let n=e.parts.filter(e=>e.type===`step-start`).length;if(n>t)return e;let r=t-n+1;return{...e,parts:[...e.parts,...Array.from({length:r},()=>({type:`step-start`}))]}}function upsertPart(e,t){let n=e.parts.findIndex(e=>partKey(e)===partKey(t)),r=n===-1?[...e.parts,t]:[...e.parts.slice(0,n),t,...e.parts.slice(n+1)];return{...e,metadata:{...e.metadata,status:t.type===`text`&&t.state===`done`?`complete`:`streaming`},parts:r}}function removeTextPart(e,t){let n=e.parts.filter(e=>e.type!==`text`||e.stepIndex!==t);return n.length===e.parts.length?e:{...e,metadata:{...e.metadata,status:`complete`},parts:n}}function updateToolPart(e,t,n){let r=e.messages.find(e=>e.role===`assistant`&&e.parts.some(e=>e.type===`dynamic-tool`&&e.toolCallId===t));return r?upsertMessage(e,upsertPart(r,n)):e}function findToolPart(e,t){for(let n of e.messages)for(let e of n.parts)if(e.type===`dynamic-tool`&&e.toolCallId===t)return e}function findToolPartByApprovalId(e,t){for(let n of e.messages)for(let e of n.parts)if(e.type===`dynamic-tool`&&e.approval?.id===t)return e}function partKey(e){switch(e.type){case`text`:return`text:${e.stepIndex??0}`;case`reasoning`:return`reasoning:${e.stepIndex??0}`;case`step-start`:return`step-start`;case`dynamic-tool`:return`dynamic-tool:${e.toolCallId}`}}function upsertMessage(e,t){let n=e.messages.findIndex(e=>e.id===t.id);return n===-1?{messages:[...e.messages,t]}:{messages:[...e.messages.slice(0,n),t,...e.messages.slice(n+1)]}}function toMessageInputRequest(e){return{allowFreeform:e.allowFreeform,display:e.display,options:e.options,prompt:e.prompt,requestId:e.requestId}}function createToolMetadata(e,t){return{eve:{inputRequest:t?.inputRequest,kind:e.kind,name:e.name}}}function mergeToolMetadata(e,t){let n=t.eve?.kind??e?.eve?.kind??`unknown`,r=t.eve?.name??e?.eve?.name??`unknown`;return{eve:{...e?.eve,...t.eve,inputRequest:t.eve?.inputRequest??e?.eve?.inputRequest,inputResponse:t.eve?.inputResponse??e?.eve?.inputResponse,kind:n,name:r}}}function approvedApproval(e){if(e?.approval?.id)return{approved:!0,id:e.approval.id,isAutomatic:e.approval.isAutomatic,reason:e.approval.reason}}function normalizeActionRequest(e){switch(e.kind){case`load-skill`:return{kind:`load-skill`,name:`load_skill`,toolName:`eve:load-skill`};case`tool-call`:return{kind:`tool-call`,name:e.toolName,toolName:e.toolName};case`subagent-call`:return{kind:`subagent-call`,name:e.subagentName,toolName:`eve:subagent:${e.subagentName}`};case`remote-agent-call`:return{kind:`subagent-call`,name:e.remoteAgentName,toolName:`eve:subagent:${e.remoteAgentName}`}}}function normalizeActionResult(e){switch(e.kind){case`load-skill-result`:return{kind:`load-skill`,name:e.name??`load_skill`,toolName:`eve:load-skill`};case`tool-result`:return{kind:`tool-call`,name:e.toolName,toolName:e.toolName};case`subagent-result`:return{kind:`subagent-call`,name:e.subagentName,toolName:`eve:subagent:${e.subagentName}`}}}function optimisticUserMessageId(e){return`optimistic:${e}:user`}function stringifyUnknown(e){if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return`Action failed.`}}export{defaultMessageReducer};
1
+ import{createAuthorizationCompletedPart,createAuthorizationRequiredPart}from"#client/authorization-message-parts.js";function defaultMessageReducer(){return{initial(){return{messages:[]}},reduce(e,t){return reduceMessageData(e,t)}}}function reduceMessageData(e,n){switch(n.type){case`client.message.submitted`:return upsertMessage(e,{id:optimisticUserMessageId(n.data.submissionId),metadata:{optimistic:!0,status:`submitted`},parts:[{type:`text`,text:n.data.message}],role:`user`});case`client.message.failed`:return upsertMessage(e,{id:optimisticUserMessageId(n.data.submissionId),metadata:{optimistic:!0,status:`failed`},parts:[{type:`text`,text:n.data.message}],role:`user`});case`client.input.responded`:{let t=e;for(let e of n.data.responses)t=respondToInputRequest(t,e);return t}case`message.received`:return upsertMessage(e,{id:`${n.data.turnId}:user`,metadata:{status:`complete`,turnId:n.data.turnId},parts:[{type:`text`,text:n.data.message,state:`done`}],role:`user`});case`step.started`:return updateAssistantMessage(e,n.data.turnId,e=>ensureStepStartPart(e,n.data.stepIndex));case`reasoning.appended`:return updateAssistantMessage(e,n.data.turnId,e=>upsertPart(ensureStepStartPart(e,n.data.stepIndex),{state:`streaming`,stepIndex:n.data.stepIndex,text:n.data.reasoningSoFar,type:`reasoning`}));case`reasoning.completed`:return updateAssistantMessage(e,n.data.turnId,e=>upsertPart(ensureStepStartPart(e,n.data.stepIndex),{state:`done`,stepIndex:n.data.stepIndex,text:n.data.reasoning,type:`reasoning`}));case`actions.requested`:{let t=e;for(let e of n.data.actions){let r=normalizeActionRequest(e);t=updateAssistantMessage(t,n.data.turnId,t=>upsertPart(ensureStepStartPart(t,n.data.stepIndex),{input:`input`in e?e.input:void 0,state:`input-available`,stepIndex:n.data.stepIndex,toolCallId:e.callId,toolMetadata:createToolMetadata(r),toolName:r.toolName,type:`dynamic-tool`}))}return t}case`input.requested`:{let t=e;for(let e of n.data.requests){let r=normalizeActionRequest(e.action);t=updateAssistantMessage(t,n.data.turnId,t=>upsertPart(ensureStepStartPart(t,n.data.stepIndex),{approval:{id:e.requestId},input:e.action.input,state:`approval-requested`,stepIndex:n.data.stepIndex,toolCallId:e.action.callId,toolMetadata:createToolMetadata(r,{inputRequest:toMessageInputRequest(e)}),toolName:r.toolName,type:`dynamic-tool`}))}return t}case`action.result`:{let t=normalizeActionResult(n.data.result),r=findToolPart(e,n.data.result.callId),i=n.data.error?.code===`TOOL_EXECUTION_DENIED`,a=n.data.status===`failed`&&!i,o=r?.approval?.id??n.data.result.callId,s=mergeToolMetadata(r?.toolMetadata,createToolMetadata(t)),c={input:r?.input,stepIndex:n.data.stepIndex,toolCallId:n.data.result.callId,toolMetadata:s,toolName:r?.toolName??t.toolName,type:`dynamic-tool`},l;return l=i?{...c,approval:{approved:!1,id:o,reason:n.data.error?.message},state:`output-denied`}:a?{...c,approval:approvedApproval(r),errorText:n.data.error?.message??stringifyUnknown(n.data.result.output),state:`output-error`}:{...c,approval:approvedApproval(r),output:n.data.result.output,state:`output-available`},r===void 0?updateAssistantMessage(e,n.data.turnId,e=>upsertPart(ensureStepStartPart(e,n.data.stepIndex),l)):updateToolPart(e,n.data.result.callId,l)}case`authorization.required`:return updateAssistantMessage(e,n.data.turnId,e=>upsertPart(ensureStepStartPart(e,n.data.stepIndex),createAuthorizationRequiredPart(n)));case`authorization.completed`:return completeAuthorization(e,n);case`message.appended`:return updateAssistantMessage(e,n.data.turnId,e=>upsertPart(ensureStepStartPart(e,n.data.stepIndex),{state:`streaming`,stepIndex:n.data.stepIndex,text:n.data.messageSoFar,type:`text`}));case`message.completed`:return updateAssistantMessage(e,n.data.turnId,e=>n.data.message===null?removeTextPart(e,n.data.stepIndex):upsertPart(ensureStepStartPart(e,n.data.stepIndex),{state:`done`,stepIndex:n.data.stepIndex,text:n.data.message,type:`text`}));case`result.completed`:return updateAssistantMetadata(e,n.data.turnId,{result:n.data.result});case`turn.completed`:return updateAssistantMetadata(e,n.data.turnId,{status:`complete`});case`turn.failed`:case`session.failed`:return e;default:return e}}function respondToInputRequest(e,t){let n=findToolPartByApprovalId(e,t.requestId);if(!n)return e;let r={id:t.requestId};return t.text!==void 0&&(r.reason=t.text),updateToolPart(e,n.toolCallId,{approval:r,input:n.input,state:`approval-responded`,stepIndex:n.stepIndex,toolCallId:n.toolCallId,toolMetadata:mergeToolMetadata(n.toolMetadata,{eve:{inputResponse:t,kind:n.toolMetadata?.eve?.kind??`unknown`,name:n.toolMetadata?.eve?.name??n.toolName}}),toolName:n.toolName,type:`dynamic-tool`})}function updateAssistantMessage(e,t,n){return upsertMessage(e,n(e.messages.find(e=>e.role===`assistant`&&e.metadata?.turnId===t)??createAssistantMessage(t)))}function updateAssistantMetadata(e,t,n){return updateAssistantMessage(e,t,e=>({...e,metadata:{...e.metadata,...n}}))}function createAssistantMessage(e){return{id:`${e}:assistant`,metadata:{status:`streaming`,turnId:e},parts:[],role:`assistant`}}function ensureStepStartPart(e,t){let n=e.parts.filter(e=>e.type===`step-start`).length;if(n>t)return e;let r=t-n+1;return{...e,parts:[...e.parts,...Array.from({length:r},()=>({type:`step-start`}))]}}function upsertPart(e,t){let n=e.parts.findIndex(e=>partKey(e)===partKey(t)),r=n===-1?[...e.parts,t]:[...e.parts.slice(0,n),t,...e.parts.slice(n+1)];return{...e,metadata:{...e.metadata,status:t.type===`text`&&t.state===`done`?`complete`:`streaming`},parts:r}}function removeTextPart(e,t){let n=e.parts.filter(e=>e.type!==`text`||e.stepIndex!==t);return n.length===e.parts.length?e:{...e,metadata:{...e.metadata,status:`complete`},parts:n}}function updateToolPart(e,t,n){let r=e.messages.find(e=>e.role===`assistant`&&e.parts.some(e=>e.type===`dynamic-tool`&&e.toolCallId===t));return r?upsertMessage(e,upsertPart(r,n)):e}function completeAuthorization(t,n){let r=findLatestPendingAuthorizationPart(t,n.data.name),i=createAuthorizationCompletedPart(n,r);return r===void 0?updateAssistantMessage(t,n.data.turnId,e=>upsertPart(ensureStepStartPart(e,n.data.stepIndex),i)):updateAuthorizationPart(t,r,i)}function updateAuthorizationPart(e,t,n){let r=e.messages.find(e=>e.role===`assistant`&&e.parts.some(e=>e===t));return r?upsertMessage(e,upsertPart(r,n)):e}function findToolPart(e,t){for(let n of e.messages)for(let e of n.parts)if(e.type===`dynamic-tool`&&e.toolCallId===t)return e}function findLatestPendingAuthorizationPart(e,t){for(let n=e.messages.length-1;n>=0;--n){let r=e.messages[n];if(r?.role===`assistant`)for(let e=r.parts.length-1;e>=0;--e){let n=r.parts[e];if(n?.type===`authorization`&&n.state===`required`&&n.name===t)return n}}}function findToolPartByApprovalId(e,t){for(let n of e.messages)for(let e of n.parts)if(e.type===`dynamic-tool`&&e.approval?.id===t)return e}function partKey(e){switch(e.type){case`text`:return`text:${e.stepIndex??0}`;case`reasoning`:return`reasoning:${e.stepIndex??0}`;case`step-start`:return`step-start`;case`authorization`:return`authorization:${e.turnId}:${e.stepIndex}:${e.name}`;case`dynamic-tool`:return`dynamic-tool:${e.toolCallId}`}}function upsertMessage(e,t){let n=e.messages.findIndex(e=>e.id===t.id);return n===-1?{messages:[...e.messages,t]}:{messages:[...e.messages.slice(0,n),t,...e.messages.slice(n+1)]}}function toMessageInputRequest(e){return{allowFreeform:e.allowFreeform,display:e.display,options:e.options,prompt:e.prompt,requestId:e.requestId}}function createToolMetadata(e,t){return{eve:{inputRequest:t?.inputRequest,kind:e.kind,name:e.name}}}function mergeToolMetadata(e,t){let n=t.eve?.kind??e?.eve?.kind??`unknown`,r=t.eve?.name??e?.eve?.name??`unknown`;return{eve:{...e?.eve,...t.eve,inputRequest:t.eve?.inputRequest??e?.eve?.inputRequest,inputResponse:t.eve?.inputResponse??e?.eve?.inputResponse,kind:n,name:r}}}function approvedApproval(e){if(e?.approval?.id)return{approved:!0,id:e.approval.id,isAutomatic:e.approval.isAutomatic,reason:e.approval.reason}}function normalizeActionRequest(e){switch(e.kind){case`load-skill`:return{kind:`load-skill`,name:`load_skill`,toolName:`eve:load-skill`};case`tool-call`:return{kind:`tool-call`,name:e.toolName,toolName:e.toolName};case`subagent-call`:return{kind:`subagent-call`,name:e.subagentName,toolName:`eve:subagent:${e.subagentName}`};case`remote-agent-call`:return{kind:`subagent-call`,name:e.remoteAgentName,toolName:`eve:subagent:${e.remoteAgentName}`}}}function normalizeActionResult(e){switch(e.kind){case`load-skill-result`:return{kind:`load-skill`,name:e.name??`load_skill`,toolName:`eve:load-skill`};case`tool-result`:return{kind:`tool-call`,name:e.toolName,toolName:e.toolName};case`subagent-result`:return{kind:`subagent-call`,name:e.subagentName,toolName:`eve:subagent:${e.subagentName}`}}}function optimisticUserMessageId(e){return`optimistic:${e}:user`}function stringifyUnknown(e){if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return`Action failed.`}}export{defaultMessageReducer};
@@ -1 +1 @@
1
- import{createRequire}from"node:module";import{existsSync,readFileSync,realpathSync}from"node:fs";import{basename,dirname,join}from"node:path";import{EVE_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const BUNDLED_FALLBACK_PACKAGE_VERSION=`0.15.4`,WORKFLOW_MODULE_ALIASES={"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`};function resolveFallbackPackageVersion(){return BUNDLED_FALLBACK_PACKAGE_VERSION.startsWith(`__`)?`0.0.0`:BUNDLED_FALLBACK_PACKAGE_VERSION}const FALLBACK_PACKAGE_INFO={name:EVE_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let n=e;for(;;){if(existsSync(join(n,`package.json`))&&!isBuildOutputPackageRoot(n))return n;let r=dirname(n);if(r===n)throw Error(`Failed to resolve package root from "${e}".`);n=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageDependencyPath(e){return require.resolve(e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let r=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(r?.name===t)return r}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),EVE_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${EVE_PACKAGE_NAME}/package.json`),EVE_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function readWorkflowVersionFromManifest(e){let t=e;for(let e of[t.devDependencies,t.dependencies,t.peerDependencies]){let t=e?.[`@workflow/core`];if(typeof t==`string`&&t.trim().length>0)return t}}function resolveExpectedWorkflowVersion(){let e=tryResolvePackageRoot();if(e!==void 0)try{return readWorkflowVersionFromManifest(JSON.parse(readFileSync(join(e,`package.json`),`utf8`)))}catch{}try{return readWorkflowVersionFromManifest(JSON.parse(readFileSync(require.resolve(`${EVE_PACKAGE_NAME}/package.json`),`utf8`)))}catch{return}}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/api`||e===`workflow/runtime`)return resolvePackageSourceFilePath(`src/internal/workflow/runtime.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveExpectedWorkflowVersion,resolveInstalledPackageInfo,resolvePackageDependencyPath,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
1
+ import{createRequire}from"node:module";import{existsSync,readFileSync,realpathSync}from"node:fs";import{basename,dirname,join}from"node:path";import{EVE_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const BUNDLED_FALLBACK_PACKAGE_VERSION=`0.15.5`,WORKFLOW_MODULE_ALIASES={"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`};function resolveFallbackPackageVersion(){return BUNDLED_FALLBACK_PACKAGE_VERSION.startsWith(`__`)?`0.0.0`:BUNDLED_FALLBACK_PACKAGE_VERSION}const FALLBACK_PACKAGE_INFO={name:EVE_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let n=e;for(;;){if(existsSync(join(n,`package.json`))&&!isBuildOutputPackageRoot(n))return n;let r=dirname(n);if(r===n)throw Error(`Failed to resolve package root from "${e}".`);n=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageDependencyPath(e){return require.resolve(e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let r=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(r?.name===t)return r}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),EVE_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${EVE_PACKAGE_NAME}/package.json`),EVE_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function readWorkflowVersionFromManifest(e){let t=e;for(let e of[t.devDependencies,t.dependencies,t.peerDependencies]){let t=e?.[`@workflow/core`];if(typeof t==`string`&&t.trim().length>0)return t}}function resolveExpectedWorkflowVersion(){let e=tryResolvePackageRoot();if(e!==void 0)try{return readWorkflowVersionFromManifest(JSON.parse(readFileSync(join(e,`package.json`),`utf8`)))}catch{}try{return readWorkflowVersionFromManifest(JSON.parse(readFileSync(require.resolve(`${EVE_PACKAGE_NAME}/package.json`),`utf8`)))}catch{return}}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/api`||e===`workflow/runtime`)return resolvePackageSourceFilePath(`src/internal/workflow/runtime.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveExpectedWorkflowVersion,resolveInstalledPackageInfo,resolvePackageDependencyPath,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
@@ -398,8 +398,8 @@ export interface CompactionCompletedStreamEvent {
398
398
  type: "compaction.completed";
399
399
  }
400
400
  /**
401
- * Stream event emitted when a connection needs user authorization before
402
- * its tools can be used.
401
+ * Stream event emitted when a connection or tool needs user authorization
402
+ * before it can continue.
403
403
  */
404
404
  export interface AuthorizationRequiredStreamEvent {
405
405
  data: {
@@ -417,10 +417,14 @@ export interface AuthorizationRequiredStreamEvent {
417
417
  * Outcome of one completed authorization attempt, emitted on
418
418
  * {@link AuthorizationCompletedStreamEvent}.
419
419
  */
420
- export type ConnectionAuthorizationOutcome = "authorized" | "declined" | "failed" | "timed-out";
420
+ export type AuthorizationOutcome = "authorized" | "declined" | "failed" | "timed-out";
421
+ /**
422
+ * @deprecated Use {@link AuthorizationOutcome}.
423
+ */
424
+ export type ConnectionAuthorizationOutcome = AuthorizationOutcome;
421
425
  /**
422
426
  * Stream event emitted once `completeAuthorization` has resolved
423
- * (successfully or otherwise) for one pending connection. Carries a
427
+ * (successfully or otherwise) for one pending authorization. Carries a
424
428
  * stable `outcome` plus an optional human-readable `reason`.
425
429
  *
426
430
  * Emitted when the tool completes authorization on resume, before the
@@ -435,7 +439,7 @@ export interface AuthorizationCompletedStreamEvent {
435
439
  */
436
440
  authorization?: ConnectionAuthorizationChallenge;
437
441
  name: string;
438
- outcome: ConnectionAuthorizationOutcome;
442
+ outcome: AuthorizationOutcome;
439
443
  reason?: string;
440
444
  sequence: number;
441
445
  stepIndex: number;
@@ -538,12 +542,12 @@ export declare function createActionsRequestedEvent(input: {
538
542
  readonly turnId: string;
539
543
  }): ActionsRequestedStreamEvent;
540
544
  /**
541
- * Creates the `authorization.required` event for one connection
542
- * that needs user authorization before its tools can be used.
545
+ * Creates the `authorization.required` event for one authorization source
546
+ * that needs user authorization before it can continue.
543
547
  *
544
548
  * `authorization` and `webhookUrl` are present together when the runtime
545
549
  * has suspended the turn on a framework-owned webhook; both are absent
546
- * for `getToken`-only connections that authorize out of band.
550
+ * for `getToken`-only authorization sources that authorize out of band.
547
551
  */
548
552
  export declare function createAuthorizationRequiredEvent(input: {
549
553
  readonly authorization?: ConnectionAuthorizationChallenge;
@@ -556,13 +560,13 @@ export declare function createAuthorizationRequiredEvent(input: {
556
560
  }): AuthorizationRequiredStreamEvent;
557
561
  /**
558
562
  * Creates the `authorization.completed` event emitted once per
559
- * connection after `completeAuthorization` has resolved or the
563
+ * authorization source after `completeAuthorization` has resolved or the
560
564
  * authorization deadline has expired.
561
565
  */
562
566
  export declare function createAuthorizationCompletedEvent(input: {
563
567
  readonly authorization?: ConnectionAuthorizationChallenge;
564
568
  readonly name: string;
565
- readonly outcome: ConnectionAuthorizationOutcome;
569
+ readonly outcome: AuthorizationOutcome;
566
570
  readonly reason?: string;
567
571
  readonly sequence: number;
568
572
  readonly stepIndex: number;
@@ -1,3 +1,3 @@
1
1
  export { useEveAgent, type PrepareSend, type UseEveAgentHelpers, type UseEveAgentOptions, type UseEveAgentSnapshot, type UseEveAgentStatus, } from "#react/use-eve-agent.js";
2
2
  export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
3
- export { defaultMessageReducer, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
3
+ export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
@@ -1,4 +1,4 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.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.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.15.4`,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{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.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.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.15.5`,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,7 +1,7 @@
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 // Open on localhost for `eve dev` and the REPL; ignored in production.\n localDev(),\n // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n vercelOidc(),\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
3
  readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-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"];\n\nexport function AgentChat() {\n const agent = useEveAgent();\n const isBusy = agent.status === "submitted" || agent.status === "streaming";\n const isEmpty = agent.data.messages.length === 0;\n\n const handleSubmit = async (message: PromptInputMessage) => {\n const text = message.text.trim();\n if (!text || isBusy) return;\n\n await agent.send({ message: text });\n };\n\n const composer = (\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputTextarea placeholder="Send a message…" />\n <PromptInputSubmit onStop={agent.stop} status={agent.status} />\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 {agent.error ? (\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">{agent.error.message}</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) => agent.send({ inputResponses })}\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 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
- readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type { EveDynamicToolPart, EveMessage, EveMessagePart } from "eve/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";\n\nexport type AgentInputResponse = {\n readonly optionId?: string;\n readonly requestId: string;\n readonly text?: string;\n};\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 "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 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 "dynamic-tool":\n return part.toolCallId;\n default:\n return `${part.type}:${index}`;\n }\n}\n';
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 { CheckCircleIcon, ExternalLinkIcon, KeyRoundIcon, XCircleIcon } 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\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 "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 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 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';
7
7
  readonly "app/page.tsx": 'import { AgentChat } from "@/app/_components/agent-chat";\n\nexport default function Page() {\n return <AgentChat />;\n}\n';
@@ -142,7 +142,13 @@ function StatusDot({ status }: { readonly status: AgentStatus }) {
142
142
  }
143
143
  `,"app/_components/agent-message.tsx":`"use client";
144
144
 
145
- import type { EveDynamicToolPart, EveMessage, EveMessagePart } from "eve/react";
145
+ import type {
146
+ EveAuthorizationPart,
147
+ EveDynamicToolPart,
148
+ EveMessage,
149
+ EveMessagePart,
150
+ } from "eve/react";
151
+ import { CheckCircleIcon, ExternalLinkIcon, KeyRoundIcon, XCircleIcon } from "lucide-react";
146
152
  import { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";
147
153
  import { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";
148
154
  import {
@@ -153,6 +159,7 @@ import {
153
159
  ToolOutput,
154
160
  } from "@/components/ai-elements/tool";
155
161
  import { Button } from "@/components/ui/button";
162
+ import { cn } from "@/lib/utils";
156
163
 
157
164
  export type AgentInputResponse = {
158
165
  readonly optionId?: string;
@@ -223,6 +230,8 @@ function AgentMessagePart({
223
230
  <ReasoningContent>{part.text}</ReasoningContent>
224
231
  </Reasoning>
225
232
  );
233
+ case "authorization":
234
+ return <AuthorizationPrompt part={part} />;
226
235
  case "dynamic-tool":
227
236
  return (
228
237
  <Tool
@@ -248,6 +257,99 @@ function AgentMessagePart({
248
257
  }
249
258
  }
250
259
 
260
+ function AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {
261
+ const isAuthorized = part.state === "completed" && part.outcome === "authorized";
262
+ const isCompleted = part.state === "completed";
263
+ const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;
264
+ const instructions = part.authorization?.instructions;
265
+ const shouldShowInstructions = instructions !== undefined && instructions !== part.description;
266
+
267
+ return (
268
+ <div
269
+ className={cn(
270
+ "space-y-3 rounded-md border p-3",
271
+ isAuthorized
272
+ ? "border-emerald-500/30 bg-emerald-500/5"
273
+ : isCompleted
274
+ ? "border-destructive/30 bg-destructive/5"
275
+ : "border-blue-500/30 bg-blue-500/5",
276
+ )}
277
+ >
278
+ <div className="flex items-start gap-3">
279
+ <span
280
+ className={cn(
281
+ "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",
282
+ isAuthorized
283
+ ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
284
+ : isCompleted
285
+ ? "bg-destructive/10 text-destructive"
286
+ : "bg-blue-500/10 text-blue-700 dark:text-blue-300",
287
+ )}
288
+ >
289
+ <Icon className="size-4" />
290
+ </span>
291
+ <div className="min-w-0 flex-1 space-y-2">
292
+ <p className="font-medium text-sm">{authorizationTitle(part)}</p>
293
+ <p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>
294
+ {shouldShowInstructions ? (
295
+ <p className="text-muted-foreground text-sm">{instructions}</p>
296
+ ) : null}
297
+ {part.state === "required" && part.authorization?.userCode ? (
298
+ <div className="flex flex-wrap items-center gap-2 text-sm">
299
+ <span className="text-muted-foreground">Code</span>
300
+ <code className="rounded-md bg-background px-2 py-1 font-mono">
301
+ {part.authorization.userCode}
302
+ </code>
303
+ </div>
304
+ ) : null}
305
+ {part.state === "required" && part.authorization?.url ? (
306
+ <Button asChild size="sm">
307
+ <a href={part.authorization.url} rel="noreferrer" target="_blank">
308
+ <ExternalLinkIcon className="size-4" />
309
+ Sign in with {part.displayName}
310
+ </a>
311
+ </Button>
312
+ ) : null}
313
+ </div>
314
+ </div>
315
+ </div>
316
+ );
317
+ }
318
+
319
+ function authorizationTitle(part: EveAuthorizationPart): string {
320
+ if (part.state === "required") {
321
+ return \`Connect \${part.displayName}\`;
322
+ }
323
+ if (part.outcome === "authorized") {
324
+ return \`\${part.displayName} connected\`;
325
+ }
326
+ return \`\${part.displayName} authorization \${formatAuthorizationOutcome(part.outcome)}\`;
327
+ }
328
+
329
+ function authorizationDescription(part: EveAuthorizationPart): string {
330
+ if (part.state === "required") {
331
+ return part.description;
332
+ }
333
+ if (part.outcome === "authorized") {
334
+ return \`\${part.displayName} connected.\`;
335
+ }
336
+ const tail = part.reason !== undefined ? \` (\${part.reason})\` : "";
337
+ return \`\${part.displayName} authorization \${formatAuthorizationOutcome(part.outcome)}\${tail}.\`;
338
+ }
339
+
340
+ function formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {
341
+ switch (outcome) {
342
+ case "authorized":
343
+ return "authorized";
344
+ case "declined":
345
+ return "declined";
346
+ case "failed":
347
+ return "failed";
348
+ case "timed-out":
349
+ return "timed out";
350
+ }
351
+ }
352
+
251
353
  function InputRequestActions({
252
354
  canRespond,
253
355
  onInputResponses,
@@ -303,6 +405,8 @@ function InputRequestActions({
303
405
 
304
406
  function partKey(part: EveMessagePart, index: number): string {
305
407
  switch (part.type) {
408
+ case "authorization":
409
+ return \`authorization:\${part.turnId}:\${part.stepIndex}:\${part.name}\`;
306
410
  case "dynamic-tool":
307
411
  return part.toolCallId;
308
412
  default:
@@ -1,3 +1,3 @@
1
1
  export { useEveAgent, type PrepareSend, type UseEveAgentOptions, type UseEveAgentReturn, type UseEveAgentSnapshot, type UseEveAgentStatus, } from "#svelte/use-eve-agent.js";
2
2
  export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
3
- export { defaultMessageReducer, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
3
+ export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-8X2UMr8q.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BEOUv37s.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-8X2UMr8q.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-BEOUv37s.js";
2
2
 
3
3
  export { useEveAgent };
@@ -1,3 +1,3 @@
1
1
  export { useEveAgent, type PrepareSend, type UseEveAgentOptions, type UseEveAgentReturn, type UseEveAgentSnapshot, type UseEveAgentStatus, } from "#vue/use-eve-agent.js";
2
2
  export { type EveAgentReducer, type EveAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
3
- export { defaultMessageReducer, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
3
+ export { defaultMessageReducer, type EveAuthorizationChallenge, type EveAuthorizationOutcome, type EveAuthorizationPart, type EveMessageData, type EveDynamicToolPart, type EveMessageInputRequest, type EveMessage, type EveMessageMetadata, type EveMessagePart, type EveMessageToolMetadata, } from "#client/message-reducer.js";
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-9ZNiSFMb.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-C25KOe9i.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -1,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-9ZNiSFMb.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-C25KOe9i.js";
2
2
 
3
3
  export { useEveAgent };
@@ -72,9 +72,9 @@ export function Chat() {
72
72
  | `stop` | Abort the active request. |
73
73
  | `reset` | Clear local events, data, errors, and the local session cursor. |
74
74
 
75
- Most chat UIs only need `data.messages` and `status`. Drop down to `events` to render lower-level activity such as tool calls and reasoning deltas that the default reducer doesn't surface.
75
+ Most chat UIs only need `data.messages` and `status`. Drop down to `events` when you need the authoritative wire events directly, for example to persist an audit log or build a custom projection.
76
76
 
77
- `data.messages` are `EveMessage[]` following the AI SDK `UIMessage` convention, so they drop straight into any AI SDK UI primitive that accepts a `UIMessage[]`. Parts include user text, assistant text, reasoning, tool calls, tool results, and input requests.
77
+ `data.messages` are `EveMessage[]` following the AI SDK `UIMessage` convention, so they drop straight into any AI SDK UI primitive that accepts a `UIMessage[]`. Parts include user text, assistant text, reasoning, tool calls, tool results, input requests, and connection authorization prompts.
78
78
 
79
79
  ## Sending and streaming
80
80
 
@@ -117,6 +117,38 @@ if (request) {
117
117
 
118
118
  `request.prompt` and `request.options` give you what you need to render the approve and deny UI. The default reducer marks the part as responded immediately, then updates it again once eve streams the resumed result.
119
119
 
120
+ ## Authorization prompts
121
+
122
+ Connections and tools that need OAuth or another grant emit `authorization.required`. The default reducer projects that into an `authorization` message part with the display name, instructions, device code, and user-facing sign-in URL. Render that part as a normal chat message, then keep the session cursor; eve resumes the parked turn when the callback completes and updates the part after `authorization.completed`:
123
+
124
+ ```tsx
125
+ import type { EveMessagePart } from "eve/react";
126
+
127
+ function AuthorizationPrompt({ part }: { part: EveMessagePart }) {
128
+ if (part.type !== "authorization") return null;
129
+
130
+ if (part.state === "completed") {
131
+ return (
132
+ <p>
133
+ {part.outcome === "authorized"
134
+ ? `${part.displayName} connected.`
135
+ : `${part.displayName} authorization ${part.outcome}.`}
136
+ </p>
137
+ );
138
+ }
139
+
140
+ return (
141
+ <section>
142
+ <p>{part.description}</p>
143
+ {part.authorization?.userCode ? <code>{part.authorization.userCode}</code> : null}
144
+ {part.authorization?.url ? <a href={part.authorization.url}>Sign in</a> : null}
145
+ </section>
146
+ );
147
+ }
148
+ ```
149
+
150
+ For fully custom state machines, `authorization.required` and `authorization.completed` are still available on `events` and `onEvent`.
151
+
120
152
  ## Attach page context per turn
121
153
 
122
154
  `clientContext` adds ephemeral context for the next model call only. Strings (or an array of strings) become user-role context messages; an object is JSON-serialized into one. It rides along with a message or HITL response, so it never dispatches a turn on its own and never lands in durable session history. Pass it directly to `send()`:
@@ -222,8 +254,6 @@ For multiple chat threads, keep one saved event log and session cursor per threa
222
254
 
223
255
  If the user can refresh or navigate immediately after pressing send, create your app-level chat row and store the pending user message before calling `send()`. After the request starts, persist the session state as soon as it contains a `sessionId`, then reconnect an interrupted in-flight turn with `session.stream({ startIndex: savedEvents.length })` from the lower-level client.
224
256
 
225
- Connection authorization prompts need one extra bit of UI state. eve emits `authorization.required` when a tool needs OAuth or another connection grant. Treat that as a parked chat turn for rendering and persistence: show the authorization prompt, disable ordinary text input for that thread, and keep the session cursor so the callback or a structured decline can continue the same session.
226
-
227
257
  ## Custom hosts and headers
228
258
 
229
259
  Pass `host` when the eve server isn't same-origin, and pass `auth` or `headers` when the channel needs credentials. Function values are re-resolved before every HTTP request, reconnects included:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.15.4",
3
+ "version": "0.15.5",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [