@ai-matrx/chat 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/dist/agents/components/chat/ChatIncognitoHeaderButton.js +1 -1
  3. package/dist/agents/components/chat/SandboxPanel.js +1 -1
  4. package/dist/agents/components/context-items/registry.js +14 -3
  5. package/dist/agents/components/conversation-history/ConversationHistorySidebar.js +1 -1
  6. package/dist/agents/components/inputs/resources/SmartAgentResourceChips.js +4 -1
  7. package/dist/agents/components/inputs/resources/useAttachRenderedArtifact.d.ts +20 -0
  8. package/dist/agents/components/inputs/resources/useAttachRenderedArtifact.js +107 -0
  9. package/dist/agents/components/inputs/smart-input/RunControlsTabPanel.js +2 -1
  10. package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPill.js +135 -204
  11. package/dist/agents/components/inputs/smart-input/composer/already-answering.d.ts +1 -1
  12. package/dist/agents/components/inputs/smart-input/composer/already-answering.js +1 -1
  13. package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.d.ts +1 -1
  14. package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.js +1 -1
  15. package/dist/agents/components/inputs/variable-input-variations/AgentVariableCards.js +6 -5
  16. package/dist/agents/components/inputs/variable-input-variations/AgentVariableForm.js +6 -5
  17. package/dist/agents/components/inputs/variable-input-variations/AgentVariablesGuided.js +6 -5
  18. package/dist/agents/components/messages-display/user/UserActionBar.js +1 -1
  19. package/dist/agents/components/run/AgentRunHeader.js +1 -1
  20. package/dist/agents/components/run/run-sidebar/SidebarHeader.js +1 -1
  21. package/dist/agents/components/run-controls/RunModelPicker.d.ts +5 -1
  22. package/dist/agents/components/run-controls/RunModelPicker.js +23 -1
  23. package/dist/agents/components/shared/AgentConversationColumn.js +3 -1
  24. package/dist/agents/components/shared/AgentHeader.js +1 -1
  25. package/dist/agents/components/shared/AgentHeaderMobile.js +1 -1
  26. package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
  27. package/dist/agents/components/shared/assistant-message-layout.d.ts +21 -0
  28. package/dist/agents/components/shared/assistant-message-layout.js +7 -1
  29. package/dist/agents/components/working-document/WorkingDocumentVersionHistory.js +1 -1
  30. package/dist/agents/redux/execution-system/instance-resources/instance-resources.selectors.js +14 -0
  31. package/dist/agents/types/instance.types.d.ts +18 -3
  32. package/dist/agents/utils/renderedArtifactContext.d.ts +41 -0
  33. package/dist/agents/utils/renderedArtifactContext.js +41 -0
  34. package/dist/conversation/hooks/useDomCapturePrint.js +4 -0
  35. package/dist/conversation/utils/frame-print.d.ts +11 -0
  36. package/dist/conversation/utils/frame-print.js +46 -0
  37. package/dist/conversation/utils/markdown-print.d.ts +9 -1
  38. package/dist/conversation/utils/markdown-print.js +55 -26
  39. package/dist/cx-chat/components/ChatMobileHeaderBar.js +1 -1
  40. package/dist/cx-chat/components/ChatPanelBackButton.js +1 -1
  41. package/dist/cx-chat/components/ChatSidebarClient.js +1 -1
  42. package/dist/cx-chat/components/user-input/ConversationInput.js +2 -2
  43. package/dist/cx-chat/components/user-input/InputActionButtons.js +2 -2
  44. package/dist/host/frame-print.d.ts +17 -0
  45. package/dist/host/frame-print.js +13 -0
  46. package/dist/surfaces/components/chrome/SurfaceAgentsHeaderButton.js +1 -1
  47. package/dist/tool-call-visualization/registry/registry.js +29 -28
  48. package/dist/voice-agent/components/VoiceAgentSurface.js +2 -2
  49. package/package.json +29 -9
@@ -1,36 +1,30 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
4
- import { AppWindow, ChevronDown, Gauge, Layers, Star, Webhook } from "lucide-react";
5
- import { Button, Tile } from "@ai-matrx/design-system/controls";
3
+ import { useContext, useState } from "react";
4
+ import { AppWindow, ChevronDown, Cpu, Gauge, Layers, Star, Webhook } from "lucide-react";
5
+ import { Button } from "@ai-matrx/design-system/controls";
6
6
  import { MANDATE_KEYS } from "@ai-matrx/agents/mandates";
7
7
  import { Popover, PopoverContent, PopoverTrigger } from "@ai-matrx/design-system";
8
- import { AgentListDropdown } from "@ai-matrx/agents/catalog/react";
9
- import { useAppDispatch, useAppSelector } from "../../../../../store/hooks.js";
8
+ import { AgentListDropdown, AgentListPanel } from "@ai-matrx/agents/catalog/react";
9
+ import { useIsMobile } from "@ai-matrx/kit/media-query";
10
+ import { useAppSelector } from "../../../../../store/hooks.js";
10
11
  import { cn } from "@ai-matrx/design-system";
11
- import { toast } from "../../../../../host/notify.js";
12
12
  import { announceAlreadyAnswering } from "./already-answering.js";
13
13
  import { QuickRunModelSelect } from "../../../run-controls/RunModelPicker.js";
