@ai-matrx/chat 0.2.9 → 0.3.2

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 (60) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/agent-copy/clipboard.d.ts +2 -4
  3. package/dist/agent-copy/clipboard.js +1 -14
  4. package/dist/agent-copy/page-capture/pageCapture.d.ts +11 -62
  5. package/dist/agent-copy/page-capture/pageCapture.js +5 -22
  6. package/dist/agents/components/context-items/bodies/WebpageBody.js +1 -1
  7. package/dist/agents/components/context-preview/AttachedContextSection.js +9 -8
  8. package/dist/agents/components/context-preview/ContextCompareView.js +13 -13
  9. package/dist/agents/components/context-preview/ContextPreviewPanel.js +13 -13
  10. package/dist/agents/components/conversation-actions/conversation-verbs.js +2 -6
  11. package/dist/agents/components/debug/SandboxFileViewer.js +1 -1
  12. package/dist/agents/components/debug/StreamDebugPanel.js +2 -2
  13. package/dist/agents/components/messages-display/TranscriptIntegrityCopyButton.js +1 -1
  14. package/dist/agents/components/observational-memory/components/MemoryStateInspector.js +4 -4
  15. package/dist/agents/components/run-controls/AgentExecutionTestModal.js +2 -2
  16. package/dist/agents/components/run-controls/PayloadTab.js +7 -1
  17. package/dist/agents/components/run-controls/panels/BackendTargetPanel.js +7 -1
  18. package/dist/agents/components/shared/AgentSaveStatus.js +22 -1
  19. package/dist/agents/hooks/useAgentAutoSave.js +14 -2
  20. package/dist/agents/menu/agent-actions.js +3 -5
  21. package/dist/agents/prompt-preview/PromptPreviewContent.js +1 -1
  22. package/dist/agents/redux/agent-definition/slice.js +1 -0
  23. package/dist/agents/redux/agent-settings/agentSettingsSlice.d.ts +3 -0
  24. package/dist/agents/redux/agent-settings/agentSettingsSlice.js +12 -2
  25. package/dist/agents/redux/agent-settings/types.d.ts +7 -0
  26. package/dist/agents/redux/agent-shortcuts/thunks.js +9 -5
  27. package/dist/agents/redux/execution-system/instance-resources/remarks.d.ts +6 -0
  28. package/dist/agents/redux/execution-system/instance-resources/remarks.js +16 -0
  29. package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateCore.js +7 -1
  30. package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateList.js +7 -1
  31. package/dist/agents/redux/execution-system/message-crud/commit-inline-edit.thunk.js +2 -1
  32. package/dist/canvas/workspace/useCanvasWorkspaceConversation.d.ts +12 -0
  33. package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +29 -1
  34. package/dist/context-menu/types.d.ts +7 -0
  35. package/dist/cx-chat/components/messages/AssistantMessage.js +8 -10
  36. package/dist/cx-chat/components/messages/UserMessage.js +7 -1
  37. package/dist/cx-conversation/AssistantMessage.js +8 -10
  38. package/dist/cx-conversation/UserMessage.js +7 -1
  39. package/dist/host/clipboard.d.ts +7 -0
  40. package/dist/host/clipboard.js +11 -0
  41. package/dist/host/db-types.d.ts +15 -12
  42. package/dist/host/ui-slots.d.ts +2 -8
  43. package/dist/host/ui-slots.js +11 -16
  44. package/dist/public-chat/components/PublicMessageOptionsMenu.js +9 -16
  45. package/dist/store/create-chat-store.d.ts +10 -1
  46. package/dist/store/create-chat-store.js +4 -2
  47. package/dist/surfaces/runtime/SurfaceRuntimeContext.d.ts +10 -0
  48. package/dist/surfaces/runtime/SurfaceRuntimeContext.js +23 -1
  49. package/dist/surfaces/services/surface-config.service.d.ts +3 -1
  50. package/dist/surfaces/services/surface-config.service.js +33 -17
  51. package/dist/tool-call-visualization/components/ToolTabBodies.d.ts +0 -4
  52. package/dist/tool-call-visualization/components/ToolTabBodies.js +2 -25
  53. package/dist/tool-call-visualization/result-fields/ResultFile.js +2 -8
  54. package/dist/tool-call-visualization/result-fields/ShortId.js +1 -23
  55. package/dist/voice-agent/components/LiveVoiceDoor.js +1 -1
  56. package/dist/voice-agent/components/VoiceDebugPanel.js +1 -1
  57. package/dist/voice-agent/hooks/useVoiceAgentInstance.js +1 -1
  58. package/dist/window-panels/windows/agents/ObservationalMemoryWindow.d.ts +0 -17
  59. package/dist/window-panels/windows/agents/ObservationalMemoryWindow.js +7 -1
  60. package/package.json +7 -7
