@agents24/chat-react 0.5.28 → 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 (37) 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 +10 -14
  6. package/dist/runtime/index.cjs.map +1 -1
  7. package/dist/runtime/index.js +12 -85
  8. package/dist/runtime/index.js.map +1 -1
  9. package/dist/runtime/input-contract.d.ts +6 -3
  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 -106
  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 -107
  18. package/dist/scaffold/index.js.map +1 -1
  19. package/dist/ui/agent-chat-composer.d.ts +7 -2
  20. package/dist/ui/composer-attachments.d.ts +1 -1
  21. package/dist/ui/index.cjs +688 -539
  22. package/dist/ui/index.cjs.map +1 -1
  23. package/dist/ui/index.d.ts +1 -0
  24. package/dist/ui/index.js +650 -540
  25. package/dist/ui/index.js.map +1 -1
  26. package/dist/ui/use-agent-chat-composer.d.ts +48 -0
  27. package/dist/ui/use-audio-recorder.d.ts +1 -1
  28. package/package.json +10 -6
  29. package/scaffold/inherit.css +4 -0
  30. package/scaffold/src/app.tsx +2 -0
  31. package/scaffold/src/chat.css +24 -0
  32. package/scaffold/src/components/chat/chat-composer.tsx +14 -105
  33. package/scaffold/src/components/chat/chat-shell.tsx +2 -1
  34. package/scaffold/src/components/chat/theme-menu.tsx +3 -1
  35. package/scaffold/src/components/theme-provider.tsx +10 -8
  36. package/scaffold/src/index.css +8 -8
  37. package/scaffold/src/index.tsx +5 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@agents24/chat-react`
2
2
 
3
- Last Updated: 2026-08-24
3
+ Last Updated: 2026-10-06
4
4
 
5
5
  Reusable React chat UI, runtime orchestration, and the canonical source-owned Agents24 chat scaffold.
6
6
 
@@ -109,3 +109,25 @@ The default scaffold uses the Soft shadcn preset `b2D1FHzlY`. Hosted Agent share
109
109
  For strict Vite CSP, load the application stylesheet with an HTML `<link>` and import the scaffold stylesheet from CSS. Do not add `unsafe-inline`.
110
110
 
111
111
  The package does not own credentials, routes, Fetch/SSE, token refresh, or product-specific citations, TTS, artifacts, and debug controls. React and ReactDOM `>=19 <20` are required.
112
+
113
+ ## Editable chat in an existing app
114
+
115
+ Install the complete source-owned chat block into an existing React 19, Tailwind 4 application with shadcn configured:
116
+
117
+ ```sh
118
+ npx shadcn@latest add https://docs.agents24.dev/r/agents24-chat.json
119
+ ```
120
+
121
+ The block installs under your configured components directory in `agents24-chat/`. It does not replace existing host controls, create routes, or install a server bridge. Import its `styles.css` from your application stylesheet, then import `Agents24ChatScaffold` from the installed `agents24-chat/index`. For example, with the standard `src/components` directory, add `@import "../components/agents24-chat/styles.css";` to `src/app/globals.css` in Next.js, or `@import "./components/agents24-chat/styles.css";` to `src/styles.css` in Vite. Vite with strict CSP must load that application stylesheet through an HTML `<link>`, rather than a JavaScript CSS import.
122
+
123
+ Pass your configured `AgentClient`, and keep authentication and routing in the host. The installed block defaults to `theme="inherit"`, using host semantic colors, fonts, radii, and dark mode. It does not manage host appearance. Soft/Sharp presets remain the defaults for hosted shares and generated applications; the installed block intentionally excludes preset styles.
124
+
125
+ The source is yours to edit. Runtime packages remain dependencies and supply shared lifecycle fixes. Review source updates before reinstalling; do not use `--overwrite` on customized files. Each registry payload records its canonical source checksum and exact Agents24 dependency versions.
126
+
127
+ ## Composer customization
128
+
129
+ `useAgentChatComposer` from `/ui` owns draft state, attachments, input validation, audio recording, submission serialization, and untouched-draft restoration. `AgentChatComposer` remains a low-level renderer; its optional `inputContract`, `recordingEnabled`, and `contextWindow` props add contract-aware controls. Without an input contract it preserves its unrestricted host-wired behavior.
130
+
131
+ `Agents24ChatComposer` and `Agents24ChatComposerProps` from `/scaffold` expose the complete default composer, including Agent/model selectors and context status. The complete scaffold accepts `components={{ Composer: MyComposer }}` alongside its Timeline slot. A replacement receives the complete composer props and must preserve accessible validation, recording, cancellation, and queue controls. When rendering the complete composer outside a scaffold, wrap it in the exported `Agents24ChatThemeProvider` and `Agents24ChatTooltipProvider`; pass `theme="inherit"` for host-owned appearance.
132
+
133
+ For a packaged scaffold embedded in a host theme, use `theme="inherit"` and import `@agents24/chat-react/scaffold/inherit.css` from your stylesheet instead of the preset scaffold stylesheet. Authentication remains host-owned; the scaffold provides the identity footer, not a sign-in gate.
@@ -0,0 +1,86 @@
1
+ // src/runtime/input-contract.ts
2
+ import { normalizeAttachmentMimeType, SUPPORTED_ATTACHMENT_MIME_TYPES } from "@agents24/client";
3
+ import { attachmentFileAccept, normalizeAttachmentMimeType as normalizeAttachmentMimeType2 } from "@agents24/client";
4
+ var SUPPORTED_INPUT_MIME_TYPES = SUPPORTED_ATTACHMENT_MIME_TYPES;
5
+ function workflowInputRule(inputs, key) {
6
+ const input = inputs?.find((candidate) => candidate.key === key);
7
+ return {
8
+ enabled: input?.enabled !== false,
9
+ required: input?.required === true
10
+ };
11
+ }
12
+ function validateWorkflowInputSubmission(inputs, text, attachmentMimeTypes) {
13
+ const present = { files: false, images: false, audio: false };
14
+ if (text.trim() && !workflowInputRule(inputs, "text").enabled) {
15
+ return "This workflow does not accept text input.";
16
+ }
17
+ for (const rawMimeType of attachmentMimeTypes) {
18
+ const mimeType = canonicalInputMimeType(rawMimeType);
19
+ const modality = mimeType.startsWith("image/") ? "images" : mimeType.startsWith("audio/") ? "audio" : "files";
20
+ const supported = SUPPORTED_INPUT_MIME_TYPES[modality].includes(mimeType);
21
+ if (!supported || !workflowInputRule(inputs, modality).enabled) {
22
+ return `The attachment type ${mimeType || "unknown"} is not accepted.`;
23
+ }
24
+ present[modality] = true;
25
+ }
26
+ if (workflowInputRule(inputs, "text").required && !text.trim()) return "Text input is required.";
27
+ for (const modality of ["files", "images", "audio"]) {
28
+ if (workflowInputRule(inputs, modality).required && !present[modality]) {
29
+ return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+ var canonicalInputMimeType = normalizeAttachmentMimeType;
35
+ function uploadByteSize(data) {
36
+ if (data instanceof Uint8Array || data instanceof ArrayBuffer) return data.byteLength;
37
+ return data.size;
38
+ }
39
+ function inputMimeTypes(contract) {
40
+ if (!contract) return [];
41
+ return [
42
+ ...contract.modalities.files.allowed_mime_types,
43
+ ...contract.modalities.images.allowed_mime_types,
44
+ ...contract.modalities.audio.allowed_mime_types
45
+ ];
46
+ }
47
+ function validateAgentChatSubmission(contract, text, files) {
48
+ const { modalities, limits } = contract;
49
+ const normalizedText = text.trim();
50
+ if (normalizedText && !modalities.text.enabled) return "This agent does not accept text input.";
51
+ if (modalities.text.required && !normalizedText) return "Text input is required.";
52
+ if (files.length > limits.max_attachments_per_turn) {
53
+ return `You can attach up to ${limits.max_attachments_per_turn} items per message.`;
54
+ }
55
+ let totalBytes = 0;
56
+ const present = { files: false, images: false, audio: false };
57
+ for (const file of files) {
58
+ const mediaType = normalizeAttachmentMimeType(file.mediaType, file.name);
59
+ const modality = mediaType.startsWith("image/") ? "images" : mediaType.startsWith("audio/") ? "audio" : "files";
60
+ const rule = modalities[modality];
61
+ if (!rule.enabled || !rule.allowed_mime_types.includes(mediaType)) {
62
+ return `The attachment type ${mediaType || "unknown"} is not accepted.`;
63
+ }
64
+ const byteSize = file.byteSize ?? uploadByteSize(file.data);
65
+ if (byteSize > limits.max_file_bytes) return "An attachment exceeds the per-file size limit.";
66
+ totalBytes += byteSize;
67
+ present[modality] = true;
68
+ }
69
+ if (totalBytes > limits.max_total_bytes) return "Attachments exceed the total size limit.";
70
+ for (const modality of ["files", "images", "audio"]) {
71
+ if (modalities[modality].required && !present[modality]) return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;
72
+ }
73
+ return null;
74
+ }
75
+
76
+ export {
77
+ SUPPORTED_INPUT_MIME_TYPES,
78
+ workflowInputRule,
79
+ validateWorkflowInputSubmission,
80
+ canonicalInputMimeType,
81
+ inputMimeTypes,
82
+ validateAgentChatSubmission,
83
+ attachmentFileAccept,
84
+ normalizeAttachmentMimeType2 as normalizeAttachmentMimeType
85
+ };
86
+ //# sourceMappingURL=chunk-INO5DSSJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/input-contract.ts"],"sourcesContent":["import { normalizeAttachmentMimeType, SUPPORTED_ATTACHMENT_MIME_TYPES, type ClientInputFeature, type PortableUpload } from \"@agents24/client\";\nexport { attachmentFileAccept, normalizeAttachmentMimeType } from \"@agents24/client\";\n\nexport type AgentChatInputContract = ClientInputFeature;\n\nexport const SUPPORTED_INPUT_MIME_TYPES = SUPPORTED_ATTACHMENT_MIME_TYPES;\n\nexport type WorkflowInputRule = {\n key: string;\n enabled?: boolean;\n required?: boolean;\n};\n\nexport function workflowInputRule(\n inputs: readonly WorkflowInputRule[] | null | undefined,\n key: string,\n): { enabled: boolean; required: boolean } {\n const input = inputs?.find((candidate) => candidate.key === key);\n return {\n enabled: input?.enabled !== false,\n required: input?.required === true,\n };\n}\n\nexport function validateWorkflowInputSubmission(\n inputs: readonly WorkflowInputRule[] | null | undefined,\n text: string,\n attachmentMimeTypes: readonly string[],\n): string | null {\n const present = { files: false, images: false, audio: false };\n if (text.trim() && !workflowInputRule(inputs, \"text\").enabled) {\n return \"This workflow does not accept text input.\";\n }\n for (const rawMimeType of attachmentMimeTypes) {\n const mimeType = canonicalInputMimeType(rawMimeType);\n const modality = mimeType.startsWith(\"image/\") ? \"images\" : mimeType.startsWith(\"audio/\") ? \"audio\" : \"files\";\n const supported = (SUPPORTED_INPUT_MIME_TYPES[modality] as readonly string[]).includes(mimeType);\n if (!supported || !workflowInputRule(inputs, modality).enabled) {\n return `The attachment type ${mimeType || \"unknown\"} is not accepted.`;\n }\n present[modality] = true;\n }\n if (workflowInputRule(inputs, \"text\").required && !text.trim()) return \"Text input is required.\";\n for (const modality of [\"files\", \"images\", \"audio\"] as const) {\n if (workflowInputRule(inputs, modality).required && !present[modality]) {\n return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;\n }\n }\n return null;\n}\n\nexport const canonicalInputMimeType = normalizeAttachmentMimeType;\n\nfunction uploadByteSize(data: PortableUpload[\"data\"]): number {\n if (data instanceof Uint8Array || data instanceof ArrayBuffer) return data.byteLength;\n return data.size;\n}\n\nexport function inputMimeTypes(contract: AgentChatInputContract | null | undefined): string[] {\n if (!contract) return [];\n return [\n ...contract.modalities.files.allowed_mime_types,\n ...contract.modalities.images.allowed_mime_types,\n ...contract.modalities.audio.allowed_mime_types,\n ];\n}\n\nexport function validateAgentChatSubmission(\n contract: AgentChatInputContract,\n text: string,\n files: readonly (Pick<PortableUpload, \"mediaType\" | \"data\"> & Partial<Pick<PortableUpload, \"name\">> & { byteSize?: number })[],\n): string | null {\n const { modalities, limits } = contract;\n const normalizedText = text.trim();\n if (normalizedText && !modalities.text.enabled) return \"This agent does not accept text input.\";\n if (modalities.text.required && !normalizedText) return \"Text input is required.\";\n if (files.length > limits.max_attachments_per_turn) {\n return `You can attach up to ${limits.max_attachments_per_turn} items per message.`;\n }\n let totalBytes = 0;\n const present = { files: false, images: false, audio: false };\n for (const file of files) {\n const mediaType = normalizeAttachmentMimeType(file.mediaType, file.name);\n const modality = mediaType.startsWith(\"image/\") ? \"images\" : mediaType.startsWith(\"audio/\") ? \"audio\" : \"files\";\n const rule = modalities[modality];\n if (!rule.enabled || !rule.allowed_mime_types.includes(mediaType)) {\n return `The attachment type ${mediaType || \"unknown\"} is not accepted.`;\n }\n const byteSize = file.byteSize ?? uploadByteSize(file.data);\n if (byteSize > limits.max_file_bytes) return \"An attachment exceeds the per-file size limit.\";\n totalBytes += byteSize;\n present[modality] = true;\n }\n if (totalBytes > limits.max_total_bytes) return \"Attachments exceed the total size limit.\";\n for (const modality of [\"files\", \"images\", \"audio\"] as const) {\n if (modalities[modality].required && !present[modality]) return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;\n }\n return null;\n}\n"],"mappings":"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","names":["normalizeAttachmentMimeType"]}