@agents24/chat-react 0.5.29 → 0.5.30

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 (36) hide show
  1. package/README.md +23 -1
  2. package/dist/chunk-INO5DSSJ.js +86 -0
  3. package/dist/chunk-INO5DSSJ.js.map +1 -0
  4. package/dist/registry/agents24-chat.json +249 -0
  5. package/dist/runtime/index.cjs +1 -1
  6. package/dist/runtime/index.cjs.map +1 -1
  7. package/dist/runtime/index.js +11 -78
  8. package/dist/runtime/index.js.map +1 -1
  9. package/dist/runtime/input-contract.d.ts +3 -1
  10. package/dist/scaffold/app.d.ts +2 -0
  11. package/dist/scaffold/components/chat/chat-composer.d.ts +2 -3
  12. package/dist/scaffold/components/chat/theme-menu.d.ts +1 -1
  13. package/dist/scaffold/components/theme-provider.d.ts +2 -1
  14. package/dist/scaffold/index.cjs +44 -102
  15. package/dist/scaffold/index.cjs.map +1 -1
  16. package/dist/scaffold/index.d.ts +2 -0
  17. package/dist/scaffold/index.js +41 -103
  18. package/dist/scaffold/index.js.map +1 -1
  19. package/dist/ui/agent-chat-composer.d.ts +7 -2
  20. package/dist/ui/index.cjs +688 -540
  21. package/dist/ui/index.cjs.map +1 -1
  22. package/dist/ui/index.d.ts +1 -0
  23. package/dist/ui/index.js +650 -541
  24. package/dist/ui/index.js.map +1 -1
  25. package/dist/ui/use-agent-chat-composer.d.ts +48 -0
  26. package/dist/ui/use-audio-recorder.d.ts +1 -1
  27. package/package.json +8 -4
  28. package/scaffold/inherit.css +4 -0
  29. package/scaffold/src/app.tsx +2 -0
  30. package/scaffold/src/chat.css +24 -0
  31. package/scaffold/src/components/chat/chat-composer.tsx +14 -97
  32. package/scaffold/src/components/chat/chat-shell.tsx +2 -1
  33. package/scaffold/src/components/chat/theme-menu.tsx +3 -1
  34. package/scaffold/src/components/theme-provider.tsx +10 -8
  35. package/scaffold/src/index.css +8 -8
  36. package/scaffold/src/index.tsx +5 -0
