@solidxai/core-ui 0.1.20-beta.1 → 0.1.21-beta.0

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 (180) hide show
  1. package/dist/components/common/FileReaderExt.d.ts.map +1 -1
  2. package/dist/components/common/FileReaderExt.js +22 -11
  3. package/dist/components/common/FileReaderExt.js.map +1 -1
  4. package/dist/components/common/FileReaderExt.tsx +27 -14
  5. package/dist/components/common/SocialMediaLogin.d.ts.map +1 -1
  6. package/dist/components/common/SocialMediaLogin.js +1 -1
  7. package/dist/components/common/SocialMediaLogin.js.map +1 -1
  8. package/dist/components/common/SocialMediaLogin.tsx +50 -19
  9. package/dist/components/common/solid-export.css +75 -0
  10. package/dist/components/core/common/SolidGenericImport/SolidImport.module.css +71 -0
  11. package/dist/components/core/form/SolidFormView.d.ts.map +1 -1
  12. package/dist/components/core/form/SolidFormView.js +14 -0
  13. package/dist/components/core/form/SolidFormView.js.map +1 -1
  14. package/dist/components/core/form/SolidFormView.tsx +13 -0
  15. package/dist/components/core/form/fields/SolidMediaMultipleField.js +3 -3
  16. package/dist/components/core/form/fields/SolidMediaMultipleField.js.map +1 -1
  17. package/dist/components/core/form/fields/SolidMediaMultipleField.tsx +3 -3
  18. package/dist/components/core/form/fields/SolidMediaSingleField.js +2 -2
  19. package/dist/components/core/form/fields/SolidMediaSingleField.js.map +1 -1
  20. package/dist/components/core/form/fields/SolidMediaSingleField.tsx +2 -2
  21. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js +2 -2
  22. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js.map +1 -1
  23. package/dist/components/core/list/columns/SolidMediaMultipleColumn.tsx +4 -4
  24. package/dist/components/core/list/columns/SolidMediaSingleColumn.js +1 -1
  25. package/dist/components/core/list/columns/SolidMediaSingleColumn.js.map +1 -1
  26. package/dist/components/core/list/columns/SolidMediaSingleColumn.tsx +3 -3
  27. package/dist/components/core/solid-agent/SolidAgentChat.d.ts +3 -1
  28. package/dist/components/core/solid-agent/SolidAgentChat.d.ts.map +1 -1
  29. package/dist/components/core/solid-agent/SolidAgentChat.js +150 -22
  30. package/dist/components/core/solid-agent/SolidAgentChat.js.map +1 -1
  31. package/dist/components/core/solid-agent/SolidAgentChat.tsx +76 -15
  32. package/dist/components/core/solid-agent/SolidAgentEmbedded.d.ts +2 -0
  33. package/dist/components/core/solid-agent/SolidAgentEmbedded.d.ts.map +1 -1
  34. package/dist/components/core/solid-agent/SolidAgentEmbedded.js +4 -4
  35. package/dist/components/core/solid-agent/SolidAgentEmbedded.js.map +1 -1
  36. package/dist/components/core/solid-agent/SolidAgentEmbedded.tsx +7 -1
  37. package/dist/components/core/solid-agent/SolidAgentHost.d.ts.map +1 -1
  38. package/dist/components/core/solid-agent/SolidAgentHost.js +79 -1
  39. package/dist/components/core/solid-agent/SolidAgentHost.js.map +1 -1
  40. package/dist/components/core/solid-agent/SolidAgentHost.tsx +69 -2
  41. package/dist/components/core/solid-agent/agentEvents.d.ts.map +1 -1
  42. package/dist/components/core/solid-agent/agentEvents.js +1 -3
  43. package/dist/components/core/solid-agent/agentEvents.js.map +1 -1
  44. package/dist/components/core/solid-agent/agentEvents.ts +5 -3
  45. package/dist/components/core/solid-agent/client/agentPublicEvents.d.ts +88 -0
  46. package/dist/components/core/solid-agent/client/agentPublicEvents.d.ts.map +1 -0
  47. package/dist/components/core/solid-agent/client/agentPublicEvents.js +26 -0
  48. package/dist/components/core/solid-agent/client/agentPublicEvents.js.map +1 -0
  49. package/dist/components/core/solid-agent/client/agentPublicEvents.ts +66 -0
  50. package/dist/components/core/solid-agent/client/agentRuntime.d.ts +18 -3
  51. package/dist/components/core/solid-agent/client/agentRuntime.d.ts.map +1 -1
  52. package/dist/components/core/solid-agent/client/agentRuntime.js +173 -31
  53. package/dist/components/core/solid-agent/client/agentRuntime.js.map +1 -1
  54. package/dist/components/core/solid-agent/client/agentRuntime.ts +126 -16
  55. package/dist/components/core/solid-agent/client/agentSocket.d.ts +3 -0
  56. package/dist/components/core/solid-agent/client/agentSocket.d.ts.map +1 -1
  57. package/dist/components/core/solid-agent/client/agentSocket.js +19 -1
  58. package/dist/components/core/solid-agent/client/agentSocket.js.map +1 -1
  59. package/dist/components/core/solid-agent/client/agentSocket.ts +11 -1
  60. package/dist/components/core/solid-agent/client/beforeUserMessageSend.d.ts +9 -0
  61. package/dist/components/core/solid-agent/client/beforeUserMessageSend.d.ts.map +1 -0
  62. package/dist/components/core/solid-agent/client/beforeUserMessageSend.js +53 -0
  63. package/dist/components/core/solid-agent/client/beforeUserMessageSend.js.map +1 -0
  64. package/dist/components/core/solid-agent/client/beforeUserMessageSend.ts +26 -0
  65. package/dist/components/core/solid-agent/embed/AgentEmbeddingPanel.d.ts.map +1 -1
  66. package/dist/components/core/solid-agent/embed/AgentEmbeddingPanel.js +4 -3
  67. package/dist/components/core/solid-agent/embed/AgentEmbeddingPanel.js.map +1 -1
  68. package/dist/components/core/solid-agent/embed/AgentEmbeddingPanel.tsx +4 -2
  69. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.d.ts +29 -0
  70. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.d.ts.map +1 -1
  71. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.js +45 -2
  72. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.js.map +1 -1
  73. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.ts +49 -2
  74. package/dist/components/core/solid-agent/embed/agentEmbedSamples.d.ts +1 -0
  75. package/dist/components/core/solid-agent/embed/agentEmbedSamples.d.ts.map +1 -1
  76. package/dist/components/core/solid-agent/embed/agentEmbedSamples.js +2 -1
  77. package/dist/components/core/solid-agent/embed/agentEmbedSamples.js.map +1 -1
  78. package/dist/components/core/solid-agent/embed/agentEmbedSamples.ts +15 -1
  79. package/dist/components/core/solid-agent/types.d.ts +13 -0
  80. package/dist/components/core/solid-agent/types.d.ts.map +1 -1
  81. package/dist/components/core/solid-agent/types.js.map +1 -1
  82. package/dist/components/core/solid-agent/types.ts +8 -0
  83. package/dist/components/core/solid-agent/useAgentChat.d.ts +1 -1
  84. package/dist/components/core/solid-agent/useAgentChat.d.ts.map +1 -1
  85. package/dist/components/core/solid-agent/useAgentChat.js +7 -4
  86. package/dist/components/core/solid-agent/useAgentChat.js.map +1 -1
  87. package/dist/components/core/solid-agent/useAgentChat.ts +9 -5
  88. package/dist/components/core/solid-agent/widgets/DefaultUserMessageChatWidget.d.ts.map +1 -1
  89. package/dist/components/core/solid-agent/widgets/DefaultUserMessageChatWidget.js +12 -4
  90. package/dist/components/core/solid-agent/widgets/DefaultUserMessageChatWidget.js.map +1 -1
  91. package/dist/components/core/solid-agent/widgets/DefaultUserMessageChatWidget.tsx +18 -0
  92. package/dist/components/core/solid-agent/widgets/SolidNavigationIntentChatWidget.d.ts +4 -0
  93. package/dist/components/core/solid-agent/widgets/SolidNavigationIntentChatWidget.d.ts.map +1 -0
  94. package/dist/components/core/solid-agent/widgets/SolidNavigationIntentChatWidget.js +30 -0
  95. package/dist/components/core/solid-agent/widgets/SolidNavigationIntentChatWidget.js.map +1 -0
  96. package/dist/components/core/solid-agent/widgets/SolidNavigationIntentChatWidget.tsx +41 -0
  97. package/dist/components/core/solid-agent/widgets/solidChatWidgets.module.css +39 -0
  98. package/dist/components/shad-cn-ui/SolidCodeEditor.d.ts +2 -1
  99. package/dist/components/shad-cn-ui/SolidCodeEditor.d.ts.map +1 -1
  100. package/dist/components/shad-cn-ui/SolidCodeEditor.js +3 -2
  101. package/dist/components/shad-cn-ui/SolidCodeEditor.js.map +1 -1
  102. package/dist/components/shad-cn-ui/SolidCodeEditor.tsx +3 -0
  103. package/dist/helpers/adminViewRefresh.d.ts +13 -0
  104. package/dist/helpers/adminViewRefresh.d.ts.map +1 -0
  105. package/dist/helpers/adminViewRefresh.js +14 -0
  106. package/dist/helpers/adminViewRefresh.js.map +1 -0
  107. package/dist/helpers/adminViewRefresh.ts +26 -0
  108. package/dist/helpers/eventBus.d.ts +1 -0
  109. package/dist/helpers/eventBus.d.ts.map +1 -1
  110. package/dist/helpers/eventBus.js +1 -0
  111. package/dist/helpers/eventBus.js.map +1 -1
  112. package/dist/helpers/eventBus.ts +1 -0
  113. package/dist/helpers/fileTypeIcon.d.ts.map +1 -1
  114. package/dist/helpers/fileTypeIcon.js +4 -1
  115. package/dist/helpers/fileTypeIcon.js.map +1 -1
  116. package/dist/helpers/fileTypeIcon.tsx +15 -0
  117. package/dist/helpers/registry.d.ts.map +1 -1
  118. package/dist/helpers/registry.js +2 -0
  119. package/dist/helpers/registry.js.map +1 -1
  120. package/dist/helpers/registry.ts +2 -0
  121. package/dist/helpers/routePaths.d.ts +13 -0
  122. package/dist/helpers/routePaths.d.ts.map +1 -1
  123. package/dist/helpers/routePaths.js +39 -0
  124. package/dist/helpers/routePaths.js.map +1 -1
  125. package/dist/helpers/routePaths.ts +48 -0
  126. package/dist/redux/features/agentSlice.d.ts +2 -1
  127. package/dist/redux/features/agentSlice.d.ts.map +1 -1
  128. package/dist/redux/features/agentSlice.js +2 -2
  129. package/dist/redux/features/agentSlice.js.map +1 -1
  130. package/dist/redux/features/agentSlice.ts +8 -3
  131. package/dist/resources/solid-custom.css +42 -0
  132. package/dist/resources/solid-tailwind-generated.css +1 -1
  133. package/dist/routes/pages/admin/core/AgentHubModuleField.css +15 -0
  134. package/dist/routes/pages/admin/core/AgentHubModuleField.d.ts +22 -0
  135. package/dist/routes/pages/admin/core/AgentHubModuleField.d.ts.map +1 -0
  136. package/dist/routes/pages/admin/core/AgentHubModuleField.js +42 -0
  137. package/dist/routes/pages/admin/core/AgentHubModuleField.js.map +1 -0
  138. package/dist/routes/pages/admin/core/AgentHubModuleField.tsx +54 -0
  139. package/dist/routes/pages/admin/core/AgentHubTagsField.d.ts +3 -1
  140. package/dist/routes/pages/admin/core/AgentHubTagsField.d.ts.map +1 -1
  141. package/dist/routes/pages/admin/core/AgentHubTagsField.js +3 -3
  142. package/dist/routes/pages/admin/core/AgentHubTagsField.js.map +1 -1
  143. package/dist/routes/pages/admin/core/AgentHubTagsField.tsx +7 -3
  144. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.d.ts.map +1 -1
  145. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.js +37 -29
  146. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.js.map +1 -1
  147. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.tsx +12 -6
  148. package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.d.ts.map +1 -1
  149. package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.js +22 -15
  150. package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.js.map +1 -1
  151. package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.tsx +17 -7
  152. package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.d.ts.map +1 -1
  153. package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.js +21 -15
  154. package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.js.map +1 -1
  155. package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.tsx +16 -9
  156. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.d.ts.map +1 -1
  157. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.js +149 -37
  158. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.js.map +1 -1
  159. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.tsx +283 -73
  160. package/dist/routes/pages/admin/core/CardPage.d.ts.map +1 -1
  161. package/dist/routes/pages/admin/core/CardPage.js +9 -1
  162. package/dist/routes/pages/admin/core/CardPage.js.map +1 -1
  163. package/dist/routes/pages/admin/core/CardPage.tsx +9 -1
  164. package/dist/routes/pages/admin/core/KanbanPage.d.ts.map +1 -1
  165. package/dist/routes/pages/admin/core/KanbanPage.js +9 -1
  166. package/dist/routes/pages/admin/core/KanbanPage.js.map +1 -1
  167. package/dist/routes/pages/admin/core/KanbanPage.tsx +9 -1
  168. package/dist/routes/pages/admin/core/ListPage.d.ts.map +1 -1
  169. package/dist/routes/pages/admin/core/ListPage.js +10 -1
  170. package/dist/routes/pages/admin/core/ListPage.js.map +1 -1
  171. package/dist/routes/pages/admin/core/ListPage.tsx +10 -1
  172. package/dist/routes/pages/admin/core/TreePage.d.ts.map +1 -1
  173. package/dist/routes/pages/admin/core/TreePage.js +9 -1
  174. package/dist/routes/pages/admin/core/TreePage.js.map +1 -1
  175. package/dist/routes/pages/admin/core/TreePage.tsx +9 -1
  176. package/dist/routes/pages/embed/AgentEmbedPage.d.ts.map +1 -1
  177. package/dist/routes/pages/embed/AgentEmbedPage.js +67 -4
  178. package/dist/routes/pages/embed/AgentEmbedPage.js.map +1 -1
  179. package/dist/routes/pages/embed/AgentEmbedPage.tsx +61 -4
  180. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useState } from "react";
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { History, LogOut, Maximize2, Minimize2, Minus, PanelRight, Plus, Sparkles, Trash2, WifiOff, X } from "lucide-react";
3
3
  import styles from "./SolidAgent.module.css";
