@ai-matrx/chat 0.3.6 → 0.3.11

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 (39) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/agents/components/context-items/bodies/WebpageBody.js +3 -8
  3. package/dist/agents/components/inputs/smart-input/InputActionButtons.d.ts +4 -0
  4. package/dist/agents/components/inputs/smart-input/InputActionButtons.js +16 -9
  5. package/dist/agents/components/inputs/smart-input/RunControlsMenu.d.ts +2 -0
  6. package/dist/agents/components/inputs/smart-input/RunControlsMenu.js +2 -0
  7. package/dist/agents/components/inputs/smart-input/SmartAgentInputStacked.js +1 -1
  8. package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPill.js +4 -6
  9. package/dist/agents/components/inputs/smart-input/composer/ComposerMenu.js +16 -2
  10. package/dist/agents/components/inputs/smart-input/composer/ComposerOutput.js +9 -4
  11. package/dist/agents/components/inputs/smart-input/composer/ComposerPlusMenu.d.ts +3 -1
  12. package/dist/agents/components/inputs/smart-input/composer/ComposerPlusMenu.js +2 -2
  13. package/dist/agents/components/inputs/smart-input/composer/composer-types.d.ts +5 -0
  14. package/dist/agents/components/inputs/variable-input-variations/AgentVariableCards.js +1 -1
  15. package/dist/agents/components/live-run/LiveRunDisplay.d.ts +7 -1
  16. package/dist/agents/components/live-run/LiveRunDisplay.js +10 -3
  17. package/dist/agents/components/messages-display/TranscriptIntegrityCopyButton.js +4 -10
  18. package/dist/agents/components/shared/AgentOptionsMenu.js +2 -2
  19. package/dist/agents/constants/system-agent-registry.d.ts +0 -3
  20. package/dist/agents/constants/system-agent-registry.js +0 -3
  21. package/dist/agents/hooks/useFloatingAgentRun.d.ts +19 -0
  22. package/dist/agents/hooks/useFloatingAgentRun.js +32 -9
  23. package/dist/agents/prompt-preview/PromptPreviewContent.js +2 -6
  24. package/dist/agents/redux/execution-system/message-crud/edit-message.thunk.js +3 -6
  25. package/dist/agents/redux/execution-system/message-crud/fork-and-resubmit-from-message.thunk.js +1 -1
  26. package/dist/compute/targets.d.ts +2 -6
  27. package/dist/context/sources/scopes.d.ts +3 -3
  28. package/dist/host/db-types.d.ts +350 -168
  29. package/dist/host/window-openers.d.ts +23 -5
  30. package/dist/surfaces/manifests/chat.manifest.js +3 -1
  31. package/dist/ui/mandates-storage/mandateStorage.d.ts +15 -0
  32. package/dist/ui/mandates-storage/shortcutStorage.d.ts +3 -0
  33. package/dist/utils/rich-editor/in-place/caret-handoff.js +2 -1
  34. package/dist/voice-agent/components/VoiceDebugPanel.js +4 -8
  35. package/dist/voice-agent/relay/VoiceRelayBar.d.ts +7 -1
  36. package/dist/voice-agent/relay/VoiceRelayBar.js +90 -24
  37. package/dist/window-panels/windows/agents/LiveRunWindow.d.ts +3 -1
  38. package/dist/window-panels/windows/agents/LiveRunWindow.js +5 -2
  39. package/package.json +8 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # @ai-matrx/chat
2
2
 
3
+ ## 0.3.11 — 2026-10-07
4
+ ### Re-sync before matrx-frontend consumes the published package (P27)
5
+
6
+ - Carries every matrx-frontend `packages/chat` edit after `ebed60e111`: floating runs settle and a failed
7
+ launch says why (`useFloatingAgentRun.settle`), live-run failure display, composer menu, window openers,
8
+ chat manifest, caret hand-off, and `host/db-types.ts` regenerated from the frontend's current schema
9
+ (the app's Supabase client is assignable to the package's again).
10
+ - `system-agent-registry` drops the three `agent_apps.*` builtins whose mandates applets B3 deleted (the
11
+ package did not type-check against `@ai-matrx/agents` 0.50.1).
12
+ - Consumer action: none.
13
+
14
+ ## 0.3.10
15
+
16
+ - Clipboard failure handling lives in the `false` branch of the kit copy, which never throws: the dead `catch` blocks around `copyText` in the webpage context body, prompt preview, voice debug panel and transcript-integrity button are gone (the hook's notify already reports the failure).
17
+
18
+ ### Consumer action
19
+
20
+ - None required.
21
+
22
+ ## 0.3.9
23
+
24
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
25
+ `git diff npm/chat/v0.3.5..npm/chat/v0.3.9 -- apps/shared/chat`).
26
+ No source changes intended and no consumer action required.
27
+
28
+ ## 0.3.8
29
+
30
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
31
+ `git diff npm/chat/v0.3.5..npm/chat/v0.3.8 -- apps/shared/chat`).
32
+ No source changes intended and no consumer action required.
33
+
34
+ ## 0.3.7 — 2026-10-07
35
+ ### Re-sync before matrx-frontend consumes the published package (P27)
36
+
37
+ - Carries the matrx-frontend `packages/chat` edits after `3d7ea46a26` not already mirrored here: composer
38
+ output, agent variable cards, agent options menu, edit/fork message thunks, compute targets, context
39
+ scopes, window openers.
40
+ - Consumer action: none.
41
+
3
42
  ## 0.3.6 — 2026-10-07
