@assistant-ui/eve 0.0.0 → 0.0.2

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.
@@ -1,5 +1,6 @@
1
1
  import { AppendMessage, RespondToToolApprovalOptions, ThreadMessage } from "@assistant-ui/core";
2
- import { EveMessage, EveMessageData, InputResponse, SendTurnPayload } from "eve/react";
2
+ import { EveMessage, EveMessageData } from "eve/react";
3
+ import { InputResponse, SendTurnPayload } from "eve/client";
3
4
 
4
5
  //#region src/convertEveMessages.d.ts
5
6
  type ConvertEveMessagesOptions = {
@@ -1 +1 @@
1
- {"version":3,"file":"convertEveMessages.d.ts","names":[],"sources":["../src/convertEveMessages.ts"],"mappings":";;;;KAmCY,yBAAA;;AAAZ;;;WAKW,SAAA;EAAA,SACA,YAAA,KAAiB,OAAA,EAAS,UAAA,KAAe,IAAI;AAAA;;;;cA0K3C,iBAAA,GACX,OAAA,EAAS,UAAA,EACT,KAAA,UACA,QAAA,WAAmB,UAAA,IACnB,OAAA,GAAS,yBAAA,KACR,aAAA;AA/KqD;AA0KxD;;AA1KwD,cAsO3C,kBAAA,GACX,IAAA,EAAM,cAAA,EACN,OAAA,GAAS,yBAAA,KACR,aAAA;;;;;cASU,oBAAA,GACX,OAAA,EAAS,aAAA,KACR,WAAA,CAAY,eAAA;;;;cAqDF,kBAAA,GACX,QAAA,EAAU,4BAAA,KACT,aAID"}
1
+ {"version":3,"file":"convertEveMessages.d.ts","names":[],"sources":["../src/convertEveMessages.ts"],"mappings":";;;;;KAkCY,yBAAA;;AAAZ;;;WAKW,SAAA;EAAA,SACA,YAAA,KAAiB,OAAA,EAAS,UAAA,KAAe,IAAI;AAAA;;;;cA0K3C,iBAAA,GACX,OAAA,EAAS,UAAA,EACT,KAAA,UACA,QAAA,WAAmB,UAAA,IACnB,OAAA,GAAS,yBAAA,KACR,aAAA;AA/KqD;AA0KxD;;AA1KwD,cAsO3C,kBAAA,GACX,IAAA,EAAM,cAAA,EACN,OAAA,GAAS,yBAAA,KACR,aAAA;;;;;cASU,oBAAA,GACX,OAAA,EAAS,aAAA,KACR,WAAA,CAAY,eAAA;;;;cAqDF,kBAAA,GACX,QAAA,EAAU,4BAAA,KACT,aAID"}
@@ -1 +1 @@
1
- {"version":3,"file":"convertEveMessages.js","names":["_exhaustiveCheck"],"sources":["../src/convertEveMessages.ts"],"sourcesContent":["import type {\n AppendMessage,\n MessageStatus,\n RespondToToolApprovalOptions,\n TextMessagePart,\n ThreadAssistantMessagePart,\n ThreadMessage,\n ThreadUserMessagePart,\n ToolApprovalOption,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type {\n EveDynamicToolPart,\n EveMessage,\n EveMessageData,\n EveMessageInputRequest,\n EveMessagePart,\n InputResponse,\n SendTurnPayload,\n} from \"eve/react\";\n\nconst ASSISTANT_COMPLETE_STATUS = {\n type: \"complete\",\n reason: \"stop\",\n} satisfies MessageStatus;\n\nconst ASSISTANT_RUNNING_STATUS = {\n type: \"running\",\n} satisfies MessageStatus;\n\nconst USER_FALLBACK_STATUS = {\n type: \"complete\",\n reason: \"unknown\",\n} satisfies MessageStatus;\n\nexport type ConvertEveMessagesOptions = {\n /**\n * Marks the last assistant message as running while Eve is submitting or\n * streaming.\n */\n readonly isRunning?: boolean | undefined;\n readonly getCreatedAt?: ((message: EveMessage) => Date) | undefined;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst toJsonObject = (value: unknown): Record<string, unknown> => {\n if (isRecord(value)) return value;\n if (value === undefined) return {};\n return { value };\n};\n\nconst stringifyArgs = (value: unknown): string => {\n try {\n return JSON.stringify(value ?? {});\n } catch {\n return \"\";\n }\n};\n\nconst toMessageStatus = (\n message: EveMessage,\n index: number,\n messages: readonly EveMessage[],\n options: ConvertEveMessagesOptions,\n): MessageStatus => {\n if (message.role !== \"assistant\") return USER_FALLBACK_STATUS;\n\n const hasPendingApproval = message.parts.some(\n (part) =>\n part.type === \"dynamic-tool\" && part.state === \"approval-requested\",\n );\n\n if (hasPendingApproval) {\n return { type: \"requires-action\", reason: \"tool-calls\" };\n }\n\n if (message.metadata?.status === \"failed\") {\n return { type: \"incomplete\", reason: \"error\" };\n }\n\n if (\n message.metadata?.status === \"streaming\" ||\n (options.isRunning === true && index === messages.length - 1)\n ) {\n return ASSISTANT_RUNNING_STATUS;\n }\n\n return ASSISTANT_COMPLETE_STATUS;\n};\n\nconst toolApprovalOptionsFromInputRequest = (\n inputRequest: EveMessageInputRequest | undefined,\n): readonly ToolApprovalOption[] | undefined => {\n const options = inputRequest?.options;\n if (!options || options.length === 0) return undefined;\n\n return options.map((option) => ({\n id: option.id,\n kind:\n option.id === \"approve\"\n ? \"allow-once\"\n : option.id === \"deny\"\n ? \"reject-once\"\n : `_${option.id}`,\n ...(option.label && { label: option.label }),\n ...(option.description && { description: option.description }),\n }));\n};\n\nconst toApproval = (\n part: EveDynamicToolPart,\n): ToolCallMessagePart[\"approval\"] | undefined => {\n if (\n part.state !== \"approval-requested\" &&\n part.state !== \"approval-responded\" &&\n part.state !== \"output-available\" &&\n part.state !== \"output-error\" &&\n part.state !== \"output-denied\"\n ) {\n return undefined;\n }\n\n const approval = part.approval;\n if (!approval) return undefined;\n const options = toolApprovalOptionsFromInputRequest(\n part.toolMetadata?.eve?.inputRequest,\n );\n\n return {\n id: approval.id,\n ...(approval.approved !== undefined && { approved: approval.approved }),\n ...(approval.reason && { reason: approval.reason }),\n ...(approval.isAutomatic !== undefined && {\n isAutomatic: approval.isAutomatic,\n }),\n ...(options && { options }),\n };\n};\n\nconst convertDynamicToolPart = (\n part: EveDynamicToolPart,\n): ToolCallMessagePart => {\n const approval = toApproval(part);\n const toolCall: ToolCallMessagePart = {\n type: \"tool-call\",\n toolCallId: part.toolCallId,\n toolName: part.toolName,\n args: toJsonObject(part.input),\n argsText: stringifyArgs(part.input),\n ...(approval && { approval }),\n };\n\n switch (part.state) {\n case \"output-available\":\n return { ...toolCall, result: part.output };\n\n case \"output-error\":\n return {\n ...toolCall,\n result: { error: part.errorText },\n isError: true,\n };\n\n case \"output-denied\":\n return {\n ...toolCall,\n result: { error: part.approval?.reason ?? \"Tool approval denied\" },\n isError: true,\n };\n\n default:\n return toolCall;\n }\n};\n\nconst convertAssistantPart = (\n part: EveMessagePart,\n): ThreadAssistantMessagePart | null => {\n switch (part.type) {\n case \"text\":\n case \"reasoning\":\n return { type: part.type, text: part.text };\n\n case \"step-start\":\n return null;\n\n case \"dynamic-tool\":\n return convertDynamicToolPart(part);\n\n default:\n return null;\n }\n};\n\nconst convertUserPart = (\n part: EveMessagePart,\n): ThreadUserMessagePart | null => {\n switch (part.type) {\n case \"text\":\n return { type: \"text\", text: part.text };\n\n default:\n return null;\n }\n};\n\n/**\n * Converts a single Eve message into an assistant-ui thread message.\n */\nexport const convertEveMessage = (\n message: EveMessage,\n index: number,\n messages: readonly EveMessage[],\n options: ConvertEveMessagesOptions = {},\n): ThreadMessage => {\n const createdAt = options.getCreatedAt?.(message) ?? new Date();\n const metadata = {\n ...(message.metadata?.optimistic && { isOptimistic: true }),\n custom: {\n ...(message.metadata ?? {}),\n },\n };\n\n const content =\n message.role === \"assistant\"\n ? message.parts.map(convertAssistantPart).filter((part) => part !== null)\n : message.parts.map(convertUserPart).filter((part) => part !== null);\n\n const fallbackTextPart: TextMessagePart = {\n type: \"text\",\n text: \"\",\n };\n\n if (message.role === \"user\") {\n return {\n id: message.id,\n createdAt,\n role: \"user\",\n content:\n content.length > 0\n ? (content as readonly ThreadUserMessagePart[])\n : [fallbackTextPart],\n attachments: [],\n metadata,\n };\n }\n\n return {\n id: message.id,\n createdAt,\n role: \"assistant\",\n content:\n content.length > 0\n ? (content as readonly ThreadAssistantMessagePart[])\n : [],\n status: toMessageStatus(message, index, messages, options),\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n ...metadata,\n },\n };\n};\n\n/**\n * Converts the full Eve message data object into assistant-ui thread messages.\n */\nexport const convertEveMessages = (\n data: EveMessageData,\n options: ConvertEveMessagesOptions = {},\n): ThreadMessage[] =>\n data.messages.map((message, index, messages) =>\n convertEveMessage(message, index, messages, options),\n );\n\n/**\n * Converts an assistant-ui append message into the message payload accepted by\n * Eve's `send` API.\n */\nexport const getEveMessageContent = (\n message: AppendMessage,\n): NonNullable<SendTurnPayload[\"message\"]> => {\n const content = [\n ...message.content,\n ...(message.attachments?.flatMap((attachment) => attachment.content) ?? []),\n ];\n\n const parts = content.map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n\n case \"file\":\n return {\n type: \"file\" as const,\n data: part.data,\n mediaType: part.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n\n case \"image\":\n return {\n type: \"file\" as const,\n data: part.image,\n mediaType: \"image/*\",\n ...(part.filename && { filename: part.filename }),\n };\n\n default: {\n const _exhaustiveCheck:\n | \"audio\"\n | \"data\"\n | \"generative-ui\"\n | \"reasoning\"\n | \"source\"\n | \"tool-call\" = type;\n throw new Error(\n `Unsupported eve append message part type: ${_exhaustiveCheck}`,\n );\n }\n }\n });\n\n if (parts.length === 1 && parts[0]?.type === \"text\") {\n return parts[0].text;\n }\n\n return parts;\n};\n\n/**\n * Converts an assistant-ui tool approval response into an Eve input response.\n */\nexport const toEveInputResponse = (\n response: RespondToToolApprovalOptions,\n): InputResponse => ({\n requestId: response.approvalId,\n optionId: response.optionId ?? (response.approved ? \"approve\" : \"deny\"),\n ...(response.reason && { text: response.reason }),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"convertEveMessages.js","names":["_exhaustiveCheck"],"sources":["../src/convertEveMessages.ts"],"sourcesContent":["import type {\n AppendMessage,\n MessageStatus,\n RespondToToolApprovalOptions,\n TextMessagePart,\n ThreadAssistantMessagePart,\n ThreadMessage,\n ThreadUserMessagePart,\n ToolApprovalOption,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type {\n EveDynamicToolPart,\n EveMessage,\n EveMessageData,\n EveMessageInputRequest,\n EveMessagePart,\n} from \"eve/react\";\nimport type { InputResponse, SendTurnPayload } from \"eve/client\";\n\nconst ASSISTANT_COMPLETE_STATUS = {\n type: \"complete\",\n reason: \"stop\",\n} satisfies MessageStatus;\n\nconst ASSISTANT_RUNNING_STATUS = {\n type: \"running\",\n} satisfies MessageStatus;\n\nconst USER_FALLBACK_STATUS = {\n type: \"complete\",\n reason: \"unknown\",\n} satisfies MessageStatus;\n\nexport type ConvertEveMessagesOptions = {\n /**\n * Marks the last assistant message as running while Eve is submitting or\n * streaming.\n */\n readonly isRunning?: boolean | undefined;\n readonly getCreatedAt?: ((message: EveMessage) => Date) | undefined;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst toJsonObject = (value: unknown): ToolCallMessagePart[\"args\"] => {\n if (isRecord(value)) return value as ToolCallMessagePart[\"args\"];\n if (value === undefined) return {};\n return { value } as ToolCallMessagePart[\"args\"];\n};\n\nconst stringifyArgs = (value: unknown): string => {\n try {\n return JSON.stringify(value ?? {});\n } catch {\n return \"\";\n }\n};\n\nconst toMessageStatus = (\n message: EveMessage,\n index: number,\n messages: readonly EveMessage[],\n options: ConvertEveMessagesOptions,\n): MessageStatus => {\n if (message.role !== \"assistant\") return USER_FALLBACK_STATUS;\n\n const hasPendingApproval = message.parts.some(\n (part) =>\n part.type === \"dynamic-tool\" && part.state === \"approval-requested\",\n );\n\n if (hasPendingApproval) {\n return { type: \"requires-action\", reason: \"tool-calls\" };\n }\n\n if (message.metadata?.status === \"failed\") {\n return { type: \"incomplete\", reason: \"error\" };\n }\n\n if (\n message.metadata?.status === \"streaming\" ||\n (options.isRunning === true && index === messages.length - 1)\n ) {\n return ASSISTANT_RUNNING_STATUS;\n }\n\n return ASSISTANT_COMPLETE_STATUS;\n};\n\nconst toolApprovalOptionsFromInputRequest = (\n inputRequest: EveMessageInputRequest | undefined,\n): readonly ToolApprovalOption[] | undefined => {\n const options = inputRequest?.options;\n if (!options || options.length === 0) return undefined;\n\n return options.map((option) => ({\n id: option.id,\n kind:\n option.id === \"approve\"\n ? \"allow-once\"\n : option.id === \"deny\"\n ? \"reject-once\"\n : `_${option.id}`,\n ...(option.label && { label: option.label }),\n ...(option.description && { description: option.description }),\n }));\n};\n\nconst toApproval = (\n part: EveDynamicToolPart,\n): ToolCallMessagePart[\"approval\"] | undefined => {\n if (\n part.state !== \"approval-requested\" &&\n part.state !== \"approval-responded\" &&\n part.state !== \"output-available\" &&\n part.state !== \"output-error\" &&\n part.state !== \"output-denied\"\n ) {\n return undefined;\n }\n\n const approval = part.approval;\n if (!approval) return undefined;\n const options = toolApprovalOptionsFromInputRequest(\n part.toolMetadata?.eve?.inputRequest,\n );\n\n return {\n id: approval.id,\n ...(approval.approved !== undefined && { approved: approval.approved }),\n ...(approval.reason && { reason: approval.reason }),\n ...(approval.isAutomatic !== undefined && {\n isAutomatic: approval.isAutomatic,\n }),\n ...(options && { options }),\n };\n};\n\nconst convertDynamicToolPart = (\n part: EveDynamicToolPart,\n): ToolCallMessagePart => {\n const approval = toApproval(part);\n const toolCall: ToolCallMessagePart = {\n type: \"tool-call\",\n toolCallId: part.toolCallId,\n toolName: part.toolName,\n args: toJsonObject(part.input),\n argsText: stringifyArgs(part.input),\n ...(approval && { approval }),\n };\n\n switch (part.state) {\n case \"output-available\":\n return { ...toolCall, result: part.output };\n\n case \"output-error\":\n return {\n ...toolCall,\n result: { error: part.errorText },\n isError: true,\n };\n\n case \"output-denied\":\n return {\n ...toolCall,\n result: { error: part.approval?.reason ?? \"Tool approval denied\" },\n isError: true,\n };\n\n default:\n return toolCall;\n }\n};\n\nconst convertAssistantPart = (\n part: EveMessagePart,\n): ThreadAssistantMessagePart | null => {\n switch (part.type) {\n case \"text\":\n case \"reasoning\":\n return { type: part.type, text: part.text };\n\n case \"step-start\":\n return null;\n\n case \"dynamic-tool\":\n return convertDynamicToolPart(part);\n\n default:\n return null;\n }\n};\n\nconst convertUserPart = (\n part: EveMessagePart,\n): ThreadUserMessagePart | null => {\n switch (part.type) {\n case \"text\":\n return { type: \"text\", text: part.text };\n\n default:\n return null;\n }\n};\n\n/**\n * Converts a single Eve message into an assistant-ui thread message.\n */\nexport const convertEveMessage = (\n message: EveMessage,\n index: number,\n messages: readonly EveMessage[],\n options: ConvertEveMessagesOptions = {},\n): ThreadMessage => {\n const createdAt = options.getCreatedAt?.(message) ?? new Date();\n const metadata = {\n ...(message.metadata?.optimistic && { isOptimistic: true }),\n custom: {\n ...(message.metadata ?? {}),\n },\n };\n\n const content =\n message.role === \"assistant\"\n ? message.parts.map(convertAssistantPart).filter((part) => part !== null)\n : message.parts.map(convertUserPart).filter((part) => part !== null);\n\n const fallbackTextPart: TextMessagePart = {\n type: \"text\",\n text: \"\",\n };\n\n if (message.role === \"user\") {\n return {\n id: message.id,\n createdAt,\n role: \"user\",\n content:\n content.length > 0\n ? (content as readonly ThreadUserMessagePart[])\n : [fallbackTextPart],\n attachments: [],\n metadata,\n };\n }\n\n return {\n id: message.id,\n createdAt,\n role: \"assistant\",\n content:\n content.length > 0\n ? (content as readonly ThreadAssistantMessagePart[])\n : [],\n status: toMessageStatus(message, index, messages, options),\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n ...metadata,\n },\n };\n};\n\n/**\n * Converts the full Eve message data object into assistant-ui thread messages.\n */\nexport const convertEveMessages = (\n data: EveMessageData,\n options: ConvertEveMessagesOptions = {},\n): ThreadMessage[] =>\n data.messages.map((message, index, messages) =>\n convertEveMessage(message, index, messages, options),\n );\n\n/**\n * Converts an assistant-ui append message into the message payload accepted by\n * Eve's `send` API.\n */\nexport const getEveMessageContent = (\n message: AppendMessage,\n): NonNullable<SendTurnPayload[\"message\"]> => {\n const content = [\n ...message.content,\n ...(message.attachments?.flatMap((attachment) => attachment.content) ?? []),\n ];\n\n const parts = content.map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n\n case \"file\":\n return {\n type: \"file\" as const,\n data: part.data,\n mediaType: part.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n\n case \"image\":\n return {\n type: \"file\" as const,\n data: part.image,\n mediaType: \"image/*\",\n ...(part.filename && { filename: part.filename }),\n };\n\n default: {\n const _exhaustiveCheck:\n | \"audio\"\n | \"data\"\n | \"generative-ui\"\n | \"reasoning\"\n | \"source\"\n | \"tool-call\" = type;\n throw new Error(\n `Unsupported eve append message part type: ${_exhaustiveCheck}`,\n );\n }\n }\n });\n\n if (parts.length === 1 && parts[0]?.type === \"text\") {\n return parts[0].text;\n }\n\n return parts;\n};\n\n/**\n * Converts an assistant-ui tool approval response into an Eve input response.\n */\nexport const toEveInputResponse = (\n response: RespondToToolApprovalOptions,\n): InputResponse => ({\n requestId: response.approvalId,\n optionId: response.optionId ?? (response.approved ? \"approve\" : \"deny\"),\n ...(response.reason && { text: response.reason }),\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useEveAgentRuntime.d.ts","names":[],"sources":["../src/useEveAgentRuntime.ts"],"mappings":";;;;KA4BY,yBAAA,GAA4B,IAAA,CACtC,kBAAA,CAAmB,cAAA,gBAGnB,0BAAA;EAAA,SACW,QAAA;IAAA,SAEM,WAAA,GAAc,iBAAA;IAAA,SACd,MAAA,GAAS,sBAAA;IAAA,SACT,SAAA,GAAY,gBAAA;IAAA,SACZ,KAAA,GAAQ,oBAAA;IAAA,SACR,QAAA,GAAW,eAAA;EAAA;AAAA;;;;;;;;cAYjB,kBAAA,GAAsB,OAAA,GAAS,yBAA8B,kCAAA,gBAAA"}
1
+ {"version":3,"file":"useEveAgentRuntime.d.ts","names":[],"sources":["../src/useEveAgentRuntime.ts"],"mappings":";;;;KA+CY,yBAAA,GAA4B,IAAA,CACtC,kBAAA,CAAmB,cAAA,gBAGnB,0BAAA;EAAA,SACW,QAAA;IAAA,SAEM,WAAA,GAAc,iBAAA;IAAA,SACd,MAAA,GAAS,sBAAA;IAAA,SACT,SAAA,GAAY,gBAAA;IAAA,SACZ,KAAA,GAAQ,oBAAA;IAAA,SACR,QAAA,GAAW,eAAA;EAAA;AAAA;;;;;;;;cAYjB,kBAAA,GAAsB,OAAA,GAAS,yBAA8B,kCAAA,gBAAA"}
@@ -1,10 +1,20 @@
1
1
  "use client";
2
2
  import { convertEveMessages, getEveMessageContent, toEveInputResponse } from "./convertEveMessages.js";
3
3
  import { useMemo, useRef, useState } from "react";
4
- import { pickExternalStoreSharedOptions } from "@assistant-ui/core";
4
+ import { fromThreadMessageLike, generateId, pickExternalStoreSharedOptions } from "@assistant-ui/core";
5
5
  import { useExternalStoreRuntime, useRuntimeAdapters } from "@assistant-ui/core/react";
6
6
  import { useEveAgent } from "eve/react";
7
7
  //#region src/useEveAgentRuntime.ts
8
+ const USER_STAGED_STATUS = {
9
+ type: "complete",
10
+ reason: "unknown"
11
+ };
12
+ const truncateThreadMessages = (messages, parentId) => {
13
+ if (parentId === null) return [];
14
+ const parentIndex = messages.findIndex((message) => message.id === parentId);
15
+ if (parentIndex === -1) return [];
16
+ return messages.slice(0, parentIndex + 1);
17
+ };
8
18
  /**
9
19
  * Connects Eve's `useEveAgent` hook to assistant-ui's runtime contract.
10
20
  *
@@ -17,10 +27,12 @@ const useEveAgentRuntime = (options = {}) => {
17
27
  const agent = useEveAgent(agentOptions);
18
28
  const runtimeAdapters = useRuntimeAdapters();
19
29
  const [toolStatuses, setToolStatuses] = useState({});
30
+ const [stagedMessages, setStagedMessages] = useState(null);
20
31
  const createdAtByMessageIdRef = useRef(/* @__PURE__ */ new Map());
32
+ const stagedInputsRef = useRef(/* @__PURE__ */ new Map());
21
33
  const hasExecutingTools = Object.values(toolStatuses).some((status) => status?.type === "executing");
22
34
  const isRunning = agent.status === "submitted" || agent.status === "streaming" || hasExecutingTools;
23
- const messages = useMemo(() => {
35
+ const convertedMessages = useMemo(() => {
24
36
  const createdAtByMessageId = createdAtByMessageIdRef.current;
25
37
  const messageIds = new Set(agent.data.messages.map((message) => message.id));
26
38
  for (const messageId of createdAtByMessageId.keys()) if (!messageIds.has(messageId)) createdAtByMessageId.delete(messageId);
@@ -35,6 +47,17 @@ const useEveAgentRuntime = (options = {}) => {
35
47
  }
36
48
  });
37
49
  }, [agent.data, isRunning]);
50
+ const messages = stagedMessages ?? convertedMessages;
51
+ const messagesRef = useRef(messages);
52
+ messagesRef.current = messages;
53
+ const stageUserMessage = (message) => {
54
+ const threadMessage = fromThreadMessageLike(message, generateId(), USER_STAGED_STATUS);
55
+ stagedInputsRef.current.set(threadMessage.id, {
56
+ message,
57
+ runConfig: message.runConfig
58
+ });
59
+ setStagedMessages([...truncateThreadMessages(messagesRef.current, message.parentId), threadMessage]);
60
+ };
38
61
  return useExternalStoreRuntime({
39
62
  ...pickExternalStoreSharedOptions(options),
40
63
  messages,
@@ -49,8 +72,19 @@ const useEveAgentRuntime = (options = {}) => {
49
72
  feedback: adapters?.feedback
50
73
  },
51
74
  onNew: async (message) => {
75
+ if (!(message.startRun ?? message.role === "user")) {
76
+ stageUserMessage(message);
77
+ return;
78
+ }
52
79
  await agent.send({ message: getEveMessageContent(message) });
53
80
  },
81
+ ...stagedMessages ? { onReload: async (parentId) => {
82
+ const staged = parentId ? stagedInputsRef.current.get(parentId) : null;
83
+ if (!staged) throw new Error("Runtime does not support reloading messages.");
84
+ stagedInputsRef.current.delete(parentId);
85
+ setStagedMessages(null);
86
+ await agent.send({ message: getEveMessageContent(staged.message) });
87
+ } } : {},
54
88
  onCancel: () => {
55
89
  agent.stop();
56
90
  return Promise.resolve();
@@ -1 +1 @@
1
- {"version":3,"file":"useEveAgentRuntime.js","names":[],"sources":["../src/useEveAgentRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useRef, useState } from \"react\";\nimport {\n pickExternalStoreSharedOptions,\n type AttachmentAdapter,\n type DictationAdapter,\n type ExternalStoreSharedOptions,\n type FeedbackAdapter,\n type RealtimeVoiceAdapter,\n type SpeechSynthesisAdapter,\n type ToolExecutionStatus,\n} from \"@assistant-ui/core\";\nimport {\n useExternalStoreRuntime,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport {\n useEveAgent,\n type EveMessageData,\n type UseEveAgentOptions,\n} from \"eve/react\";\nimport {\n convertEveMessages,\n getEveMessageContent,\n toEveInputResponse,\n} from \"./convertEveMessages\";\n\nexport type UseEveAgentRuntimeOptions = Omit<\n UseEveAgentOptions<EveMessageData>,\n \"reducer\"\n> &\n ExternalStoreSharedOptions & {\n readonly adapters?:\n | {\n readonly attachments?: AttachmentAdapter | undefined;\n readonly speech?: SpeechSynthesisAdapter | undefined;\n readonly dictation?: DictationAdapter | undefined;\n readonly voice?: RealtimeVoiceAdapter | undefined;\n readonly feedback?: FeedbackAdapter | undefined;\n }\n | undefined;\n };\n\n/**\n * Connects Eve's `useEveAgent` hook to assistant-ui's runtime contract.\n *\n * The runtime renders Eve messages, forwards new user messages to the Eve\n * session, supports cancellation, and maps Eve input requests to assistant-ui\n * tool approval UI.\n */\nexport const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {\n const {\n adapters,\n isDisabled: _isDisabled,\n isSendDisabled: _isSendDisabled,\n suggestions: _suggestions,\n unstable_capabilities: _unstable_capabilities,\n ...agentOptions\n } = options;\n true satisfies keyof typeof agentOptions &\n keyof ExternalStoreSharedOptions extends never\n ? true\n : never;\n\n const agent = useEveAgent(agentOptions);\n const runtimeAdapters = useRuntimeAdapters();\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n const createdAtByMessageIdRef = useRef(new Map<string, Date>());\n\n const hasExecutingTools = Object.values(toolStatuses).some(\n (status) => status?.type === \"executing\",\n );\n const isRunning =\n agent.status === \"submitted\" ||\n agent.status === \"streaming\" ||\n hasExecutingTools;\n\n const messages = useMemo(() => {\n const createdAtByMessageId = createdAtByMessageIdRef.current;\n const messageIds = new Set(\n agent.data.messages.map((message) => message.id),\n );\n for (const messageId of createdAtByMessageId.keys()) {\n if (!messageIds.has(messageId)) createdAtByMessageId.delete(messageId);\n }\n\n return convertEveMessages(agent.data, {\n isRunning,\n getCreatedAt: (message) => {\n const existing = createdAtByMessageId.get(message.id);\n if (existing) return existing;\n\n const createdAt = new Date();\n createdAtByMessageId.set(message.id, createdAt);\n return createdAt;\n },\n });\n }, [agent.data, isRunning]);\n\n return useExternalStoreRuntime({\n ...pickExternalStoreSharedOptions(options),\n messages,\n isRunning,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n adapters: {\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n },\n onNew: async (message) => {\n await agent.send({ message: getEveMessageContent(message) });\n },\n onCancel: () => {\n agent.stop();\n return Promise.resolve();\n },\n onRespondToToolApproval: async (response) => {\n await agent.send({ inputResponses: [toEveInputResponse(response)] });\n },\n });\n};\n"],"mappings":";;;;;;;;;;;;;;AAmDA,MAAa,sBAAsB,UAAqC,CAAC,MAAM;CAC7E,MAAM,EACJ,UACA,YAAY,aACZ,gBAAgB,iBAChB,aAAa,cACb,uBAAuB,wBACvB,GAAG,iBACD;CAMJ,MAAM,QAAQ,YAAY,YAAY;CACtC,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,CAAC,cAAc,mBAAmB,SAEtC,CAAC,CAAC;CACJ,MAAM,0BAA0B,uBAAO,IAAI,IAAkB,CAAC;CAE9D,MAAM,oBAAoB,OAAO,OAAO,YAAY,CAAC,CAAC,MACnD,WAAW,QAAQ,SAAS,WAC/B;CACA,MAAM,YACJ,MAAM,WAAW,eACjB,MAAM,WAAW,eACjB;CAEF,MAAM,WAAW,cAAc;EAC7B,MAAM,uBAAuB,wBAAwB;EACrD,MAAM,aAAa,IAAI,IACrB,MAAM,KAAK,SAAS,KAAK,YAAY,QAAQ,EAAE,CACjD;EACA,KAAK,MAAM,aAAa,qBAAqB,KAAK,GAChD,IAAI,CAAC,WAAW,IAAI,SAAS,GAAG,qBAAqB,OAAO,SAAS;EAGvE,OAAO,mBAAmB,MAAM,MAAM;GACpC;GACA,eAAe,YAAY;IACzB,MAAM,WAAW,qBAAqB,IAAI,QAAQ,EAAE;IACpD,IAAI,UAAU,OAAO;IAErB,MAAM,4BAAY,IAAI,KAAK;IAC3B,qBAAqB,IAAI,QAAQ,IAAI,SAAS;IAC9C,OAAO;GACT;EACF,CAAC;CACH,GAAG,CAAC,MAAM,MAAM,SAAS,CAAC;CAE1B,OAAO,wBAAwB;EAC7B,GAAG,+BAA+B,OAAO;EACzC;EACA;EACA,gCAAgC;EAChC;EACA,UAAU;GACR,aAAa,UAAU,eAAe,iBAAiB;GACvD,QAAQ,UAAU;GAClB,WAAW,UAAU;GACrB,OAAO,UAAU;GACjB,UAAU,UAAU;EACtB;EACA,OAAO,OAAO,YAAY;GACxB,MAAM,MAAM,KAAK,EAAE,SAAS,qBAAqB,OAAO,EAAE,CAAC;EAC7D;EACA,gBAAgB;GACd,MAAM,KAAK;GACX,OAAO,QAAQ,QAAQ;EACzB;EACA,yBAAyB,OAAO,aAAa;GAC3C,MAAM,MAAM,KAAK,EAAE,gBAAgB,CAAC,mBAAmB,QAAQ,CAAC,EAAE,CAAC;EACrE;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"useEveAgentRuntime.js","names":[],"sources":["../src/useEveAgentRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useRef, useState } from \"react\";\nimport {\n fromThreadMessageLike,\n generateId,\n pickExternalStoreSharedOptions,\n type AppendMessage,\n type AttachmentAdapter,\n type DictationAdapter,\n type ExternalStoreSharedOptions,\n type FeedbackAdapter,\n type RealtimeVoiceAdapter,\n type SpeechSynthesisAdapter,\n type ThreadMessage,\n type ToolExecutionStatus,\n} from \"@assistant-ui/core\";\nimport {\n useExternalStoreRuntime,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport {\n useEveAgent,\n type EveMessageData,\n type UseEveAgentOptions,\n} from \"eve/react\";\nimport {\n convertEveMessages,\n getEveMessageContent,\n toEveInputResponse,\n} from \"./convertEveMessages\";\n\nconst USER_STAGED_STATUS = {\n type: \"complete\",\n reason: \"unknown\",\n} as const;\n\nconst truncateThreadMessages = (\n messages: readonly ThreadMessage[],\n parentId: string | null,\n) => {\n if (parentId === null) return [];\n const parentIndex = messages.findIndex((message) => message.id === parentId);\n if (parentIndex === -1) return [];\n return messages.slice(0, parentIndex + 1);\n};\n\nexport type UseEveAgentRuntimeOptions = Omit<\n UseEveAgentOptions<EveMessageData>,\n \"reducer\"\n> &\n ExternalStoreSharedOptions & {\n readonly adapters?:\n | {\n readonly attachments?: AttachmentAdapter | undefined;\n readonly speech?: SpeechSynthesisAdapter | undefined;\n readonly dictation?: DictationAdapter | undefined;\n readonly voice?: RealtimeVoiceAdapter | undefined;\n readonly feedback?: FeedbackAdapter | undefined;\n }\n | undefined;\n };\n\n/**\n * Connects Eve's `useEveAgent` hook to assistant-ui's runtime contract.\n *\n * The runtime renders Eve messages, forwards new user messages to the Eve\n * session, supports cancellation, and maps Eve input requests to assistant-ui\n * tool approval UI.\n */\nexport const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {\n const {\n adapters,\n isDisabled: _isDisabled,\n isSendDisabled: _isSendDisabled,\n suggestions: _suggestions,\n unstable_capabilities: _unstable_capabilities,\n ...agentOptions\n } = options;\n true satisfies keyof typeof agentOptions &\n keyof ExternalStoreSharedOptions extends never\n ? true\n : never;\n\n const agent = useEveAgent(agentOptions);\n const runtimeAdapters = useRuntimeAdapters();\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n const [stagedMessages, setStagedMessages] = useState<ThreadMessage[] | null>(\n null,\n );\n const createdAtByMessageIdRef = useRef(new Map<string, Date>());\n const stagedInputsRef = useRef(\n new Map<\n string,\n { message: AppendMessage; runConfig: AppendMessage[\"runConfig\"] }\n >(),\n );\n\n const hasExecutingTools = Object.values(toolStatuses).some(\n (status) => status?.type === \"executing\",\n );\n const isRunning =\n agent.status === \"submitted\" ||\n agent.status === \"streaming\" ||\n hasExecutingTools;\n\n const convertedMessages = useMemo(() => {\n const createdAtByMessageId = createdAtByMessageIdRef.current;\n const messageIds = new Set(\n agent.data.messages.map((message) => message.id),\n );\n for (const messageId of createdAtByMessageId.keys()) {\n if (!messageIds.has(messageId)) createdAtByMessageId.delete(messageId);\n }\n\n return convertEveMessages(agent.data, {\n isRunning,\n getCreatedAt: (message) => {\n const existing = createdAtByMessageId.get(message.id);\n if (existing) return existing;\n\n const createdAt = new Date();\n createdAtByMessageId.set(message.id, createdAt);\n return createdAt;\n },\n });\n }, [agent.data, isRunning]);\n\n const messages = stagedMessages ?? convertedMessages;\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n\n const stageUserMessage = (message: AppendMessage) => {\n const threadMessage = fromThreadMessageLike(\n message,\n generateId(),\n USER_STAGED_STATUS,\n );\n stagedInputsRef.current.set(threadMessage.id, {\n message,\n runConfig: message.runConfig,\n });\n setStagedMessages([\n ...truncateThreadMessages(messagesRef.current, message.parentId),\n threadMessage,\n ]);\n };\n\n return useExternalStoreRuntime({\n ...pickExternalStoreSharedOptions(options),\n messages,\n isRunning,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n adapters: {\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n },\n onNew: async (message) => {\n if (!(message.startRun ?? message.role === \"user\")) {\n stageUserMessage(message);\n return;\n }\n await agent.send({ message: getEveMessageContent(message) });\n },\n ...(stagedMessages\n ? {\n onReload: async (parentId: string | null) => {\n const staged = parentId\n ? stagedInputsRef.current.get(parentId)\n : null;\n if (!staged)\n throw new Error(\"Runtime does not support reloading messages.\");\n stagedInputsRef.current.delete(parentId!);\n setStagedMessages(null);\n await agent.send({ message: getEveMessageContent(staged.message) });\n },\n }\n : {}),\n onCancel: () => {\n agent.stop();\n return Promise.resolve();\n },\n onRespondToToolApproval: async (response) => {\n await agent.send({ inputResponses: [toEveInputResponse(response)] });\n },\n });\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/eve",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "Eve runtime adapter for assistant-ui",
5
5
  "keywords": [
6
6
  "eve",
@@ -28,11 +28,11 @@
28
28
  ],
29
29
  "sideEffects": false,
30
30
  "dependencies": {
31
- "@assistant-ui/core": "^0.2.18"
31
+ "@assistant-ui/core": "^0.2.19"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@types/react": "*",
35
- "eve": "^0.11.4",
35
+ "eve": "^0.12.0",
36
36
  "react": "^18 || ^19"
37
37
  },
38
38
  "peerDependenciesMeta": {
@@ -42,10 +42,10 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "^19.2.17",
45
- "eve": "^0.11.4",
45
+ "eve": "^0.12.0",
46
46
  "react": "^19.2.7",
47
- "vitest": "^4.1.8",
48
- "@assistant-ui/x-buildutils": "0.0.15"
47
+ "vitest": "^4.1.9",
48
+ "@assistant-ui/x-buildutils": "0.0.16"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public",
@@ -106,7 +106,7 @@ describe("convertEveMessages", () => {
106
106
  });
107
107
  });
108
108
 
109
- it("handles denied tool parts without approval metadata", () => {
109
+ it("handles denied tool parts without an approval reason", () => {
110
110
  const data = {
111
111
  messages: [
112
112
  {
@@ -119,6 +119,7 @@ describe("convertEveMessages", () => {
119
119
  toolCallId: "call_1",
120
120
  toolName: "send_email",
121
121
  input: { to: "dev@example.com" },
122
+ approval: { id: "req_1", approved: false },
122
123
  },
123
124
  ],
124
125
  },
@@ -164,6 +165,7 @@ describe("getEveMessageContent", () => {
164
165
  it("returns plain text for text-only messages", () => {
165
166
  const message = {
166
167
  role: "user",
168
+ createdAt: new Date(),
167
169
  parentId: null,
168
170
  sourceId: null,
169
171
  runConfig: undefined,
@@ -15,9 +15,8 @@ import type {
15
15
  EveMessageData,
16
16
  EveMessageInputRequest,
17
17
  EveMessagePart,
18
- InputResponse,
19
- SendTurnPayload,
20
18
  } from "eve/react";
19
+ import type { InputResponse, SendTurnPayload } from "eve/client";
21
20
 
22
21
  const ASSISTANT_COMPLETE_STATUS = {
23
22
  type: "complete",
@@ -45,10 +44,10 @@ export type ConvertEveMessagesOptions = {
45
44
  const isRecord = (value: unknown): value is Record<string, unknown> =>
46
45
  typeof value === "object" && value !== null && !Array.isArray(value);
47
46
 
48
- const toJsonObject = (value: unknown): Record<string, unknown> => {
49
- if (isRecord(value)) return value;
47
+ const toJsonObject = (value: unknown): ToolCallMessagePart["args"] => {
48
+ if (isRecord(value)) return value as ToolCallMessagePart["args"];
50
49
  if (value === undefined) return {};
51
- return { value };
50
+ return { value } as ToolCallMessagePart["args"];
52
51
  };
53
52
 
54
53
  const stringifyArgs = (value: unknown): string => {
@@ -2,13 +2,17 @@
2
2
 
3
3
  import { useMemo, useRef, useState } from "react";
4
4
  import {
5
+ fromThreadMessageLike,
6
+ generateId,
5
7
  pickExternalStoreSharedOptions,
8
+ type AppendMessage,
6
9
  type AttachmentAdapter,
7
10
  type DictationAdapter,
8
11
  type ExternalStoreSharedOptions,
9
12
  type FeedbackAdapter,
10
13
  type RealtimeVoiceAdapter,
11
14
  type SpeechSynthesisAdapter,
15
+ type ThreadMessage,
12
16
  type ToolExecutionStatus,
13
17
  } from "@assistant-ui/core";
14
18
  import {
@@ -26,6 +30,21 @@ import {
26
30
  toEveInputResponse,
27
31
  } from "./convertEveMessages";
28
32
 
33
+ const USER_STAGED_STATUS = {
34
+ type: "complete",
35
+ reason: "unknown",
36
+ } as const;
37
+
38
+ const truncateThreadMessages = (
39
+ messages: readonly ThreadMessage[],
40
+ parentId: string | null,
41
+ ) => {
42
+ if (parentId === null) return [];
43
+ const parentIndex = messages.findIndex((message) => message.id === parentId);
44
+ if (parentIndex === -1) return [];
45
+ return messages.slice(0, parentIndex + 1);
46
+ };
47
+
29
48
  export type UseEveAgentRuntimeOptions = Omit<
30
49
  UseEveAgentOptions<EveMessageData>,
31
50
  "reducer"
@@ -68,7 +87,16 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
68
87
  const [toolStatuses, setToolStatuses] = useState<
69
88
  Record<string, ToolExecutionStatus>
70
89
  >({});
90
+ const [stagedMessages, setStagedMessages] = useState<ThreadMessage[] | null>(
91
+ null,
92
+ );
71
93
  const createdAtByMessageIdRef = useRef(new Map<string, Date>());
94
+ const stagedInputsRef = useRef(
95
+ new Map<
96
+ string,
97
+ { message: AppendMessage; runConfig: AppendMessage["runConfig"] }
98
+ >(),
99
+ );
72
100
 
73
101
  const hasExecutingTools = Object.values(toolStatuses).some(
74
102
  (status) => status?.type === "executing",
@@ -78,7 +106,7 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
78
106
  agent.status === "streaming" ||
79
107
  hasExecutingTools;
80
108
 
81
- const messages = useMemo(() => {
109
+ const convertedMessages = useMemo(() => {
82
110
  const createdAtByMessageId = createdAtByMessageIdRef.current;
83
111
  const messageIds = new Set(
84
112
  agent.data.messages.map((message) => message.id),
@@ -100,6 +128,26 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
100
128
  });
101
129
  }, [agent.data, isRunning]);
102
130
 
131
+ const messages = stagedMessages ?? convertedMessages;
132
+ const messagesRef = useRef(messages);
133
+ messagesRef.current = messages;
134
+
135
+ const stageUserMessage = (message: AppendMessage) => {
136
+ const threadMessage = fromThreadMessageLike(
137
+ message,
138
+ generateId(),
139
+ USER_STAGED_STATUS,
140
+ );
141
+ stagedInputsRef.current.set(threadMessage.id, {
142
+ message,
143
+ runConfig: message.runConfig,
144
+ });
145
+ setStagedMessages([
146
+ ...truncateThreadMessages(messagesRef.current, message.parentId),
147
+ threadMessage,
148
+ ]);
149
+ };
150
+
103
151
  return useExternalStoreRuntime({
104
152
  ...pickExternalStoreSharedOptions(options),
105
153
  messages,
@@ -114,8 +162,26 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
114
162
  feedback: adapters?.feedback,
115
163
  },
116
164
  onNew: async (message) => {
165
+ if (!(message.startRun ?? message.role === "user")) {
166
+ stageUserMessage(message);
167
+ return;
168
+ }
117
169
  await agent.send({ message: getEveMessageContent(message) });
118
170
  },
171
+ ...(stagedMessages
172
+ ? {
173
+ onReload: async (parentId: string | null) => {
174
+ const staged = parentId
175
+ ? stagedInputsRef.current.get(parentId)
176
+ : null;
177
+ if (!staged)
178
+ throw new Error("Runtime does not support reloading messages.");
179
+ stagedInputsRef.current.delete(parentId!);
180
+ setStagedMessages(null);
181
+ await agent.send({ message: getEveMessageContent(staged.message) });
182
+ },
183
+ }
184
+ : {}),
119
185
  onCancel: () => {
120
186
  agent.stop();
121
187
  return Promise.resolve();