@nocobase/portal-template-default 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.MD +10 -5
  2. package/package.json +17 -17
  3. package/registry/nocobase-ai/components/chat/chat-message.tsx +8 -6
  4. package/registry/nocobase-ai/components/chat/chat-messages.tsx +8 -3
  5. package/registry/nocobase-ai/components/chat/markdown-message.tsx +26 -0
  6. package/registry/nocobase-ai/components/chat/sub-agent-conversation.tsx +3 -3
  7. package/registry/nocobase-ai/demo/configuration-gate.tsx +83 -35
  8. package/registry/nocobase-i18n/README.md +23 -32
  9. package/registry/nocobase-i18n/components/language-switcher.tsx +1 -1
  10. package/registry/nocobase-i18n/demo/index.tsx +2 -2
  11. package/registry/nocobase-i18n/demo/prompt-generator.tsx +3 -2
  12. package/registry/nocobase-i18n/extension.tsx +3 -4
  13. package/registry/nocobase-i18n/index.ts +4 -3
  14. package/registry/nocobase-i18n/locales/en-US.ts +2 -52
  15. package/registry/nocobase-i18n/locales/index.ts +8 -0
  16. package/registry/nocobase-i18n/locales/zh-CN.ts +2 -52
  17. package/registry/nocobase-i18n/provider.tsx +18 -6
  18. package/registry/nocobase-i18n/server-resources.ts +13 -31
  19. package/registry/nocobase-i18n/tests/i18n-regression.mjs +14 -5
  20. package/registry/nocobase-route-surfaces/README.md +21 -0
  21. package/registry/nocobase-route-surfaces/demo/index.tsx +266 -0
  22. package/{src/extensions/nocobase-acl → registry/nocobase-route-surfaces}/demo/lazy-route.tsx +2 -1
  23. package/registry/nocobase-route-surfaces/demo/prompt-generator.tsx +90 -0
  24. package/registry/nocobase-route-surfaces/demo/scenarios.ts +95 -0
  25. package/registry/nocobase-route-surfaces/extension.tsx +119 -0
  26. package/registry/nocobase-route-surfaces/index.ts +7 -0
  27. package/registry/nocobase-route-surfaces/route-dialog.tsx +145 -0
  28. package/registry/nocobase-route-surfaces/route-drawer.tsx +186 -0
  29. package/registry/nocobase-route-surfaces/route-page.tsx +29 -0
  30. package/registry/nocobase-route-surfaces/route-surface-context.ts +13 -0
  31. package/registry/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +106 -0
  32. package/registry/nocobase-route-surfaces/use-route-surface-close.ts +15 -0
  33. package/registry/nocobase-route-surfaces/use-route-surface-state.ts +65 -0
  34. package/registry.config.json +30 -3
  35. package/scripts/registry.mjs +0 -2
  36. package/src/App.tsx +65 -35
  37. package/src/app/extension.ts +1 -2
  38. package/src/app/extensions.tsx +0 -14
  39. package/src/components/app-shell/breadcrumb.tsx +67 -2
  40. package/src/components/app-shell/sidebar.tsx +2 -2
  41. package/src/components/resources/views/list-view.tsx +3 -2
  42. package/src/components/ui/sheet.tsx +22 -21
  43. package/src/lib/nocobase/client.ts +6 -1
  44. package/src/locales/en-US.ts +81 -0
  45. package/src/locales/zh-CN.ts +77 -0
  46. package/src/pages/users/create.tsx +76 -53
  47. package/src/pages/users/edit.tsx +87 -56
  48. package/src/pages/users/index.ts +3 -0
  49. package/src/pages/users/layout.tsx +15 -0
  50. package/src/pages/users/list.tsx +19 -21
  51. package/src/pages/users/role-badges.tsx +43 -0
  52. package/src/pages/users/role-detail.tsx +272 -0
  53. package/src/pages/users/role-utils.ts +6 -0
  54. package/src/pages/users/routes.ts +24 -0
  55. package/src/pages/users/show.tsx +195 -104
  56. package/src/pages/users/types.ts +13 -0
  57. package/src/providers/i18n/index.ts +3 -0
  58. package/{registry/nocobase-i18n → src/providers/i18n}/runtime.ts +63 -16
  59. package/src/providers/system-settings/context.ts +26 -0
  60. package/src/providers/system-settings/index.ts +2 -0
  61. package/src/providers/system-settings/provider.tsx +94 -0
  62. package/src/extensions/nocobase-acl/README.md +0 -19
  63. package/src/extensions/nocobase-acl/components/acl-boundary.tsx +0 -106
  64. package/src/extensions/nocobase-acl/components/index.ts +0 -3
  65. package/src/extensions/nocobase-acl/components/role-options.ts +0 -36
  66. package/src/extensions/nocobase-acl/components/role-switcher.tsx +0 -135
  67. package/src/extensions/nocobase-acl/demo/boundary-api.tsx +0 -186
  68. package/src/extensions/nocobase-acl/demo/components.tsx +0 -231
  69. package/src/extensions/nocobase-acl/demo/index.tsx +0 -505
  70. package/src/extensions/nocobase-acl/demo/prompt-generator.tsx +0 -134
  71. package/src/extensions/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -198
  72. package/src/extensions/nocobase-acl/demo/scenario-section.tsx +0 -37
  73. package/src/extensions/nocobase-acl/extension.tsx +0 -71
  74. package/src/extensions/nocobase-acl/index.ts +0 -1
  75. package/src/extensions/nocobase-ai/README.md +0 -70
  76. package/src/extensions/nocobase-ai/adapters/react-hook-form.ts +0 -20
  77. package/src/extensions/nocobase-ai/assets/nocobase-ai-chat.svg +0 -50
  78. package/src/extensions/nocobase-ai/components/ai-root-provider.tsx +0 -32
  79. package/src/extensions/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -31
  80. package/src/extensions/nocobase-ai/components/chat/chat-attachment.tsx +0 -92
  81. package/src/extensions/nocobase-ai/components/chat/chat-compact.tsx +0 -90
  82. package/src/extensions/nocobase-ai/components/chat/chat-composer.tsx +0 -429
  83. package/src/extensions/nocobase-ai/components/chat/chat-empty-state.tsx +0 -51
  84. package/src/extensions/nocobase-ai/components/chat/chat-header.tsx +0 -88
  85. package/src/extensions/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -77
  86. package/src/extensions/nocobase-ai/components/chat/chat-message.tsx +0 -252
  87. package/src/extensions/nocobase-ai/components/chat/chat-messages.tsx +0 -173
  88. package/src/extensions/nocobase-ai/components/chat/chat-window.tsx +0 -118
  89. package/src/extensions/nocobase-ai/components/chat/conversation-list.tsx +0 -343
  90. package/src/extensions/nocobase-ai/components/chat/markdown-message.tsx +0 -165
  91. package/src/extensions/nocobase-ai/components/chat/model-select-options.tsx +0 -30
  92. package/src/extensions/nocobase-ai/components/chat/reasoning-panel.tsx +0 -29
  93. package/src/extensions/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -142
  94. package/src/extensions/nocobase-ai/components/chat/tool-call-card.tsx +0 -316
  95. package/src/extensions/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -99
  96. package/src/extensions/nocobase-ai/components/chat/work-context-chip.tsx +0 -114
  97. package/src/extensions/nocobase-ai/components/index.ts +0 -59
  98. package/src/extensions/nocobase-ai/components/page-elements/ai-form.tsx +0 -44
  99. package/src/extensions/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -469
  100. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  101. package/src/extensions/nocobase-ai/components/surfaces/chat-inline.tsx +0 -18
  102. package/src/extensions/nocobase-ai/components/surfaces/chat-page.tsx +0 -18
  103. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -54
  104. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  105. package/src/extensions/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -54
  106. package/src/extensions/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -31
  107. package/src/extensions/nocobase-ai/components/tools/business-report-dialog.tsx +0 -364
  108. package/src/extensions/nocobase-ai/components/tools/business-report-renderer.tsx +0 -122
  109. package/src/extensions/nocobase-ai/components/tools/business-report-utils.tsx +0 -282
  110. package/src/extensions/nocobase-ai/components/tools/chart-renderer.tsx +0 -99
  111. package/src/extensions/nocobase-ai/components/tools/echarts-preview.tsx +0 -59
  112. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -18
  113. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -20
  114. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -34
  115. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -24
  116. package/src/extensions/nocobase-ai/components/tools/echarts-runtime.ts +0 -138
  117. package/src/extensions/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -67
  118. package/src/extensions/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -59
  119. package/src/extensions/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -55
  120. package/src/extensions/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -18
  121. package/src/extensions/nocobase-ai/components/tools/workflow-renderer.tsx +0 -108
  122. package/src/extensions/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +0 -73
  123. package/src/extensions/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -149
  124. package/src/extensions/nocobase-ai/demo/configuration-gate.tsx +0 -49
  125. package/src/extensions/nocobase-ai/demo/container-showcase.tsx +0 -210
  126. package/src/extensions/nocobase-ai/demo/floating.tsx +0 -134
  127. package/src/extensions/nocobase-ai/demo/index.tsx +0 -346
  128. package/src/extensions/nocobase-ai/demo/interaction-showcase.tsx +0 -151
  129. package/src/extensions/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -355
  130. package/src/extensions/nocobase-ai/demo/page-context.tsx +0 -884
  131. package/src/extensions/nocobase-ai/demo/page-element-showcase.tsx +0 -197
  132. package/src/extensions/nocobase-ai/demo/prompt-card.tsx +0 -41
  133. package/src/extensions/nocobase-ai/demo/prompt-generator.tsx +0 -452
  134. package/src/extensions/nocobase-ai/demo/shortcut.tsx +0 -1207
  135. package/src/extensions/nocobase-ai/demo/tool-cards.tsx +0 -530
  136. package/src/extensions/nocobase-ai/extension.tsx +0 -158
  137. package/src/extensions/nocobase-ai/global-ai-chat.tsx +0 -213
  138. package/src/extensions/nocobase-ai/index.ts +0 -3
  139. package/src/extensions/nocobase-ai/providers/ai-provider.tsx +0 -323
  140. package/src/extensions/nocobase-ai/providers/avatars.ts +0 -817
  141. package/src/extensions/nocobase-ai/providers/chat-context.tsx +0 -109
  142. package/src/extensions/nocobase-ai/providers/chat-controller.ts +0 -84
  143. package/src/extensions/nocobase-ai/providers/chat-message-utils.ts +0 -198
  144. package/src/extensions/nocobase-ai/providers/chat-provider.tsx +0 -871
  145. package/src/extensions/nocobase-ai/providers/chat-reducer.ts +0 -147
  146. package/src/extensions/nocobase-ai/providers/chat-task-utils.ts +0 -105
  147. package/src/extensions/nocobase-ai/providers/chat-transport.ts +0 -184
  148. package/src/extensions/nocobase-ai/providers/form-registry.tsx +0 -271
  149. package/src/extensions/nocobase-ai/providers/frontend-tool-registry.tsx +0 -231
  150. package/src/extensions/nocobase-ai/providers/index.ts +0 -61
  151. package/src/extensions/nocobase-ai/providers/model.ts +0 -32
  152. package/src/extensions/nocobase-ai/providers/page-context.tsx +0 -136
  153. package/src/extensions/nocobase-ai/providers/stream-coalescer.ts +0 -60
  154. package/src/extensions/nocobase-ai/providers/stream-event-utils.ts +0 -82
  155. package/src/extensions/nocobase-ai/providers/stream-parser.ts +0 -61
  156. package/src/extensions/nocobase-ai/providers/sub-agent-stream.ts +0 -316
  157. package/src/extensions/nocobase-ai/providers/types.ts +0 -183
  158. package/src/extensions/nocobase-ai/providers/ui-message-stream.ts +0 -463
  159. package/src/extensions/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -94
  160. package/src/extensions/nocobase-ai/providers/use-chat-attachments.ts +0 -206
  161. package/src/extensions/nocobase-ai/providers/use-chat-message-actions.ts +0 -554
  162. package/src/extensions/nocobase-ai/providers/use-chat-runtime.ts +0 -210
  163. package/src/extensions/nocobase-ai/providers/use-chat-work-context.ts +0 -93
  164. package/src/extensions/nocobase-ai/providers/use-conversation-catalog.ts +0 -108
  165. package/src/extensions/nocobase-ai/providers/use-conversation-history.ts +0 -178
  166. package/src/extensions/nocobase-ai/services/index.ts +0 -7
  167. package/src/extensions/nocobase-ai/services/nocobase-ai-service.ts +0 -454
  168. package/src/extensions/nocobase-ai/services/types.ts +0 -82
  169. /package/{registry/nocobase-i18n → src/providers/i18n}/i18n-provider.ts +0 -0
  170. /package/{registry/nocobase-i18n → src/providers/i18n}/locale-store.ts +0 -0