14
- import { ModelListDropdown } from "../../../../../host/ui-slots.js";
15
- import { seedOverrides } from "../../../../redux/execution-system/instance-model-overrides/instance-model-overrides.slice.js";
16
- import { selectInstanceOverrideState } from "../../../../redux/execution-system/instance-model-overrides/instance-model-overrides.selectors.js";
17
- import {
18
- effectiveOfferingPin,
19
- setOfferingPin
20
- } from "../../../../redux/execution-system/instance-model-overrides/offering-pin.js";
21
- import { useSessionKnob } from "../../../../../host/prefs-react.js";
22
- import { knobRefusalSentence, setKnobOverride } from "../../../../../host/prefs.js";
23
- import { CHAT_DEFAULT_MODEL_KNOB } from "../../../../../ui/chat-default-model-knob.js";
24
- import { selectModelLabelWithClass } from "@ai-matrx/agents/models";
25
- import { useModelRecords } from "../../../../identity/model-catalog.js";
14
+ import { selectIsManualExecutionMode } from "../../../../redux/execution-system/selectors/aggregate.selectors.js";
26
15
  import {
16
+ COMPOSER_MENU_NO_ENTRANCE,
17
+ ComposerMenuCloseAllContext,
27
18
  ComposerMenuDivider,
28
- ComposerMenuHelp,
29
19
  ComposerMenuLabel,
20
+ ComposerMenuLevel,
21
+ ComposerMenuPresentationContext,
30
22
  ComposerMenuRow,
31
23
  ComposerSubmenu,
32
- ComposerFoldedSection
24
+ ComposerFoldedSection,
25
+ ignoreOwnWrapper
33
26
  } from "./ComposerMenu.js";
27
+ import { ComposerMenuSheet } from "./ComposerMenuSheet.js";
34
28
  import { ComposerOutputPanel } from "./ComposerOutput.js";
35
29
  import { ComposerEffortRows, useComposerEffort } from "./ComposerEffortPill.js";
36
30
  import { composerShows } from "./composer-mode-visibility.js";
@@ -38,21 +32,24 @@ import { composerPillClass } from "./composer-chip.js";
38
32
  import { useComposerAgent, useEffectiveModelId } from "./useComposerAgent.js";
39
33
  import { useComposerAgentFilter } from "./useComposerAgentFilter.js";
40
34
  import { ComposerAgentPeek } from "./ComposerAgentPeek.js";
41
- import { presentOrganizationRefusal } from "../../../../../host/org.js";
42
- import { ensureOrgId, isOrganizationSelectionCancelled } from "../../../../../host/org.js";
35
+ const AGENT_PICKER_PANEL = "w-[680px] h-[min(528px,80dvh)] p-0";
36
+ const MODEL_PICKER_PANEL = "w-[min(1060px,calc(100vw-24px))] h-[min(640px,80dvh)] p-0";
37
+ const CUSTOM_AGENT_LABEL = "Custom Agent";
43
38
  function pillLabel(info, mode) {
44
39
  if (!composerShows(mode, "agent.presets")) return info.agentName ?? "Agent";
45
- const name = info.isCustom ? "Custom" : info.preset?.name ?? info.agentName ?? "Agent";
40
+ const name = info.isCustom ? CUSTOM_AGENT_LABEL : info.preset?.name ?? info.agentName ?? "Agent";
46
41
  return info.effectiveModelLabel ? `${name} \xB7 ${info.effectiveModelLabel}` : name;
47
42
  }
