@ai-matrx/chat 0.4.15 → 0.4.18

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 (59) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/agent-copy/AlchemySurfaceBridge.js +1 -1
  3. package/dist/agents/components/agent-widgets/chat-assistant/AssistantCardStack.js +1 -1
  4. package/dist/agents/components/ambient-assistant/ScrollAssistantLauncher.js +1 -1
  5. package/dist/agents/components/conversation-actions/rename/ConversationRenameDialogHost.js +1 -1
  6. package/dist/agents/components/messages-display/assistant/AgentEmptyMessageDisplay.js +1 -1
  7. package/dist/agents/components/messages-display/assistant/AssistantMessageFooter.js +3 -1
  8. package/dist/agents/components/messages-display/user/AgentUserMessage.js +95 -85
  9. package/dist/agents/components/messages-display/user/useCollapsibleMessageText.d.ts +1 -1
  10. package/dist/agents/components/messages-display/user/useCollapsibleMessageText.js +5 -4
  11. package/dist/agents/components/shared/AgentConversationColumn.d.ts +7 -1
  12. package/dist/agents/components/shared/AgentConversationColumn.js +14 -7
  13. package/dist/agents/components/shared/transcript-layout.d.ts +31 -0
  14. package/dist/agents/components/shared/transcript-layout.js +38 -0
  15. package/dist/agents/redux/execution-system/message-crud/answer-text-splice.js +1 -1
  16. package/dist/agents/redux/execution-system/messages/messages.selectors.js +1 -1
  17. package/dist/canvas/workspace/ChatPanelTitleMenu.js +8 -2
  18. package/dist/canvas/workspace/ShellChatDock.js +60 -10
  19. package/dist/canvas/workspace/shell-chat-route.d.ts +5 -4
  20. package/dist/canvas/workspace/shell-chat-route.js +2 -4
  21. package/dist/canvas/workspace/side-chat-address.d.ts +91 -0
  22. package/dist/canvas/workspace/side-chat-address.js +110 -0
  23. package/dist/canvas/workspace/useCanvasWorkspaceConversation.d.ts +31 -12
  24. package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +105 -36
  25. package/dist/cx-chat/components/ChatMobileAgentName.js +1 -1
  26. package/dist/cx-chat/components/ChatSidebarClient.js +1 -1
  27. package/dist/cx-chat/components/ChatWelcomeClient.js +1 -1
  28. package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
  29. package/dist/cx-chat/components/user-input/ConversationInput.js +1 -1
  30. package/dist/cx-conversation/ConversationInput.js +1 -1
  31. package/dist/cx-conversation/MessageList.js +1 -1
  32. package/dist/host/db-types.d.ts +9 -12
  33. package/dist/host/ui-slots.js +17 -2
  34. package/dist/surfaces/components/chrome/SurfaceAgentsHeaderButton.d.ts +1 -1
  35. package/dist/surfaces/components/chrome/SurfaceAgentsHeaderButton.js +1 -1
  36. package/dist/tool-call-visualization/db-renderer/DbToolRenderer.js +1 -1
  37. package/dist/{next → ui}/lazy/AgentAssistantMessage.js +3 -4
  38. package/dist/{next → ui}/lazy/AgentPickerSheet.js +3 -4
  39. package/dist/{next → ui}/lazy/ChatVariableInputs.js +5 -7
  40. package/dist/{next → ui}/lazy/ConversationRenameDialogHostImpl.d.ts +1 -1
  41. package/dist/{next → ui}/lazy/ConversationRenameDialogHostImpl.js +3 -3
  42. package/dist/{next → ui}/lazy/CxAssistantMessage.js +3 -4
  43. package/dist/{next → ui}/lazy/DbToolRendererImpl.js +3 -3
  44. package/dist/{next → ui}/lazy/IconResolver.js +3 -4
  45. package/dist/{next → ui}/lazy/PublicChatVariableInputs.js +5 -7
  46. package/dist/{next → ui}/lazy/ScrollAssistantLaunchers.d.ts +1 -1
  47. package/dist/{next → ui}/lazy/ScrollAssistantLaunchers.js +3 -5
  48. package/dist/{next → ui}/lazy/SurfaceAgentsPanelImpl.js +2 -3
  49. package/dist/utils/client-lazy.d.ts +16 -0
  50. package/dist/utils/client-lazy.js +27 -0
  51. package/package.json +7 -7
  52. /package/dist/{next → ui}/lazy/AgentAssistantMessage.d.ts +0 -0
  53. /package/dist/{next → ui}/lazy/AgentPickerSheet.d.ts +0 -0
  54. /package/dist/{next → ui}/lazy/ChatVariableInputs.d.ts +0 -0
  55. /package/dist/{next → ui}/lazy/CxAssistantMessage.d.ts +0 -0
  56. /package/dist/{next → ui}/lazy/DbToolRendererImpl.d.ts +0 -0
  57. /package/dist/{next → ui}/lazy/IconResolver.d.ts +0 -0
  58. /package/dist/{next → ui}/lazy/PublicChatVariableInputs.d.ts +0 -0
  59. /package/dist/{next → ui}/lazy/SurfaceAgentsPanelImpl.d.ts +0 -0