4
4
  import { agentModeChanged, agentPrefillConsumed } from "../../../redux/features/agentSlice";
@@ -14,6 +14,7 @@ import { signOutOfAgent } from "./client/agentAuth";
14
14
  import { AgentModelIndicator } from "./AgentModelIndicator";
15
15
  import type { AgentModelAssignments } from "./types";
16
16
  import type { AgentInputDefinition } from "./embed/agentEmbedProtocol";
17
+ import type { BeforeUserMessageSendRequest, EmbedMessageContext } from "./embed/agentEmbedProtocol";
17
18
  import { SolidButton } from "../../shad-cn-ui/SolidButton";
18
19
  import { SolidDialog, SolidDialogBody } from "../../shad-cn-ui/SolidDialog";
19
20
  import { SolidInput } from "../../shad-cn-ui/SolidInput";
@@ -28,6 +29,7 @@ type Props = {
28
29
  requiredInputs?: AgentInputDefinition[];
29
30
  externalEmbed?: boolean;
30
31
  onInputsLocked?: (locked: boolean) => void;
32
+ beforeSend?: (request: BeforeUserMessageSendRequest, signal: AbortSignal) => Promise<EmbedMessageContext | AgentInputContext | undefined>;
31
33
  /** Rendered inside a page (tab/form widget) instead of the floating window: no window controls. */
32
34
  embedded?: boolean;
33
35
  /** Only one mounted chat should act on SDK prefill requests; the floating window does by default. */
@@ -69,7 +71,7 @@ function parseInputValues(fields: AgentInputDefinition[], rawValues: Record<stri
69
71
  const values: Record<string, unknown> = {};
70
72
  for (const field of fields) {
71
73
  const raw = rawValues[field.name] ?? "";
72
- if (!raw.trim() && (field.optional || field.defaultValue !== null && field.defaultValue !== undefined)) continue;
74
+ if (!raw.trim() && field.optional) continue;
73
75
  if (field.dataType === "boolean") {
74
76
  if (raw !== "true" && raw !== "false") return { error: `Choose true or false for “${field.name}”.` };
75
77
  values[field.name] = raw === "true";
@@ -115,6 +117,7 @@ export function SolidAgentChat({
115
117
  requiredInputs = [],
116
118
  externalEmbed = false,
117
119
  onInputsLocked,
120
+ beforeSend,
118
121
  embedded = false,
119
122
  // SDK prefill goes through the shared "solidx" conversation only.
120
123
  handlesPrefill = !embedded && agentRuntime === "solidx",
@@ -136,10 +139,23 @@ export function SolidAgentChat({
136
139
  const [collectedInputs, setCollectedInputs] = useState<Record<string, unknown> | null>(null);
137
140
  const [rawInputValues, setRawInputValues] = useState<Record<string, string>>({});
138
141
  const [inputError, setInputError] = useState("");
142
+ const requiredInputsSignature = JSON.stringify(requiredInputs);
143
+ const inputAgentUrl = useRef(agentUrl);
139
144
  useEffect(() => {
140
- setRawInputValues(Object.fromEntries(requiredInputs.map((field) => [field.name,
141
- field.defaultValue === null || field.defaultValue === undefined ? "" : typeof field.defaultValue === "string" ? field.defaultValue : JSON.stringify(field.defaultValue)])));
142
- }, [requiredInputs]);
145
+ const definitions: AgentInputDefinition[] = JSON.parse(requiredInputsSignature);
146
+ setRawInputValues((current) => {
147
+ const agentChanged = inputAgentUrl.current !== agentUrl;
148
+ inputAgentUrl.current = agentUrl;
149
+ const next: Record<string, string> = {};
150
+ for (const field of definitions) {
151
+ const defaultValue = field.defaultValue === null || field.defaultValue === undefined
152
+ ? ""
153
+ : typeof field.defaultValue === "string" ? field.defaultValue : JSON.stringify(field.defaultValue);
154
+ next[field.name] = agentChanged ? defaultValue : current[field.name] ?? defaultValue;
155
+ }
156
+ return next;
157
+ });
158
+ }, [agentUrl, requiredInputsSignature]);
143
159
  const shouldPromptForInputs = !externalEmbed && agentRuntime === "agentHub" && signedIn
144
160
  && requiredInputs.length > 0 && inputs === undefined && collectedInputs === null;
145
161
  const messageInputs = inputs ?? collectedInputs ?? undefined;
@@ -195,9 +211,9 @@ export function SolidAgentChat({
195
211
  const { prompt, autoSend, context, nonce } = agent.prefill;
196
212
  dispatch(agentPrefillConsumed());
197
213
  setShowHistory(false);
198
- if (autoSend) runtime.sendMessage(prompt, { ...inputContext, ...(context ?? {}) });
214
+ if (autoSend) runtime.sendMessage(prompt, { ...inputContext, ...(context ?? {}) }, [], messageInputs, beforeSend, "prefill");
199
215
  else setSeed({ text: prompt, key: nonce });
200
- }, [agent.prefill, handlesPrefill, dispatch, runtime, inputContext]);
216
+ }, [agent.prefill, handlesPrefill, dispatch, runtime, inputContext, messageInputs, beforeSend]);
201
217
 
202
218
  useEffect(() => {
203
219
  if (!showHistory || !signedIn) return;
@@ -210,19 +226,64 @@ export function SolidAgentChat({
210
226
 
211
227
  const setMode = (mode: AgentMode) => dispatch(agentModeChanged(mode));
212
228
  const windowMode = windowControls?.mode ?? agent.mode;
213
- const send = useCallback((text: string) => {
229
+ const send = useCallback(async (text: string) => {
214
230
  if (!text.trim() || !runtime) return;
215
231
  onInputsLocked?.(true);
216
- return runtime.sendMessage(text, inputContext, [], messageInputs);
217
- }, [runtime, inputContext, messageInputs, onInputsLocked]);
232
+ try { return await runtime.sendMessage(text, inputContext, [], messageInputs, beforeSend, "widget"); }
233
+ catch (error) { onInputsLocked?.(false); throw error; }
234
+ }, [runtime, inputContext, messageInputs, beforeSend, onInputsLocked]);
218
235
  const sendWithAttachments = useCallback(
219
- (text: string, attachments: AgentAttachment[]) => {
236
+ async (text: string, attachments: AgentAttachment[]) => {
220
237
  if ((!text.trim() && !attachments.length) || !runtime) return;
221
238
  onInputsLocked?.(true);
222
- return runtime.sendMessage(text, inputContext, attachments, messageInputs);
239
+ try { return await runtime.sendMessage(text, inputContext, attachments, messageInputs, beforeSend, "composer"); }
240
+ catch (error) { onInputsLocked?.(false); throw error; }
223
241
  },
224
- [runtime, inputContext, messageInputs, onInputsLocked],
242
+ [runtime, inputContext, messageInputs, beforeSend, onInputsLocked],
225
243
  );
244
+ useEffect(() => {
245
+ const onEmbedCommand = (event: Event) => {
246
+ event.preventDefault();
247
+ const detail = (event as CustomEvent<{ command: string; payload: Record<string, unknown>; respond: (result?: unknown, error?: unknown) => void }>).detail;
248
+ if (!detail || typeof detail.respond !== "function") return;
249
+ const run = async () => {
250
+ switch (detail.command) {
251
+ case "sendMessage": {
252
+ if (typeof detail.payload.text !== "string" || !runtime) throw new Error("Chat is not ready to send a message.");
253
+ onInputsLocked?.(true);
254
+ try {
255
+ await runtime.sendMessage(detail.payload.text, inputContext, [], messageInputs, beforeSend,
256
+ detail.payload.source === "prefill" ? "prefill" : "sdk");
257
+ } catch (error) { onInputsLocked?.(false); throw error; }
258
+ return { queued: true };
259
+ }
260
+ case "prefill": {
261
+ if (typeof detail.payload.text !== "string") throw new Error("prefill requires text.");
262
+ const autoSend = detail.payload.autoSend === true;
263
+ if (autoSend) {
264
+ if (!runtime) throw new Error("Chat is not ready to send a message.");
265
+ try { await runtime.sendMessage(detail.payload.text, inputContext, [], messageInputs, beforeSend, "prefill"); }
266
+ catch (error) { onInputsLocked?.(false); throw error; }
267
+ } else setSeed({ text: detail.payload.text, key: Date.now() });
268
+ return { queued: autoSend };
269
+ }
270
+ case "newChat":
271
+ if (!runtime) throw new Error("Chat is not ready.");
272
+ runtime.newChat(); setShowHistory(false); onInputsLocked?.(false); return { started: true };
273
+ case "cancelTurn":
274
+ runtime?.cancel(); return { requested: true };
275
+ case "reconnect":
276
+ runtime?.socket.connect(); return { requested: true };
277
+ case "setContext":
278
+ return { stored: true };
279
+ default: throw new Error("Unsupported embed command.");
280
+ }
281
+ };
282
+ void run().then((result) => detail.respond(result)).catch((failure) => detail.respond(undefined, failure));
283
+ };
284
+ window.addEventListener("solid-agent-embed:command", onEmbedCommand);
285
+ return () => window.removeEventListener("solid-agent-embed:command", onEmbedCommand);
286
+ }, [runtime, inputContext, messageInputs, beforeSend, onInputsLocked]);
226
287
  const offline = agent.connection === "reconnecting" || agent.connection === "offline";
227
288
  const statusColor = agent.connection === "open" ? (agent.working ? "#eab308" : "#22c55e") : offline ? "#dc2626" : "#94a3b8";
228
289
  const statusText =
@@ -264,7 +325,7 @@ export function SolidAgentChat({
264
325
  >
265
326
  <SolidDialogBody className={styles.inputDialogBody}>
266
327
  <form className={styles.inputForm} autoComplete="off" onSubmit={submitRequiredInputs}>
267
- <p>Sign-in is complete. Enter the values this agent needs before starting the conversation.</p>
328
+ <p>Sign-in is complete. Enter the values this agent needs before starting the conversation. All inputs are required unless explicitly marked optional.</p>
268
329
  <div className={styles.inputFields}>
269
330
  {requiredInputs.map((field, index) => {
270
331
  const id = `solid-agent-test-input-${index}`;
@@ -434,7 +495,7 @@ export function SolidAgentChat({
434
495
  context={agent.context ?? undefined}
435
496
  emptyState={empty}
436
497
  onLoadOlder={() => void runtime?.loadOlder()}
437
- onReply={(widgetId, widget, value) => runtime?.replyToWidget(widgetId, widget, value)}
498
+ onReply={(widgetId, widget, value) => runtime?.replyToWidget(widgetId, widget, value, beforeSend)}
438
499
  onPrompt={send}
439
500
  />
440
501
  )}
@@ -1,5 +1,6 @@
1
1
  import { type AgentInputContext } from "./useAdminInputContext";
2
2
  import type { AgentInputDefinition } from "./embed/agentEmbedProtocol";
3
+ import type { BeforeUserMessageSendRequest, EmbedMessageContext } from "./embed/agentEmbedProtocol";
3
4
  type SharedProps = {
4
5
  title?: string;
5
6
  /** Additional key/value context sent with each message to the agent. */
@@ -12,6 +13,7 @@ type SharedProps = {
12
13
  externalEmbed?: {
13
14
  agentUrl: string;
14
15
  onInputsLocked?: (locked: boolean) => void;
16
+ beforeSend?: (request: BeforeUserMessageSendRequest, signal: AbortSignal) => Promise<EmbedMessageContext>;
15
17
  };
16
18
  /** Container height; the chat fills it. */
17
19
  height?: number | string;
@@ -1 +1 @@
1
- {"version":3,"file":"SolidAgentEmbedded.d.ts","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentEmbedded.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEvE,KAAK,WAAW,GAAG;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,0FAA0F;IAC1F,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACxC,2FAA2F;IAC3F,aAAa,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;KAAE,CAAC;IACjF,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,cAAc,CAAC,EAAE;QACb,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;QAC7B,MAAM,EAAE,MAAM,IAAI,CAAC;QACnB,UAAU,EAAE,MAAM,IAAI,CAAC;QACvB,SAAS,EAAE,MAAM,IAAI,CAAC;QACtB,OAAO,EAAE,MAAM,IAAI,CAAC;KACvB,CAAC;CACL,CAAC;AAEF,KAAK,KAAK,GAAG,WAAW,GAAG,CACrB;IACE,4EAA4E;IAC5E,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,GACC;IACE,oFAAoF;IACpF,YAAY,EAAE,UAAU,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACnB,CACJ,CAAC;AAIF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,kDAkC9C"}
1
+ {"version":3,"file":"SolidAgentEmbedded.d.ts","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentEmbedded.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEpG,KAAK,WAAW,GAAG;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,0FAA0F;IAC1F,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACxC,2FAA2F;IAC3F,aAAa,CAAC,EAAE;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QAC3C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;KAC7G,CAAC;IACF,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,cAAc,CAAC,EAAE;QACb,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;QAC7B,MAAM,EAAE,MAAM,IAAI,CAAC;QACnB,UAAU,EAAE,MAAM,IAAI,CAAC;QACvB,SAAS,EAAE,MAAM,IAAI,CAAC;QACtB,OAAO,EAAE,MAAM,IAAI,CAAC;KACvB,CAAC;CACL,CAAC;AAEF,KAAK,KAAK,GAAG,WAAW,GAAG,CACrB;IACE,4EAA4E;IAC5E,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,GACC;IACE,oFAAoF;IACpF,YAAY,EAAE,UAAU,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACnB,CACJ,CAAC;AAIF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,kDAmC9C"}
@@ -21,10 +21,10 @@ var AGENT_LABEL = { solidx: "SolidX Agent", agentHub: "AgentHub Agent" };
21
21
  * With agentRuntime "solidx" it shares the same connection and thread as the launcher.
22
22
  */
23
23
  export function SolidAgentEmbedded(props) {
24
- var _a, _b, _c, _d, _e;
25
- var _f = props.agentRuntime, agentRuntime = _f === void 0 ? "solidx" : _f, agentId = props.agentId, suppliedInputContext = props.inputContext, _g = props.height, height = _g === void 0 ? 560 : _g, suggestions = props.suggestions, className = props.className, windowControls = props.windowControls;
24
+ var _a, _b, _c, _d, _e, _f;
25
+ var _g = props.agentRuntime, agentRuntime = _g === void 0 ? "solidx" : _g, agentId = props.agentId, suppliedInputContext = props.inputContext, _h = props.height, height = _h === void 0 ? 560 : _h, suggestions = props.suggestions, className = props.className, windowControls = props.windowControls;
26
26
  var title = (_a = props.title) !== null && _a !== void 0 ? _a : AGENT_LABEL[agentRuntime];
27
- var _h = useAgentAvailability(!props.externalEmbed), ready = _h.ready, enabled = _h.enabled, agentUrls = _h.agentUrls, canUse = _h.canUse;
27
+ var _j = useAgentAvailability(!props.externalEmbed), ready = _j.ready, enabled = _j.enabled, agentUrls = _j.agentUrls, canUse = _j.canUse;
28
28
  var inferredInputContext = useAdminInputContext();
29
29
  var inputContext = props.externalEmbed ? (suppliedInputContext !== null && suppliedInputContext !== void 0 ? suppliedInputContext : {}) : __assign(__assign({}, inferredInputContext), (suppliedInputContext !== null && suppliedInputContext !== void 0 ? suppliedInputContext : {}));
30
30
  if (!props.externalEmbed && !ready)
@@ -41,6 +41,6 @@ export function SolidAgentEmbedded(props) {
41
41
  if (!agentUrl) {
42
42
  return (_jsxs("div", { className: styles.root, style: { padding: 16, color: "var(--solid-agent-muted)" }, children: ["No backend URL is configured for the ", AGENT_LABEL[agentRuntime], "."] }));
43
43
  }
44
- return (_jsx("div", { className: "".concat(styles.root, " ").concat(className !== null && className !== void 0 ? className : ""), style: { height: height, display: "flex", flexDirection: "column", border: "1px solid var(--solid-agent-border)", borderRadius: 12, overflow: "hidden", background: "var(--solid-agent-bg)" }, children: _jsx(SolidAgentChat, { agentUrl: agentUrl, agentRuntime: agentRuntime, title: title, inputContext: inputContext, inputs: props.inputs, requiredInputs: props.requiredInputs, externalEmbed: !!props.externalEmbed, onInputsLocked: (_e = props.externalEmbed) === null || _e === void 0 ? void 0 : _e.onInputsLocked, embedded: true, suggestions: suggestions, windowControls: windowControls }, agentUrl) }));
44
+ return (_jsx("div", { className: "".concat(styles.root, " ").concat(className !== null && className !== void 0 ? className : ""), style: { height: height, display: "flex", flexDirection: "column", border: "1px solid var(--solid-agent-border)", borderRadius: 12, overflow: "hidden", background: "var(--solid-agent-bg)" }, children: _jsx(SolidAgentChat, { agentUrl: agentUrl, agentRuntime: agentRuntime, title: title, inputContext: inputContext, inputs: props.inputs, requiredInputs: props.requiredInputs, externalEmbed: !!props.externalEmbed, onInputsLocked: (_e = props.externalEmbed) === null || _e === void 0 ? void 0 : _e.onInputsLocked, beforeSend: (_f = props.externalEmbed) === null || _f === void 0 ? void 0 : _f.beforeSend, embedded: true, suggestions: suggestions, windowControls: windowControls }, agentUrl) }));
45
45
  }
46
46
  //# sourceMappingURL=SolidAgentEmbedded.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SolidAgentEmbedded.js","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentEmbedded.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from \"react\";\nimport styles from \"./SolidAgent.module.css\";\nimport { SolidAgentChat } from \"./SolidAgentChat\";\nimport { useAgentAvailability } from \"./useAgentAvailability\";\nimport type { AgentRuntimeType } from \"./types\";\nimport { withAgentId } from \"./client/agentUrls\";\nimport { useAdminInputContext, type AgentInputContext } from \"./useAdminInputContext\";\nimport type { AgentInputDefinition } from \"./embed/agentEmbedProtocol\";\n\ntype SharedProps = {\n title?: string;\n /** Additional key/value context sent with each message to the agent. */\n inputContext?: AgentInputContext;\n /** Values for the AgentHub agent's declared requiredInputs. */\n inputs?: Record<string, unknown>;\n /** AgentHub input schema; used by the admin test surface when no values were supplied. */\n requiredInputs?: AgentInputDefinition[];\n /** External bootstrap: the standalone route registers credentials for this private URL. */\n externalEmbed?: { agentUrl: string; onInputsLocked?: (locked: boolean) => void };\n /** Container height; the chat fills it. */\n height?: number | string;\n suggestions?: string[];\n className?: string;\n /** Adds the standard chat window controls with actions managed by the embedding surface. */\n windowControls?: {\n mode: \"docked\" | \"maximized\";\n onDock: () => void;\n onMaximize: () => void;\n onRestore: () => void;\n onClose: () => void;\n };\n};\n\ntype Props = SharedProps & (\n | {\n /** SolidX runtime (setting solidxAgentBackendUrl); agent ID is optional. */\n agentRuntime?: \"solidx\";\n agentId?: number;\n }\n | {\n /** AgentHub runtime (setting solidxAgentHubBackendUrl); an agent ID is required. */\n agentRuntime: \"agentHub\";\n agentId: number;\n }\n);\n\nconst AGENT_LABEL: Record<AgentRuntimeType, string> = { solidx: \"SolidX Agent\", agentHub: \"AgentHub Agent\" };\n\n/**\n * The agent chat rendered inside a page (a tab or form widget) instead of the floating window.\n * With agentRuntime \"solidx\" it shares the same connection and thread as the launcher.\n */\nexport function SolidAgentEmbedded(props: Props) {\n const { agentRuntime = \"solidx\", agentId, inputContext: suppliedInputContext, height = 560, suggestions, className, windowControls } = props;\n const title = props.title ?? AGENT_LABEL[agentRuntime];\n const { ready, enabled, agentUrls, canUse } = useAgentAvailability(!props.externalEmbed);\n const inferredInputContext = useAdminInputContext();\n const inputContext = props.externalEmbed ? (suppliedInputContext ?? {}) : { ...inferredInputContext, ...(suppliedInputContext ?? {}) };\n if (!props.externalEmbed && !ready) return null;\n const backendUrl = agentUrls[agentRuntime] ?? null;\n // The chat URL names the agent, so each agent has its own login.\n const agentUrl = props.externalEmbed?.agentUrl ?? (backendUrl ? withAgentId(backendUrl, agentId) : null);\n if (!props.externalEmbed && (!enabled || !canUse)) {\n return <div className={styles.root} style={{ padding: 16, color: \"var(--solid-agent-muted)\" }}>{title} is not available for your account.</div>;\n }\n if (agentRuntime === \"agentHub\" && agentId === undefined) {\n return <div className={styles.root} style={{ padding: 16, color: \"var(--solid-agent-muted)\" }}>No agent is selected for the {AGENT_LABEL[agentRuntime]}.</div>;\n }\n if (!agentUrl) {\n return (\n <div className={styles.root} style={{ padding: 16, color: \"var(--solid-agent-muted)\" }}>\n No backend URL is configured for the {AGENT_LABEL[agentRuntime]}.\n </div>\n );\n }\n return (\n <div\n className={`${styles.root} ${className ?? \"\"}`}\n style={{ height, display: \"flex\", flexDirection: \"column\", border: \"1px solid var(--solid-agent-border)\", borderRadius: 12, overflow: \"hidden\", background: \"var(--solid-agent-bg)\" }}\n >\n {/* key: switching type remounts the chat onto the other backend's conversation. */}\n <SolidAgentChat key={agentUrl} agentUrl={agentUrl} agentRuntime={agentRuntime} title={title} inputContext={inputContext}\n inputs={props.inputs} requiredInputs={props.requiredInputs} externalEmbed={!!props.externalEmbed} onInputsLocked={props.externalEmbed?.onInputsLocked}\n embedded suggestions={suggestions} windowControls={windowControls} />\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"SolidAgentEmbedded.js","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentEmbedded.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from \"react\";\nimport styles from \"./SolidAgent.module.css\";\nimport { SolidAgentChat } from \"./SolidAgentChat\";\nimport { useAgentAvailability } from \"./useAgentAvailability\";\nimport type { AgentRuntimeType } from \"./types\";\nimport { withAgentId } from \"./client/agentUrls\";\nimport { useAdminInputContext, type AgentInputContext } from \"./useAdminInputContext\";\nimport type { AgentInputDefinition } from \"./embed/agentEmbedProtocol\";\nimport type { BeforeUserMessageSendRequest, EmbedMessageContext } from \"./embed/agentEmbedProtocol\";\n\ntype SharedProps = {\n title?: string;\n /** Additional key/value context sent with each message to the agent. */\n inputContext?: AgentInputContext;\n /** Values for the AgentHub agent's declared requiredInputs. */\n inputs?: Record<string, unknown>;\n /** AgentHub input schema; used by the admin test surface when no values were supplied. */\n requiredInputs?: AgentInputDefinition[];\n /** External bootstrap: the standalone route registers credentials for this private URL. */\n externalEmbed?: {\n agentUrl: string;\n onInputsLocked?: (locked: boolean) => void;\n beforeSend?: (request: BeforeUserMessageSendRequest, signal: AbortSignal) => Promise<EmbedMessageContext>;\n };\n /** Container height; the chat fills it. */\n height?: number | string;\n suggestions?: string[];\n className?: string;\n /** Adds the standard chat window controls with actions managed by the embedding surface. */\n windowControls?: {\n mode: \"docked\" | \"maximized\";\n onDock: () => void;\n onMaximize: () => void;\n onRestore: () => void;\n onClose: () => void;\n };\n};\n\ntype Props = SharedProps & (\n | {\n /** SolidX runtime (setting solidxAgentBackendUrl); agent ID is optional. */\n agentRuntime?: \"solidx\";\n agentId?: number;\n }\n | {\n /** AgentHub runtime (setting solidxAgentHubBackendUrl); an agent ID is required. */\n agentRuntime: \"agentHub\";\n agentId: number;\n }\n);\n\nconst AGENT_LABEL: Record<AgentRuntimeType, string> = { solidx: \"SolidX Agent\", agentHub: \"AgentHub Agent\" };\n\n/**\n * The agent chat rendered inside a page (a tab or form widget) instead of the floating window.\n * With agentRuntime \"solidx\" it shares the same connection and thread as the launcher.\n */\nexport function SolidAgentEmbedded(props: Props) {\n const { agentRuntime = \"solidx\", agentId, inputContext: suppliedInputContext, height = 560, suggestions, className, windowControls } = props;\n const title = props.title ?? AGENT_LABEL[agentRuntime];\n const { ready, enabled, agentUrls, canUse } = useAgentAvailability(!props.externalEmbed);\n const inferredInputContext = useAdminInputContext();\n const inputContext = props.externalEmbed ? (suppliedInputContext ?? {}) : { ...inferredInputContext, ...(suppliedInputContext ?? {}) };\n if (!props.externalEmbed && !ready) return null;\n const backendUrl = agentUrls[agentRuntime] ?? null;\n // The chat URL names the agent, so each agent has its own login.\n const agentUrl = props.externalEmbed?.agentUrl ?? (backendUrl ? withAgentId(backendUrl, agentId) : null);\n if (!props.externalEmbed && (!enabled || !canUse)) {\n return <div className={styles.root} style={{ padding: 16, color: \"var(--solid-agent-muted)\" }}>{title} is not available for your account.</div>;\n }\n if (agentRuntime === \"agentHub\" && agentId === undefined) {\n return <div className={styles.root} style={{ padding: 16, color: \"var(--solid-agent-muted)\" }}>No agent is selected for the {AGENT_LABEL[agentRuntime]}.</div>;\n }\n if (!agentUrl) {\n return (\n <div className={styles.root} style={{ padding: 16, color: \"var(--solid-agent-muted)\" }}>\n No backend URL is configured for the {AGENT_LABEL[agentRuntime]}.\n </div>\n );\n }\n return (\n <div\n className={`${styles.root} ${className ?? \"\"}`}\n style={{ height, display: \"flex\", flexDirection: \"column\", border: \"1px solid var(--solid-agent-border)\", borderRadius: 12, overflow: \"hidden\", background: \"var(--solid-agent-bg)\" }}\n >\n {/* key: switching type remounts the chat onto the other backend's conversation. */}\n <SolidAgentChat key={agentUrl} agentUrl={agentUrl} agentRuntime={agentRuntime} title={title} inputContext={inputContext}\n inputs={props.inputs} requiredInputs={props.requiredInputs} externalEmbed={!!props.externalEmbed} onInputsLocked={props.externalEmbed?.onInputsLocked}\n beforeSend={props.externalEmbed?.beforeSend}\n embedded suggestions={suggestions} windowControls={windowControls} />\n </div>\n );\n}\n"]}
@@ -6,6 +6,7 @@ import type { AgentRuntimeType } from "./types";
6
6
  import { withAgentId } from "./client/agentUrls";
7
7
  import { useAdminInputContext, type AgentInputContext } from "./useAdminInputContext";
8
8
  import type { AgentInputDefinition } from "./embed/agentEmbedProtocol";
9
+ import type { BeforeUserMessageSendRequest, EmbedMessageContext } from "./embed/agentEmbedProtocol";
9
10
 
10
11
  type SharedProps = {
11
12
  title?: string;
@@ -16,7 +17,11 @@ type SharedProps = {
16
17
  /** AgentHub input schema; used by the admin test surface when no values were supplied. */
17
18
  requiredInputs?: AgentInputDefinition[];
18
19
  /** External bootstrap: the standalone route registers credentials for this private URL. */
19
- externalEmbed?: { agentUrl: string; onInputsLocked?: (locked: boolean) => void };
20
+ externalEmbed?: {
21
+ agentUrl: string;
22
+ onInputsLocked?: (locked: boolean) => void;
23
+ beforeSend?: (request: BeforeUserMessageSendRequest, signal: AbortSignal) => Promise<EmbedMessageContext>;
24
+ };
20
25
  /** Container height; the chat fills it. */
21
26
  height?: number | string;
22
27
  suggestions?: string[];
@@ -81,6 +86,7 @@ export function SolidAgentEmbedded(props: Props) {
81
86
  {/* key: switching type remounts the chat onto the other backend's conversation. */}
82
87
  <SolidAgentChat key={agentUrl} agentUrl={agentUrl} agentRuntime={agentRuntime} title={title} inputContext={inputContext}
83
88
  inputs={props.inputs} requiredInputs={props.requiredInputs} externalEmbed={!!props.externalEmbed} onInputsLocked={props.externalEmbed?.onInputsLocked}
89
+ beforeSend={props.externalEmbed?.beforeSend}
84
90
  embedded suggestions={suggestions} windowControls={windowControls} />
85
91
  </div>
86
92
  );
@@ -1 +1 @@
1
- {"version":3,"file":"SolidAgentHost.d.ts","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentHost.tsx"],"names":[],"mappings":"AAiCA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,mDA4HrC"}
1
+ {"version":3,"file":"SolidAgentHost.d.ts","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentHost.tsx"],"names":[],"mappings":"AAuCA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,mDAyLrC"}
@@ -12,15 +12,21 @@ var __assign = (this && this.__assign) || function () {
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { useCallback, useEffect, useRef, useState } from "react";
14
14
  import { useDispatch, useSelector } from "react-redux";
15
+ import { useNavigate } from "react-router-dom";
15
16
  import { Sparkles } from "lucide-react";
16
17
  import styles from "./SolidAgent.module.css";
17
18
  import { agentModeChanged, agentOpenRequested } from "../../../redux/features/agentSlice";
18
19
  import { SolidAgentChat } from "./SolidAgentChat";
19
20
  import { useAgentAvailability } from "./useAgentAvailability";
20
21
  import { useAdminInputContext } from "./useAdminInputContext";
22
+ import { onBeforeUserMessageSend } from "./client/beforeUserMessageSend";
23
+ import { onAgentPublicEvent } from "./client/agentPublicEvents";
24
+ import { buildAdminCustomPath, buildAdminDashboardPath, buildAdminModelViewPath, buildAdminRecordFormPath } from "../../../helpers/routePaths";
25
+ import { requestAdminViewRefresh } from "../../../helpers/adminViewRefresh";
21
26
  var PREFS_KEY = "solid-agent.window";
22
27
  var MIN_DOCK = 360;
23
28
  var MAX_DOCK = 720;
29
+ var _DEBUG_AGENT_EVENTS = true;
24
30
  function readPrefs() {
25
31
  var _a;
26
32
  try {
@@ -49,14 +55,86 @@ function writePrefs(prefs) {
49
55
  export default function SolidAgentHost() {
50
56
  var _a;
51
57
  var dispatch = useDispatch();
58
+ var navigate = useNavigate();
52
59
  var _b = useAgentAvailability(), ready = _b.ready, enabled = _b.enabled, agentUrl = _b.agentUrl, canUse = _b.canUse;
53
60
  var inputContext = useAdminInputContext();
61
+ var inputContextRef = useRef(inputContext);
62
+ inputContextRef.current = inputContext;
54
63
  var agent = useSelector(function (state) { return state.solidAgent; });
55
64
  var _c = useState(function () { return readPrefs().dockWidth; }), dockWidth = _c[0], setDockWidth = _c[1];
56
65
  var prefsHydrated = useRef(false);
57
66
  var skipNextPrefsWrite = useRef(false);
58
67
  var windowRef = useRef(null);
59
68
  var openerRef = useRef(null);
69
+ useEffect(function () { return onBeforeUserMessageSend(function (_request, signal) {
70
+ if (signal.aborted)
71
+ throw new Error("Message preparation was cancelled.");
72
+ var context = inputContextRef.current;
73
+ if (Object.keys(context).length === 0)
74
+ return undefined;
75
+ var summary = [context.moduleName, context.modelName, context.viewName, context.modelId]
76
+ .filter(function (value) { return value !== undefined && value !== null && value !== ""; })
77
+ .join(" · ");
78
+ var contribution = {
79
+ schemaVersion: 1,
80
+ namespaces: {
81
+ "solidx.admin": __assign({ context: context, revision: JSON.stringify(context) }, (summary ? { displaySummary: summary } : {})),
82
+ },
83
+ };
84
+ return contribution;
85
+ }); }, []);
86
+ useEffect(function () {
87
+ return onAgentPublicEvent(function (_a) {
88
+ var event = _a.event, payload = _a.payload, source = _a.agentType;
89
+ if (_DEBUG_AGENT_EVENTS && source === "solidx")
90
+ console.log("[SolidAgentHost] ".concat(event), payload);
91
+ if (event !== "onAgentNavigationRequested" || source !== "solidx")
92
+ return;
93
+ if (payload.schemaVersion !== 1 || payload.kind !== "navigation" || payload.domain !== "solidx.admin")
94
+ return;
95
+ var _b = payload.payload, operation = _b.operation, scope = _b.scope, moduleName = _b.moduleName, modelName = _b.modelName, viewName = _b.viewName, recordId = _b.recordId, dashboardName = _b.dashboardName, customRoute = _b.customRoute;
96
+ if (typeof moduleName !== "string" || typeof viewName !== "string")
97
+ return;
98
+ if (!/^[A-Za-z0-9_-]+$/.test(moduleName.trim()))
99
+ return;
100
+ if (operation === "refresh") {
101
+ if (typeof modelName !== "string" || !/^[A-Za-z0-9_-]+$/.test(modelName.trim()))
102
+ return;
103
+ if (scope === "collection" && ["list", "tree", "kanban", "card"].includes(viewName)) {
104
+ requestAdminViewRefresh({ moduleName: moduleName, modelName: modelName, viewName: viewName, scope: scope });
105
+ }
106
+ else if (scope === "record" && viewName === "form" && (typeof recordId === "string" || typeof recordId === "number")) {
107
+ var refreshRecordId = String(recordId);
108
+ if (!/^[A-Za-z0-9_-]+$/.test(refreshRecordId))
109
+ return;
110
+ requestAdminViewRefresh({ moduleName: moduleName, modelName: modelName, viewName: viewName, scope: scope, recordId: refreshRecordId });
111
+ }
112
+ return;
113
+ }
114
+ if (operation !== undefined && operation !== "navigate")
115
+ return;
116
+ var path = "";
117
+ if (viewName === "dashboard" && typeof dashboardName === "string") {
118
+ path = buildAdminDashboardPath({ moduleName: moduleName, dashboardName: dashboardName });
119
+ }
120
+ else if (viewName === "custom" && typeof customRoute === "string") {
121
+ path = buildAdminCustomPath(customRoute);
122
+ }
123
+ else if (["list", "tree", "kanban", "card"].includes(viewName) && typeof modelName === "string") {
124
+ path = buildAdminModelViewPath({ moduleName: moduleName, modelName: modelName, viewName: viewName });
125
+ }
126
+ else if (viewName === "form" && typeof modelName === "string"
127
+ && /^[A-Za-z0-9_-]+$/.test(modelName.trim())
128
+ && (typeof recordId === "string" || typeof recordId === "number")) {
129
+ var recordKey = String(recordId);
130
+ if (!/^[A-Za-z0-9_-]+$/.test(recordKey))
131
+ return;
132
+ path = buildAdminRecordFormPath({ moduleName: moduleName, modelName: modelName, recordId: recordKey, viewMode: "view" });
133
+ }
134
+ if (path)
135
+ navigate(path);
136
+ });
137
+ }, [navigate]);
60
138
  var available = ready && enabled && canUse && !!agentUrl && !!agent;
61
139
  var mode = (_a = agent === null || agent === void 0 ? void 0 : agent.mode) !== null && _a !== void 0 ? _a : "bubble";
62
140
  var isOpen = available && mode !== "bubble";
@@ -147,6 +225,6 @@ export default function SolidAgentHost() {
147
225
  event.stopPropagation();
148
226
  dispatch(agentModeChanged("bubble"));
149
227
  }
150
- }, children: [mode === "docked" && _jsx("div", { className: styles.resizeHandle, onPointerDown: startResize, "aria-hidden": "true" }), _jsx(SolidAgentChat, { agentUrl: agentUrl, inputContext: inputContext })] })) : (_jsxs("button", { type: "button", className: styles.bubble, onClick: toggle, "aria-label": "Open the SolidX Agent (Ctrl+J)", title: "SolidX Agent (Ctrl+J)", children: [_jsx(Sparkles, { size: 22 }), (agent === null || agent === void 0 ? void 0 : agent.working) && _jsx("span", { className: styles.bubbleBadge, "aria-label": "Agent is working" })] })) }));
228
+ }, children: [mode === "docked" && _jsx("div", { className: styles.resizeHandle, onPointerDown: startResize, "aria-hidden": "true" }), _jsx(SolidAgentChat, { agentUrl: agentUrl })] })) : (_jsxs("button", { type: "button", className: styles.bubble, onClick: toggle, "aria-label": "Open the SolidX Agent (Ctrl+J)", title: "SolidX Agent (Ctrl+J)", children: [_jsx(Sparkles, { size: 22 }), (agent === null || agent === void 0 ? void 0 : agent.working) && _jsx("span", { className: styles.bubbleBadge, "aria-label": "Agent is working" })] })) }));
151
229
  }
152
230
  //# sourceMappingURL=SolidAgentHost.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SolidAgentHost.js","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentHost.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { Sparkles } from \"lucide-react\";\nimport styles from \"./SolidAgent.module.css\";\nimport { agentModeChanged, agentOpenRequested, type AgentState } from \"../../../redux/features/agentSlice\";\nimport { SolidAgentChat } from \"./SolidAgentChat\";\nimport { useAgentAvailability } from \"./useAgentAvailability\";\nimport { useAdminInputContext } from \"./useAdminInputContext\";\nimport type { AgentMode } from \"./types\";\n\nconst PREFS_KEY = \"solid-agent.window\";\nconst MIN_DOCK = 360;\nconst MAX_DOCK = 720;\ntype WindowPrefs = { dockWidth: number; open?: boolean; mode?: Exclude<AgentMode, \"bubble\"> };\n\nfunction readPrefs(): WindowPrefs {\n try {\n const parsed = JSON.parse(localStorage.getItem(PREFS_KEY) ?? \"{}\");\n const mode = [\"compact\", \"docked\", \"maximized\"].includes(parsed.mode) ? parsed.mode as WindowPrefs[\"mode\"] : undefined;\n return { dockWidth: Number(parsed.dockWidth) || 420, open: parsed.open === true, mode };\n } catch {\n return { dockWidth: 420 };\n }\n}\n\nfunction writePrefs(prefs: Partial<WindowPrefs>) {\n try {\n localStorage.setItem(PREFS_KEY, JSON.stringify({ ...readPrefs(), ...prefs }));\n } catch {\n // ignore\n }\n}\n\n/**\n * The single mount point of the SolidX Agent in the admin shell (rendered by AdminLayout).\n * Shows the floating bubble and the chat window; hidden unless the `solidAgentEnabled` setting\n * is on, `solidxAgentBackendUrl` is set and the user has agent:invoke. Ctrl/Cmd+J toggles the window,\n * Esc inside it minimizes back to the bubble.\n */\nexport default function SolidAgentHost() {\n const dispatch = useDispatch();\n const { ready, enabled, agentUrl, canUse } = useAgentAvailability();\n const inputContext = useAdminInputContext();\n const agent = useSelector((state: any) => state.solidAgent as AgentState | undefined);\n const [dockWidth, setDockWidth] = useState(() => readPrefs().dockWidth);\n const prefsHydrated = useRef(false);\n const skipNextPrefsWrite = useRef(false);\n const windowRef = useRef<HTMLDivElement>(null);\n const openerRef = useRef<Element | null>(null);\n\n const available = ready && enabled && canUse && !!agentUrl && !!agent;\n const mode = agent?.mode ?? \"bubble\";\n const isOpen = available && mode !== \"bubble\";\n\n // Restore the last visible presentation after the host has verified this user's access.\n useEffect(() => {\n if (!ready || !available || prefsHydrated.current) return;\n prefsHydrated.current = true;\n const prefs = readPrefs();\n if (prefs.open && prefs.mode) {\n if (mode !== prefs.mode) skipNextPrefsWrite.current = true;\n dispatch(agentModeChanged(prefs.mode));\n }\n }, [ready, available, dispatch, mode]);\n\n // Keep visibility and presentation in localStorage. When closed, retain the last mode so\n // the preference still records how the window had been presented.\n useEffect(() => {\n if (!prefsHydrated.current || !available) return;\n if (skipNextPrefsWrite.current) {\n skipNextPrefsWrite.current = false;\n return;\n }\n if (mode !== \"bubble\") writePrefs({ open: true, mode });\n else writePrefs({ open: false });\n }, [available, mode]);\n\n const toggle = useCallback(() => {\n if (!available) return;\n if (isOpen) dispatch(agentModeChanged(\"bubble\"));\n else {\n openerRef.current = document.activeElement;\n dispatch(agentOpenRequested());\n }\n }, [available, isOpen, dispatch]);\n\n // Ctrl/Cmd + J toggles the chat from anywhere in the admin app.\n useEffect(() => {\n if (!available) return;\n const onKey = (event: KeyboardEvent) => {\n if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === \"j\") {\n event.preventDefault();\n toggle();\n }\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [available, toggle]);\n\n // Docked mode pushes the admin shell over instead of covering it.\n useEffect(() => {\n const docked = isOpen && mode === \"docked\";\n document.body.classList.toggle(\"solid-agent-docked\", docked);\n document.documentElement.style.setProperty(\"--solid-agent-docked-width\", `${dockWidth}px`);\n return () => document.body.classList.remove(\"solid-agent-docked\");\n }, [isOpen, mode, dockWidth]);\n\n // Focus returns to whatever opened the chat when it is minimized.\n useEffect(() => {\n if (!isOpen && openerRef.current instanceof HTMLElement) {\n openerRef.current.focus();\n openerRef.current = null;\n }\n }, [isOpen]);\n\n const startResize = (event: React.PointerEvent) => {\n event.preventDefault();\n const onMove = (move: PointerEvent) => {\n setDockWidth(Math.max(MIN_DOCK, Math.min(MAX_DOCK, window.innerWidth - move.clientX)));\n };\n const onUp = () => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n setDockWidth((width) => {\n writePrefs({ dockWidth: width });\n return width;\n });\n };\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n };\n\n if (!available || !agentUrl) return null;\n\n const frameClass = mode === \"docked\" ? styles.docked : mode === \"maximized\" ? styles.maximized : styles.compact;\n\n return (\n <div className={styles.root}>\n {isOpen ? (\n <div\n ref={windowRef}\n className={`${styles.window} ${frameClass}`}\n style={mode === \"docked\" ? { width: dockWidth } : undefined}\n role=\"complementary\"\n aria-label=\"SolidX Agent\"\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n dispatch(agentModeChanged(\"bubble\"));\n }\n }}\n >\n {mode === \"docked\" && <div className={styles.resizeHandle} onPointerDown={startResize} aria-hidden=\"true\" />}\n <SolidAgentChat agentUrl={agentUrl} inputContext={inputContext} />\n </div>\n ) : (\n <button type=\"button\" className={styles.bubble} onClick={toggle} aria-label=\"Open the SolidX Agent (Ctrl+J)\" title=\"SolidX Agent (Ctrl+J)\">\n <Sparkles size={22} />\n {agent?.working && <span className={styles.bubbleBadge} aria-label=\"Agent is working\" />}\n </button>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"SolidAgentHost.js","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/SolidAgentHost.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAmB,MAAM,oCAAoC,CAAC;AAC3G,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC/I,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAE5E,IAAM,SAAS,GAAG,oBAAoB,CAAC;AACvC,IAAM,QAAQ,GAAG,GAAG,CAAC;AACrB,IAAM,QAAQ,GAAG,GAAG,CAAC;AACrB,IAAM,mBAAmB,GAAG,IAAI,CAAC;AAGjC,SAAS,SAAS;;IACd,IAAI;QACA,IAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAA,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,mCAAI,IAAI,CAAC,CAAC;QACnE,IAAM,IAAI,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAA2B,CAAC,CAAC,CAAC,SAAS,CAAC;QACvH,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,IAAI,MAAA,EAAE,CAAC;KAC3F;IAAC,WAAM;QACJ,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;KAC7B;AACL,CAAC;AAED,SAAS,UAAU,CAAC,KAA2B;IAC3C,IAAI;QACA,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,uBAAM,SAAS,EAAE,GAAK,KAAK,EAAG,CAAC,CAAC;KACjF;IAAC,WAAM;QACJ,SAAS;KACZ;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc;;IAClC,IAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,IAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IACzB,IAAA,KAAuC,oBAAoB,EAAE,EAA3D,KAAK,WAAA,EAAE,OAAO,aAAA,EAAE,QAAQ,cAAA,EAAE,MAAM,YAA2B,CAAC;IACpE,IAAM,YAAY,GAAG,oBAAoB,EAAE,CAAC;IAC5C,IAAM,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7C,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;IACvC,IAAM,KAAK,GAAG,WAAW,CAAC,UAAC,KAAU,IAAK,OAAA,KAAK,CAAC,UAAoC,EAA1C,CAA0C,CAAC,CAAC;IAChF,IAAA,KAA4B,QAAQ,CAAC,cAAM,OAAA,SAAS,EAAE,CAAC,SAAS,EAArB,CAAqB,CAAC,EAAhE,SAAS,QAAA,EAAE,YAAY,QAAyC,CAAC;IACxE,IAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,IAAM,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACzC,IAAM,SAAS,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC/C,IAAM,SAAS,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAE/C,SAAS,CAAC,cAAM,OAAA,uBAAuB,CAAC,UAAC,QAAQ,EAAE,MAAM;QACrD,IAAI,MAAM,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC1E,IAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;QACxC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACxD,IAAM,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC;aACrF,MAAM,CAAC,UAAC,KAAK,IAAK,OAAA,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAArD,CAAqD,CAAC;aACxE,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,IAAM,YAAY,GAAwB;YACtC,aAAa,EAAE,CAAC;YAChB,UAAU,EAAE;gBACR,cAAc,aACV,OAAO,EAAE,OAA+D,EACxE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAC9B,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAClD;aACJ;SACJ,CAAC;QACF,OAAO,YAAY,CAAC;IACxB,CAAC,CAAC,EAlBc,CAkBd,EAAE,EAAE,CAAC,CAAC;IACR,SAAS,CAAC;QACN,OAAO,kBAAkB,CAAC,UAAC,EAAqC;gBAAnC,KAAK,WAAA,EAAE,OAAO,aAAA,EAAa,MAAM,eAAA;YAC1D,IAAI,mBAAmB,IAAI,MAAM,KAAK,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,2BAAoB,KAAK,CAAE,EAAE,OAAO,CAAC,CAAC;YAClG,IAAI,KAAK,KAAK,4BAA4B,IAAI,MAAM,KAAK,QAAQ;gBAAE,OAAO;YAC1E,IAAI,OAAO,CAAC,aAAa,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,IAAI,OAAO,CAAC,MAAM,KAAK,cAAc;gBAAE,OAAO;YAExG,IAAA,KAA8F,OAAO,CAAC,OAAO,EAA3G,SAAS,eAAA,EAAE,KAAK,WAAA,EAAE,UAAU,gBAAA,EAAE,SAAS,eAAA,EAAE,QAAQ,cAAA,EAAE,QAAQ,cAAA,EAAE,aAAa,mBAAA,EAAE,WAAW,iBAAoB,CAAC;YACpH,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAAE,OAAO;YAC3E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;gBAAE,OAAO;YACxD,IAAI,SAAS,KAAK,SAAS,EAAE;gBACzB,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;oBAAE,OAAO;gBACxF,IAAI,KAAK,KAAK,YAAY,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;oBACjF,uBAAuB,CAAC,EAAE,UAAU,YAAA,EAAE,SAAS,WAAA,EAAE,QAAQ,EAAE,QAA+C,EAAE,KAAK,OAAA,EAAE,CAAC,CAAC;iBACxH;qBAAM,IAAI,KAAK,KAAK,QAAQ,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,CAAC,EAAE;oBACpH,IAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACzC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,eAAe,CAAC;wBAAE,OAAO;oBACtD,uBAAuB,CAAC,EAAE,UAAU,YAAA,EAAE,SAAS,WAAA,EAAE,QAAQ,UAAA,EAAE,KAAK,OAAA,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;iBAClG;gBACD,OAAO;aACV;YACD,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,UAAU;gBAAE,OAAO;YAChE,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,IAAI,QAAQ,KAAK,WAAW,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE;gBAC/D,IAAI,GAAG,uBAAuB,CAAC,EAAE,UAAU,YAAA,EAAE,aAAa,eAAA,EAAE,CAAC,CAAC;aACjE;iBAAM,IAAI,QAAQ,KAAK,QAAQ,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;gBACjE,IAAI,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;aAC5C;iBAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;gBAC/F,IAAI,GAAG,uBAAuB,CAAC,EAAE,UAAU,YAAA,EAAE,SAAS,WAAA,EAAE,QAAQ,UAAA,EAAE,CAAC,CAAC;aACvE;iBAAM,IAAI,QAAQ,KAAK,MAAM,IAAI,OAAO,SAAS,KAAK,QAAQ;mBACxD,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;mBACzC,CAAC,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,CAAC,EAAE;gBACnE,IAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACnC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC;oBAAE,OAAO;gBAChD,IAAI,GAAG,wBAAwB,CAAC,EAAE,UAAU,YAAA,EAAE,SAAS,WAAA,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;aACrG;YACD,IAAI,IAAI;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACP,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,IAAM,SAAS,GAAG,KAAK,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC;IACtE,IAAM,IAAI,GAAG,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,mCAAI,QAAQ,CAAC;IACrC,IAAM,MAAM,GAAG,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC;IAE9C,wFAAwF;IACxF,SAAS,CAAC;QACN,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,IAAI,aAAa,CAAC,OAAO;YAAE,OAAO;QAC1D,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;QAC7B,IAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAC1B,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE;YAC1B,IAAI,IAAI,KAAK,KAAK,CAAC,IAAI;gBAAE,kBAAkB,CAAC,OAAO,GAAG,IAAI,CAAC;YAC3D,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;SAC1C;IACL,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IAEvC,yFAAyF;IACzF,kEAAkE;IAClE,SAAS,CAAC;QACN,IAAI,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,SAAS;YAAE,OAAO;QACjD,IAAI,kBAAkB,CAAC,OAAO,EAAE;YAC5B,kBAAkB,CAAC,OAAO,GAAG,KAAK,CAAC;YACnC,OAAO;SACV;QACD,IAAI,IAAI,KAAK,QAAQ;YAAE,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,MAAA,EAAE,CAAC,CAAC;;YACnD,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrC,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IAEtB,IAAM,MAAM,GAAG,WAAW,CAAC;QACvB,IAAI,CAAC,SAAS;YAAE,OAAO;QACvB,IAAI,MAAM;YAAE,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;aAC5C;YACD,SAAS,CAAC,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC;YAC3C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,CAAC;SAClC;IACL,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAElC,gEAAgE;IAChE,SAAS,CAAC;QACN,IAAI,CAAC,SAAS;YAAE,OAAO;QACvB,IAAM,KAAK,GAAG,UAAC,KAAoB;YAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE;gBACzG,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,MAAM,EAAE,CAAC;aACZ;QACL,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC1C,OAAO,cAAM,OAAA,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,EAA5C,CAA4C,CAAC;IAC9D,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IAExB,kEAAkE;IAClE,SAAS,CAAC;QACN,IAAM,MAAM,GAAG,MAAM,IAAI,IAAI,KAAK,QAAQ,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;QAC7D,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,4BAA4B,EAAE,UAAG,SAAS,OAAI,CAAC,CAAC;QAC3F,OAAO,cAAM,OAAA,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAApD,CAAoD,CAAC;IACtE,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAE9B,kEAAkE;IAClE,SAAS,CAAC;QACN,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,OAAO,YAAY,WAAW,EAAE;YACrD,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC1B,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC;SAC5B;IACL,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,IAAM,WAAW,GAAG,UAAC,KAAyB;QAC1C,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAM,MAAM,GAAG,UAAC,IAAkB;YAC9B,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC3F,CAAC,CAAC;QACF,IAAM,IAAI,GAAG;YACT,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAClD,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YAC9C,YAAY,CAAC,UAAC,KAAK;gBACf,UAAU,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;gBACjC,OAAO,KAAK,CAAC;YACjB,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAC/C,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC,CAAC;IAEF,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEzC,IAAM,UAAU,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAEhH,OAAO,CACH,cAAK,SAAS,EAAE,MAAM,CAAC,IAAI,YACtB,MAAM,CAAC,CAAC,CAAC,CACN,eACI,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,UAAG,MAAM,CAAC,MAAM,cAAI,UAAU,CAAE,EAC3C,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,EAC3D,IAAI,EAAC,eAAe,gBACT,cAAc,EACzB,SAAS,EAAE,UAAC,KAAK;gBACb,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE;oBACxB,KAAK,CAAC,eAAe,EAAE,CAAC;oBACxB,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;iBACxC;YACL,CAAC,aAEA,IAAI,KAAK,QAAQ,IAAI,cAAK,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE,WAAW,iBAAc,MAAM,GAAG,EAC5G,KAAC,cAAc,IAAC,QAAQ,EAAE,QAAQ,GAAI,IACpC,CACT,CAAC,CAAC,CAAC,CACA,kBAAQ,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,gBAAa,gCAAgC,EAAC,KAAK,EAAC,uBAAuB,aACtI,KAAC,QAAQ,IAAC,IAAI,EAAE,EAAE,GAAI,EACrB,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,OAAO,KAAI,eAAM,SAAS,EAAE,MAAM,CAAC,WAAW,gBAAa,kBAAkB,GAAG,IACnF,CACZ,GACC,CACT,CAAC;AACN,CAAC","sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { useNavigate } from \"react-router-dom\";\nimport { Sparkles } from \"lucide-react\";\nimport styles from \"./SolidAgent.module.css\";\nimport { agentModeChanged, agentOpenRequested, type AgentState } from \"../../../redux/features/agentSlice\";\nimport { SolidAgentChat } from \"./SolidAgentChat\";\nimport { useAgentAvailability } from \"./useAgentAvailability\";\nimport { useAdminInputContext } from \"./useAdminInputContext\";\nimport { onBeforeUserMessageSend } from \"./client/beforeUserMessageSend\";\nimport { onAgentPublicEvent } from \"./client/agentPublicEvents\";\nimport type { AgentMessageContext, AgentMode } from \"./types\";\nimport { buildAdminCustomPath, buildAdminDashboardPath, buildAdminModelViewPath, buildAdminRecordFormPath } from \"../../../helpers/routePaths\";\nimport { requestAdminViewRefresh } from \"../../../helpers/adminViewRefresh\";\n\nconst PREFS_KEY = \"solid-agent.window\";\nconst MIN_DOCK = 360;\nconst MAX_DOCK = 720;\nconst _DEBUG_AGENT_EVENTS = true;\ntype WindowPrefs = { dockWidth: number; open?: boolean; mode?: Exclude<AgentMode, \"bubble\"> };\n\nfunction readPrefs(): WindowPrefs {\n try {\n const parsed = JSON.parse(localStorage.getItem(PREFS_KEY) ?? \"{}\");\n const mode = [\"compact\", \"docked\", \"maximized\"].includes(parsed.mode) ? parsed.mode as WindowPrefs[\"mode\"] : undefined;\n return { dockWidth: Number(parsed.dockWidth) || 420, open: parsed.open === true, mode };\n } catch {\n return { dockWidth: 420 };\n }\n}\n\nfunction writePrefs(prefs: Partial<WindowPrefs>) {\n try {\n localStorage.setItem(PREFS_KEY, JSON.stringify({ ...readPrefs(), ...prefs }));\n } catch {\n // ignore\n }\n}\n\n/**\n * The single mount point of the SolidX Agent in the admin shell (rendered by AdminLayout).\n * Shows the floating bubble and the chat window; hidden unless the `solidAgentEnabled` setting\n * is on, `solidxAgentBackendUrl` is set and the user has agent:invoke. Ctrl/Cmd+J toggles the window,\n * Esc inside it minimizes back to the bubble.\n */\nexport default function SolidAgentHost() {\n const dispatch = useDispatch();\n const navigate = useNavigate();\n const { ready, enabled, agentUrl, canUse } = useAgentAvailability();\n const inputContext = useAdminInputContext();\n const inputContextRef = useRef(inputContext);\n inputContextRef.current = inputContext;\n const agent = useSelector((state: any) => state.solidAgent as AgentState | undefined);\n const [dockWidth, setDockWidth] = useState(() => readPrefs().dockWidth);\n const prefsHydrated = useRef(false);\n const skipNextPrefsWrite = useRef(false);\n const windowRef = useRef<HTMLDivElement>(null);\n const openerRef = useRef<Element | null>(null);\n\n useEffect(() => onBeforeUserMessageSend((_request, signal) => {\n if (signal.aborted) throw new Error(\"Message preparation was cancelled.\");\n const context = inputContextRef.current;\n if (Object.keys(context).length === 0) return undefined;\n const summary = [context.moduleName, context.modelName, context.viewName, context.modelId]\n .filter((value) => value !== undefined && value !== null && value !== \"\")\n .join(\" · \");\n const contribution: AgentMessageContext = {\n schemaVersion: 1,\n namespaces: {\n \"solidx.admin\": {\n context: context as AgentMessageContext[\"namespaces\"][string][\"context\"],\n revision: JSON.stringify(context),\n ...(summary ? { displaySummary: summary } : {}),\n },\n },\n };\n return contribution;\n }), []);\n useEffect(() => {\n return onAgentPublicEvent(({ event, payload, agentType: source }) => {\n if (_DEBUG_AGENT_EVENTS && source === \"solidx\") console.log(`[SolidAgentHost] ${event}`, payload);\n if (event !== \"onAgentNavigationRequested\" || source !== \"solidx\") return;\n if (payload.schemaVersion !== 1 || payload.kind !== \"navigation\" || payload.domain !== \"solidx.admin\") return;\n\n const { operation, scope, moduleName, modelName, viewName, recordId, dashboardName, customRoute } = payload.payload;\n if (typeof moduleName !== \"string\" || typeof viewName !== \"string\") return;\n if (!/^[A-Za-z0-9_-]+$/.test(moduleName.trim())) return;\n if (operation === \"refresh\") {\n if (typeof modelName !== \"string\" || !/^[A-Za-z0-9_-]+$/.test(modelName.trim())) return;\n if (scope === \"collection\" && [\"list\", \"tree\", \"kanban\", \"card\"].includes(viewName)) {\n requestAdminViewRefresh({ moduleName, modelName, viewName: viewName as \"list\" | \"tree\" | \"kanban\" | \"card\", scope });\n } else if (scope === \"record\" && viewName === \"form\" && (typeof recordId === \"string\" || typeof recordId === \"number\")) {\n const refreshRecordId = String(recordId);\n if (!/^[A-Za-z0-9_-]+$/.test(refreshRecordId)) return;\n requestAdminViewRefresh({ moduleName, modelName, viewName, scope, recordId: refreshRecordId });\n }\n return;\n }\n if (operation !== undefined && operation !== \"navigate\") return;\n let path = \"\";\n if (viewName === \"dashboard\" && typeof dashboardName === \"string\") {\n path = buildAdminDashboardPath({ moduleName, dashboardName });\n } else if (viewName === \"custom\" && typeof customRoute === \"string\") {\n path = buildAdminCustomPath(customRoute);\n } else if ([\"list\", \"tree\", \"kanban\", \"card\"].includes(viewName) && typeof modelName === \"string\") {\n path = buildAdminModelViewPath({ moduleName, modelName, viewName });\n } else if (viewName === \"form\" && typeof modelName === \"string\"\n && /^[A-Za-z0-9_-]+$/.test(modelName.trim())\n && (typeof recordId === \"string\" || typeof recordId === \"number\")) {\n const recordKey = String(recordId);\n if (!/^[A-Za-z0-9_-]+$/.test(recordKey)) return;\n path = buildAdminRecordFormPath({ moduleName, modelName, recordId: recordKey, viewMode: \"view\" });\n }\n if (path) navigate(path);\n });\n }, [navigate]);\n\n const available = ready && enabled && canUse && !!agentUrl && !!agent;\n const mode = agent?.mode ?? \"bubble\";\n const isOpen = available && mode !== \"bubble\";\n\n // Restore the last visible presentation after the host has verified this user's access.\n useEffect(() => {\n if (!ready || !available || prefsHydrated.current) return;\n prefsHydrated.current = true;\n const prefs = readPrefs();\n if (prefs.open && prefs.mode) {\n if (mode !== prefs.mode) skipNextPrefsWrite.current = true;\n dispatch(agentModeChanged(prefs.mode));\n }\n }, [ready, available, dispatch, mode]);\n\n // Keep visibility and presentation in localStorage. When closed, retain the last mode so\n // the preference still records how the window had been presented.\n useEffect(() => {\n if (!prefsHydrated.current || !available) return;\n if (skipNextPrefsWrite.current) {\n skipNextPrefsWrite.current = false;\n return;\n }\n if (mode !== \"bubble\") writePrefs({ open: true, mode });\n else writePrefs({ open: false });\n }, [available, mode]);\n\n const toggle = useCallback(() => {\n if (!available) return;\n if (isOpen) dispatch(agentModeChanged(\"bubble\"));\n else {\n openerRef.current = document.activeElement;\n dispatch(agentOpenRequested());\n }\n }, [available, isOpen, dispatch]);\n\n // Ctrl/Cmd + J toggles the chat from anywhere in the admin app.\n useEffect(() => {\n if (!available) return;\n const onKey = (event: KeyboardEvent) => {\n if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === \"j\") {\n event.preventDefault();\n toggle();\n }\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [available, toggle]);\n\n // Docked mode pushes the admin shell over instead of covering it.\n useEffect(() => {\n const docked = isOpen && mode === \"docked\";\n document.body.classList.toggle(\"solid-agent-docked\", docked);\n document.documentElement.style.setProperty(\"--solid-agent-docked-width\", `${dockWidth}px`);\n return () => document.body.classList.remove(\"solid-agent-docked\");\n }, [isOpen, mode, dockWidth]);\n\n // Focus returns to whatever opened the chat when it is minimized.\n useEffect(() => {\n if (!isOpen && openerRef.current instanceof HTMLElement) {\n openerRef.current.focus();\n openerRef.current = null;\n }\n }, [isOpen]);\n\n const startResize = (event: React.PointerEvent) => {\n event.preventDefault();\n const onMove = (move: PointerEvent) => {\n setDockWidth(Math.max(MIN_DOCK, Math.min(MAX_DOCK, window.innerWidth - move.clientX)));\n };\n const onUp = () => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n setDockWidth((width) => {\n writePrefs({ dockWidth: width });\n return width;\n });\n };\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n };\n\n if (!available || !agentUrl) return null;\n\n const frameClass = mode === \"docked\" ? styles.docked : mode === \"maximized\" ? styles.maximized : styles.compact;\n\n return (\n <div className={styles.root}>\n {isOpen ? (\n <div\n ref={windowRef}\n className={`${styles.window} ${frameClass}`}\n style={mode === \"docked\" ? { width: dockWidth } : undefined}\n role=\"complementary\"\n aria-label=\"SolidX Agent\"\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n dispatch(agentModeChanged(\"bubble\"));\n }\n }}\n >\n {mode === \"docked\" && <div className={styles.resizeHandle} onPointerDown={startResize} aria-hidden=\"true\" />}\n <SolidAgentChat agentUrl={agentUrl} />\n </div>\n ) : (\n <button type=\"button\" className={styles.bubble} onClick={toggle} aria-label=\"Open the SolidX Agent (Ctrl+J)\" title=\"SolidX Agent (Ctrl+J)\">\n <Sparkles size={22} />\n {agent?.working && <span className={styles.bubbleBadge} aria-label=\"Agent is working\" />}\n </button>\n )}\n </div>\n );\n}\n"]}
@@ -1,16 +1,22 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { useDispatch, useSelector } from "react-redux";
3
+ import { useNavigate } from "react-router-dom";
3
4
  import { Sparkles } from "lucide-react";
4
5
  import styles from "./SolidAgent.module.css";
5
6
  import { agentModeChanged, agentOpenRequested, type AgentState } from "../../../redux/features/agentSlice";
6
7
  import { SolidAgentChat } from "./SolidAgentChat";
7
8
  import { useAgentAvailability } from "./useAgentAvailability";
8
9
  import { useAdminInputContext } from "./useAdminInputContext";
9
- import type { AgentMode } from "./types";
10
+ import { onBeforeUserMessageSend } from "./client/beforeUserMessageSend";
11
+ import { onAgentPublicEvent } from "./client/agentPublicEvents";
12
+ import type { AgentMessageContext, AgentMode } from "./types";
13
+ import { buildAdminCustomPath, buildAdminDashboardPath, buildAdminModelViewPath, buildAdminRecordFormPath } from "../../../helpers/routePaths";
14
+ import { requestAdminViewRefresh } from "../../../helpers/adminViewRefresh";
10
15
 
11
16
  const PREFS_KEY = "solid-agent.window";
12
17
  const MIN_DOCK = 360;
13
18
  const MAX_DOCK = 720;
19
+ const _DEBUG_AGENT_EVENTS = true;
14
20
  type WindowPrefs = { dockWidth: number; open?: boolean; mode?: Exclude<AgentMode, "bubble"> };
15
21
 
16
22
  function readPrefs(): WindowPrefs {
@@ -39,8 +45,11 @@ function writePrefs(prefs: Partial<WindowPrefs>) {
39
45
  */
40
46
  export default function SolidAgentHost() {
41
47
  const dispatch = useDispatch();
48
+ const navigate = useNavigate();
42
49
  const { ready, enabled, agentUrl, canUse } = useAgentAvailability();
43
50
  const inputContext = useAdminInputContext();
51
+ const inputContextRef = useRef(inputContext);
52
+ inputContextRef.current = inputContext;
44
53
  const agent = useSelector((state: any) => state.solidAgent as AgentState | undefined);
45
54
  const [dockWidth, setDockWidth] = useState(() => readPrefs().dockWidth);
46
55
  const prefsHydrated = useRef(false);
@@ -48,6 +57,64 @@ export default function SolidAgentHost() {
48
57
  const windowRef = useRef<HTMLDivElement>(null);
49
58
  const openerRef = useRef<Element | null>(null);
50
59
 
60
+ useEffect(() => onBeforeUserMessageSend((_request, signal) => {
61
+ if (signal.aborted) throw new Error("Message preparation was cancelled.");
62
+ const context = inputContextRef.current;
63
+ if (Object.keys(context).length === 0) return undefined;
64
+ const summary = [context.moduleName, context.modelName, context.viewName, context.modelId]
65
+ .filter((value) => value !== undefined && value !== null && value !== "")
66
+ .join(" · ");
67
+ const contribution: AgentMessageContext = {
68
+ schemaVersion: 1,
69
+ namespaces: {
70
+ "solidx.admin": {
71
+ context: context as AgentMessageContext["namespaces"][string]["context"],
72
+ revision: JSON.stringify(context),
73
+ ...(summary ? { displaySummary: summary } : {}),
74
+ },
75
+ },
76
+ };
77
+ return contribution;
78
+ }), []);
79
+ useEffect(() => {
80
+ return onAgentPublicEvent(({ event, payload, agentType: source }) => {
81
+ if (_DEBUG_AGENT_EVENTS && source === "solidx") console.log(`[SolidAgentHost] ${event}`, payload);
82
+ if (event !== "onAgentNavigationRequested" || source !== "solidx") return;
83
+ if (payload.schemaVersion !== 1 || payload.kind !== "navigation" || payload.domain !== "solidx.admin") return;
84
+
85
+ const { operation, scope, moduleName, modelName, viewName, recordId, dashboardName, customRoute } = payload.payload;
86
+ if (typeof moduleName !== "string" || typeof viewName !== "string") return;
87
+ if (!/^[A-Za-z0-9_-]+$/.test(moduleName.trim())) return;
88
+ if (operation === "refresh") {
89
+ if (typeof modelName !== "string" || !/^[A-Za-z0-9_-]+$/.test(modelName.trim())) return;
90
+ if (scope === "collection" && ["list", "tree", "kanban", "card"].includes(viewName)) {
91
+ requestAdminViewRefresh({ moduleName, modelName, viewName: viewName as "list" | "tree" | "kanban" | "card", scope });
92
+ } else if (scope === "record" && viewName === "form" && (typeof recordId === "string" || typeof recordId === "number")) {
93
+ const refreshRecordId = String(recordId);
94
+ if (!/^[A-Za-z0-9_-]+$/.test(refreshRecordId)) return;
95
+ requestAdminViewRefresh({ moduleName, modelName, viewName, scope, recordId: refreshRecordId });
96
+ }
97
+ return;
98
+ }
99
+ if (operation !== undefined && operation !== "navigate") return;
100
+ let path = "";
101
+ if (viewName === "dashboard" && typeof dashboardName === "string") {
102
+ path = buildAdminDashboardPath({ moduleName, dashboardName });
103
+ } else if (viewName === "custom" && typeof customRoute === "string") {
104
+ path = buildAdminCustomPath(customRoute);
105
+ } else if (["list", "tree", "kanban", "card"].includes(viewName) && typeof modelName === "string") {
106
+ path = buildAdminModelViewPath({ moduleName, modelName, viewName });
107
+ } else if (viewName === "form" && typeof modelName === "string"
108
+ && /^[A-Za-z0-9_-]+$/.test(modelName.trim())
109
+ && (typeof recordId === "string" || typeof recordId === "number")) {
110
+ const recordKey = String(recordId);
111
+ if (!/^[A-Za-z0-9_-]+$/.test(recordKey)) return;
112
+ path = buildAdminRecordFormPath({ moduleName, modelName, recordId: recordKey, viewMode: "view" });
113
+ }
114
+ if (path) navigate(path);
115
+ });
116
+ }, [navigate]);
117
+
51
118
  const available = ready && enabled && canUse && !!agentUrl && !!agent;
52
119
  const mode = agent?.mode ?? "bubble";
53
120
  const isOpen = available && mode !== "bubble";
@@ -151,7 +218,7 @@ export default function SolidAgentHost() {
151
218
  }}
152
219
  >
153
220
  {mode === "docked" && <div className={styles.resizeHandle} onPointerDown={startResize} aria-hidden="true" />}
154
- <SolidAgentChat agentUrl={agentUrl} inputContext={inputContext} />
221
+ <SolidAgentChat agentUrl={agentUrl} />
155
222
  </div>
156
223
  ) : (
157
224
  <button type="button" className={styles.bubble} onClick={toggle} aria-label="Open the SolidX Agent (Ctrl+J)" title="SolidX Agent (Ctrl+J)">
@@ -1 +1 @@
1
- {"version":3,"file":"agentEvents.d.ts","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/agentEvents.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAE3H;;;;;;;GAOG;AAEH,6FAA6F;AAC7F,MAAM,MAAM,gBAAgB,GAAG;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,wBAAgB,iBAAiB,IAAI,gBAAgB,CAapD;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAKzE;AAGD,eAAO,MAAM,UAAU,WAAY,MAAM,WAAuC,CAAC;AA0DjF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,UAAQ,EAAE,EAAE,SAAa,QAsK9G;AAED,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,aAAa,EAAE,CAU3D"}
1
+ {"version":3,"file":"agentEvents.d.ts","sourceRoot":"","sources":["../../../../src/components/core/solid-agent/agentEvents.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAE3H;;;;;;;GAOG;AAEH,6FAA6F;AAC7F,MAAM,MAAM,gBAAgB,GAAG;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,wBAAgB,iBAAiB,IAAI,gBAAgB,CAapD;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAKzE;AAGD,eAAO,MAAM,UAAU,WAAY,MAAM,WAAuC,CAAC;AA0DjF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,UAAQ,EAAE,EAAE,SAAa,QAwK9G;AAED,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,aAAa,EAAE,CAU3D"}