48
43
  function ComposerAgentPill({ conversationId, mode, size, agentControl, menuSide, folded = false }) {
49
44
  const [open, setOpen] = useState(false);
45
+ const isMobile = useIsMobile();
50
46
  const effort = useComposerEffort(conversationId, useEffectiveModelId(conversationId));
51
47
  const info = useComposerAgent(conversationId);
52
48
  const label = pillLabel(info, mode);
53
49
  const onSelectAgent = agentControl?.onSelectAgent;
54
50
  const agentFilter = useComposerAgentFilter(conversationId);
55
- const showOverride = info.modelOverridden && !info.isCustom && composerShows(mode, "agent.presets");
51
+ const chatPresets = composerShows(mode, "agent.presets");
52
+ const showOverride = info.modelOverridden && chatPresets;
56
53
  const overrideTitle = info.agentModelLabel ? `Agent's own model: ${info.agentModelLabel}` : "Not the agent's own model";
57
54
  const pill = /* @__PURE__ */ jsx(
58
55
  Button,
@@ -62,55 +59,12 @@ function ComposerAgentPill({ conversationId, mode, size, agentControl, menuSide,
62
59
  "aria-label": `Agent: ${label}${showOverride ? " (model changed)" : ""}`,
63
60
  title: showOverride ? `${label} \u2014 ${overrideTitle}` : label,
64
61
  "data-model-override": showOverride ? "" : void 0,
62
+ onClick: isMobile ? () => setOpen(true) : void 0,
65
63
  className: "min-w-0 shrink",
66
64
  children: showOverride ? `${label} \xB7 changed` : label
67
65
  }
68
66
  );
69
- if (folded) {
70
- const chatPresets = composerShows(mode, "agent.presets");
71
- const showEffort = Boolean(effort) && composerShows(mode, "meta.effort");
72
- return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, modal: false, children: [
73
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: pill }),
74
- /* @__PURE__ */ jsxs(
75
- PopoverContent,
76
- {
77
- side: menuSide,
78
- align: "end",
79
- sideOffset: 8,
80
- className: "flex w-80 max-h-[var(--radix-popover-content-available-height)] flex-col overflow-y-auto p-1",
81
- children: [
82
- chatPresets ? /* @__PURE__ */ jsx(
83
- ChatPresetsPanel,
84
- {
85
- conversationId,
86
- info,
87
- agentControl,
88
- close: () => setOpen(false)
89
- }
90
- ) : onSelectAgent ? /* @__PURE__ */ jsx(
91
- AgentListDropdown,
92
- {
93
- onSelect: (agentId) => {
94
- setOpen(false);
95
- if (agentId !== info.agentId) onSelectAgent(agentId);
96
- else announceAlreadyAnswering(info);
97
- },
98
- activeAgentId: info.agentId,
99
- agentFilter,
100
- contentSide: "left",
101
- triggerSlot: /* @__PURE__ */ jsx(Tile, { variant: "quiet", icon: /* @__PURE__ */ jsx(Webhook, {}), title: info.agentName ?? "Agent", end: /* @__PURE__ */ jsx(ChevronDown, {}) })
102
- }
103
- ) : /* @__PURE__ */ jsx(ComposerMenuRow, { icon: Webhook, label: info.agentName ?? "Agent", checked: true }),
104
- /* @__PURE__ */ jsxs(ComposerFoldedSection, { children: [
105
- /* @__PURE__ */ jsx(ComposerSubmenu, { row: { icon: AppWindow, label: "Output" }, panelClassName: "w-80", children: /* @__PURE__ */ jsx(ComposerOutputPanel, { conversationId }) }),
106
- showEffort && effort ? /* @__PURE__ */ jsx(ComposerSubmenu, { row: { icon: Gauge, label: "Effort", detail: effort.word }, panelClassName: "w-56 p-1", children: (close) => /* @__PURE__ */ jsx(ComposerEffortRows, { effort, onChosen: close }) }) : null
107
- ] })
108
- ]
109
- }
110
- )
111
- ] });
112
- }
113
- if (!composerShows(mode, "agent.presets")) {
67
+ if (!chatPresets && !folded) {
114
68
  if (!onSelectAgent) {
115
69
  return /* @__PURE__ */ jsx("span", { className: cn(composerPillClass(size, false), "hover:bg-transparent"), title: "This page always answers with this agent", children: /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: label }) });
116
70
  }
@@ -128,6 +82,39 @@ function ComposerAgentPill({ conversationId, mode, size, agentControl, menuSide,
128
82
  }
129
83
  );
130
84
  }
85
+ const close = () => setOpen(false);
86
+ const choose = (agentId) => {
87
+ close();
88
+ if (!onSelectAgent) return;
89
+ if (agentId !== info.agentId) onSelectAgent(agentId);
90
+ else announceAlreadyAnswering(info);
91
+ };
92
+ const showEffort = folded && Boolean(effort) && composerShows(mode, "meta.effort");
93
+ const body = /* @__PURE__ */ jsx(ComposerMenuCloseAllContext.Provider, { value: close, children: /* @__PURE__ */ jsxs(ComposerMenuLevel, { children: [
94
+ chatPresets ? /* @__PURE__ */ jsx(ChatAgentMenu, { conversationId, info, agentControl, choose, close }) : onSelectAgent ? (
95
+ // Work+, narrow: the agent picker is one row of the folded menu.
96
+ /* @__PURE__ */ jsx(
97
+ AnyAgentRow,
98
+ {
99
+ icon: Webhook,
100
+ label: `Agent: ${info.agentName ?? "Agent"}`,
101
+ info,
102
+ agentFilter,
103
+ choose
104
+ }
105
+ )
106
+ ) : /* @__PURE__ */ jsx(ComposerMenuRow, { icon: Webhook, label: `Agent: ${info.agentName ?? "Agent"}` }),
107
+ folded ? /* @__PURE__ */ jsxs(ComposerFoldedSection, { children: [
108
+ /* @__PURE__ */ jsx(ComposerSubmenu, { row: { icon: AppWindow, label: "Output" }, panelClassName: "w-80", children: /* @__PURE__ */ jsx(ComposerOutputPanel, { conversationId }) }),
109
+ showEffort && effort ? /* @__PURE__ */ jsx(ComposerSubmenu, { row: { icon: Gauge, label: "Effort", detail: effort.word }, panelClassName: "w-56 p-1", children: (closePanel) => /* @__PURE__ */ jsx(ComposerEffortRows, { effort, onChosen: closePanel }) }) : null
110
+ ] }) : null
111
+ ] }) });
112
+ if (isMobile) {
113
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ pill,
115
+ /* @__PURE__ */ jsx(ComposerMenuSheet, { open, onOpenChange: setOpen, accessibleName: "Agent and model", children: body })
116
+ ] });
117
+ }
131
118
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, modal: false, children: [
132
119
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: pill }),
133
120
  /* @__PURE__ */ jsx(
@@ -136,163 +123,107 @@ function ComposerAgentPill({ conversationId, mode, size, agentControl, menuSide,
136
123
  side: menuSide,
137
124
  align: "end",
138
125
  sideOffset: 8,
139
- className: "flex w-80 max-h-[var(--radix-popover-content-available-height)] flex-col overflow-y-auto p-1",
140
- children: /* @__PURE__ */ jsx(
141
- ChatPresetsPanel,
142
- {
143
- conversationId,
144
- info,
145
- agentControl,
146
- close: () => setOpen(false)
147
- }
148
- )
126
+ onPointerDownOutside: ignoreOwnWrapper,
127
+ className: cn(
128
+ "flex w-80 max-h-[var(--radix-popover-content-available-height)] flex-col overflow-y-auto p-1",
129
+ COMPOSER_MENU_NO_ENTRANCE
130
+ ),
131
+ children: body
149
132
  }
150
133
  )
151
134
  ] });