@@ -18,4 +18,6 @@ export declare function workflowInputRule(inputs: readonly WorkflowInputRule[] |
18
18
  export declare function validateWorkflowInputSubmission(inputs: readonly WorkflowInputRule[] | null | undefined, text: string, attachmentMimeTypes: readonly string[]): string | null;
19
19
  export declare const canonicalInputMimeType: typeof normalizeAttachmentMimeType;
20
20
  export declare function inputMimeTypes(contract: AgentChatInputContract | null | undefined): string[];
21
- export declare function validateAgentChatSubmission(contract: AgentChatInputContract, text: string, files: readonly (Pick<PortableUpload, "mediaType" | "data"> & Partial<Pick<PortableUpload, "name">>)[]): string | null;
21
+ export declare function validateAgentChatSubmission(contract: AgentChatInputContract, text: string, files: readonly (Pick<PortableUpload, "mediaType" | "data"> & Partial<Pick<PortableUpload, "name">> & {
22
+ byteSize?: number;
23
+ })[]): string | null;
@@ -3,6 +3,7 @@ import { type AgentChatActiveThreadChangeHandler, type AgentChatMcpAuthorization
3
3
  import type { HitlActionState } from "@agents24/chat-react/ui";
4
4
  import { type ChatHitlPart, type ChatMessage, type ChatPart } from "@agents24/react";
5
5
  import * as React from "react";
6
+ import type { Agents24ChatComposerProps } from "./components/chat/chat-composer";
6
7
  import { type Agents24ChatIdentity } from "./identity";
7
8
  export type Agents24ChatAppProps = {
8
9
  activeThreadId?: string | null;
@@ -19,6 +20,7 @@ export type Agents24ChatAppProps = {
19
20
  };
20
21
  export type Agents24ChatScaffoldComponents = {
21
22
  Timeline?: React.ComponentType<Agents24ChatTimelineSlotProps>;
23
+ Composer?: React.ComponentType<Agents24ChatComposerProps>;
22
24
  };
23
25
  export type ChatHitlResponse = HitlResponse;
24
26
  export type Agents24ChatTimelineSlotProps = {
@@ -1,7 +1,7 @@
1
1
  import { type AgentChatComposerFile } from "@agents24/chat-react/ui";
2
2
  import { type AgentChatInputContract } from "@agents24/chat-react/runtime";
3
3
  import type { ContextWindow } from "@agents24/client/protocol";
4
- type ChatComposerProps = {
4
+ export type Agents24ChatComposerProps = {
5
5
  inputContract: AgentChatInputContract;
6
6
  disabled?: boolean;
7
7
  contextWindow?: ContextWindow | null;
@@ -30,5 +30,4 @@ type ChatComposerProps = {
30
30
  } | null;
31
31
  utilityRow?: "inline" | "below";
32
32
  };
33
- export declare function ChatComposer({ inputContract, disabled, contextWindow, focusKey, forceExpanded, isCancelling, isRunning, agentId, agents, modelId, models, onModelChange, onAgentChange, onStop, onSubmit, restoredDraft, utilityRow, }: ChatComposerProps): import("react/jsx-runtime").JSX.Element;
34
- export {};
33
+ export declare function ChatComposer({ inputContract, disabled, contextWindow, focusKey, forceExpanded, isCancelling, isRunning, agentId, agents, modelId, models, onModelChange, onAgentChange, onStop, onSubmit, restoredDraft, utilityRow, }: Agents24ChatComposerProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- export declare function ThemeMenu(): import("react/jsx-runtime").JSX.Element;
1
+ export declare function ThemeMenu(): import("react/jsx-runtime").JSX.Element | null;
@@ -1,12 +1,13 @@
1
1
  import * as React from "react";
2
2
  export type Agents24ChatAppearance = "system" | "light" | "dark";
3
- export type Agents24ChatTheme = "soft" | "sharp";
3
+ export type Agents24ChatTheme = "soft" | "sharp" | "inherit";
4
4
  export type Agents24ChatAppearanceController = {
5
5
  appearance: Agents24ChatAppearance;
6
6
  setAppearance: (appearance: Agents24ChatAppearance) => void;
7
7
  };
8
8
  type ThemeContextValue = {
9
9
  appearance: Agents24ChatAppearance;
10
+ inherited: boolean;
10
11
  portalContainer: HTMLElement | null;
11
12
  setAppearance: (appearance: Agents24ChatAppearance) => void;
12
13
  };
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ "use client";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -30,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
31
  // scaffold/src/index.tsx
31
32
  var src_exports = {};
32
33
  __export(src_exports, {
34
+ Agents24ChatComposer: () => ChatComposer,
33
35
  Agents24ChatScaffold: () => Agents24ChatScaffold,
34
36
  Agents24ChatScaffoldApp: () => App,
35
37
  Agents24ChatThemeProvider: () => ThemeProvider,
@@ -39,11 +41,11 @@ module.exports = __toCommonJS(src_exports);
39
41
 
40
42
  // scaffold/src/app.tsx
41
43
  var import_browser = require("@agents24/client/browser");
42
- var import_runtime3 = require("@agents24/chat-react/runtime");
44
+ var import_runtime2 = require("@agents24/chat-react/runtime");
43
45
  var import_react2 = require("@agents24/react");
44
46
 
45
47
  // scaffold/src/components/chat/chat-shell.tsx
46
- var import_runtime2 = require("@agents24/chat-react/runtime");
48
+ var import_runtime = require("@agents24/chat-react/runtime");
47
49
  var import_ui4 = require("@agents24/chat-react/ui");
48
50
  var import_lucide_react11 = require("lucide-react");
49
51
  var React8 = __toESM(require("react"), 1);
@@ -52,7 +54,6 @@ var React8 = __toESM(require("react"), 1);
52
54
  var import_lucide_react3 = require("lucide-react");
53
55
  var React2 = __toESM(require("react"), 1);
54
56
  var import_ui = require("@agents24/chat-react/ui");
55
- var import_runtime = require("@agents24/chat-react/runtime");
56
57
 
57
58
  // scaffold/src/components/ui/button.tsx
58
59
  var import_class_variance_authority = require("class-variance-authority");
@@ -144,11 +145,11 @@ function ThemeProvider({
144
145
  const [portalContainer, setPortalContainer] = React.useState(null);
145
146
  const appearance = controller?.appearance ?? internalAppearance;
146
147
  React.useEffect(() => {
147
- if (!controller) setInternalAppearance(storedAppearance());
148
+ if (theme !== "inherit" && !controller) setInternalAppearance(storedAppearance());
148
149
  setMounted(true);
149
- }, [controller]);
150
+ }, [controller, theme]);
150
151
  React.useEffect(() => {
151
- if (!mounted || typeof window === "undefined" || typeof window.matchMedia !== "function") return;
152
+ if (theme === "inherit" || !mounted || typeof window === "undefined" || typeof window.matchMedia !== "function") return;
152
153
  const media = window.matchMedia("(prefers-color-scheme: dark)");
153
154
  const apply = () => setResolvedAppearance(
154
155
  appearance === "system" ? media.matches ? "dark" : "light" : appearance
@@ -156,8 +157,9 @@ function ThemeProvider({
156
157
  apply();
157
158
  media.addEventListener("change", apply);
158
159
  return () => media.removeEventListener("change", apply);
159
- }, [appearance, mounted]);
160
+ }, [appearance, mounted, theme]);
160
161
  const change = React.useCallback((next) => {
162
+ if (theme === "inherit") return;
161
163
  if (controller) {
162
164
  controller.setAppearance(next);
163
165
  return;
@@ -167,12 +169,12 @@ function ThemeProvider({
167
169
  } catch {
168
170
  }
169
171
  setInternalAppearance(next);
170
- }, [controller]);
171
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeContext.Provider, { value: { appearance, portalContainer, setAppearance: change }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
172
+ }, [controller, theme]);
173
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeContext.Provider, { value: { appearance, inherited: theme === "inherit", portalContainer, setAppearance: change }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
172
174
  "div",
173
175
  {
174
176
  ref: setPortalContainer,
175
- className: `${resolvedAppearance} size-full`,
177
+ className: theme === "inherit" ? "size-full" : `${resolvedAppearance} size-full`,
176
178
  "data-agents24-theme": theme,
177
179
  "data-agents24-theme-root": "",
178
180
  children
@@ -523,49 +525,31 @@ function ChatComposer({
523
525
  restoredDraft,
524
526
  utilityRow = "inline"
525
527
  }) {
526
- const [text, setText] = React2.useState("");
527
- const [files, setFiles] = React2.useState([]);
528
- const [attachmentError, setAttachmentError] = React2.useState(null);
529
528
  const inputRef = React2.useRef(null);
530
529
  const textareaRef = React2.useRef(null);
531
- const filesRef = React2.useRef([]);
532
- const inFlightFilesRef = React2.useRef([]);
533
- const submittingRef = React2.useRef(false);
534
- const draftRevisionRef = React2.useRef(0);
535
- const attachmentMimeTypes = (0, import_runtime.inputMimeTypes)(inputContract);
536
- const allowAttachments = attachmentMimeTypes.length > 0;
537
- const recordingAllowed = inputContract.modalities.audio.recording_enabled;
538
- React2.useEffect(() => {
539
- filesRef.current = files;
540
- }, [files]);
541
- React2.useEffect(() => () => {
542
- (0, import_ui.revokeAgentChatComposerFiles)(filesRef.current);
543
- (0, import_ui.revokeAgentChatComposerFiles)(inFlightFilesRef.current);
544
- }, []);
545
- const focusComposer = (0, import_ui.useChatComposerFocus)({
546
- textareaRef,
547
- disabled: Boolean(disabled),
530
+ const composer = (0, import_ui.useAgentChatComposer)({
531
+ inputContract,
532
+ disabled,
533
+ isRunning,
534
+ isCancelling,
548
535
  focusKey,
549
536
  restoredDraft,
550
- onRestore: (draft) => {
551
- draftRevisionRef.current += 1;
552
- (0, import_ui.revokeAgentChatComposerFiles)(filesRef.current);
553
- setText(draft.text);
554
- setFiles(draft.files);
555
- }
537
+ textareaRef,
538
+ onSubmit: ({ text: text2, files: files2 }) => onSubmit(text2, files2)
556
539
  });
557
- const addRecording = React2.useCallback((file) => {
558
- draftRevisionRef.current += 1;
559
- setFiles((current) => [...current, (0, import_ui.createAgentChatComposerFile)(file)]);
560
- }, []);
561
- const recorder = (0, import_ui.useAudioRecorder)(addRecording);
562
- const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
563
- const validationError = (0, import_runtime.validateAgentChatSubmission)(
564
- inputContract,
540
+ const {
565
541
  text,
566
- files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType }))
567
- );
568
- const action = (0, import_ui.chatComposerAction)({ hasDraft: Boolean(text.trim() || files.length), isRunning, isCancelling });
542
+ files,
543
+ setText,
544
+ validationError,
545
+ attachmentError,
546
+ recorder,
547
+ recordingAllowed,
548
+ allowAttachments,
549
+ action,
550
+ submit
551
+ } = composer;
552
+ const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
569
553
  const selectedAgent = agents.find((item) => item.id === agentId);
570
554
  const selectedModel = models.find((item) => item.id === modelId);
571
555
  const modelSelector = models.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-fit max-w-40", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Select, { disabled: disabled || isRunning, onValueChange: onModelChange, value: modelId || void 0, children: [
@@ -605,35 +589,6 @@ function ChatComposer({
605
589
  ] }) : modelSelector;
606
590
  const hasContextWindow = Number.isFinite(Number(contextWindow?.max_tokens)) && Number(contextWindow?.max_tokens) > 0;
607
591
  const showUtilityRow = utilityRow === "below";
608
- const submit = async () => {
609
- if (disabled || isCancelling || submittingRef.current || validationError) return;
610
- const value = text;
611
- const selectedFiles = files;
612
- const clearedRevision = draftRevisionRef.current + 1;
613
- draftRevisionRef.current = clearedRevision;
614
- submittingRef.current = true;
615
- inFlightFilesRef.current = selectedFiles;
616
- setText("");
617
- setFiles([]);
618
- focusComposer();
619
- try {
620
- await onSubmit(value, selectedFiles);
621
- (0, import_ui.revokeAgentChatComposerFiles)(selectedFiles);
622
- inFlightFilesRef.current = [];
623
- } catch {
624
- if (draftRevisionRef.current === clearedRevision) {
625
- draftRevisionRef.current += 1;
626
- setText(value);
627
- setFiles(selectedFiles);
628
- inFlightFilesRef.current = [];
629
- } else {
630
- (0, import_ui.revokeAgentChatComposerFiles)(selectedFiles);
631
- inFlightFilesRef.current = [];
632
- }
633
- } finally {
634
- submittingRef.current = false;
635
- }
636
- };
637
592
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
638
593
  "div",
639
594
  {
@@ -645,13 +600,7 @@ function ChatComposer({
645
600
  {
646
601
  attachments: files,
647
602
  onRemove: (attachment) => {
648
- if (!attachment.id) return;
649
- draftRevisionRef.current += 1;
650
- setFiles((current) => {
651
- const removed = current.find((file) => file.id === attachment.id);
652
- if (removed) (0, import_ui.revokeAgentChatComposerFiles)([removed]);
653
- return current.filter((file) => file.id !== attachment.id);
654
- });
603
+ if (attachment.id) composer.removeFile(attachment.id);
655
604
  }
656
605
  }
657
606
  ) }),
@@ -673,11 +622,9 @@ function ChatComposer({
673
622
  className: "sr-only",
674
623
  type: "file",
675
624
  multiple: true,
676
- accept: (0, import_runtime.attachmentFileAccept)(attachmentMimeTypes),
625
+ accept: composer.fileAccept,
677
626
  onChange: (event) => {
678
- const selected = Array.from(event.target.files ?? []);
679
- setFiles((current) => [...current, ...selected.map(import_ui.createAgentChatComposerFile)]);
680
- setAttachmentError(null);
627
+ composer.addFiles(Array.from(event.target.files ?? []));
681
628
  event.currentTarget.value = "";
682
629
  }
683
630
  }
@@ -697,16 +644,8 @@ function ChatComposer({
697
644
  "agents24-composer-textarea max-h-56 min-h-9 field-sizing-content resize-none overflow-y-auto border-0 bg-transparent px-2 shadow-none focus-visible:ring-0 dark:bg-transparent",
698
645
  isExpanded ? "py-2" : "h-9 py-2"
699
646
  ),
700
- onChange: (event) => {
701
- draftRevisionRef.current += 1;
702
- setText(event.target.value);
703
- },
704
- onKeyDown: (event) => {
705
- if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
706
- event.preventDefault();
707
- void submit();
708
- }
709
- }
647
+ onChange: (event) => setText(event.target.value),
648
+ onKeyDown: composer.onKeyDown
710
649
  }
711
650
  ),
712
651
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "agents24-composer-actions flex items-center gap-1", children: [
@@ -738,7 +677,7 @@ function ChatComposer({
738
677
  ) : validationError ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Tooltip, { children: [
739
678
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { tabIndex: 0, "aria-label": validationError, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { size: "icon", "aria-label": isRunning ? "Queue message" : "Send message", disabled: true, onClick: () => void submit(), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.ArrowUp, {}) }) }) }),
740
679
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TooltipContent, { children: validationError })
741
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { size: "icon", "aria-label": isRunning ? "Queue message" : "Send message", disabled, onClick: () => void submit(), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.ArrowUp, {}) })
680
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { size: "icon", "aria-label": isRunning ? "Queue message" : "Send message", disabled: disabled || !composer.canSubmit, onClick: () => void submit(), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.ArrowUp, {}) })
742
681
  ] })
743
682
  ]
