@assistant-ui/eve 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.1",
4
4
  "description": "Eve runtime adapter for assistant-ui",
5
5
  "keywords": [
6
6
  "eve",
@@ -27,12 +27,17 @@
27
27
  "README.md"
28
28
  ],
29
29
  "sideEffects": false,
30
+ "scripts": {
31
+ "build": "aui-build",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest"
34
+ },
30
35
  "dependencies": {
31
36
  "@assistant-ui/core": "^0.2.18"
32
37
  },
33
38
  "peerDependencies": {
34
39
  "@types/react": "*",
35
- "eve": "^0.11.4",
40
+ "eve": "^0.12.0",
36
41
  "react": "^18 || ^19"
37
42
  },
38
43
  "peerDependenciesMeta": {
@@ -41,11 +46,11 @@
41
46
  }
42
47
  },
43
48
  "devDependencies": {
49
+ "@assistant-ui/x-buildutils": "workspace:*",
44
50
  "@types/react": "^19.2.17",
45
- "eve": "^0.11.4",
51
+ "eve": "^0.12.0",
46
52
  "react": "^19.2.7",
47
- "vitest": "^4.1.8",
48
- "@assistant-ui/x-buildutils": "0.0.15"
53
+ "vitest": "^4.1.9"
49
54
  },
50
55
  "publishConfig": {
51
56
  "access": "public",
@@ -59,10 +64,5 @@
59
64
  },
60
65
  "bugs": {
61
66
  "url": "https://github.com/assistant-ui/assistant-ui/issues"
62
- },
63
- "scripts": {
64
- "build": "aui-build",
65
- "test": "vitest run",
66
- "test:watch": "vitest"
67
67
  }
68
- }
68
+ }
@@ -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();
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 AgentbaseAI Inc.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.