@@ -7,8 +7,13 @@ import { resumeConversation } from "../../agents/redux/execution-system/thunks/r
7
7
  import { useAppDispatch, useAppSelector } from "../../store/hooks.js";
8
8
  import { selectShouldPromptForOrganization } from "../../host/ui-slots.js";
9
9
  import { selectIsCacheOnly } from "../../agents/redux/execution-system/conversations/conversations.selectors.js";
10
+ import { selectLatestRequestId } from "../../agents/redux/execution-system/selectors/aggregate.selectors.js";
11
+ import { selectRequestError } from "../../agents/redux/execution-system/active-requests/active-requests.selectors.js";
12
+ import { extractFlatText } from "../../agents/redux/execution-system/messages/messages.selectors.js";
13
+ import { setUserInputText } from "../../agents/redux/execution-system/instance-user-input/instance-user-input.slice.js";
10
14
  import { replaceAddressWithoutNavigating } from "@ai-matrx/rich-content/utils/addressWithoutNavigating";
11
15
  import { describeLaunchError } from "./describe-launch-error.js";
16
+ import { sideChatAddress } from "./side-chat-address.js";
12
17
  import { selectOrganizationId, ensureOrganizationContext, isOrganizationSelectionCancelled } from "../../host/org.js";
13
18
  function initialRequest(start) {
14
19
  if (start?.kind === "agent") return { kind: "agent", agentId: start.agentId, nonce: 0 };
@@ -16,9 +21,13 @@ function initialRequest(start) {
16
21
  return { kind: "open", conversationId: start.conversationId, agentId: start.agentId ?? null, nonce: 0 };
17
22
  return { kind: "new", nonce: 0 };
18
23
  }
19
- function conversationInAddress(param, search) {
20
- const value = new URLSearchParams(search).get(param);
21
- return value && isUuidShape(value) ? value : null;
24
+ function requestFromLink(link) {
25
+ const c = link?.conversation;
26
+ if (!c) return null;
27
+ if (c.kind === "new") return { kind: "new", nonce: 1 };
28
+ if (c.kind === "agent") return { kind: "agent", agentId: c.agentId, nonce: 1 };
29
+ if (c.kind === "mandate") return { kind: "new", nonce: 1, mandateKey: c.mandateKey };
30
+ return { kind: "open", conversationId: c.conversationId, agentId: null, nonce: 0 };
22
31
  }
23
32
  function workspaceChatMemoryKey(surfaceKey) {
24
33
  return `matrx:workspace-chat:${surfaceKey}`;
@@ -60,29 +69,35 @@ function releaseWorkspaceConversationId(surfaceKey) {
60
69
  } catch {
61
70
  }
62
71
  }
63
- function addressWithConversation(param, search, conversationId) {
64
- const params = new URLSearchParams(search);
65
- if (conversationId) params.set(param, conversationId);
66
- else params.delete(param);
67
- const qs = params.toString();
68
- return qs ? `?${qs}` : "";
72
+ function isConversationIdTaken(error) {
73
+ if (!error) return false;
74
+ const text = `${typeof error.error_type === "string" ? error.error_type : ""} ${typeof error.message === "string" ? error.message : ""}`;
75
+ return /conversation_already_exists|Conversation already exists/i.test(text);
69
76
  }
70
77
  function isCanvasWorkspaceStart(input) {
71
78
  return "kind" in input;
72
79
  }
73
80
  function resolveStartOptions(input) {
74
- if (!input) return { enabled: true, start: void 0, ownSurface: false, addressParam: null };
81
+ if (!input) return { enabled: true, start: void 0, ownSurface: false, address: null };
75
82
  if (isCanvasWorkspaceStart(input))
76
- return { enabled: true, start: input, ownSurface: false, addressParam: null };
83
+ return { enabled: true, start: input, ownSurface: false, address: null };
77
84
  return {
78
85
  enabled: input.enabled ?? true,
79
86
  start: input.start,
80
87
  ownSurface: input.surfaceName === null,
81
- addressParam: input.addressParam ?? null
88
+ address: input.address ?? null
82
89
  };
83
90
  }
84
91
  function useCanvasWorkspaceConversation(surfaceKey, input) {
85
- const { enabled, start, ownSurface, addressParam } = resolveStartOptions(input);
92
+ const { enabled, start, ownSurface, address } = resolveStartOptions(input);
93
+ const hasAddress = address !== null;
94
+ const link = address?.link;
95
+ const addressOpen = address?.open ?? false;
96
+ const hostedElsewhere = address?.hostedElsewhere ?? false;
97
+ const onAddressWrite = useRef(address?.onWrite);
98
+ useEffect(() => {
99
+ onAddressWrite.current = address?.onWrite;
100
+ });
86
101
  const dispatch = useAppDispatch();
87
102
  const { launchMandate, launchAgent } = useAgentLauncher();
88
103
  const [request, setRequest] = useState(() => initialRequest(start));
@@ -92,39 +107,94 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
92
107
  const organizationId = useAppSelector(selectOrganizationId);
93
108
  const promptForOrganization = useAppSelector(selectShouldPromptForOrganization);
94
109
  const waitingForOrganization = request.kind !== "open" && !organizationId;
95
- const [addressRead, setAddressRead] = useState(addressParam === null);
110
+ const [addressRead, setAddressRead] = useState(!hasAddress);
111
+ const appliedLink = useRef(null);
96
112
  const serverHasIt = useAppSelector(
97
113
  (state) => conversationId ? !selectIsCacheOnly(conversationId)(state) : false
98
114
  );
99
- const [keyedFor, setKeyedFor] = useState({ surfaceKey, addressParam });
100
- if (keyedFor.surfaceKey !== surfaceKey || keyedFor.addressParam !== addressParam) {
101
- setKeyedFor({ surfaceKey, addressParam });
115
+ const latestRequestId = useAppSelector(
116
+ (state) => conversationId ? selectLatestRequestId(conversationId)(state) : void 0
117
+ );
118
+ const sendStarted = latestRequestId !== void 0;
119
+ const idTaken = useAppSelector(
120
+ (state) => latestRequestId ? isConversationIdTaken(selectRequestError(latestRequestId)(state)) : false
121
+ );
122
+ const refusedText = useAppSelector((state) => {
123
+ if (!idTaken || !conversationId) return "";
124
+ const entry = state.messages?.byConversationId?.[conversationId];
125
+ const lastUser = entry?.orderedIds.map((id) => entry.byId[id]).filter((record) => record?.role === "user").at(-1);
126
+ const sent = lastUser ? extractFlatText(lastUser).trim() : "";
127
+ const typed = state.instanceUserInput?.byConversationId?.[conversationId]?.text?.trim() ?? "";
128
+ return [sent, typed === sent ? "" : typed].filter(Boolean).join("\n\n");
129
+ });
130
+ const [keyedFor, setKeyedFor] = useState({ surfaceKey, hasAddress });
131
+ if (keyedFor.surfaceKey !== surfaceKey || keyedFor.hasAddress !== hasAddress) {
132
+ setKeyedFor({ surfaceKey, hasAddress });
102
133
  setRequest(initialRequest(start));
103
134
  setConversationId(null);
104
135
  setFailure(null);
105
- setAddressRead(addressParam === null);
136
+ setAddressRead(!hasAddress);
106
137
  }
107
138
  useEffect(() => {
108
- if (addressRead || addressParam === null) return;
109
- const named = conversationInAddress(addressParam, window.location.search);
110
- const recalled = named ? null : recallWorkspaceConversation(surfaceKey);
111
- const target = named ?? recalled;
112
- if (target && !start) {
113
- setRequest(
114
- (current) => current.kind === "new" && current.nonce === 0 ? { kind: "open", conversationId: target, agentId: null, nonce: 0, recalled: !named } : current
115
- );
139
+ if (addressRead || !hasAddress || link === void 0) return;
140
+ appliedLink.current = link;
141
+ const linked = requestFromLink(link);
142
+ if (linked && !start) setRequest(linked);
143
+ else if (!start) {
144
+ const recalled = recallWorkspaceConversation(surfaceKey);
145
+ if (recalled) {
146
+ setRequest(
147
+ (current) => current.kind === "new" && current.nonce === 0 ? { kind: "open", conversationId: recalled, agentId: null, nonce: 0, recalled: true } : current
148
+ );
149
+ }
116
150
  }
117
151
  setAddressRead(true);
118
- }, [addressRead, addressParam, start, surfaceKey]);
152
+ }, [addressRead, hasAddress, link, start, surfaceKey]);
153
+ useEffect(() => {
154
+ if (!addressRead || !link || link === appliedLink.current) return;
155
+ appliedLink.current = link;
156
+ const linked = requestFromLink(link);
157
+ if (!linked) return;
158
+ if (linked.kind === "open" && linked.conversationId === conversationId) return;
159
+ setConversationId(null);
160
+ setFailure(null);
161
+ setRequest((current) => ({ ...linked, nonce: current.nonce + 1 }));
162
+ }, [addressRead, link, conversationId]);
119
163
  useEffect(() => {
120
- if (addressParam === null || !addressRead || !conversationId) return;
121
- const shown = request.kind === "open" || serverHasIt ? conversationId : null;
122
- rememberWorkspaceConversation(surfaceKey, shown);
123
- if (shown) releaseWorkspaceConversationId(surfaceKey);
124
- const search = addressWithConversation(addressParam, window.location.search, shown);
164
+ if (!hasAddress || !addressRead) return;
165
+ if (!conversationId && addressOpen) return;
166
+ const shown = conversationId && (request.kind === "open" || serverHasIt) ? conversationId : null;
167
+ if (conversationId) {
168
+ const remembered = shown ?? (sendStarted && !idTaken ? conversationId : null);
169
+ rememberWorkspaceConversation(surfaceKey, remembered);
170
+ if (remembered) releaseWorkspaceConversationId(surfaceKey);
171
+ }
172
+ const search = sideChatAddress(window.location.search, { conversationId: shown, open: addressOpen, hostedElsewhere });
125
173
  if (search === window.location.search) return;
174
+ onAddressWrite.current?.(search);
126
175
  replaceAddressWithoutNavigating(`${window.location.pathname}${search}${window.location.hash}`);
127
- }, [addressParam, addressRead, conversationId, request.kind, serverHasIt, surfaceKey]);
176
+ }, [hasAddress, addressRead, addressOpen, hostedElsewhere, conversationId, request.kind, serverHasIt, sendStarted, idTaken, surfaceKey]);
177
+ const healed = useRef(null);
178
+ const carry = useRef(null);
179
+ useEffect(() => {
180
+ if (!idTaken || serverHasIt || !conversationId || !latestRequestId) return;
181
+ if (healed.current === latestRequestId) return;
182
+ healed.current = latestRequestId;
183
+ console.warn("[canvas-workspace] the new chat's id was already taken (409); starting it again under a fresh id", conversationId);
184
+ releaseWorkspaceConversationId(surfaceKey);
185
+ if (hasAddress) rememberWorkspaceConversation(surfaceKey, null);
186
+ carry.current = refusedText ? { text: refusedText, from: conversationId } : null;
187
+ setConversationId(null);
188
+ setFailure(null);
189
+ setRequest(
190
+ (current) => current.kind === "agent" ? { kind: "agent", agentId: current.agentId, nonce: current.nonce + 1 } : { kind: "new", nonce: current.nonce + 1, ...current.kind === "new" && current.mandateKey ? { mandateKey: current.mandateKey } : {} }
191
+ );
192
+ }, [idTaken, serverHasIt, conversationId, latestRequestId, surfaceKey, hasAddress, refusedText]);
193
+ useEffect(() => {
194
+ if (!conversationId || !carry.current || carry.current.from === conversationId) return;
195
+ dispatch(setUserInputText({ conversationId, text: carry.current.text }));
196
+ carry.current = null;
197
+ }, [conversationId, dispatch]);
128
198
  useEffect(() => {
129
199
  if (!enabled || waitingForOrganization || !addressRead) return;
130
200
  const key = `${surfaceKey}#${request.kind}#${request.nonce}`;
@@ -133,7 +203,7 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
133
203
  const stale = () => handled.current !== key;
134
204
  if (request.kind === "new" || request.kind === "agent") {
135
205
  const runtime = ownSurface ? { runtime: { surfaceName: null } } : {};
136
- const reserved = addressParam !== null ? reserveWorkspaceConversationId(surfaceKey, request.nonce > 0) : void 0;
206
+ const reserved = hasAddress ? reserveWorkspaceConversationId(surfaceKey, request.nonce > 0) : void 0;
137
207
  const reservation = reserved ? { conversationId: reserved } : {};
138
208
  const launch = request.kind === "agent" ? launchAgent(request.agentId, { surfaceKey, sourceFeature: "chat", ...reservation, ...runtime }) : launchMandate(request.mandateKey ?? DEFAULT_NEW_CHAT_MANDATE_KEY, {
139
209
  surfaceKey,
@@ -215,8 +285,7 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
215
285
  return { conversation, conversationId, startNew, openExisting, startWith };
216
286
  }
217
287
  export {
218
- addressWithConversation,
219
- conversationInAddress,
288
+ isConversationIdTaken,
220
289
  recallWorkspaceConversation,
221
290
  releaseWorkspaceConversationId,
222
291
  rememberWorkspaceConversation,
@@ -3,7 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
4
  import { ChevronDown } from "lucide-react";
5
5
  import { usePathname, useSearchParams, useRouter } from "../../host/navigation.js";
6
- import { AgentPickerSheet } from "../../next/lazy/AgentPickerSheet.js";
6
+ import { AgentPickerSheet } from "../../ui/lazy/AgentPickerSheet.js";
7
7
  import { useAgentName } from "../../agents/identity/agent-identity.js";
8
8
  import { pushAppHref } from "../../host/ui-slots.js";
9
9
  import { Button } from "@ai-matrx/design-system/controls";
@@ -2,7 +2,7 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback, useRef, useEffect } from "react";
4
4
  import { useRouter, usePathname, useSearchParams } from "../../host/navigation.js";
5
- import { AgentPickerSheet } from "../../next/lazy/AgentPickerSheet.js";
5
+ import { AgentPickerSheet } from "../../ui/lazy/AgentPickerSheet.js";
6
6
  import { ChevronDown } from "lucide-react";
7
7
  import { SidebarActions } from "./sidebar/SidebarActions.js";
8
8
  import { SsrSidebarAgents } from "./SsrSidebarAgents.js";
@@ -2,7 +2,7 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState, useCallback } from "react";
4
4
  import { useRouter } from "../../host/navigation.js";
5
- import { AgentPickerSheet } from "../../next/lazy/AgentPickerSheet.js";
5
+ import { AgentPickerSheet } from "../../ui/lazy/AgentPickerSheet.js";
6
6
  import { useAppSelector, useAppDispatch } from "../../store/hooks.js";
7
7
  import { selectAgentById } from "../../agents/redux/agent-definition/selectors.js";
8
8
  import { selectLatestConversationId } from "../../agents/redux/execution-system/selectors/aggregate.selectors.js";
@@ -2,7 +2,7 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useCallback, useState } from "react";
4
4
  import { useRouter } from "../../../host/navigation.js";
5
- import { AgentPickerSheet } from "../../../next/lazy/AgentPickerSheet.js";
5
+ import { AgentPickerSheet } from "../../../ui/lazy/AgentPickerSheet.js";
6
6
  import { useAppSelector } from "../../../store/hooks.js";
7
7
  import { selectIsAdmin, selectIsAuthenticated } from "../../../host/identity.js";
8
8
  import { useDebugContext } from "../../../host/prefs-react.js";
@@ -9,7 +9,7 @@ import {
9
9
  useMemo
10
10
  } from "react";
11
11
  import { useDebugContext } from "../../../host/prefs-react.js";
12
- import { GuidedVariableInputs, StackedVariableInputs } from "../../../next/lazy/ChatVariableInputs.js";
12
+ import { GuidedVariableInputs, StackedVariableInputs } from "../../../ui/lazy/ChatVariableInputs.js";
13
13
  import { useRouter, useSearchParams, usePathname } from "../../../host/navigation.js";
14
14
  import {
15
15
  CornerDownLeft,
@@ -8,7 +8,7 @@ import {
8
8
  useCallback,
9
9
  useMemo
10
10
  } from "react";
11
- import { GuidedVariableInputs, PublicVariableInputs } from "../next/lazy/PublicChatVariableInputs.js";
11
+ import { GuidedVariableInputs, PublicVariableInputs } from "../ui/lazy/PublicChatVariableInputs.js";
12
12
  import { useRouter, useSearchParams, usePathname } from "../host/navigation.js";
13
13
  import {
14
14
  CornerDownLeft,
@@ -10,7 +10,7 @@ import {
10
10
  selectUIState
11
11
  } from "./_legacy-stubs.js";
12
12
  import { chatConversationsActions } from "./_legacy-stubs.js";
13
- import { AssistantMessage } from "../next/lazy/CxAssistantMessage.js";
13
+ import { AssistantMessage } from "../ui/lazy/CxAssistantMessage.js";
14
14
  import { MessageErrorBoundary } from "./MessageErrorBoundary.js";
15
15
  import { UserMessage } from "./UserMessage.js";
16
16
  function StreamingAssistantMessage({
@@ -28706,6 +28706,15 @@ export type ChatDatabase = {
28706
28706
  };
28707
28707
  Returns: Json;
28708
28708
  };
28709
+ _knob_steward_refusal: {
28710
+ Args: {
28711
+ p_organization_id: string;
28712
+ p_scope_id: string;
28713
+ p_scope_kind: string;
28714
+ p_uid: string;
28715
+ };
28716
+ Returns: Json;
28717
+ };
28709
28718
  _lifecycle_partition_guard: {
28710
28719
  Args: {
28711
28720
  p_partition: string;
@@ -43832,18 +43841,6 @@ export type ChatDatabase = {
43832
43841
  };
43833
43842
  Returns: Json;
43834
43843
  };
43835
- mnd_run_history: {
43836
- Args: {
43837
- p_limit?: number;
43838
- p_mandate_key: string;
43839
- p_offset?: number;
43840
- p_org_id?: string;
43841
- p_status?: string;
43842
- p_user_id?: string;
43843
- p_view?: string;
43844
- };
43845
- Returns: Json;
43846
- };
43847
43844
  move_file: {
43848
43845
  Args: {
43849
43846
  p_file_id: string;
@@ -291,7 +291,22 @@ const readListRpc = slotFn("readListRpc");
291
291
  const ReadFailure = slotComponent("ReadFailure", unregisteredWidget("ReadFailure"));
292
292
  const ItemRow = slotComponent("ItemRow", unregisteredWidget("ItemRow"));
293
293
  const UntrustedCount = slotComponent("UntrustedCount", unregisteredWidget("UntrustedCount"));
294
- const StaleDataNotice = slotComponent("StaleDataNotice", unregisteredWidget("StaleDataNotice"));
294
+ function PlainStaleDataNotice(props) {
295
+ const what = props.what ?? "this";
296
+ const sentence = props.hasData ? `Showing what was last read; ${what} could not be refreshed.` : `Could not read ${what}.`;
297
+ return createElement(
298
+ "div",
299
+ { role: "status", className: "text-xs text-muted-foreground", "data-stale-data-notice": "" },
300
+ sentence,
301
+ props.detail ? ` ${props.detail}` : null,
302
+ props.onRetry ? createElement(
303
+ "button",
304
+ { type: "button", className: "ml-2 underline", onClick: () => props.onRetry?.() },
305
+ "Try again"
306
+ ) : null
307
+ );
308
+ }
309
+ const StaleDataNotice = slotComponent("StaleDataNotice", PlainStaleDataNotice);
295
310
  const WorkspaceGate = slotComponent("WorkspaceGate", DefaultWorkspaceGate);
296
311
  const OrganizationContextNotice = slotComponent("OrganizationContextNotice", unregisteredWidget("OrganizationContextNotice"));
297
312
  const JsonInspector = slotComponent("JsonInspector", unregisteredWidget("JsonInspector"));
@@ -315,7 +330,7 @@ const ReferenceCopyMenuItem = slotComponent("ReferenceCopyMenuItem", unregistere
315
330
  const ReferenceCopyButton = slotComponent("ReferenceCopyButton", unregisteredWidget("ReferenceCopyButton"));
316
331
  const MandateNotesPanel = slotComponent("MandateNotesPanel", unregisteredWidget("MandateNotesPanel"));
317
332
  const SurfaceBoundAgentsList = slotComponent("SurfaceBoundAgentsList", unregisteredWidget("SurfaceBoundAgentsList"));
318
- const ProposedDirectivesZone = slotComponent("ProposedDirectivesZone", unregisteredWidget("ProposedDirectivesZone"));
333
+ const ProposedDirectivesZone = slotComponent("ProposedDirectivesZone", optionalWidget("ProposedDirectivesZone"));
319
334
  const EntityCommentPopover = slotComponent("EntityCommentPopover", unregisteredWidget("EntityCommentPopover"));
320
335
  const ProTextarea = slotComponent("ProTextarea", (props) => {
321
336
  const { onChange, value, className, placeholder, rows, id, ref, autoFocus, disabled } = props;
@@ -1,4 +1,4 @@
1
- import { SurfaceAgentsPanelImpl } from "../../../next/lazy/SurfaceAgentsPanelImpl.js";
1
+ import { SurfaceAgentsPanelImpl } from "../../../ui/lazy/SurfaceAgentsPanelImpl.js";
2
2
  /** What a guest is told when they reach for Agents — one copy for every door. */
3
3
  export declare const AGENTS_AUTH_GATE: {
4
4
  featureName: string;
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useRef, useState } from "react";
4
- import { SurfaceAgentsPanelImpl } from "../../../next/lazy/SurfaceAgentsPanelImpl.js";
4
+ import { SurfaceAgentsPanelImpl } from "../../../ui/lazy/SurfaceAgentsPanelImpl.js";
5
5
  import { TapTargetButtonTransparent } from "@ai-matrx/tap-target";
6
6
  import { INTELLIGENCE_ICON } from "@ai-matrx/icons/domain";
7
7
  import {
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { DbToolRendererImplLazy as LazyImpl } from "../../next/lazy/DbToolRendererImpl.js";
3
+ import { DbToolRendererImplLazy as LazyImpl } from "../../ui/lazy/DbToolRendererImpl.js";
4
4
  const DbToolRenderer = (props) => {
5
5
  return /* @__PURE__ */ jsx(LazyImpl, { ...props });
6
6
  };
@@ -1,12 +1,11 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const AgentAssistantMessage = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const AgentAssistantMessage = clientLazy(
4
4
  () => import("../../agents/components/messages-display/assistant/AgentAssistantMessage.js").then(
5
5
  (m) => ({
6
6
  default: m.AgentAssistantMessage
7
7
  })
8
- ),
9
- { ssr: false }
8
+ )
10
9
  );
11
10
  export {
12
11
  AgentAssistantMessage
@@ -1,10 +1,9 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const AgentPickerSheet = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const AgentPickerSheet = clientLazy(
4
4
  () => import("../../cx-chat/components/agent/AgentPickerSheet.js").then(
5
5
  (m) => ({ default: m.AgentPickerSheet })
6
- ),
7
- { ssr: false }
6
+ )
8
7
  );
9
8
  export {
10
9
  AgentPickerSheet
@@ -1,16 +1,14 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const GuidedVariableInputs = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const GuidedVariableInputs = clientLazy(
4
4
  () => import("../../cx-chat/components/user-input/GuidedVariableInputs.js").then(
5
5
  (m) => ({ default: m.GuidedVariableInputs })
6
- ),
7
- { ssr: false }
6
+ )
8
7
  );
9
- const StackedVariableInputs = dynamic(
8
+ const StackedVariableInputs = clientLazy(
10
9
  () => import("../../cx-chat/components/user-input/StackedVariableInputs.js").then(
11
10
  (m) => ({ default: m.StackedVariableInputs })
12
- ),
13
- { ssr: false }
11
+ )
14
12
  );
15
13
  export {
16
14
  GuidedVariableInputs,
@@ -1 +1 @@
1
- export declare const ConversationRenameDialogHostImpl: import("react").ComponentType<{}>;
1
+ export declare const ConversationRenameDialogHostImpl: import("react").ComponentType<object>;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const ConversationRenameDialogHostImpl = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const ConversationRenameDialogHostImpl = clientLazy(
4
4
  () => import("../../agents/components/conversation-actions/rename/ConversationRenameDialogHostImpl.js"),
5
- { ssr: false, loading: () => null }
5
+ { loading: () => null }
6
6
  );
7
7
  export {
8
8
  ConversationRenameDialogHostImpl
@@ -1,10 +1,9 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const AssistantMessage = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const AssistantMessage = clientLazy(
4
4
  () => import("../../cx-conversation/AssistantMessage.js").then(
5
5
  (m) => m.AssistantMessage
6
- ),
7
- { ssr: false }
6
+ )
8
7
  );
9
8
  export {
10
9
  AssistantMessage
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const DbToolRendererImplLazy = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const DbToolRendererImplLazy = clientLazy(
4
4
  () => import("../../tool-call-visualization/db-renderer/DbToolRendererImpl.js").then((m) => ({
5
5
  default: m.DbToolRendererImpl
6
6
  })),
7
- { ssr: false, loading: () => null }
7
+ { loading: () => null }
8
8
  );
9
9
  export {
10
10
  DbToolRendererImplLazy
@@ -1,9 +1,8 @@
1
- import dynamic from "next/dynamic";
2
- const IconResolver = dynamic(
1
+ import { clientLazy } from "../../utils/client-lazy.js";
2
+ const IconResolver = clientLazy(
3
3
  () => import("@ai-matrx/icons").then((m) => ({
4
4
  default: m.IconResolver
5
- })),
6
- { ssr: false }
5
+ }))
7
6
  );
8
7
  export {
9
8
  IconResolver
@@ -1,16 +1,14 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const GuidedVariableInputs = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const GuidedVariableInputs = clientLazy(
4
4
  () => import("../../public-chat/components/GuidedVariableInputs.js").then(
5
5
  (m) => ({ default: m.GuidedVariableInputs })
6
- ),
7
- { ssr: false }
6
+ )
8
7
  );
9
- const PublicVariableInputs = dynamic(
8
+ const PublicVariableInputs = clientLazy(
10
9
  () => import("../../public-chat/components/PublicVariableInputs.js").then(
11
10
  (m) => ({ default: m.PublicVariableInputs })
12
- ),
13
- { ssr: false }
11
+ )
14
12
  );
15
13
  export {
16
14
  GuidedVariableInputs,
@@ -1,3 +1,3 @@
1
1
  import type { ScrollAssistantLauncherImplProps } from "../../agents/components/ambient-assistant/ScrollAssistantLauncherImpl.js";
2
2
  export declare const ScrollAssistantLauncherImpl: import("react").ComponentType<ScrollAssistantLauncherImplProps>;
3
- export declare const ScrollVoiceAssistantLauncherImpl: import("react").ComponentType<{}>;
3
+ export declare const ScrollVoiceAssistantLauncherImpl: import("react").ComponentType<object>;
@@ -1,18 +1,16 @@
1
1
  "use client";
2
- import dynamic from "next/dynamic";
3
- const ScrollAssistantLauncherImpl = dynamic(
2
+ import { clientLazy } from "../../utils/client-lazy.js";
3
+ const ScrollAssistantLauncherImpl = clientLazy(
4
4
  () => import("../../agents/components/ambient-assistant/ScrollAssistantLauncherImpl.js"),
5
5
  {
6
- ssr: false,
7
6
  // A prop-blind loading shell would jump between the two supported heights.
8
7
  // Reveal the correctly sized implementation once its chunk is ready.
9
8
  loading: () => null
10
9
  }
11
10
  );
12
- const ScrollVoiceAssistantLauncherImpl = dynamic(
11
+ const ScrollVoiceAssistantLauncherImpl = clientLazy(
13
12
  () => import("../../agents/components/ambient-assistant/ScrollVoiceAssistantLauncherImpl.js"),
14
13
  {
15
- ssr: false,
16
14
  loading: () => null
17
15
  }
18
16
  );
@@ -1,11 +1,10 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import dynamic from "next/dynamic";
3
+ import { clientLazy } from "../../utils/client-lazy.js";
4
4
  import { Loader2 } from "lucide-react";
5
- const SurfaceAgentsPanelImpl = dynamic(
5
+ const SurfaceAgentsPanelImpl = clientLazy(
6
6
  () => import("../../surfaces/components/chrome/SurfaceAgentsPanelImpl.js"),
7
7
  {
8
- ssr: false,
9
8
  loading: () => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 p-8 text-xs text-muted-foreground", children: [
10
9
  /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
11
10
  "Loading agents\u2026"
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `clientLazy` — the package's host-neutral replacement for `next/dynamic(..., { ssr: false })`.
3
+ * A lazily loaded component that renders only on the client: on the server and during hydration
4
+ * it renders `loading` (hydration-safe through `useSyncExternalStore`); on the client it renders
5
+ * the loaded component inside a Suspense boundary with the same `loading` as the fallback.
6
+ *
7
+ * Plain React (`lazy` + `Suspense`), so a Vite / Electron / extension host needs no `next`
8
+ * installed (HOST-OWNED-SOURCE-PLAN P28e). Guard: `host/__tests__/no-next-import-outside-next-binding`.
9
+ */
10
+ import { type ComponentType } from "react";
11
+ export declare function useIsClient(): boolean;
12
+ export declare function clientLazy<P extends object>(load: () => Promise<{
13
+ default: ComponentType<P>;
14
+ } | ComponentType<P>>, options?: {
15
+ loading?: ComponentType;
16
+ }): ComponentType<P>;
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import { createElement, lazy, Suspense, useSyncExternalStore } from "react";
3
+ const subscribe = () => () => {
4
+ };
5
+ const onClient = () => true;
6
+ const onServer = () => false;
7
+ function useIsClient() {
8
+ return useSyncExternalStore(subscribe, onClient, onServer);
9
+ }
10
+ function clientLazy(load, options = {}) {
11
+ const Lazy = lazy(async () => {
12
+ const m = await load();
13
+ return typeof m === "function" || !("default" in m) ? { default: m } : m;
14
+ });
15
+ const Loading = options.loading;
16
+ function ClientLazy(props) {
17
+ const client = useIsClient();
18
+ const fallback = Loading ? createElement(Loading) : null;
19
+ if (!client) return fallback;
20
+ return createElement(Suspense, { fallback }, createElement(Lazy, props));
21
+ }
22
+ return ClientLazy;
23
+ }
24
+ export {
25
+ clientLazy,
26
+ useIsClient
27
+ };