@@ -1,554 +0,0 @@
1
- import type { Chat } from "@ai-sdk/react";
2
- import {
3
- useCallback,
4
- type Dispatch,
5
- type MutableRefObject,
6
- type SetStateAction,
7
- } from "react";
8
- import type { AIChatAction, AIChatState } from "./chat-reducer";
9
- import { findChatMessage, isAIToolPart } from "./chat-message-utils";
10
- import { getAIWorkContextToolScope } from "./page-context";
11
- import type { NocoBaseChatTransport } from "./chat-transport";
12
- import type { useAI } from "./ai-provider";
13
- import type { AIConversationRuntimeContext } from "./use-chat-runtime";
14
- import { useAutomaticToolApproval } from "./use-automatic-tool-approval";
15
- import type {
16
- AIChatAttachment,
17
- AIChatMessage,
18
- AIToolCallDecision,
19
- AIToolCallInvocationContext,
20
- AIWorkContextItem,
21
- } from "./types";
22
-
23
- type AIContextValue = ReturnType<typeof useAI>;
24
-
25
- export type AIMessageEditingSnapshot = {
26
- conversationId: string;
27
- messages: AIChatMessage[];
28
- attachments: AIChatAttachment[];
29
- workContext: AIWorkContextItem[];
30
- };
31
-
32
- type RefreshConversationMessages = (
33
- conversationId: string,
34
- targetChat: Chat<AIChatMessage>,
35
- options?: { updateRead?: boolean }
36
- ) => Promise<AIChatMessage[]>;
37
-
38
- const isChatRunning = (chat: Chat<AIChatMessage>) =>
39
- chat.status === "streaming" || chat.status === "submitted";
40
-
41
- const getToolInvocationContext = ({
42
- conversationId,
43
- messages,
44
- rootIndex,
45
- runtimeContext,
46
- messageId,
47
- toolCallId,
48
- toolName,
49
- automatic,
50
- }: {
51
- conversationId: string;
52
- messages: AIChatMessage[];
53
- rootIndex: number;
54
- runtimeContext: AIConversationRuntimeContext;
55
- messageId: string;
56
- toolCallId: string;
57
- toolName: string;
58
- automatic?: boolean;
59
- }): AIToolCallInvocationContext => {
60
- const userContext = messages
61
- .slice(0, rootIndex + 1)
62
- .reverse()
63
- .find((message) => message.role === "user")?.metadata?.workContext;
64
- return {
65
- sessionId: conversationId,
66
- messageId,
67
- toolCallId,
68
- toolName,
69
- automatic,
70
- ...getAIWorkContextToolScope([
71
- ...(runtimeContext.task?.workContext ?? []),
72
- ...(userContext ?? []),
73
- ]),
74
- };
75
- };
76
-
77
- export function useChatMessageActions({
78
- ai,
79
- activeChat,
80
- stateRef,
81
- chatSurfaceOpenRef,
82
- transportsRef,
83
- getRuntimeContext,
84
- refreshConversationMessages,
85
- setHistoryError,
86
- editingSnapshotRef,
87
- setEditingMessageId,
88
- getConversationAttachments,
89
- getConversationWorkContext,
90
- setConversationAttachments,
91
- setConversationWorkContext,
92
- dispatch,
93
- requestComposerFocus,
94
- }: {
95
- ai: AIContextValue;
96
- activeChat: Chat<AIChatMessage>;
97
- stateRef: MutableRefObject<AIChatState>;
98
- chatSurfaceOpenRef: MutableRefObject<boolean>;
99
- transportsRef: MutableRefObject<Map<string, NocoBaseChatTransport>>;
100
- getRuntimeContext: (
101
- conversationId: string
102
- ) => AIConversationRuntimeContext;
103
- refreshConversationMessages: RefreshConversationMessages;
104
- setHistoryError: Dispatch<SetStateAction<Error | undefined>>;
105
- editingSnapshotRef: MutableRefObject<AIMessageEditingSnapshot | undefined>;
106
- setEditingMessageId: Dispatch<SetStateAction<string | undefined>>;
107
- getConversationAttachments: (conversationId: string) => AIChatAttachment[];
108
- getConversationWorkContext: (conversationId: string) => AIWorkContextItem[];
109
- setConversationAttachments: (
110
- conversationId: string,
111
- attachments: AIChatAttachment[]
112
- ) => void;
113
- setConversationWorkContext: (
114
- conversationId: string,
115
- context: AIWorkContextItem[]
116
- ) => void;
117
- dispatch: Dispatch<AIChatAction>;
118
- requestComposerFocus: () => void;
119
- }) {
120
- const resolveServerMessage = useCallback(
121
- async (
122
- conversationId: string,
123
- targetChat: Chat<AIChatMessage>,
124
- message: AIChatMessage
125
- ) => {
126
- let messages = targetChat.messages;
127
- const resolvedMessage = findChatMessage(messages, message.id);
128
- if (!resolvedMessage) {
129
- throw new Error(
130
- "The message is no longer available in this conversation."
131
- );
132
- }
133
- let { targetMessage, rootIndex } = resolvedMessage;
134
-
135
- if (!targetMessage.metadata?.serverMessageId) {
136
- messages = await refreshConversationMessages(
137
- conversationId,
138
- targetChat,
139
- {
140
- updateRead:
141
- stateRef.current.activeConversationId === conversationId &&
142
- chatSurfaceOpenRef.current,
143
- }
144
- );
145
- const refreshedMatch = findChatMessage(messages, message.id);
146
- const refreshedTarget = refreshedMatch?.targetMessage;
147
- if (!refreshedTarget) {
148
- throw new Error(
149
- "Unable to resolve the server message for this action."
150
- );
151
- }
152
- targetMessage = refreshedTarget;
153
- rootIndex = refreshedMatch.rootIndex;
154
- }
155
-
156
- return {
157
- conversationId,
158
- messages,
159
- targetMessage,
160
- rootIndex,
161
- serverMessageId: targetMessage.metadata?.serverMessageId,
162
- };
163
- },
164
- [
165
- chatSurfaceOpenRef,
166
- refreshConversationMessages,
167
- stateRef,
168
- ]
169
- );
170
-
171
- const retryMessage = useCallback(
172
- async (message: AIChatMessage) => {
173
- if (
174
- message.role !== "assistant" ||
175
- activeChat.status === "streaming" ||
176
- activeChat.status === "submitted"
177
- ) {
178
- return;
179
- }
180
-
181
- setHistoryError(undefined);
182
- try {
183
- const resolved = await resolveServerMessage(
184
- stateRef.current.activeConversationId,
185
- activeChat,
186
- message
187
- );
188
- if (!resolved.serverMessageId) {
189
- throw new Error("The server message id is unavailable for retry.");
190
- }
191
- const transport = transportsRef.current.get(resolved.conversationId);
192
- if (!transport) {
193
- throw new Error("The NocoBase chat transport is unavailable.");
194
- }
195
- transport.prepareResend(resolved.serverMessageId);
196
- try {
197
- await activeChat.regenerate({ messageId: resolved.targetMessage.id });
198
- } catch (error) {
199
- transport.cancelResend(resolved.serverMessageId);
200
- throw error;
201
- }
202
- } catch (error) {
203
- setHistoryError(
204
- error instanceof Error ? error : new Error("Unable to retry message")
205
- );
206
- }
207
- },
208
- [
209
- activeChat,
210
- resolveServerMessage,
211
- setHistoryError,
212
- stateRef,
213
- transportsRef,
214
- ]
215
- );
216
-
217
- const decideConversationToolCall = useCallback(
218
- async (
219
- conversationId: string,
220
- targetChat: Chat<AIChatMessage>,
221
- decision: AIToolCallDecision,
222
- options: { automatic?: boolean } = {}
223
- ) => {
224
- if (isChatRunning(targetChat)) return;
225
-
226
- setHistoryError(undefined);
227
- try {
228
- const message = findChatMessage(
229
- targetChat.messages,
230
- decision.messageId
231
- )?.targetMessage;
232
- if (!message) {
233
- throw new Error("The tool-call message is no longer available.");
234
- }
235
- const resolved = await resolveServerMessage(
236
- conversationId,
237
- targetChat,
238
- message
239
- );
240
- if (!resolved.serverMessageId) {
241
- throw new Error(
242
- "The server message id is unavailable for this tool decision."
243
- );
244
- }
245
- const toolPart = resolved.targetMessage.parts
246
- .filter(isAIToolPart)
247
- .find((part) => part.toolCallId === decision.toolCallId);
248
- const toolName =
249
- toolPart?.type === "dynamic-tool"
250
- ? toolPart.toolName
251
- : toolPart?.type.startsWith("tool-")
252
- ? toolPart.type.slice(5)
253
- : decision.toolName;
254
- const runtimeContext = getRuntimeContext(resolved.conversationId);
255
- const invocationContext = getToolInvocationContext({
256
- conversationId: resolved.conversationId,
257
- messages: resolved.messages,
258
- rootIndex: resolved.rootIndex,
259
- runtimeContext,
260
- messageId: resolved.serverMessageId,
261
- toolCallId: decision.toolCallId,
262
- toolName,
263
- automatic: options.automatic,
264
- });
265
- if (
266
- options.automatic &&
267
- !ai.canAutoApproveToolCall(
268
- toolName,
269
- toolPart?.input ?? decision.input,
270
- invocationContext
271
- )
272
- ) {
273
- if (toolPart && "callProviderMetadata" in toolPart) {
274
- const providerMetadata = toolPart.callProviderMetadata ?? {};
275
- const nocobase =
276
- providerMetadata.nocobase &&
277
- typeof providerMetadata.nocobase === "object"
278
- ? providerMetadata.nocobase
279
- : {};
280
- toolPart.callProviderMetadata = {
281
- ...providerMetadata,
282
- nocobase: {
283
- ...nocobase,
284
- autoApprove: false,
285
- requiresApproval: true,
286
- },
287
- };
288
- targetChat.messages = [...targetChat.messages];
289
- }
290
- return;
291
- }
292
- const userDecision =
293
- decision.decision === "approve"
294
- ? ({ type: "approve" } as const)
295
- : decision.decision === "reject"
296
- ? ({
297
- type: "reject",
298
- ...(typeof decision.input === "string"
299
- ? { message: decision.input }
300
- : {}),
301
- } as const)
302
- : ({
303
- type: "edit",
304
- editedAction: { name: toolName, args: decision.input },
305
- } as const);
306
- const result = await ai.updateToolCallDecision({
307
- sessionId: resolved.conversationId,
308
- messageId: resolved.serverMessageId,
309
- toolCallId: decision.toolCallId,
310
- userDecision,
311
- });
312
- if (!result.updated) {
313
- await refreshConversationMessages(
314
- resolved.conversationId,
315
- targetChat,
316
- {
317
- updateRead:
318
- stateRef.current.activeConversationId ===
319
- resolved.conversationId && chatSurfaceOpenRef.current,
320
- }
321
- );
322
- return;
323
- }
324
-
325
- const interruptingToolCalls = result.toolCalls.filter(
326
- (toolCall) =>
327
- toolCall.willInterrupt === true ||
328
- toolCall.execution === "frontend" ||
329
- toolCall.auto === false
330
- );
331
- const allWaiting =
332
- interruptingToolCalls.length > 0 &&
333
- interruptingToolCalls.every(
334
- (toolCall) =>
335
- String(toolCall.invokeStatus).toLowerCase() === "waiting"
336
- );
337
- if (!allWaiting) return;
338
-
339
- const toolCallIds = result.toolCalls.map((toolCall) => toolCall.id);
340
- const toolCallResults: Array<{ id: string; result: unknown }> = [];
341
- for (const toolCall of result.toolCalls) {
342
- const invocation = await ai.invokeToolCall(
343
- toolCall.name,
344
- toolCall.args,
345
- {
346
- ...invocationContext,
347
- toolCallId: toolCall.id,
348
- toolName: toolCall.name,
349
- }
350
- );
351
- if (invocation.handled) {
352
- toolCallResults.push({
353
- id: toolCall.id,
354
- result: invocation.result,
355
- });
356
- }
357
- }
358
-
359
- const transport = transportsRef.current.get(resolved.conversationId);
360
- if (!transport) {
361
- throw new Error("The NocoBase chat transport is unavailable.");
362
- }
363
- if (resolved.rootIndex !== targetChat.messages.length - 1) {
364
- targetChat.messages = resolved.messages.slice(
365
- 0,
366
- resolved.rootIndex + 1
367
- );
368
- }
369
- const responseMessageId = `assistant-${crypto.randomUUID()}`;
370
- targetChat.messages = [
371
- ...targetChat.messages,
372
- {
373
- id: responseMessageId,
374
- role: "assistant",
375
- metadata: {
376
- createdAt: new Date().toISOString(),
377
- employeeUsername: runtimeContext.employeeUsername,
378
- },
379
- parts: [],
380
- },
381
- ];
382
- transport.prepareToolResume(
383
- resolved.serverMessageId,
384
- responseMessageId,
385
- toolCallIds,
386
- toolCallResults
387
- );
388
- try {
389
- await targetChat.resumeStream();
390
- } catch (error) {
391
- transport.cancelToolResume(resolved.serverMessageId);
392
- targetChat.messages = targetChat.messages.filter(
393
- (item) => item.id !== responseMessageId
394
- );
395
- throw error;
396
- }
397
- } catch (error) {
398
- setHistoryError(
399
- error instanceof Error
400
- ? error
401
- : new Error("Unable to process the tool decision")
402
- );
403
- throw error;
404
- }
405
- },
406
- [
407
- ai,
408
- chatSurfaceOpenRef,
409
- getRuntimeContext,
410
- refreshConversationMessages,
411
- resolveServerMessage,
412
- setHistoryError,
413
- stateRef,
414
- transportsRef,
415
- ]
416
- );
417
-
418
- const decideToolCall = useCallback(
419
- (decision: AIToolCallDecision) =>
420
- decideConversationToolCall(
421
- stateRef.current.activeConversationId,
422
- activeChat,
423
- decision
424
- ),
425
- [activeChat, decideConversationToolCall, stateRef]
426
- );
427
-
428
- const {
429
- clearConversation: clearAutomaticToolApproval,
430
- process: processAutomaticToolApprovals,
431
- } = useAutomaticToolApproval({
432
- enabled: true,
433
- decide: decideConversationToolCall,
434
- });
435
-
436
- const startEditingMessage = useCallback(
437
- async (message: AIChatMessage) => {
438
- if (
439
- message.role !== "user" ||
440
- activeChat.status === "streaming" ||
441
- activeChat.status === "submitted"
442
- ) {
443
- return;
444
- }
445
- let messages = activeChat.messages;
446
- let targetMessage = message;
447
- let index = messages.findIndex((item) => item.id === message.id);
448
- if (index < 0) return;
449
- const conversationId = stateRef.current.activeConversationId;
450
- if (!message.metadata?.serverMessageId) {
451
- const userMessageIndex =
452
- messages.slice(0, index + 1).filter((item) => item.role === "user")
453
- .length - 1;
454
- try {
455
- messages = await refreshConversationMessages(
456
- conversationId,
457
- activeChat,
458
- { updateRead: true }
459
- );
460
- } catch (error) {
461
- setHistoryError(
462
- error instanceof Error
463
- ? error
464
- : new Error("Unable to refresh conversation messages")
465
- );
466
- return;
467
- }
468
- if (stateRef.current.activeConversationId !== conversationId) return;
469
- targetMessage = messages.filter((item) => item.role === "user")[
470
- userMessageIndex
471
- ];
472
- if (!targetMessage?.metadata?.serverMessageId) return;
473
- index = messages.findIndex((item) => item.id === targetMessage.id);
474
- if (index < 0) return;
475
- }
476
- const serverMessageId = targetMessage.metadata?.serverMessageId;
477
- editingSnapshotRef.current = {
478
- conversationId,
479
- messages: [...messages],
480
- attachments: getConversationAttachments(conversationId),
481
- workContext: getConversationWorkContext(conversationId),
482
- };
483
- setEditingMessageId(serverMessageId ?? targetMessage.id);
484
- activeChat.messages = messages.slice(0, index);
485
- setConversationAttachments(
486
- conversationId,
487
- targetMessage.metadata?.attachments ?? []
488
- );
489
- setConversationWorkContext(
490
- conversationId,
491
- targetMessage.metadata?.workContext ?? []
492
- );
493
- dispatch({
494
- type: "set-draft",
495
- conversationId,
496
- value: targetMessage.parts
497
- .filter((part) => part.type === "text")
498
- .map((part) => part.text)
499
- .join("\n"),
500
- });
501
- requestComposerFocus();
502
- },
503
- [
504
- activeChat,
505
- dispatch,
506
- editingSnapshotRef,
507
- getConversationAttachments,
508
- getConversationWorkContext,
509
- refreshConversationMessages,
510
- requestComposerFocus,
511
- setConversationAttachments,
512
- setConversationWorkContext,
513
- setEditingMessageId,
514
- setHistoryError,
515
- stateRef,
516
- ]
517
- );
518
-
519
- const cancelEditingMessage = useCallback(() => {
520
- const snapshot = editingSnapshotRef.current;
521
- if (
522
- snapshot &&
523
- snapshot.conversationId === stateRef.current.activeConversationId
524
- ) {
525
- activeChat.messages = snapshot.messages;
526
- setConversationAttachments(snapshot.conversationId, snapshot.attachments);
527
- setConversationWorkContext(snapshot.conversationId, snapshot.workContext);
528
- dispatch({
529
- type: "set-draft",
530
- conversationId: snapshot.conversationId,
531
- value: "",
532
- });
533
- }
534
- setEditingMessageId(undefined);
535
- editingSnapshotRef.current = undefined;
536
- }, [
537
- activeChat,
538
- dispatch,
539
- editingSnapshotRef,
540
- setConversationAttachments,
541
- setConversationWorkContext,
542
- setEditingMessageId,
543
- stateRef,
544
- ]);
545
-
546
- return {
547
- retryMessage,
548
- decideToolCall,
549
- startEditingMessage,
550
- cancelEditingMessage,
551
- clearAutomaticToolApproval,
552
- processAutomaticToolApprovals,
553
- };
554
- }