744
683
  }
@@ -1231,7 +1170,7 @@ var options = [
1231
1170
  { value: "dark", label: "Dark", icon: import_lucide_react6.Moon }
1232
1171
  ];
1233
1172
  function ThemeMenu() {
1234
- const { appearance, setAppearance } = useTheme();
1173
+ const { appearance, setAppearance, inherited } = useTheme();
1235
1174
  const [open, setOpen] = React5.useState(false);
1236
1175
  const rootRef = React5.useRef(null);
1237
1176
  const firstItemRef = React5.useRef(null);
@@ -1253,6 +1192,7 @@ function ThemeMenu() {
1253
1192
  document.removeEventListener("keydown", closeOnEscape);
1254
1193
  };
1255
1194
  }, [open]);
1195
+ if (inherited) return null;
1256
1196
  const label = appearance === "system" ? "System" : appearance === "light" ? "Light" : "Dark";
1257
1197
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { ref: rootRef, className: "relative", children: [
1258
1198
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
@@ -2078,7 +2018,7 @@ function OperationErrorNotice({ runtime }) {
2078
2018
  if (!error) return null;
2079
2019
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "pointer-events-none absolute inset-x-0 top-3 z-40 flex justify-center px-4", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "pointer-events-auto flex max-w-lg items-start gap-2 rounded-lg border border-destructive/20 bg-background px-3 py-2 text-sm shadow-md", role: "alert", children: [
2080
2020
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.CircleAlert, { className: "mt-0.5 size-4 shrink-0 text-destructive" }),
2081
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "min-w-0 flex-1 wrap-break-word", children: (0, import_runtime2.agentChatRuntimeErrorMessage)(error) || "The chat could not complete that action." }),
2021
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "min-w-0 flex-1 wrap-break-word", children: (0, import_runtime.agentChatRuntimeErrorMessage)(error) || "The chat could not complete that action." }),
2082
2022
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2083
2023
  Button,