152
135
  }
153
- function ThisAgentRow({ info, close }) {
154
- if (!info.agentId) return null;
155
- return /* @__PURE__ */ jsx(
156
- ComposerSubmenu,
157
- {
158
- row: { icon: Webhook, label: info.agentName ?? "Agent", description: "View, open or duplicate" },
159
- panelClassName: "w-[340px] h-[420px] p-0",
160
- children: /* @__PURE__ */ jsx(ComposerAgentPeek, { agentId: info.agentId, onDone: close })
161
- }
162
- );
163
- }
164
- function ChatPresetsPanel({
136
+ function ChatAgentMenu({
165
137
  conversationId,
166
138
  info,
167
139
  agentControl,
140
+ choose,
168
141
  close
169
142
  }) {
170
- const dispatch = useAppDispatch();
171
- const organizationId = useAppSelector((s) => s.appContext?.organization_id ?? null);
172
- const userId = useAppSelector((s) => s.userAuth?.id ?? null);
173
- const personalModel = useSessionKnob(CHAT_DEFAULT_MODEL_KNOB);
174
- const personalModelId = typeof personalModel === "string" && personalModel.trim() ? personalModel : null;
175
- const overrideState = useAppSelector(selectInstanceOverrideState(conversationId));
176
- const pinnedOfferingId = effectiveOfferingPin(overrideState);
177
- const personalModelLabel = useModelRecords((s) => selectModelLabelWithClass(s, personalModelId, pinnedOfferingId)) ?? null;
178
- const onSelectAgent = agentControl?.onSelectAgent;
143
+ const isManualMode = useAppSelector(selectIsManualExecutionMode(conversationId));
144
+ const inSheet = useContext(ComposerMenuPresentationContext) === "sheet";
179
145
  const agentFilter = useComposerAgentFilter(conversationId);
180
- if (!onSelectAgent) {
181
- return /* @__PURE__ */ jsxs(Fragment, { children: [
182
- /* @__PURE__ */ jsx(ComposerMenuLabel, { children: "Agent" }),
183
- /* @__PURE__ */ jsx(ThisAgentRow, { info, close }),
184
- /* @__PURE__ */ jsx(ComposerMenuHelp, { children: "This page always answers with this agent." }),
185
- /* @__PURE__ */ jsx(ComposerMenuDivider, {}),
186
- /* @__PURE__ */ jsx(ComposerMenuLabel, { children: "Model for this chat" }),
187
- /* @__PURE__ */ jsx("div", { className: "px-1.5 pb-1", children: /* @__PURE__ */ jsx(QuickRunModelSelect, { conversationId, className: "h-8 w-full" }) })
188
- ] });
189
- }
190
- const choose = (agentId) => {
191
- close();
192
- if (agentId !== info.agentId) onSelectAgent(agentId);
193
- else announceAlreadyAnswering(info);
194
- };
146
+ const onSelectAgent = agentControl?.onSelectAgent;
147
+ const agentLabel = info.isCustom ? CUSTOM_AGENT_LABEL : info.agentName ?? "Agent";
195
148
  const chooseCustom = () => {
196
- if (!info.customAgentId) return;
149
+ if (!info.customAgentId || !onSelectAgent) return;
197
150
  close();
198
- if (!info.isCustom) {
199
- onSelectAgent(info.customAgentId, { mandateKey: MANDATE_KEYS.chat__default_new_chat });
200
- }
201
- };
202
- const savePersonalDefault = async (key, value) => {
203
- if (!userId) {
204
- toast.error("Sign in first \u2014 your default model is saved to your account.");
205
- return false;
206
- }
207
- let targetOrganizationId;
208
- try {
209
- targetOrganizationId = organizationId ?? await ensureOrgId(null);
210
- } catch (error) {
211
- if (isOrganizationSelectionCancelled(error)) return false;
212
- if (presentOrganizationRefusal(error, { subject: "Your default model", act: "saved" })) return false;
213
- throw error;
214
- }
215
- const result = await setKnobOverride({
216
- feature: "agents.model_prefs",
217
- key,
218
- scopeKind: "user",
219
- scopeId: userId,
220
- organizationId: targetOrganizationId,
221
- value,
222
- note: "Picked under Custom in the chat composer"
223
- });
224
- if (!result.ok) {
225
- toast.error(`Your default chat model was not saved: ${knobRefusalSentence(result)}`);
226
- return false;
227
- }
228
- return true;
229
- };
230
- const setPersonalModel = async (modelId) => {
231
- if (!await savePersonalDefault("chat_default_model", modelId)) return;
232
- if (info.isCustom) {
233
- dispatch(seedOverrides({ conversationId, changes: { model: modelId } }));
234
- } else {
235
- chooseCustom();
236
- }
237
- };
238
- const setPersonalClass = (offeringId) => {
239
- dispatch(setOfferingPin({ conversationId, offeringId, seeded: true }));
240
- void savePersonalDefault("chat_default_offering", offeringId ?? "");
151
+ if (info.isCustom) announceAlreadyAnswering(info);
152
+ else onSelectAgent(info.customAgentId, { mandateKey: MANDATE_KEYS.chat__cx_default });
241
153
  };
242
154
  return /* @__PURE__ */ jsxs(Fragment, { children: [
243
- /* @__PURE__ */ jsx(ComposerMenuLabel, { children: "This chat's agent" }),
244
- /* @__PURE__ */ jsx(ThisAgentRow, { info, close }),
245
- /* @__PURE__ */ jsx(ComposerMenuDivider, {}),
246
- /* @__PURE__ */ jsx(ComposerMenuLabel, { children: "Chat agents" }),
247
- info.presets.map((preset) => /* @__PURE__ */ jsx(
248
- ComposerMenuRow,
249
- {
250
- icon: Star,
251
- label: preset.name,
252
- detail: preset.modelLabel ?? void 0,
253
- checked: preset.id === info.agentId,
254
- onClick: () => choose(preset.id)
255
- },
256
- preset.id
257
- )),
258
- /* @__PURE__ */ jsx(
155
+ info.agentId ? /* @__PURE__ */ jsx(ComposerSubmenu, { row: { icon: Webhook, label: `Agent: ${agentLabel}` }, panelClassName: "w-[340px] h-[420px] p-0", children: /* @__PURE__ */ jsx(ComposerAgentPeek, { agentId: info.agentId, onDone: close }) }) : null,
156
+ isManualMode ? /* @__PURE__ */ jsx(
259
157
  ComposerMenuRow,
260
158
  {
261
- label: "Custom",
262
- description: "Your own default chat, on the model you pick",
263
- checked: info.isCustom,
264
- disabled: !info.customAgentId,
265
- onClick: chooseCustom
159
+ icon: Cpu,
160
+ label: `Model: ${info.effectiveModelLabel ?? "Agent's model"}`,
161
+ disabled: true,
162
+ title: "Manual mode runs the agent's own model"
266
163
  }
267
- ),
268
- /* @__PURE__ */ jsx(
269
- AgentListDropdown,
164
+ ) : /* @__PURE__ */ jsx(
165
+ ComposerSubmenu,
270
166
  {
271
- onSelect: (agentId) => choose(agentId),
272
- activeAgentId: info.agentId,
273
- agentFilter,
274
- contentSide: "left",
275
- triggerSlot: /* @__PURE__ */ jsx(Tile, { variant: "quiet", icon: /* @__PURE__ */ jsx(Layers, {}), title: "All agents", end: /* @__PURE__ */ jsx(ChevronDown, {}) })
167
+ row: { icon: Cpu, label: `Model: ${info.effectiveModelLabel ?? "Choose a model"}` },
168
+ panelClassName: MODEL_PICKER_PANEL,
169
+ children: (closePanel) => /* @__PURE__ */ jsx(
170
+ QuickRunModelSelect,
171
+ {
172
+ conversationId,
173
+ presentation: "inline",
174
+ onChosen: () => {
175
+ closePanel();
176
+ close();
177
+ }
178
+ }
179
+ )
276
180
  }
277
181
  ),
278
- /* @__PURE__ */ jsx(ComposerMenuDivider, {}),
279
- /* @__PURE__ */ jsx(ComposerMenuLabel, { children: info.isCustom ? "Model \u2014 your default for Custom" : "Model for this chat" }),
280
- /* @__PURE__ */ jsx("div", { className: "px-1.5 pb-1", children: info.isCustom ? /* @__PURE__ */ jsx(
281
- ModelListDropdown,
282
- {
283
- value: personalModelId ?? info.effectiveModelId,
284
- onValueChange: (modelId) => void setPersonalModel(modelId),
285
- pinnedOfferingId,
286
- onOfferingPinChange: setPersonalClass,
287
- inputModalities: [],
288
- outputModalities: ["text"],
289
- placeholder: personalModelLabel ?? info.effectiveModelLabel ?? "Pick a model",
290
- "aria-label": "Model for Custom",
291
- className: "w-full"
292
- }
293
- ) : /* @__PURE__ */ jsx(QuickRunModelSelect, { conversationId, className: "h-8 w-full" }) })
182
+ onSelectAgent ? /* @__PURE__ */ jsxs(Fragment, { children: [
183
+ inSheet ? null : /* @__PURE__ */ jsx(ComposerMenuDivider, {}),
184
+ /* @__PURE__ */ jsx(ComposerMenuLabel, { children: "Choose your chat agent" }),
185
+ info.presets.map((preset) => /* @__PURE__ */ jsx(
186
+ ComposerMenuRow,
187
+ {
188
+ icon: Star,
189
+ label: preset.name,
190
+ checked: preset.id === info.agentId,
191
+ onClick: () => choose(preset.id)
192
+ },
193
+ preset.id
194
+ )),
195
+ /* @__PURE__ */ jsx(
196
+ ComposerMenuRow,
197
+ {
198
+ icon: Star,
199
+ label: CUSTOM_AGENT_LABEL,
200
+ checked: info.isCustom,
201
+ disabled: !info.customAgentId,
202
+ onClick: chooseCustom
203
+ }
204
+ ),
205
+ /* @__PURE__ */ jsx(AnyAgentRow, { icon: Layers, label: "Choose any agent", info, agentFilter, choose })
206
+ ] }) : null
294
207
  ] });
295
208
  }
209
+ function AnyAgentRow({
210
+ icon,
211
+ label,
212
+ info,
213
+ agentFilter,
214
+ choose
215
+ }) {
216
+ return /* @__PURE__ */ jsx(ComposerSubmenu, { row: { icon, label }, panelClassName: AGENT_PICKER_PANEL, children: /* @__PURE__ */ jsx(
217
+ AgentListPanel,
218
+ {
219
+ consumerId: "composer-agent-pill",
220
+ fill: true,
221
+ onSelect: choose,
222
+ activeAgentId: info.agentId,
223
+ agentFilter
224
+ }
225
+ ) });
226
+ }
296
227
  export {
297
228
  ComposerAgentPill
298
229
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Choosing the agent that is already answering is never silent: the composer
3
- * pill says "Custom" when that agent holds the default-chat job, while the
3
+ * pill says "Custom Agent" for the Matrx Chat agent, while the
4
4
  * agent list names it by its own name — so the press answers who is in the seat.
5
5
  */
6
6
  export declare function announceAlreadyAnswering(info: {
@@ -2,7 +2,7 @@ import { toast } from "../../../../../host/notify.js";
2
2
  function announceAlreadyAnswering(info) {
3
3
  const name = info.agentName ?? "This agent";
4
4
  toast.info(
5
- info.isCustom ? `${name} is your Custom chat, and it is already answering this conversation.` : `${name} is already answering this conversation.`
5
+ info.isCustom ? `Custom Agent (${name}) is already answering this conversation.` : `${name} is already answering this conversation.`
6
6
  );
7
7
  }
8
8
  export {
@@ -15,7 +15,7 @@ export interface ComposerAgentInfo {
15
15
  modelOverridden: boolean;
16
16
  /** The agent's own model label — what runs when no override is in force. */
17
17
  agentModelLabel: string | null;
18
- /** The agent currently holding `chat.default_new_chat` — shown as "Custom". */
18
+ /** The Matrx Chat agent (`chat.cx_default`) — shown as "Custom Agent". */
19
19
  customAgentId: string | null;
20
20
  isCustom: boolean;
21
21
  preset: ComposerPreset | null;
@@ -52,7 +52,7 @@ function useComposerAgent(conversationId) {
52
52
  const effectiveModelLabel = useModelRecords((state) => selectModelLabelWithClass(state, effectiveModelId, effectivePin)) ?? null;
53
53
  const agentOwnModelId = useAgentOwnModelId(conversationId);
54
54
  const agentModelLabel = useModelRecords((state) => selectModelLabelWithClass(state, agentOwnModelId, agentOwnPin)) ?? null;
55
- const { mandate: customMandate } = useMandate(MANDATE_KEYS.chat__default_new_chat, { optional: true });
55
+ const { mandate: customMandate } = useMandate(MANDATE_KEYS.chat__cx_default, { optional: true });
56
56
  const customAgentId = customMandate?.agentId ?? null;
57
57
  const rows = useAgentCatalogRows();
58
58
  const presetRows = rows.filter(isPresetRow);
@@ -10,11 +10,12 @@ import { selectShowVariablePanel } from "../../../redux/execution-system/instanc
10
10
  import { setUserVariableValue } from "../../../redux/execution-system/instance-variable-values/instance-variable-values.slice.js";
11
11
  import { selectUserVariableValues } from "../../../redux/execution-system/instance-variable-values/instance-variable-values.selectors.js";
12
12
  import { isMediaVariableType } from "../../../types/agent-definition.types.js";
13
- import { ImageVariableInput } from "../input-components/ImageVariableInput.js";
14
- import { AudioVariableInput } from "../input-components/AudioVariableInput.js";
15
- import { VideoVariableInput } from "../input-components/VideoVariableInput.js";
16
- import { DocumentVariableInput } from "../input-components/DocumentVariableInput.js";
17
- import { YoutubeVariableInput } from "../input-components/YoutubeVariableInput.js";
13
+ import { clientLazy } from "@ai-matrx/kit/client-lazy";
14
+ const ImageVariableInput = clientLazy(() => import("../input-components/ImageVariableInput.js").then((m) => ({ default: m.ImageVariableInput })));
15
+ const AudioVariableInput = clientLazy(() => import("../input-components/AudioVariableInput.js").then((m) => ({ default: m.AudioVariableInput })));
16
+ const VideoVariableInput = clientLazy(() => import("../input-components/VideoVariableInput.js").then((m) => ({ default: m.VideoVariableInput })));
17
+ const DocumentVariableInput = clientLazy(() => import("../input-components/DocumentVariableInput.js").then((m) => ({ default: m.DocumentVariableInput })));
18
+ const YoutubeVariableInput = clientLazy(() => import("../input-components/YoutubeVariableInput.js").then((m) => ({ default: m.YoutubeVariableInput })));
18
19
  import { ArrowRight, Minus, Plus } from "lucide-react";
19
20
  import { variableRunLabel } from "@ai-matrx/agents";
20
21
  import { Button } from "@ai-matrx/design-system/controls";
@@ -28,12 +28,13 @@ import {
28
28
  import { VoiceTextarea } from "../../../../host/ui-slots.js";
29
29
  import { formatText, humanizeIdentifier } from "@ai-matrx/kit/text-case";
30
30
  import { cn } from "@ai-matrx/design-system";
31
+ import { clientLazy } from "@ai-matrx/kit/client-lazy";
31
32
  import { isMediaVariableType } from "../../../types/agent-definition.types.js";
32
- import { ImageVariableInput } from "../input-components/ImageVariableInput.js";
33
- import { AudioVariableInput } from "../input-components/AudioVariableInput.js";
34
- import { VideoVariableInput } from "../input-components/VideoVariableInput.js";
35
- import { DocumentVariableInput } from "../input-components/DocumentVariableInput.js";
36
- import { YoutubeVariableInput } from "../input-components/YoutubeVariableInput.js";
33
+ const ImageVariableInput = clientLazy(() => import("../input-components/ImageVariableInput.js").then((m) => ({ default: m.ImageVariableInput })));
34
+ const AudioVariableInput = clientLazy(() => import("../input-components/AudioVariableInput.js").then((m) => ({ default: m.AudioVariableInput })));
35
+ const VideoVariableInput = clientLazy(() => import("../input-components/VideoVariableInput.js").then((m) => ({ default: m.VideoVariableInput })));
36
+ const DocumentVariableInput = clientLazy(() => import("../input-components/DocumentVariableInput.js").then((m) => ({ default: m.DocumentVariableInput })));
37
+ const YoutubeVariableInput = clientLazy(() => import("../input-components/YoutubeVariableInput.js").then((m) => ({ default: m.YoutubeVariableInput })));
37
38
  const IOS_INPUT_STYLE = { fontSize: "16px" };
38
39
  function AgentVariableForm({ conversationId }) {
39
40
  const dispatch = useAppDispatch();
@@ -9,11 +9,12 @@ import { Minus, Plus } from "lucide-react";
9
9
  import { variableRunLabel } from "@ai-matrx/agents";
10
10
  import { cn } from "@ai-matrx/design-system";
11
11
  import { isMediaVariableType } from "../../../types/agent-definition.types.js";
12
- import { ImageVariableInput } from "../input-components/ImageVariableInput.js";
13
- import { AudioVariableInput } from "../input-components/AudioVariableInput.js";
14
- import { VideoVariableInput } from "../input-components/VideoVariableInput.js";
15
- import { DocumentVariableInput } from "../input-components/DocumentVariableInput.js";
16
- import { YoutubeVariableInput } from "../input-components/YoutubeVariableInput.js";
12
+ import { clientLazy } from "@ai-matrx/kit/client-lazy";
13
+ const ImageVariableInput = clientLazy(() => import("../input-components/ImageVariableInput.js").then((m) => ({ default: m.ImageVariableInput })));
14
+ const AudioVariableInput = clientLazy(() => import("../input-components/AudioVariableInput.js").then((m) => ({ default: m.AudioVariableInput })));
15
+ const VideoVariableInput = clientLazy(() => import("../input-components/VideoVariableInput.js").then((m) => ({ default: m.VideoVariableInput })));
16
+ const DocumentVariableInput = clientLazy(() => import("../input-components/DocumentVariableInput.js").then((m) => ({ default: m.DocumentVariableInput })));
17
+ const YoutubeVariableInput = clientLazy(() => import("../input-components/YoutubeVariableInput.js").then((m) => ({ default: m.YoutubeVariableInput })));
17
18
  import { useAppSelector, useAppDispatch } from "../../../../store/hooks.js";
18
19
  import {
19
20
  selectInstanceVariableDefinitions,
@@ -15,7 +15,7 @@ import {
15
15
  import {
16
16
  TapTargetButtonForGroup,
17
17
  TapTargetButtonGroup
18
- } from "@ai-matrx/tap-target";
18
+ } from "@ai-matrx/design-system/tap-target";
19
19
  import { SpeakerButton } from "../../../../host/ui-slots.js";
20
20
  import { CopySplitButton } from "@ai-matrx/rich-content/copy/CopySplitButton";
21
21
  import { copyRichContent } from "@ai-matrx/rich-content/copy/copy-commands";
@@ -3,7 +3,7 @@ import { AgentModeController } from "../shared/AgentModeController.js";
3
3
  import { AgentSaveStatus, AgentOptionsMenu } from "../../../host/ui-slots.js";
4
4
  import { ReviewAnswersLink } from "../../../host/ui-slots.js";
5
5
  import { AgentSelectorIsland } from "../shared/AgentSelectorIsland.js";
6
- import { ChevronLeftTapButton } from "@ai-matrx/tap-target/buttons";
6
+ import { ChevronLeftTapButton } from "@ai-matrx/design-system/tap-target/buttons";
7
7
  import { AgentNewRunButton } from "../shared/AgentNewRunButton.js";
8
8
  import { AgentHeaderMobile } from "../shared/AgentHeaderMobile.js";
9
9
  function AgentRunHeader({
@@ -4,7 +4,7 @@ import { AgentSelectorIsland } from "../../shared/AgentSelectorIsland.js";
4
4
  import {
5
5
  PanelLeftTapButton,
6
6
  ChevronLeftTapButton
7
- } from "@ai-matrx/tap-target/buttons";
7
+ } from "@ai-matrx/design-system/tap-target/buttons";
8
8
  import { PowerTapButton } from "@ai-matrx/icons/tap-buttons";
9
9
  import { SearchGroup, SearchGroupTrigger } from "../../../../host/ui-slots.js";
10
10
  import { AgentNewRunButton } from "../../shared/AgentNewRunButton.js";
@@ -4,9 +4,13 @@
4
4
  * ModelListDropdown as `/agents/[id]/build`; only the write target differs.
5
5
  * agent default pinned first; reset icon appears only when overridden.
6
6
  */
7
- export declare function QuickRunModelSelect({ conversationId, className, }: {
7
+ export declare function QuickRunModelSelect({ conversationId, className, presentation, onChosen, }: {
8
8
  conversationId: string;
9
9
  className?: string;
10
+ /** `inline`: the picker body fills its parent (a menu's panel or page). */
11
+ presentation?: "picker" | "inline";
12
+ /** A model was picked (an inline host closes its panel). */
13
+ onChosen?: () => void;
10
14
  }): import("react").JSX.Element;
11
15
  export declare function RunModelPicker({ conversationId }: {
12
16
  conversationId: string;
@@ -50,7 +50,9 @@ function useModelOverride(conversationId) {
50
50
  }
51
51
  function QuickRunModelSelect({
52
52
  conversationId,
53
- className
53
+ className,
54
+ presentation = "picker",
55
+ onChosen
54
56
  }) {
55
57
  const isManualMode = useAppSelector(
56
58
  selectIsManualExecutionMode(conversationId)
@@ -69,6 +71,26 @@ function QuickRunModelSelect({
69
71
  handleReset
70
72
  } = useModelOverride(conversationId);
71
73
  const displayModelId = isManualMode ? agentModelId ?? effectiveModel ?? baseModel ?? null : effectiveModel ?? baseModel ?? null;
74
+ if (presentation === "inline") {
75
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 min-w-0 flex-col", className), children: [
76
+ isOverridden && !isManualMode ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 justify-end border-b border-border p-1", children: /* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(RotateCcw, {}), onClick: handleReset, children: "Agent's own model" }) }) : null,
77
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx(
78
+ ModelListDropdown,
79
+ {
80
+ presentation: "inline",
81
+ value: displayModelId,
82
+ onValueChange: (modelId) => {
83
+ handleChange(modelId);
84
+ onChosen?.();
85
+ },
86
+ pinnedOfferingId,
87
+ onOfferingPinChange: handleOfferingPinChange,
88
+ inputModalities: [],
89
+ outputModalities: ["text"]
90
+ }
91
+ ) })
92
+ ] });
93
+ }
72
94
  return /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 items-center gap-1", className), children: [
73
95
  /* @__PURE__ */ jsx(
74
96
  ModelListDropdown,
@@ -33,7 +33,8 @@ import {
33
33
  } from "./transcript-audience.js";
34
34
  import { cn } from "@ai-matrx/design-system";
35
35
  import {
36
- ASSISTANT_MESSAGE_COLUMN_INSET_CLASS
36
+ ASSISTANT_MESSAGE_COLUMN_INSET_CLASS,
37
+ WIDE_FIGURE_SCOPE_ATTRIBUTE
37
38
  } from "./assistant-message-layout.js";
38
39
  import {
39
40
  TranscriptLayoutProvider,
@@ -246,6 +247,7 @@ function AgentConversationColumn({
246
247
  {
247
248
  ref: scrollRef,
248
249
  onScroll: handleScroll,
250
+ ...edgeScroll && resolvedLayout.width === "reading" && resolvedLayout.align === "center" ? { [WIDE_FIGURE_SCOPE_ATTRIBUTE]: "" } : {},
249
251
  className: cn(
250
252
  // A SIZE container: what renders inside (the empty-state hero)
251
253
  // answers the pane's real height — a 438px split pane beside
@@ -5,7 +5,7 @@ import { AgentSaveStatus, AgentOptionsMenu } from "../../../host/ui-slots.js";
5
5
  import { AgentReferenceCopyButton } from "./AgentReferenceCopyButton.js";
6
6
  import { AgentHeaderMobile } from "./AgentHeaderMobile.js";
7
7
  import { ReviewAnswersLink } from "../../../host/ui-slots.js";
8
- import { ChevronLeftTapButton } from "@ai-matrx/tap-target/buttons";
8
+ import { ChevronLeftTapButton } from "@ai-matrx/design-system/tap-target/buttons";
9
9
  function AgentHeader({
10
10
  agentId,
11
11
  agentName,