@lowdefy/blocks-antd-x 5.4.0 → 5.5.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.
@@ -23,7 +23,7 @@ import { getFileCardType, getFileCardIcon } from './fileCardUtils.js';
23
23
  import DrawerWrapper from './DrawerWrapper.js';
24
24
  import createLowdefyChatTransport from './LowdefyChatTransport.js';
25
25
  import MessageList from './MessageList.js';
26
- import useAgentEvents from './useAgentEvents.js';
26
+ import useAgentEvents, { collectExternalEventIds } from './useAgentEvents.js';
27
27
  import WelcomeScreen from './WelcomeScreen.js';
28
28
  function AgentChat({ blockId, components: { Icon }, methods, pageId, properties }) {
29
29
  const { agentId, urlQuery, sharedState, welcome, messageDisplay, sender, conversationId, messages: externalMessages, display, drawer: drawerConfig, suggestions } = properties;
@@ -160,6 +160,10 @@ function AgentChat({ blockId, components: { Icon }, methods, pageId, properties
160
160
  count: 0,
161
161
  lastId: null
162
162
  });
163
+ // Event-dedup ids of externally loaded messages. Restored history must not
164
+ // replay onToolCall / onToolResult / onUserMessage / onTitleGenerated side
165
+ // effects — useAgentEvents suppresses ids in this ref.
166
+ const externalIdsRef = useRef(null);
163
167
  useEffect(()=>{
164
168
  if (type.isUndefined(externalMessages)) return;
165
169
  const msgs = externalMessages ?? [];
@@ -170,6 +174,7 @@ function AgentChat({ blockId, components: { Icon }, methods, pageId, properties
170
174
  count,
171
175
  lastId
172
176
  };
177
+ externalIdsRef.current = collectExternalEventIds(msgs);
173
178
  setMessages(msgs);
174
179
  }
175
180
  }, [
@@ -184,7 +189,9 @@ function AgentChat({ blockId, components: { Icon }, methods, pageId, properties
184
189
  } : undefined);
185
190
  });
186
191
  methods.registerMethod('setMessages', (args)=>{
187
- setMessages(args?.messages ?? []);
192
+ const msgs = args?.messages ?? [];
193
+ externalIdsRef.current = collectExternalEventIds(msgs);
194
+ setMessages(msgs);
188
195
  });
189
196
  methods.registerMethod('sendMessage', (args)=>{
190
197
  if (args?.text) {
@@ -250,7 +257,8 @@ function AgentChat({ blockId, components: { Icon }, methods, pageId, properties
250
257
  status,
251
258
  methods,
252
259
  finishMetaRef,
253
- conversationId: effectiveConversationId
260
+ conversationId: effectiveConversationId,
261
+ externalIdsRef
254
262
  });
255
263
  const isEmpty = messages.length === 0;
256
264
  const isBusy = status === 'streaming' || status === 'submitted';
@@ -13,7 +13,38 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { useRef, useEffect } from 'react';
16
- function useAgentEvents({ messages, status, methods, finishMetaRef, conversationId }) {
16
+ // Collect the event-dedup ids present in a set of messages. Used to seed the
17
+ // suppression sets when messages are loaded externally (restored history):
18
+ // restored messages must not replay onToolCall / onToolResult / onUserMessage /
19
+ // onTitleGenerated side effects.
20
+ function collectExternalEventIds(msgs) {
21
+ const ids = {
22
+ toolCallIds: new Set(),
23
+ toolResultIds: new Set(),
24
+ userMessageIds: new Set(),
25
+ titleKeys: new Set()
26
+ };
27
+ for (const message of msgs ?? []){
28
+ if (message.role === 'user') {
29
+ if (message.id) ids.userMessageIds.add(message.id);
30
+ continue;
31
+ }
32
+ if (message.role !== 'assistant') continue;
33
+ for (const part of message.parts ?? []){
34
+ if ((part.type?.startsWith?.('tool-') || part.type === 'dynamic-tool') && part.toolCallId) {
35
+ ids.toolCallIds.add(part.toolCallId);
36
+ if (part.state === 'output-available' || part.state === 'output-error') {
37
+ ids.toolResultIds.add(part.toolCallId);
38
+ }
39
+ }
40
+ if (part.type === 'data-chat-title' && part.data?.title) {
41
+ ids.titleKeys.add(`${message.id}-${part.data.title}`);
42
+ }
43
+ }
44
+ }
45
+ return ids;
46
+ }
47
+ function useAgentEvents({ messages, status, methods, finishMetaRef, conversationId, externalIdsRef }) {
17
48
  const prevStatusRef = useRef(status);
18
49
  const firedToolCallIds = useRef(new Set());
19
50
  const firedToolResultIds = useRef(new Set());
@@ -61,6 +92,8 @@ function useAgentEvents({ messages, status, methods, finishMetaRef, conversation
61
92
  const lastMessage = messages[messages.length - 1];
62
93
  if (lastMessage && lastMessage.role === 'user' && !firedUserMessageIds.current.has(lastMessage.id)) {
63
94
  firedUserMessageIds.current.add(lastMessage.id);
95
+ // Externally loaded (restored) messages are history, not new input.
96
+ if (externalIdsRef?.current?.userMessageIds?.has(lastMessage.id)) return;
64
97
  // Announce the conversation once, on its first user message, with the
65
98
  // effective (possibly auto-minted) conversationId so apps can persist or
66
99
  // track with a guaranteed id.
@@ -102,29 +135,36 @@ function useAgentEvents({ messages, status, methods, finishMetaRef, conversation
102
135
  if (part.type?.startsWith?.('tool-') || part.type === 'dynamic-tool') {
103
136
  const toolCallId = part.toolCallId;
104
137
  if (!toolCallId) continue;
138
+ // The input JSON streams incrementally — fire onToolCall only once
139
+ // the input is complete, or the event carries a truncated input.
140
+ if (part.state === 'input-streaming') continue;
105
141
  const toolName = part.toolName ?? part.type?.replace('tool-', '');
106
142
  if (!firedToolCallIds.current.has(toolCallId)) {
107
143
  firedToolCallIds.current.add(toolCallId);
108
- methods.triggerEvent({
109
- name: 'onToolCall',
110
- event: {
111
- toolName,
112
- toolCallId,
113
- input: part.input
114
- }
115
- });
144
+ if (!externalIdsRef?.current?.toolCallIds?.has(toolCallId)) {
145
+ methods.triggerEvent({
146
+ name: 'onToolCall',
147
+ event: {
148
+ toolName,
149
+ toolCallId,
150
+ input: part.input
151
+ }
152
+ });
153
+ }
116
154
  }
117
155
  if ((part.state === 'output-available' || part.state === 'output-error') && !firedToolResultIds.current.has(toolCallId)) {
118
156
  firedToolResultIds.current.add(toolCallId);
119
- methods.triggerEvent({
120
- name: 'onToolResult',
121
- event: {
122
- toolName,
123
- toolCallId,
124
- output: part.output,
125
- error: part.state === 'output-error'
126
- }
127
- });
157
+ if (!externalIdsRef?.current?.toolResultIds?.has(toolCallId)) {
158
+ methods.triggerEvent({
159
+ name: 'onToolResult',
160
+ event: {
161
+ toolName,
162
+ toolCallId,
163
+ output: part.output,
164
+ error: part.state === 'output-error'
165
+ }
166
+ });
167
+ }
128
168
  }
129
169
  }
130
170
  }
@@ -142,12 +182,14 @@ function useAgentEvents({ messages, status, methods, finishMetaRef, conversation
142
182
  const titleKey = `${message.id}-${part.data.title}`;
143
183
  if (!firedTitleIds.current.has(titleKey)) {
144
184
  firedTitleIds.current.add(titleKey);
145
- methods.triggerEvent({
146
- name: 'onTitleGenerated',
147
- event: {
148
- title: part.data.title
149
- }
150
- });
185
+ if (!externalIdsRef?.current?.titleKeys?.has(titleKey)) {
186
+ methods.triggerEvent({
187
+ name: 'onTitleGenerated',
188
+ event: {
189
+ title: part.data.title
190
+ }
191
+ });
192
+ }
151
193
  }
152
194
  }
153
195
  }
@@ -170,4 +212,5 @@ function useAgentEvents({ messages, status, methods, finishMetaRef, conversation
170
212
  messages.length
171
213
  ]);
172
214
  }
215
+ export { collectExternalEventIds };
173
216
  export default useAgentEvents;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/blocks-antd-x",
3
- "version": "5.4.0",
3
+ "version": "5.5.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lowdefy Ant Design X Blocks",
6
6
  "homepage": "https://lowdefy.com",
@@ -41,8 +41,8 @@
41
41
  "@ant-design/icons": "6.1.0",
42
42
  "@ant-design/x": "2.7.0",
43
43
  "@ant-design/x-markdown": "2.7.0",
44
- "@lowdefy/block-utils": "5.4.0",
45
- "@lowdefy/helpers": "5.4.0",
44
+ "@lowdefy/block-utils": "5.5.1",
45
+ "@lowdefy/helpers": "5.5.1",
46
46
  "ai": "6.0.176"
47
47
  },
48
48
  "devDependencies": {