4
43
  ### Re-sync before matrx-frontend consumes the published package (P27)
5
44
 
@@ -9,7 +9,6 @@ import {
9
9
  TooltipContent,
10
10
  TooltipTrigger
11
11
  } from "@ai-matrx/design-system";
12
- import { toast } from "../../../../host/notify.js";
13
12
  import { WebpageSnapshotView } from "../../../../host/ui-slots.js";
14
13
  import { webpageTitle, webpageUrl } from "../../../resources/webpage-snapshot.js";
15
14
  import { Button } from "@ai-matrx/design-system/controls";
@@ -47,13 +46,9 @@ function WebpageFooter({ item }) {
47
46
  const [copied, setCopied] = useState(false);
48
47
  if (!url) return null;
49
48
  const copy = async () => {
50
- try {
51
- if (!await copyText(url)) return;
52
- setCopied(true);
53
- setTimeout(() => setCopied(false), 1500);
54
- } catch {
55
- toast.error("Failed to copy");
56
- }
49
+ if (!await copyText(url)) return;
50
+ setCopied(true);
51
+ setTimeout(() => setCopied(false), 1500);
57
52
  };
58
53
  return /* @__PURE__ */ jsxs(Fragment, { children: [
59
54
  /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-mono text-[11px] text-muted-foreground", children: url }),
@@ -10,6 +10,7 @@
10
10
  * the parent via `onVoiceBusyChange` so Enter-to-send is gated too.
11
11
  */
12
12
  import React from "react";
13
+ import type { SourceFeature } from "@ai-matrx/agents/generated/source-attribution";
13
14
  import type { ComposerMode, ComposerSize } from "./composer/composer-types.js";
14
15
  /**
15
16
  * Shared resting (idle) tint for every toolbar button in this row. Exported so
@@ -67,6 +68,9 @@ interface InputActionButtonsProps {
67
68
  * Absent = Full's button row inside the card.
68
69
  */
69
70
  part?: "send" | "controls" | "launcher";
71
+ /** Attribution for a Live audio session started here. Absent = "chat". */
72
+ sourceFeature?: SourceFeature;
73
+ liveAudioStartsOn?: boolean;
70
74
  };
71
75
  }
72
76
  export declare function InputActionButtons({ conversationId, showSendButton, showVariableIcon, surfaceKey, disableSend, onVoiceBusyChange, extraRightControls, onRequestInputExpand, composer, }: InputActionButtonsProps): React.JSX.Element | null;
@@ -4,7 +4,6 @@ import { useCallback, useState } from "react";
4
4
  import {
5
5
  CornerDownLeft,
6
6
  Braces,
7
- AudioLines,
8
7
  Loader2,
9
8
  Square
10
9
  } from "lucide-react";
@@ -14,7 +13,8 @@ import {
14
13
  TooltipTrigger
15
14
  } from "@ai-matrx/design-system";
16
15
  import { useAppSelector, useAppDispatch } from "../../../../store/hooks.js";
17
- import { announceComingSoon } from "../../../../host/ui-slots.js";
16
+ import { selectAgentIdFromInstance } from "../../../redux/execution-system/conversations/conversations.selectors.js";
17
+ import { VoiceRelayBar } from "../../../../voice-agent/relay/VoiceRelayBar.js";
18
18
  import { AgentMicrophoneButton } from "./AgentMicrophoneButton.js";
19
19
  import { RunControlsMenu } from "./RunControlsMenu.js";
20
20
  import {
@@ -75,6 +75,8 @@ function InputActionButtons({
75
75
  }) {
76
76
  const dispatch = useAppDispatch();
77
77
  const [voiceBusy, setVoiceBusy] = useState(false);
78
+ const [liveAudioOn, setLiveAudioOn] = useState(composer.liveAudioStartsOn ?? false);
79
+ const agentId = useAppSelector(selectAgentIdFromInstance(conversationId)) ?? null;
78
80
  const { isExecuting, executingConversationId } = useSurfaceExecution(
79
81
  conversationId,
80
82
  surfaceKey
@@ -104,12 +106,16 @@ function InputActionButtons({
104
106
  const handleStop = useCallback(() => {
105
107
  dispatch(cancelExecution(executingConversationId ?? conversationId));
106
108
  }, [executingConversationId, conversationId, dispatch]);
107
- const liveAudioButton = showSendButton ? /* @__PURE__ */ jsx(
108
- InputButton,
109
+ const liveAudioButton = showSendButton && agentId ? /* @__PURE__ */ jsx(
110
+ VoiceRelayBar,
109
111
  {
110
- icon: AudioLines,
111
- tooltip: "Live audio",
112
- onClick: () => void announceComingSoon("chat.live-audio")
112
+ variant: "composer",
113
+ enabled: liveAudioOn,
114
+ onEnabledChange: setLiveAudioOn,
115
+ primaryAgentId: agentId,
116
+ conversationId,
117
+ surfaceKey: surfaceKey ?? `composer:${conversationId}`,
118
+ sourceFeature: composer.sourceFeature ?? "chat"
113
119
  }
114
120
  ) : null;
115
121
  const variablesToggle = shouldShowVariables && showVariableIcon ? /* @__PURE__ */ jsx(
@@ -135,7 +141,8 @@ function InputActionButtons({
135
141
  surfaceKey,
136
142
  folded: composer.folded,
137
143
  // Compact's live audio leaves the row when narrow and rides +.
138
- foldLiveAudio: composer.folded && composer.part === "controls"
144
+ foldLiveAudio: composer.folded && composer.part === "controls" && Boolean(liveAudioButton),
145
+ onLiveAudio: () => setLiveAudioOn(true)
139
146
  }
140
147
  }
141
148
  );
@@ -178,7 +185,7 @@ function InputActionButtons({
178
185
  /* @__PURE__ */ jsx(DesktopPresenceIndicator, { conversationId }),
179
186
  variablesToggle,
180
187
  micGroup,
181
- composer.folded ? null : liveAudioButton,
188
+ composer.folded && !liveAudioOn ? null : liveAudioButton,
182
189
  composer.leading
183
190
  ] }),
184
191
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
@@ -26,6 +26,8 @@ export interface RunControlsMenuProps {
26
26
  folded?: boolean;
27
27
  /** Compact and narrow: live audio lives in the + menu too. */
28
28
  foldLiveAudio?: boolean;
29
+ /** The + menu's Live audio row turns the composer's voice on. */
30
+ onLiveAudio?: () => void;
29
31
  };
30
32
  }
31
33
  export declare function RunControlsMenu({ conversationId, variant, includeAttach, align, side, foldToolbarExtras, onRequestInputExpand, composer, }: RunControlsMenuProps): import("react").JSX.Element;
@@ -113,6 +113,7 @@ function RunControlsMenu({
113
113
  size: composer.size,
114
114
  folded: composer.folded,
115
115
  foldLiveAudio: composer.foldLiveAudio,
116
+ onLiveAudio: composer.onLiveAudio,
116
117
  side,
117
118
  surfaceKey: composer.surfaceKey,
118
119
  onRequestInputExpand
@@ -144,6 +145,7 @@ function RunControlsMenu({
144
145
  size: composer.size,
145
146
  folded: composer.folded,
146
147
  foldLiveAudio: composer.foldLiveAudio,
148
+ onLiveAudio: composer.onLiveAudio,
147
149
  side,
148
150
  surfaceKey: composer.surfaceKey,
149
151
  onRequestInputExpand,
@@ -179,7 +179,7 @@ function SmartAgentInputStacked({
179
179
  surfaceKey,
180
180
  disableSend: sendBlocked
181
181
  };
182
- const composerParts = { size: composer.size, mode: composer.mode, folded };
182
+ const composerParts = { size: composer.size, mode: composer.mode, folded, sourceFeature: composer.sourceFeature, liveAudioStartsOn: composer.liveAudioStartsOn };
183
183
  if (composer.size === "launcher") {
184
184
  return /* @__PURE__ */ jsx(
185
185
  "div",
@@ -53,18 +53,16 @@ function ComposerAgentPill({ conversationId, mode, size, agentControl, menuSide,
53
53
  const agentFilter = useComposerAgentFilter(conversationId);
54
54
  const showOverride = info.modelOverridden && !info.isCustom && composerShows(mode, "agent.presets");
55
55
  const overrideTitle = info.agentModelLabel ? `Agent's own model: ${info.agentModelLabel}` : "Not the agent's own model";
56
- const pill = /* @__PURE__ */ jsxs(
56
+ const pill = /* @__PURE__ */ jsx(
57
57
  Button,
58
58
  {
59
59
  variant: "quiet",
60
60
  iconEnd: /* @__PURE__ */ jsx(ChevronDown, {}),
61
61
  "aria-label": `Agent: ${label}${showOverride ? " (model changed)" : ""}`,
62
62
  title: showOverride ? `${label} \u2014 ${overrideTitle}` : label,
63
- className: "min-w-0",
64
- children: [
65
- label,
66
- showOverride ? /* @__PURE__ */ jsx("span", { "data-testid": "composer-model-override-chip", children: " \xB7 changed" }) : null
67
- ]
63
+ "data-model-override": showOverride ? "" : void 0,
64
+ className: "min-w-0 shrink",
65
+ children: showOverride ? `${label} \xB7 changed` : label
68
66
  }
69
67
  );
70
68
  if (folded) {
@@ -225,6 +225,17 @@ function ComposerMenuLevel({ children }) {
225
225
  };
226
226
  return /* @__PURE__ */ jsx(ComposerMenuLevelContext.Provider, { value: { openId, setOpenId, request, cancel }, children });
227
227
  }
228
+ function useNarrowViewport() {
229
+ const [narrow, setNarrow] = useState(false);
230
+ useEffect(() => {
231
+ const query = window.matchMedia("(max-width: 639px)");
232
+ const sync = () => setNarrow(query.matches);
233
+ sync();
234
+ query.addEventListener("change", sync);
235
+ return () => query.removeEventListener("change", sync);
236
+ }, []);
237
+ return narrow;
238
+ }
228
239
  function ComposerSubmenu({
229
240
  row,
230
241
  children,
@@ -249,6 +260,7 @@ function ComposerSubmenu({
249
260
  const closeAll = useContext(ComposerMenuCloseAllContext);
250
261
  const presentation = useContext(ComposerMenuPresentationContext);
251
262
  const sheetNav = useContext(ComposerSheetNavContext);
263
+ const narrow = useNarrowViewport();
252
264
  if (sheetNav) {
253
265
  return /* @__PURE__ */ jsx(
254
266
  ComposerMenuRow,
@@ -286,9 +298,10 @@ function ComposerSubmenu({
286
298
  /* @__PURE__ */ jsx(
287
299
  PopoverContent,
288
300
  {
289
- side: "right",
301
+ side: narrow ? "top" : "right",
290
302
  align: "start",
291
303
  sideOffset: 6,
304
+ collisionPadding: 8,
292
305
  onEscapeKeyDown: () => {
293
306
  close();
294
307
  closeAll?.();
@@ -305,7 +318,8 @@ function ComposerSubmenu({
305
318
  className: cn(
306
319
  "flex max-h-[var(--radix-popover-content-available-height)] flex-col overflow-hidden p-1",
307
320
  COMPOSER_MENU_NO_ENTRANCE,
308
- panelClassName ?? "w-72"
321
+ panelClassName ?? "w-72",
322
+ "max-sm:!w-[calc(100vw-1rem)]"
309
323
  ),
310
324
  children: /* @__PURE__ */ jsx(ComposerMenuLevel, { children: drillInPlace ? /* @__PURE__ */ jsx(InPlaceNav, { children: typeof children === "function" ? children(close) : children }) : typeof children === "function" ? children(close) : children })
311
325
  }
@@ -222,8 +222,8 @@ function ShapePicker({
222
222
  if (lock.status === "reading") {
223
223
  return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1 px-2.5 py-2", "aria-label": "Checking the agent's shape", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-full rounded-md" }) });
224
224
  }
225
- return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-col", children: [
226
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-2.5 pb-1 pt-2", children: [
225
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [
226
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 px-2.5 pb-1 pt-2", children: [
227
227
  /* @__PURE__ */ jsx("span", { className: "truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Shapes" }),
228
228
  kinds.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] tabular-nums text-muted-foreground", children: [
229
229
  kinds.length,
@@ -315,7 +315,7 @@ function ShapePicker({
315
315
  }
316
316
  function ComposerOutputPanel({ conversationId }) {
317
317
  const output = useComposerOutput(conversationId);
318
- return /* @__PURE__ */ jsxs("div", { className: "@container flex min-h-0 flex-col", children: [
318
+ return /* @__PURE__ */ jsxs("div", { className: "@container flex min-h-0 flex-1 flex-col", children: [
319
319
  /* @__PURE__ */ jsx(ComposerMenuLabel, { children: "Output types" }),
320
320
  /* @__PURE__ */ jsx(TypeGrid, { types: output.types, onToggle: output.toggleType }),
321
321
  /* @__PURE__ */ jsx(ComposerMenuHelp, { children: "Narrows the agent list. Not sent to the model." }),
@@ -333,6 +333,7 @@ function ComposerOutputPill({
333
333
  menuSide
334
334
  }) {
335
335
  const [open, setOpen] = useState(false);
336
+ const [roomierSide, setRoomierSide] = useState(null);
336
337
  const output = useComposerOutput(conversationId);
337
338
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, modal: false, children: [
338
339
  /* @__PURE__ */ jsxs(SplitButton, { className: "min-w-0", children: [
@@ -345,6 +346,10 @@ function ComposerOutputPill({
345
346
  "aria-label": `Output: ${output.label}`,
346
347
  title: output.lockWarning ?? void 0,
347
348
  className: "min-w-0",
349
+ onClick: (event) => {
350
+ const rect = event.currentTarget.getBoundingClientRect();
351
+ setRoomierSide(rect.top > window.innerHeight - rect.bottom ? "top" : "bottom");
352
+ },
348
353
  children: output.label
349
354
  }
350
355
  ) }),
@@ -353,7 +358,7 @@ function ComposerOutputPill({
353
358
  /* @__PURE__ */ jsx(
354
359
  PopoverContent,
355
360
  {
356
- side: menuSide,
361
+ side: roomierSide ?? menuSide,
357
362
  align: "start",
358
363
  sideOffset: 8,
359
364
  className: "flex max-h-[var(--radix-popover-content-available-height)] w-[340px] max-w-[calc(100vw-1rem)] flex-col overflow-hidden p-1",
@@ -35,6 +35,8 @@ interface ComposerPlusMenuProps {
35
35
  folded?: boolean;
36
36
  /** Compact and narrow: live audio lives here too. */
37
37
  foldLiveAudio?: boolean;
38
+ /** Turns the composer's Live audio on (the row is folded, so it lives here). */
39
+ onLiveAudio?: () => void;
38
40
  side: "top" | "bottom";
39
41
  /** The host's surface key — auto-clear keeps its display slot in step. */
40
42
  surfaceKey?: string;
@@ -45,7 +47,7 @@ interface ComposerPlusMenuProps {
45
47
  open?: boolean;
46
48
  onOpenChange?: (open: boolean) => void;
47
49
  }
48
- export declare function ComposerPlusMenu({ conversationId, surfaceKey, trigger, mode, folded, foldLiveAudio, side, onRequestInputExpand, presentation, open: openProp, onOpenChange, }: ComposerPlusMenuProps): import("react").JSX.Element;
50
+ export declare function ComposerPlusMenu({ conversationId, surfaceKey, trigger, mode, folded, foldLiveAudio, onLiveAudio, side, onRequestInputExpand, presentation, open: openProp, onOpenChange, }: ComposerPlusMenuProps): import("react").JSX.Element;
49
51
  /**
50
52
  * Environment (brief §6) — where the agent runs, as ONE flat list: Cloud, then
51
53
  * every computer and sandbox this person can use. One click chooses (Arman,
@@ -81,7 +81,6 @@ import {
81
81
  ComposerMenuSwitchRow,
82
82
  ComposerSubmenu
83
83
  } from "./ComposerMenu.js";
84
- import { announceComingSoon } from "../../../../../host/ui-slots.js";
85
84
  import { ComposerMenuSheet } from "./ComposerMenuSheet.js";
86
85
  import { composerShows } from "./composer-mode-visibility.js";
87
86
  import { useTouchOnlyDevice } from "../../../../../ui/composer/useTouchOnlyDevice.js";
@@ -101,6 +100,7 @@ function ComposerPlusMenu({
101
100
  mode,
102
101
  folded = false,
103
102
  foldLiveAudio = false,
103
+ onLiveAudio,
104
104
  side,
105
105
  onRequestInputExpand,
106
106
  presentation = "popover",
@@ -360,7 +360,7 @@ function ComposerPlusMenu({
360
360
  label: "Live audio",
361
361
  onClick: () => {
362
362
  close();
363
- void announceComingSoon("chat.live-audio");
363
+ onLiveAudio?.();
364
364
  }
365
365
  }
366
366
  ) : null
@@ -1,3 +1,4 @@
1
+ import type { SourceFeature } from "@ai-matrx/agents/generated/source-attribution";
1
2
  import type { AnyMandateKey } from "@ai-matrx/agents/mandates";
2
3
  import type { EditableContextMenuProps } from "../../../../../host/ui-slots.js";
3
4
  import type { ApplicationScope } from "../../../../types/scope.types.js";
@@ -83,4 +84,8 @@ export interface ComposerPresentation {
83
84
  maxInputHeightPx?: number;
84
85
  /** The right-click agent menu on the textarea. Absent = no menu. */
85
86
  textMenu?: ComposerTextMenu;
87
+ /** Who started a Live audio session here (attribution). Absent = "chat". */
88
+ sourceFeature?: SourceFeature;
89
+ /** Live audio starts switched on (the person already chose to talk, e.g. a voice interview). */
90
+ liveAudioStartsOn?: boolean;
86
91
  }
@@ -25,7 +25,7 @@ function AgentVariableInputCard({
25
25
  }) {
26
26
  const dispatch = useAppDispatch();
27
27
  const userValues = useAppSelector(selectUserVariableValues(conversationId));
28
- const currentValue = userValues[variable.name];
28
+ const currentValue = userValues[variable.name] ?? variable.defaultValue;
29
29
  const comp = variable.customComponent;
30
30
  const type = comp?.type ?? "textarea";
31
31
  const setValue = (value) => {
@@ -19,6 +19,12 @@ export interface LiveRunDisplayProps {
19
19
  * the status line alive from the very first click (no dead moment).
20
20
  */
21
21
  pending?: boolean;
22
+ /**
23
+ * The run failed before any stream existed (the launch was refused — e.g. a
24
+ * Mandate with no Holder). Shown in the error slot instead of "Starting…",
25
+ * so a refused launch never reads as a run still on its way.
26
+ */
27
+ failure?: string | null;
22
28
  /** Renders a dismiss (X) affordance; caller tears the run down. */
23
29
  onDismiss?: () => void;
24
30
  className?: string;
@@ -36,4 +42,4 @@ export interface LiveRunDisplayProps {
36
42
  */
37
43
  variant?: "card" | "bare";
38
44
  }
39
- export declare function LiveRunDisplay({ conversationId, requestId: requestIdProp, label, pending, onDismiss, className, bodyClassName, variant, }: LiveRunDisplayProps): import("react").JSX.Element | null;
45
+ export declare function LiveRunDisplay({ conversationId, requestId: requestIdProp, label, pending, failure, onDismiss, className, bodyClassName, variant, }: LiveRunDisplayProps): import("react").JSX.Element | null;
@@ -20,12 +20,19 @@ function LiveRunDisplay({
20
20
  requestId: requestIdProp,
21
21
  label,
22
22
  pending = false,
23
+ failure = null,
23
24
  onDismiss,
24
25
  className,
25
26
  bodyClassName,
26
27
  variant = "card"
27
28
  }) {
28
- const { requestId, isActive, statusText, errorMessage, chunkCount } = useLiveRunStatus(conversationId, requestIdProp, pending);
29
+ const status = useLiveRunStatus(
30
+ conversationId,
31
+ requestIdProp,
32
+ pending && !failure
33
+ );
34
+ const { requestId, isActive, statusText, chunkCount } = status;
35
+ const errorMessage = status.errorMessage ?? (failure || null);
29
36
  const conversationMessages = useAppSelector(
30
37
  conversationId ? selectConversationMessages(conversationId) : selectNoConversationMessages
31
38
  );
@@ -40,7 +47,7 @@ function LiveRunDisplay({
40
47
  if (el) el.scrollTop = el.scrollHeight;
41
48
  }, [chunkCount, isActive]);
42
49
  const hasRun = Boolean(conversationId || requestId);
43
- if (!hasRun && !pending) return null;
50
+ if (!hasRun && !pending && !failure) return null;
44
51
  if (variant === "bare") {
45
52
  return /* @__PURE__ */ jsxs("div", { className: cn("h-full min-h-0", className), children: [
46
53
  errorMessage ? /* @__PURE__ */ jsxs("p", { className: "pb-2 text-xs text-destructive", children: [
@@ -94,7 +101,7 @@ function LiveRunDisplay({
94
101
  ),
95
102
  children: [
96
103
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border px-2.5 py-1.5 text-xs text-muted-foreground", children: [
97
- isActive || pending && !requestId ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin text-primary" }) : errorMessage ? /* @__PURE__ */ jsx(TriangleAlert, { className: "h-3.5 w-3.5 shrink-0 text-destructive" }) : null,
104
+ isActive || pending && !requestId && !errorMessage ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin text-primary" }) : errorMessage ? /* @__PURE__ */ jsx(TriangleAlert, { className: "h-3.5 w-3.5 shrink-0 text-destructive" }) : null,
98
105
  /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate font-medium", children: [
99
106
  label ?? "AI run",
100
107
  serverPhase ? /* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground/80", children: [
@@ -67,16 +67,10 @@ function TranscriptIntegrityCopyButtonInner({
67
67
  pathname,
68
68
  effectiveVisibleGroupLimit
69
69
  });
70
- try {
71
- if (!await copyText(formatTranscriptIntegrityReport(report))) return;
72
- toast.success(
73
- report.anomalies.length === 0 ? "Transcript report copied \u2014 no anomalies detected" : `Transcript report copied \u2014 ${report.anomalies.length} anomal${report.anomalies.length === 1 ? "y" : "ies"} flagged`
74
- );
75
- } catch (err) {
76
- toast.error(
77
- `Could not copy the transcript report: ${err instanceof Error ? err.message : String(err)}`
78
- );
79
- }
70
+ if (!await copyText(formatTranscriptIntegrityReport(report))) return;
71
+ toast.success(
72
+ report.anomalies.length === 0 ? "Transcript report copied \u2014 no anomalies detected" : `Transcript report copied \u2014 ${report.anomalies.length} anomal${report.anomalies.length === 1 ? "y" : "ies"} flagged`
73
+ );
80
74
  };
81
75
  return /* @__PURE__ */ jsx(
82
76
  Button,
@@ -274,7 +274,7 @@ function AgentOptionsMenu({
274
274
  setOpen(false);
275
275
  } else if (label === "Create App") {
276
276
  console.log("[AGENT OPTIONS MENU] Creating app, Agent ID:", agentId);
277
- openCreateApp({ agentId: agentId ?? null });
277
+ openCreateApp();
278
278
  setOpen(false);
279
279
  } else if (label === "Add Data Storage Support") {
280
280
  console.log(
@@ -618,7 +618,7 @@ function MobileMenuContent({
618
618
  openFindUsages({ agentId: agentId ?? null });
619
619
  onClose();
620
620
  } else if (label === "Create App") {
621
- openCreateApp({ agentId: agentId ?? null });
621
+ openCreateApp();
622
622
  onClose();
623
623
  } else if (label === "Add Data Storage Support") {
624
624
  openDataStorage({ agentId: agentId ?? null });
@@ -15,9 +15,6 @@ import { type MandateKey } from "@ai-matrx/agents/mandates";
15
15
  * `services/mandates/client_mandates.py`, then add its key here.
16
16
  */
17
17
  export declare const SYSTEM_AGENT_MANDATES: {
18
- readonly "prompt-app-auto-create": "agent_apps.auto_create";
19
- readonly "prompt-app-auto-create-lightning": "agent_apps.auto_create_lightning";
20
- readonly "prompt-app-metadata-generator": "agent_apps.metadata";
21
18
  readonly "prompt-app-ui-editor": "code_editor.prompt_app_ui_edit";
22
19
  readonly "generic-code-editor": "code_editor.code_edit";
23
20
  readonly "code-editor-dynamic-context": "code_editor.dynamic_context_edit";
@@ -1,8 +1,5 @@
1
1
  import { MANDATE_KEYS } from "@ai-matrx/agents/mandates";
2
2
  const SYSTEM_AGENT_MANDATES = {
3
- "prompt-app-auto-create": MANDATE_KEYS.agent_apps__auto_create,
4
- "prompt-app-auto-create-lightning": MANDATE_KEYS.agent_apps__auto_create_lightning,
5
- "prompt-app-metadata-generator": MANDATE_KEYS.agent_apps__metadata,
6
3
  "prompt-app-ui-editor": MANDATE_KEYS.code_editor__prompt_app_ui_edit,
7
4
  "generic-code-editor": MANDATE_KEYS.code_editor__code_edit,
8
5
  "code-editor-dynamic-context": MANDATE_KEYS.code_editor__dynamic_context_edit,
@@ -1,4 +1,10 @@
1
1
  import { type LiveAgentRunOptions } from "./useLiveAgentRun.js";
2
+ /**
3
+ * The words a failed launch shows in its window: the server's own reason when
4
+ * the run carried one (a Mandate with no Holder names the rungs it checked),
5
+ * else the error's message.
6
+ */
7
+ export declare function floatingFailureReason(error: unknown): string;
2
8
  /**
3
9
  * A run this hook only WATCHES — launched and owned elsewhere, its state read
4
10
  * straight out of React. Pass it as `track` and the window follows it; there is
@@ -48,6 +54,19 @@ export interface FloatingRun {
48
54
  bindRequest: (requestId: string) => void;
49
55
  /** Close this run's window (and release its instance when owned). */
50
56
  close: () => void;
57
+ /**
58
+ * The launch failed: the window stops saying "Starting…" and shows `reason`.
59
+ * Call it from the launch's failure path — a window left pending after a
60
+ * refused launch reads as a run that is still coming (the Spaces "Build
61
+ * with AI" incident, 2026-10-07).
62
+ */
63
+ fail: (reason: string) => void;
64
+ /**
65
+ * Call once the launch has returned, however it ended: a window still
66
+ * waiting for its first stream handle shows `reason` instead of "Starting…"
67
+ * forever. A bound window is left alone — its own stream tells the story.
68
+ */
69
+ settle: (reason: string) => void;
51
70
  }
52
71
  export interface UseFloatingRunWindow {
53
72
  /** Open the window BEFORE launching, then bind the run to it. */
@@ -7,6 +7,13 @@ import {
7
7
  useLiveAgentRun
8
8
  } from "./useLiveAgentRun.js";
9
9
  import { useLiveRunHandle } from "./useLiveRunHandle.js";
10
+ import { HeadlessAgentRunError } from "./useHeadlessAgentJson.js";
11
+ import { isOrganizationSelectionCancelled } from "../../host/org.js";
12
+ function floatingFailureReason(error) {
13
+ if (error instanceof HeadlessAgentRunError && error.detail) return error.detail;
14
+ if (error instanceof Error && error.message) return error.message;
15
+ return "The run could not start.";
16
+ }
10
17
  function useFloatingRunWindow(options = {}) {
11
18
  const { instanceId, width, height, owns = true, track } = options;
12
19
  const openWindow = useOpenLiveRunWindow();
@@ -76,13 +83,22 @@ function useFloatingRunWindow(options = {}) {
76
83
  height
77
84
  });
78
85
  handleRef.current = handle;
86
+ let bound = false;
79
87
  return {
80
88
  bind: (conversationId) => {
89
+ bound = true;
81
90
  if (owns) run.claim(conversationId);
82
91
  handle.update({ conversationId, pending: false });
83
92
  },
84
- bindRequest: (requestId) => handle.update({ requestId, pending: false }),
85
- close
93
+ bindRequest: (requestId) => {
94
+ bound = true;
95
+ handle.update({ requestId, pending: false });
96
+ },
97
+ close,
98
+ fail: (reason) => handle.update({ pending: false, failure: reason }),
99
+ settle: (reason) => {
100
+ if (!bound) handle.update({ pending: false, failure: reason });
101
+ }
86
102
  };
87
103
  };
88
104
  return { start, close };
@@ -93,13 +109,19 @@ function useFloatingAgentRun(options = {}) {
93
109
  async function run(opts) {
94
110
  const { label, ...runOptions } = opts;
95
111
  const bound = floating.start(label ?? options.label ?? "AI is working");
96
- return live.run({
97
- ...runOptions,
98
- onConversationCreated: (conversationId) => {
99
- bound.bind(conversationId);
100
- opts.onConversationCreated?.(conversationId);
101
- }
102
- });
112
+ try {
113
+ return await live.run({
114
+ ...runOptions,
115
+ onConversationCreated: (conversationId) => {
116
+ bound.bind(conversationId);
117
+ opts.onConversationCreated?.(conversationId);
118
+ }
119
+ });
120
+ } catch (error) {
121
+ if (isOrganizationSelectionCancelled(error)) bound.close();
122
+ else bound.fail(floatingFailureReason(error));
123
+ throw error;
124
+ }
103
125
  }
104
126
  return {
105
127
  run,
@@ -114,6 +136,7 @@ function useFloatingAgentRun(options = {}) {
114
136
  };
115
137
  }
116
138
  export {
139
+ floatingFailureReason,
117
140
  useFloatingAgentRun,
118
141
  useFloatingRunWindow
119
142
  };
@@ -43,12 +43,8 @@ function PromptPreviewContent({
43
43
  };
44
44
  }, [conversationId, store, nonce]);
45
45
  const copy = async (label, text) => {
46
- try {
47
- if (!await copyText(text)) return;
48
- toast.success(`${label} copied`);
49
- } catch {
50
- toast.error("Couldn't copy");
51
- }
46
+ if (!await copyText(text)) return;
47
+ toast.success(`${label} copied`);
52
48
  };
53
49
  return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-3 bg-textured p-4", children: [
54
50
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
@@ -3,6 +3,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
3
3
  import { supabase } from "../../../../host/db.js";
4
4
  import { updateMessageRecord } from "../messages/messages.slice.js";
5
5
  import { extractFlatText } from "../messages/messages.selectors.js";
6
+ import { saveOutputFeedback } from "../../../../host/ui-slots.js";
6
7
  import {
7
8
  clearRequestEditedText,
8
9
  setRequestEditedText
@@ -165,12 +166,8 @@ const editMessage = createAsyncThunk(
165
166
  if (prevRecord.role === "assistant") {
166
167
  void (async () => {
167
168
  try {
168
- const [{ saveOutputFeedback }, { extractFlatText: extractFlatText2 }] = await Promise.all([
169
- import("../../../../host/ui-slots.js"),
170
- import("../messages/messages.selectors.js")
171
- ]);
172
- const originalText = extractFlatText2(prevRecord);
173
- const correctedText = extractFlatText2({
169
+ const originalText = extractFlatText(prevRecord);
170
+ const correctedText = extractFlatText({
174
171
  ...prevRecord,
175
172
  content: newContent
176
173
  });