@assistant-ui/eve 0.0.7 → 0.0.9

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,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useMemo, useRef, useState } from "react";
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
4
  import {
5
5
  fromThreadMessageLike,
6
6
  generateId,
@@ -24,6 +24,7 @@ import {
24
24
  type EveMessageData,
25
25
  type UseEveAgentOptions,
26
26
  } from "eve/react";
27
+ import type { SendTurnPayload } from "eve/client";
27
28
  import {
28
29
  convertEveMessages,
29
30
  getEveMessageContent,
@@ -35,15 +36,27 @@ const USER_STAGED_STATUS = {
35
36
  reason: "unknown",
36
37
  } as const;
37
38
 
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
- };
39
+ const hasRunConfig = (
40
+ runConfig: AppendMessage["runConfig"],
41
+ ): runConfig is NonNullable<AppendMessage["runConfig"]> =>
42
+ runConfig?.custom !== undefined && Object.keys(runConfig.custom).length > 0;
43
+
44
+ /**
45
+ * Only the `custom` bag crosses the wire. Eve reads `clientContext` as its own
46
+ * namespace and serializes it into a model-visible context message, so sending
47
+ * the assistant-ui envelope would surface a literal `"custom"` key in the
48
+ * prompt and to every eve-side handler.
49
+ */
50
+ const toEveClientContext = (
51
+ runConfig: AppendMessage["runConfig"],
52
+ ): Pick<SendTurnPayload, "clientContext"> =>
53
+ hasRunConfig(runConfig)
54
+ ? {
55
+ clientContext: runConfig.custom as NonNullable<
56
+ SendTurnPayload["clientContext"]
57
+ >,
58
+ }
59
+ : {};
47
60
 
48
61
  export type UseEveAgentRuntimeOptions = Omit<
49
62
  UseEveAgentOptions<EveMessageData>,
@@ -117,6 +130,7 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
117
130
 
118
131
  return convertEveMessages(agent.data, {
119
132
  isRunning,
133
+ error: agent.error,
120
134
  getCreatedAt: (message) => {
121
135
  const existing = createdAtByMessageId.get(message.id);
122
136
  if (existing) return existing;
@@ -126,12 +140,38 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
126
140
  return createdAt;
127
141
  },
128
142
  });
129
- }, [agent.data, isRunning]);
143
+ }, [agent.data, agent.error, isRunning]);
130
144
 
131
145
  const messages = stagedMessages ?? convertedMessages;
132
146
  const messagesRef = useRef(messages);
133
147
  messagesRef.current = messages;
134
148
 
149
+ useEffect(() => {
150
+ if (stagedInputsRef.current.size === 0) return;
151
+ const baseIds = new Set(convertedMessages.map((message) => message.id));
152
+ const remaining = messagesRef.current.filter(
153
+ (message) =>
154
+ stagedInputsRef.current.has(message.id) && !baseIds.has(message.id),
155
+ );
156
+ setStagedMessages(
157
+ remaining.length > 0 ? [...convertedMessages, ...remaining] : null,
158
+ );
159
+ }, [convertedMessages]);
160
+
161
+ const getStagedRun = (parentId: string | null) => {
162
+ if (!parentId || !stagedInputsRef.current.has(parentId)) return null;
163
+ const staged: {
164
+ message: ThreadMessage;
165
+ input: { message: AppendMessage; runConfig: AppendMessage["runConfig"] };
166
+ }[] = [];
167
+ for (const message of messagesRef.current) {
168
+ const input = stagedInputsRef.current.get(message.id);
169
+ if (input) staged.push({ message, input });
170
+ if (message.id === parentId) break;
171
+ }
172
+ return staged;
173
+ };
174
+
135
175
  const stageUserMessage = (message: AppendMessage) => {
136
176
  const threadMessage = fromThreadMessageLike(
137
177
  message,
@@ -142,10 +182,9 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
142
182
  message,
143
183
  runConfig: message.runConfig,
144
184
  });
145
- setStagedMessages([
146
- ...truncateThreadMessages(messagesRef.current, message.parentId),
147
- threadMessage,
148
- ]);
185
+ const nextMessages = [...messagesRef.current, threadMessage];
186
+ messagesRef.current = nextMessages;
187
+ setStagedMessages(nextMessages);
149
188
  };
150
189
 
151
190
  return useExternalStoreRuntime({
@@ -166,19 +205,46 @@ export const useEveAgentRuntime = (options: UseEveAgentRuntimeOptions = {}) => {
166
205
  stageUserMessage(message);
167
206
  return;
168
207
  }
169
- await agent.send({ message: getEveMessageContent(message) });
208
+ await agent.send({
209
+ message: getEveMessageContent(message),
210
+ ...toEveClientContext(message.runConfig),
211
+ });
170
212
  },
171
213
  ...(stagedMessages
172
214
  ? {
173
- onReload: async (parentId: string | null) => {
174
- const staged = parentId
175
- ? stagedInputsRef.current.get(parentId)
176
- : null;
177
- if (!staged)
215
+ onReload: async (parentId: string | null, config) => {
216
+ const stagedRun = getStagedRun(parentId);
217
+ if (!stagedRun)
178
218
  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) });
219
+ for (const { message: stagedMessage, input } of stagedRun) {
220
+ const previousMessages = messagesRef.current;
221
+ stagedInputsRef.current.delete(stagedMessage.id);
222
+ const nextMessages = previousMessages.filter(
223
+ (message) => message.id !== stagedMessage.id,
224
+ );
225
+ messagesRef.current = nextMessages;
226
+ setStagedMessages(
227
+ stagedInputsRef.current.size > 0 ? nextMessages : null,
228
+ );
229
+ // The reload config belongs to the message being reloaded; the
230
+ // drafts promoted ahead of it keep the config they were staged
231
+ // with, or reloading the tail would rewrite their context too.
232
+ const runConfig =
233
+ stagedMessage.id === parentId && hasRunConfig(config.runConfig)
234
+ ? config.runConfig
235
+ : input.runConfig;
236
+ try {
237
+ await agent.send({
238
+ message: getEveMessageContent(input.message),
239
+ ...toEveClientContext(runConfig),
240
+ });
241
+ } catch (error) {
242
+ stagedInputsRef.current.set(stagedMessage.id, input);
243
+ messagesRef.current = previousMessages;
244
+ setStagedMessages(previousMessages);
245
+ throw error;
246
+ }
247
+ }
182
248
  },
183
249
  }
184
250
  : {}),
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.