@@ -163,7 +163,7 @@ export interface ChatUiSlots {
163
163
  SystemInstructionModal: AnyComponent;
164
164
  confirm: AnyFn;
165
165
  copyRichContent: AnyFn;
166
- copyToClipboard: AnyFn;
166
+ copyContent: AnyFn;
167
167
  useTablesEverywhere: AnyFn;
168
168
  useTextareaFormatting: AnyFn;
169
169
  useClipboardPaste: AnyFn;
@@ -317,12 +317,10 @@ export interface ChatUiSlots {
317
317
  dispatchWarRoomTool: AnyFn;
318
318
  dispatchWarRoomMasterTool: AnyFn;
319
319
  resolveGmailSendConnection: AnyFn;
320
- voiceDisplayName: AnyFn;
321
320
  canvasGetVersionHistory: (canvasId: string) => Promise<CanvasItemRow[]>;
322
321
  canvasGetById: (canvasId: string) => Promise<CanvasItemRow | null>;
323
322
  createSandboxFilesystemAdapter: AnyFn;
324
323
  notesGetById: AnyFn;
325
- isLiveConversationVoice: AnyFn;
326
324
  createHtmlPage: AnyFn;
327
325
  convertMarkdownToHtml: AnyFn;
328
326
  sklActions: Record<string, AnyFn>;
@@ -338,7 +336,6 @@ export interface ChatUiSlots {
338
336
  WorkspaceGate: AnyComponent;
339
337
  OrganizationContextNotice: AnyComponent;
340
338
  JsonInspector: AnyComponent;
341
- InlineCopyButton: AnyComponent;
342
339
  ConfirmDialog: SlotComponent<OpenChangeProps>;
343
340
  ModelListDropdown: SlotComponent<ModelListDropdownProps>;
344
341
  TextWithDoors: AnyComponent;
@@ -510,7 +507,7 @@ export declare const WebpageSnapshotView: AnyComponent;
510
507
  export declare function hostFn<K extends keyof ChatUiSlots>(name: K, fallback?: AnyFn): ChatUiSlots[K];
511
508
  export declare const confirm: AnyFn;
512
509
  export declare const copyRichContent: AnyFn;
513
- export declare const copyToClipboard: AnyFn;
510
+ export declare const copyContent: AnyFn;
514
511
  export declare const useTablesEverywhere: AnyFn;
515
512
  export declare const useTextareaFormatting: AnyFn;
516
513
  export declare const useClipboardPaste: AnyFn;
@@ -604,7 +601,6 @@ export declare const StaleDataNotice: AnyComponent;
604
601
  export declare const WorkspaceGate: AnyComponent;
605
602
  export declare const OrganizationContextNotice: AnyComponent;
606
603
  export declare const JsonInspector: AnyComponent;
607
- export declare const InlineCopyButton: AnyComponent;
608
604
  export declare const ConfirmDialog: SlotComponent<OpenChangeProps>;
609
605
  export declare const ModelListDropdown: SlotComponent<ModelListDropdownProps>;
610
606
  export declare const TextWithDoors: AnyComponent;
@@ -712,12 +708,10 @@ export declare const adoptCloudBrowserRunFromStream: AnyFn;
712
708
  export declare const dispatchWarRoomTool: AnyFn;
713
709
  export declare const dispatchWarRoomMasterTool: AnyFn;
714
710
  export declare const resolveGmailSendConnection: AnyFn;
715
- export declare const voiceDisplayName: AnyFn;
716
711
  export declare const canvasGetVersionHistory: (canvasId: string) => Promise<CanvasItemRow[]>;
717
712
  export declare const canvasGetById: (canvasId: string) => Promise<CanvasItemRow | null>;
718
713
  export declare const createSandboxFilesystemAdapter: AnyFn;
719
714
  export declare const notesGetById: AnyFn;
720
- export declare const isLiveConversationVoice: AnyFn;
721
715
  export declare const createHtmlPage: AnyFn;
722
716
  export declare const convertMarkdownToHtml: AnyFn;
723
717
  /** The host's skill-library action creators (`sklActions.x(...)`); resolved at call time, a bare host throws naming the slot. */
@@ -4,6 +4,7 @@ import { formatCost, usdToPoints } from "@ai-matrx/kit/format";
4
4
  import { reportUnregisteredHostSlot as reportUnregistered } from "./diagnostics.js";
5
5
  import { DefaultWebpageSnapshotView } from "./defaults/webpage-snapshot-view.js";
6
6
  import { DefaultFullScreenOverlay, DefaultWindowPanel } from "./defaults/window-panel.js";
7
+ import { copyText } from "@ai-matrx/kit/clipboard";
7
8
  const slots = {};
8
9
  function registerChatUi(next) {
9
10
  Object.assign(slots, next);
@@ -68,13 +69,13 @@ const failingThunk = (name) => () => () => {
68
69
  const STAND_INS = {
69
70
  confirm: async () => false,
70
71
  copyRichContent: async () => false,
71
- copyToClipboard: async (text) => {
72
- try {
73
- await navigator.clipboard.writeText(String(text));
74
- return true;
75
- } catch {
76
- return false;
77
- }
72
+ // Content-shaped like the host's `copyContent(content, options)`: a string copies as text, anything
73
+ // else as JSON. A failed copy is reported through `onError` and returns false, never swallowed.
74
+ copyContent: async (content, options) => {
75
+ const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
76
+ const landed = await copyText(text ?? "");
77
+ if (!landed) options?.onError?.(new Error("Failed to copy"));
78
+ return landed;
78
79
  },
79
80
  useTablesEverywhere: () => NONE,
80
81
  useCenterControlFit: () => true,
@@ -234,7 +235,7 @@ function hostFn(name, fallback) {
234
235
  }
235
236
  const confirm = slotFn("confirm");
236
237
  const copyRichContent = slotFn("copyRichContent");
237
- const copyToClipboard = slotFn("copyToClipboard");
238
+ const copyContent = slotFn("copyContent");
238
239
  const useTablesEverywhere = slotFn("useTablesEverywhere");
239
240
  const useTextareaFormatting = slotFn("useTextareaFormatting", () => void 0);
240
241
  const useClipboardPaste = slotFn("useClipboardPaste", () => void 0);
@@ -327,7 +328,6 @@ const StaleDataNotice = slotComponent("StaleDataNotice", unregisteredWidget("Sta
327
328
  const WorkspaceGate = slotComponent("WorkspaceGate", unregisteredWidget("WorkspaceGate"));
328
329
  const OrganizationContextNotice = slotComponent("OrganizationContextNotice", unregisteredWidget("OrganizationContextNotice"));
329
330
  const JsonInspector = slotComponent("JsonInspector", unregisteredWidget("JsonInspector"));
330
- const InlineCopyButton = slotComponent("InlineCopyButton", unregisteredWidget("InlineCopyButton"));
331
331
  const ConfirmDialog = slotComponent("ConfirmDialog", unregisteredWidget("ConfirmDialog"));
332
332
  const ModelListDropdown = slotComponent("ModelListDropdown", unregisteredWidget("ModelListDropdown"));
333
333
  const TextWithDoors = slotComponent("TextWithDoors", unregisteredWidget("TextWithDoors"));
@@ -417,12 +417,10 @@ const adoptCloudBrowserRunFromStream = slotFn("adoptCloudBrowserRunFromStream");
417
417
  const dispatchWarRoomTool = slotFn("dispatchWarRoomTool");
418
418
  const dispatchWarRoomMasterTool = slotFn("dispatchWarRoomMasterTool");
419
419
  const resolveGmailSendConnection = slotFn("resolveGmailSendConnection");
420
- const voiceDisplayName = slotFn("voiceDisplayName", (_set, id) => id);
421
420
  const canvasGetVersionHistory = slotFn("canvasGetVersionHistory");
422
421
  const canvasGetById = slotFn("canvasGetById");
423
422
  const createSandboxFilesystemAdapter = slotFn("createSandboxFilesystemAdapter");
424
423
  const notesGetById = slotFn("notesGetById");
425
- const isLiveConversationVoice = slotFn("isLiveConversationVoice", () => false);
426
424
  const createHtmlPage = slotFn("createHtmlPage");
427
425
  const convertMarkdownToHtml = slotFn("convertMarkdownToHtml");
428
426
  const sklActions = new Proxy({}, { get: (_t, prop) => (...args) => {
@@ -556,7 +554,6 @@ export {
556
554
  IconButton,
557
555
  InPlaceEditor,
558
556
  InfoHint,
559
- InlineCopyButton,
560
557
  InputCapabilitiesEditor,
561
558
  ItemMenu,
562
559
  ItemRow,
@@ -636,8 +633,8 @@ export {
636
633
  confirm,
637
634
  connectorDefinitionFromMcp,
638
635
  convertMarkdownToHtml,
636
+ copyContent,
639
637
  copyRichContent,
640
- copyToClipboard,
641
638
  createHtmlPage,
642
639
  createSandboxFilesystemAdapter,
643
640
  currentCostUnit,
@@ -668,7 +665,6 @@ export {
668
665
  humanLines,
669
666
  invalidateCanvasItemCache,
670
667
  invalidateMandateCatalogueCache,
671
- isLiveConversationVoice,
672
668
  isSkillStreamEvent,
673
669
  isUuidValue,
674
670
  isWarRoomThreadAgentSurface,
@@ -771,6 +767,5 @@ export {
771
767
  useSkills,
772
768
  useTablesEverywhere,
773
769
  useTextareaFormatting,
774
- useUrlSync,
775
- voiceDisplayName
770
+ useUrlSync
776
771
  };
@@ -17,7 +17,7 @@ import {
17
17
  Share2
18
18
  } from "lucide-react";
19
19
  import { getMarkdownStylesheet } from "@ai-matrx/print/markdown";
20
- import { AdvancedMenu, copyToClipboard } from "../../host/ui-slots.js";
20
+ import { AdvancedMenu, copyContent } from "../../host/ui-slots.js";
21
21
  import { EmailInputDialog } from "../../host/ui-slots.js";
22
22
  import { AuthGateDialog } from "../../host/ui-slots.js";
23
23
  import { notesCreate } from "../../host/ui-slots.js";
@@ -29,6 +29,7 @@ import { openOverlay, CHAT_WINDOWS } from "../../host/windows.js";
29
29
  import { kindTextToMarkdown } from "../../utils/content-ir/surfaces/kind-text-to-markdown.js";
30
30
  import { removeCodeSpans, replaceFences } from "@ai-matrx/content-ir/source";
31
31
  import { selectOrganizationId, ensureOrganizationContext, isOrganizationSelectionCancelled } from "../../host/org.js";
32
+ import { downloadFile } from "@ai-matrx/kit/download";
32
33
  const PENDING_ACTION_KEY = "matrx_pending_post_auth_action";
33
34
  const PublicMessageOptionsMenu = ({
34
35
  content: answerContent,
@@ -124,7 +125,7 @@ const PublicMessageOptionsMenu = ({
124
125
  onClose();
125
126
  };
126
127
  const handleCopyPlain = async () => {
127
- await copyToClipboard(content, {
128
+ await copyContent(content, {
128
129
  onSuccess: () => {
129
130
  },
130
131
  onError: (error) => {
@@ -133,7 +134,7 @@ const PublicMessageOptionsMenu = ({
133
134
  });
134
135
  };
135
136
  const handleCopyGoogleDocs = async () => {
136
- await copyToClipboard(content, {
137
+ await copyContent(content, {
137
138
  isMarkdown: true,
138
139
  formatForGoogleDocs: true,
139
140
  onSuccess: () => {
@@ -146,7 +147,7 @@ const PublicMessageOptionsMenu = ({
146
147
  });
147
148
  };
148
149
  const handleCopyWithThinking = async () => {
149
- await copyToClipboard(content, {
150
+ await copyContent(content, {
150
151
  isMarkdown: true,
151
152
  includeThinking: true,
152
153
  onSuccess: () => {
@@ -166,7 +167,7 @@ const PublicMessageOptionsMenu = ({
166
167
  toast.info("HTML preview not available here");
167
168
  return;
168
169
  }
169
- await copyToClipboard(content, {
170
+ await copyContent(content, {
170
171
  isMarkdown: true,
171
172
  formatForWordPress: true,
172
173
  showHtmlPreview: true,
@@ -209,7 +210,7 @@ const PublicMessageOptionsMenu = ({
209
210
  }
210
211
  };
211
212
  const handleCopyCompleteHTML = async () => {
212
- await copyToClipboard(content, {
213
+ await copyContent(content, {
213
214
  isMarkdown: true,
214
215
  formatForWordPress: true,
215
216
  showHtmlPreview: true,
@@ -228,7 +229,7 @@ const PublicMessageOptionsMenu = ({
228
229
  ${filteredHtml}
229
230
  </body>
230
231
  </html>`;
231
- await copyToClipboard(completeHTML, {
232
+ await copyContent(completeHTML, {
232
233
  onSuccess: () => {
233
234
  },
234
235
  onError: (error) => {
@@ -248,15 +249,7 @@ const PublicMessageOptionsMenu = ({
248
249
  };
249
250
  const handleSaveAsFile = () => {
250
251
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
251
- const blob = new Blob([content], { type: "text/markdown;charset=utf-8" });
252
- const url = URL.createObjectURL(blob);
253
- const link = document.createElement("a");
254
- link.href = url;
255
- link.download = `ai-response-${timestamp}.md`;
256
- document.body.appendChild(link);
257
- link.click();
258
- document.body.removeChild(link);
259
- URL.revokeObjectURL(url);
252
+ downloadFile(`ai-response-${timestamp}.md`, content, "text/markdown;charset=utf-8");
260
253
  onClose();
261
254
  };
262
255
  const handleEmailToMe = async () => {
@@ -1,5 +1,14 @@
1
+ import { type FilesHost } from "@ai-matrx/media/files/engine";
1
2
  import type { ChatState } from "./state.js";
2
- export declare function createChatStore(preloadedState?: Partial<ChatState>): import("@reduxjs/toolkit").EnhancedStore<{
3
+ export interface CreateChatStoreOptions {
4
+ /**
5
+ * The files engine's host (database, server client, optional scope/org/share/notify) for a
6
+ * host that has not called `configureFilesHost` itself. The engine is then wired to THIS
7
+ * store, so file thunks read and write the slice chat renders from.
8
+ */
9
+ files?: Omit<FilesHost, "store">;
10
+ }
11
+ export declare function createChatStore(preloadedState?: Partial<ChatState>, options?: CreateChatStoreOptions): import("@reduxjs/toolkit").EnhancedStore<{
3
12
  voiceAgent: import("../voice-agent/types.js").VoiceAgentState;
4
13
  agentDefinition: import("../agents/types/agent-definition.types.js").AgentDefinitionSliceState;
5
14
  conversationList: import("../agents/redux/conversation-list/conversation-list.types.js").ConversationListState;
@@ -4,12 +4,13 @@ import { fork } from "redux-saga/effects";
4
4
  import {
5
5
  cloudFilesMutationToastMiddleware,
6
6
  cloudFilesRealtimeMiddleware,
7
- cloudFilesReducer
7
+ cloudFilesReducer,
8
+ configureFilesHost
8
9
  } from "@ai-matrx/media/files/engine";
9
10
  import { chatReducers } from "./slices.js";
10
11
  import { chatMiddlewares } from "./middlewares.js";
11
12
  import { chatSagas } from "./sagas.js";
12
- function createChatStore(preloadedState) {
13
+ function createChatStore(preloadedState, options = {}) {
13
14
  const sagaMiddleware = createSagaMiddleware();
14
15
  const store = configureStore({
15
16
  // Files are part of chat (P16f): the files engine's slice and its two middlewares come
@@ -30,6 +31,7 @@ function createChatStore(preloadedState) {
30
31
  ),
31
32
  devTools: process.env.NODE_ENV !== "production"
32
33
  });
34
+ if (options.files) configureFilesHost({ ...options.files, store: () => store });
33
35
  sagaMiddleware.run(function* chatRootSaga() {
34
36
  for (const saga of chatSagas()) yield fork(saga);
35
37
  });
@@ -1,3 +1,4 @@
1
+ import { type LiveValuesStore } from "@ai-matrx/alchemy/surface";
1
2
  import { type ReactNode } from "react";
2
3
  import type { SurfaceScopePayload } from "../types.js";
3
4
  import type { InstanceContextEntry } from "../../agents/types/instance.types.js";
@@ -198,6 +199,15 @@ export interface SurfaceRegistry {
198
199
  /** Called after every registration change. */
199
200
  subscribe: (listener: () => void) => () => void;
200
201
  }
202
+ /**
203
+ * THE PAGE'S LIVE VALUES REACH THE AGENT (ALC-18). A value the surface DECLARES that its runtime
204
+ * and contributions left unset is answered from the page's live capture (`@ai-matrx/alchemy/surface`
205
+ * `liveValues`: identity, selection, sections), only while the publishing page IS this surface —
206
+ * the same rule as `createSurfacePort({ live })`'s `getValue`. A supplied value always wins, and an
207
+ * undeclared live name (the request log, a section nobody declared) never rides along. Every reader
208
+ * of `getScope()` — the send path's context entries, the context window, write-back — gets it.
209
+ */
210
+ export declare function withLiveValues(surfaceName: string, loaded: SurfaceScopePayload, live?: LiveValuesStore): SurfaceScopePayload;
201
211
  /**
202
212
  * The hosts `surfaceName` was mounted inside earlier in this page session
203
213
  * that are STILL mounted, deepest first. Empty when `surfaceName` itself is
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { announceUndeclaredLoadedValues } from "./loaded-value-check.js";
4
+ import { liveValueOf, liveValues } from "@ai-matrx/alchemy/surface";
5
+ import { getManifest } from "./registry.js";
4
6
  import {
5
7
  createContext,
6
8
  useCallback,
@@ -15,6 +17,25 @@ import {
15
17
  useAlchemySurfaceHandle
16
18
  } from "../../agent-copy/AlchemySurfaceBridge.js";
17
19
  let nextId = 0;
20
+ function withLiveValues(surfaceName, loaded, live = liveValues) {
21
+ const declared = getManifest(surfaceName)?.values ?? [];
22
+ const missing = declared.filter((v) => !(v.name in loaded));
23
+ if (missing.length === 0) return loaded;
24
+ let capture;
25
+ try {
26
+ capture = live.readFor(surfaceName);
27
+ } catch (err) {
28
+ console.error(`[surfaces] the live values of "${surfaceName}" could not be read`, err);
29
+ return loaded;
30
+ }
31
+ if (!capture) return loaded;
32
+ const filled = { ...loaded };
33
+ for (const v of missing) {
34
+ const value = liveValueOf(capture, v.name);
35
+ if (value !== void 0) filled[v.name] = value;
36
+ }
37
+ return filled;
38
+ }
18
39
  function createRegistry(kind, onRegister) {
19
40
  let stack = [];
20
41
  const listeners = /* @__PURE__ */ new Set();
@@ -70,7 +91,7 @@ function createRegistry(kind, onRegister) {
70
91
  }
71
92
  const loaded = { ...own, ...contributed };
72
93
  announceUndeclaredLoadedValues(surfaceName, loaded);
73
- return loaded;
94
+ return withLiveValues(surfaceName, loaded);
74
95
  };
75
96
  return () => {
76
97
  const own = getScope();
@@ -531,5 +552,6 @@ export {
531
552
  waitForCapturedRuntime,
532
553
  wasPageOwnConversationOf,
533
554
  wasSurfaceMountedThisSession,
555
+ withLiveValues,
534
556
  withScopeContributions
535
557
  };
@@ -115,7 +115,9 @@ export declare function setRoleSelection(args: {
115
115
  scope: PrefScopeInput;
116
116
  }): Promise<void>;
117
117
  /** Move one agent choice / roster item to Trash (restorable; re-selecting revives it). */
118
- export declare function deleteRolePref(prefId: string): Promise<void>;
118
+ export declare function deleteRolePref(prefId: string, opts?: {
119
+ platformTier?: boolean;
120
+ }): Promise<void>;
119
121
  export declare function addRosterItem(args: {
120
122
  surfaceName: string;
121
123
  roleName: string;
@@ -279,6 +279,9 @@ function matchScope(q, scope, tierOrganization) {
279
279
  }
280
280
  return q.is("user_id", null).is("scope_id", null).eq("organization_id", tierOrganization);
281
281
  }
282
+ function withPlatformRoleAdminLane(request, on) {
283
+ return on ? request.setHeader("x-matrx-admin-lane", "1") : request;
284
+ }
282
285
  async function setRoleSelection(args) {
283
286
  const {
284
287
  surfaceName,
@@ -291,33 +294,46 @@ async function setRoleSelection(args) {
291
294
  const client = sb();
292
295
  let q = client.schema("ui").from("ui_surface_agent_pref").select("id").eq("surface_name", surfaceName).eq("role_name", roleName).eq("kind", "selection").eq("position", position);
293
296
  q = matchScope(q, scope, await tierOrganizationFor(scope));
294
- const { data: existing, error: findErr } = await q.maybeSingle();
297
+ const platformLane = scope.global === true;
298
+ const { data: existing, error: findErr } = await withPlatformRoleAdminLane(
299
+ q,
300
+ platformLane
301
+ ).maybeSingle();
295
302
  if (findErr) throw findErr;
296
303
  if (existing) {
297
304
  await writeOne(
298
- client.schema("ui").from("ui_surface_agent_pref").update({
299
- agent_id: agentId,
300
- deleted_at: null,
301
- ...settings ? { settings } : {}
302
- }).eq("id", existing.id).select("id"),
305
+ withPlatformRoleAdminLane(
306
+ client.schema("ui").from("ui_surface_agent_pref").update({
307
+ agent_id: agentId,
308
+ deleted_at: null,
309
+ ...settings ? { settings } : {}
310
+ }).eq("id", existing.id).select("id"),
311
+ platformLane
312
+ ),
303
313
  { action: "save", noun: "agent choice" }
304
314
  );
305
315
  return;
306
316
  }
307
- const { error } = await client.schema("ui").from("ui_surface_agent_pref").insert({
308
- surface_name: surfaceName,
309
- role_name: roleName,
310
- agent_id: agentId,
311
- kind: "selection",
312
- position,
313
- settings: settings ?? {},
314
- ...await scopeInsertColumns(scope)
315
- });
317
+ const { error } = await withPlatformRoleAdminLane(
318
+ client.schema("ui").from("ui_surface_agent_pref").insert({
319
+ surface_name: surfaceName,
320
+ role_name: roleName,
321
+ agent_id: agentId,
322
+ kind: "selection",
323
+ position,
324
+ settings: settings ?? {},
325
+ ...await scopeInsertColumns(scope)
326
+ }),
327
+ platformLane
328
+ );
316
329
  if (error) throw error;
317
330
  }
318
- async function deleteRolePref(prefId) {
331
+ async function deleteRolePref(prefId, opts = {}) {
319
332
  await writeOne(
320
- sb().schema("ui").from("ui_surface_agent_pref").update({ deleted_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", prefId).is("deleted_at", null).select("id"),
333
+ withPlatformRoleAdminLane(
334
+ sb().schema("ui").from("ui_surface_agent_pref").update({ deleted_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", prefId).is("deleted_at", null).select("id"),
335
+ opts.platformTier === true
336
+ ),
321
337
  { action: "remove", noun: "agent choice" }
322
338
  );
323
339
  }
@@ -1,10 +1,6 @@
1
1
  import React from "react";
2
2
  import type { ToolLifecycleEntry } from "../../agents/types/request.types.js";
3
3
  import type { ToolOverlayTabSpec, ToolRendererProps } from "../types.js";
4
- export declare const CopyButton: React.FC<{
5
- text: string;
6
- className?: string;
7
- }>;
8
4
  export declare const InputView: React.FC<{
9
5
  entry: ToolLifecycleEntry;
10
6
  }>;
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { resultToHuman } from "../utils/human-copy.js";
4
- import React, { useState } from "react";
4
+ import React from "react";
5
5
  import { useToolConversationId } from "./ToolConversationContext.js";
6
- import { Check, CircleAlert, Copy, FileCode2, Route, Settings2 } from "lucide-react";
6
+ import { CircleAlert, FileCode2, Route, Settings2 } from "lucide-react";
7
7
  import { Badge } from "@ai-matrx/design-system";
8
8
  import { CopyButtons } from "../../host/ui-slots.js";
9
9
  import { JsonInspector } from "../../host/ui-slots.js";
@@ -19,7 +19,6 @@ import {
19
19
  toolEntryBundleToHuman
20
20
  } from "../utils/toolEntryBundle.js";
21
21
  import { ErrorAlchemyMenu } from "../../host/ui-slots.js";
22
- import { Button } from "@ai-matrx/design-system/controls";
23
22
  function buildAgentInput(entry, description, data) {
24
23
  return {
25
24
  kind: "tool-result",
@@ -29,27 +28,6 @@ function buildAgentInput(entry, description, data) {
29
28
  attributes: { tool: entry.toolName, status: entry.status }
30
29
  };
31
30
  }
32
- const CopyButton = ({
33
- text,
34
- className
35
- }) => {
36
- const [copied, setCopied] = useState(false);
37
- const handleCopy = async () => {
38
- try {
39
- await navigator.clipboard.writeText(text);
40
- } catch {
41
- const textarea = document.createElement("textarea");
42
- textarea.value = text;
43
- document.body.appendChild(textarea);
44
- textarea.select();
45
- document.execCommand("copy");
46
- document.body.removeChild(textarea);
47
- }
48
- setCopied(true);
49
- setTimeout(() => setCopied(false), 2e3);
50
- };
51
- return /* @__PURE__ */ jsx(Button, { variant: "quiet", icon: copied ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(Copy, {}), glyphTone: copied ? "success" : void 0, onClick: handleCopy, title: copied ? "Copied!" : "Copy to clipboard", className, children: copied ? "Copied" : "Copy" });
52
- };
53
31
  const InputView = ({
54
32
  entry
55
33
  }) => {
@@ -225,7 +203,6 @@ const CustomOverlayBody = ({ entry, Component, conversationId: conversationIdPro
225
203
  ) }) });
226
204
  };
227
205
  export {
228
- CopyButton,
229
206
  CustomOverlayBody,
230
207
  EntryResultsBody,
231
208
  ErrorView,
@@ -8,6 +8,7 @@ import { formatFileSize } from "@ai-matrx/kit/format";
8
8
  import { useFileActions } from "../../host/ui-slots.js";
9
9
  import { useMediaResolution } from "@ai-matrx/media/core";
10
10
  import { Button } from "@ai-matrx/design-system/controls";
11
+ import { downloadUrl } from "@ai-matrx/kit/download";
11
12
  function deriveName(file) {
12
13
  if (file.file_name && file.file_name.trim().length > 0) return file.file_name.trim();
13
14
  const src = file.download_url ?? file.url;
@@ -33,14 +34,7 @@ function deriveTypeLabel(file, name) {
33
34
  return null;
34
35
  }
35
36
  function triggerAnchorDownload(href, name) {
36
- const a = document.createElement("a");
37
- a.href = href;
38
- a.download = name;
39
- a.target = "_blank";
40
- a.rel = "noopener noreferrer";
41
- document.body.appendChild(a);
42
- a.click();
43
- a.remove();
37
+ downloadUrl(href, name, { newTab: true });
44
38
  }
45
39
  const ResultFile = ({ file, density = "inline", className }) => {
46
40
  const [busy, setBusy] = useState(false);
@@ -2,31 +2,9 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  import { Check, Copy } from "lucide-react";
5
+ import { copyText } from "@ai-matrx/kit/clipboard";
5
6
  import { cn } from "@ai-matrx/design-system";
6
7
  import { Button } from "@ai-matrx/design-system/controls";
7
- async function copyText(text) {
8
- try {
9
- if (navigator.clipboard?.writeText) {
10
- await navigator.clipboard.writeText(text);
11
- return true;
12
- }
13
- } catch {
14
- }
15
- try {
16
- const ta = document.createElement("textarea");
17
- ta.value = text;
18
- ta.style.position = "fixed";
19
- ta.style.opacity = "0";
20
- ta.style.pointerEvents = "none";
21
- document.body.appendChild(ta);
22
- ta.select();
23
- const ok = document.execCommand("copy");
24
- document.body.removeChild(ta);
25
- return ok;
26
- } catch {
27
- return false;
28
- }
29
- }
30
8
  const ShortId = ({
31
9
  value,
32
10
  variant = "compact",
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { SettingDoor } from "../../host/prefs-react.js";
4
4
  import { useAppSelector } from "../../store/hooks.js";
5
- import { voiceDisplayName } from "../../host/ui-slots.js";
5
+ import { voiceDisplayName } from "@ai-matrx/media/voices";
6
6
  function LiveVoiceDoor({
7
7
  voiceId,
8
8
  agentId
@@ -75,7 +75,7 @@ function VoiceDebugPanel({
75
75
  )
76
76
  ];
77
77
  try {
78
- await copyText(lines.join("\n"));
78
+ if (!await copyText(lines.join("\n"))) return;
79
79
  setCopied(true);
80
80
  setTimeout(() => setCopied(false), 1500);
81
81
  toast.success("Voice debug copied");
@@ -12,7 +12,7 @@ import {
12
12
  useRealtimeSessionConfig
13
13
  } from "../realtimeSession.js";
14
14
  import { useSessionKnob } from "../../host/prefs-react.js";
15
- import { isLiveConversationVoice } from "../../host/ui-slots.js";
15
+ import { isLiveConversationVoice } from "@ai-matrx/media/voices";
16
16
  const LIVE_CONVERSATION_VOICE_KNOB = "media.conversation.voice";
17
17
  function isVoiceId(v) {
18
18
  return isLiveConversationVoice(v);
@@ -1,20 +1,3 @@
1
- /**
2
- * ObservationalMemoryWindow
3
- *
4
- * Admin-only floating inspector for the Observational Memory feature.
5
- * Wraps `ObservationalMemoryCore` in the standard WindowPanel shell with a
6
- * sidebar listing every conversation the session has seen memory activity
7
- * for — that means either:
8
- * - the conversation has a persisted `observational_memory` metadata block
9
- * (hydrated on conversation load), OR
10
- * - we have received at least one `memory_*` stream event for it this
11
- * session, OR
12
- * - an authoritative cost summary has been fetched for it.
13
- *
14
- * See `/window-panel-authoring` for the registration contract. This window is
15
- * persisted (NOT ephemeral) — admins get their last-selected conversation
16
- * back after a reload, matching the per-conversation nature of the feature.
17
- */
18
1
  import React from "react";
19
2
  interface ObservationalMemoryWindowProps {
20
3
  isOpen: boolean;
@@ -1,5 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useClipboard } from "@ai-matrx/kit/clipboard";
4
+ import { toast } from "../../../host/notify.js";
3
5
  import { useCallback, useMemo, useState } from "react";
4
6
  import {
5
7
  Brain,
@@ -36,9 +38,13 @@ import {
36
38
  import { CHAT_WINDOWS } from "../../../host/windows.js";
37
39
  import { Button } from "@ai-matrx/design-system/controls";
38
40
  function useCopyText(text) {
41
+ const { copyText } = useClipboard({
42
+ notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
43
+ });
39
44
  const [copied, setCopied] = useState(false);
40
45
  const copy = useCallback(() => {
41
- navigator.clipboard.writeText(text).then(() => {
46
+ copyText(text).then((copied2) => {
47
+ if (!copied2) return;
42
48
  setCopied(true);
43
49
  setTimeout(() => setCopied(false), 1500);
44
50
  });