2084
2024
  {
@@ -2097,6 +2037,7 @@ function ChatShell({
2097
2037
  runtime,
2098
2038
  sidebar
2099
2039
  }) {
2040
+ const Composer = components?.Composer || ChatComposer;
2100
2041
  const { controller } = runtime;
2101
2042
  const isUploading = controller.runState === "uploading";
2102
2043
  const isRunning = controller.runState === "streaming" || controller.runState === "reconnecting" || controller.runState === "paused" || controller.runState === "cancelling";
@@ -2254,7 +2195,7 @@ function ChatShell({
2254
2195
  }
2255
2196
  ),
2256
2197
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2257
- ChatComposer,
2198
+ Composer,
2258
2199
  {
2259
2200
  restoredDraft,
2260
2201
  inputContract: runtime.inputContract || LOADING_INPUT_CONTRACT,
@@ -2314,7 +2255,7 @@ function RuntimeChat({
2314
2255
  identity = GUEST_CHAT_IDENTITY,
2315
2256
  ...props
2316
2257
  }) {
2317
- const runtime = (0, import_runtime3.useAgentChatRuntime)({
2258
+ const runtime = (0, import_runtime2.useAgentChatRuntime)({
2318
2259
  ...props,
2319
2260
  mcpRedirectUri: props.mcpRedirectUri || (typeof window === "undefined" ? void 0 : window.location.href)
2320
2261
  });
@@ -2332,6 +2273,7 @@ function Agents24ChatScaffold({ appearanceController, theme, ...props }) {
2332
2273
  }
2333
2274
  // Annotate the CommonJS export names for ESM import in node:
2334
2275
  0 && (module.exports = {
2276
+ Agents24ChatComposer,
2335
2277
  Agents24ChatScaffold,
2336
2278
  Agents24ChatScaffoldApp,
2337
2279
  Agents24ChatThemeProvider,