@agents24/chat-react 0.5.37 → 0.5.38
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.
- package/README.md +31 -1
- package/compatibility.json +5 -5
- package/dist/registry/agents24-chat.json +7 -7
- package/dist/ui/agent-chat-composer.d.ts +8 -1
- package/dist/ui/index.cjs +27 -4
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +27 -4
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/use-agent-chat-composer.d.ts +11 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@agents24/chat-react`
|
|
2
2
|
|
|
3
|
-
Last Updated: 2026-10-
|
|
3
|
+
Last Updated: 2026-10-25
|
|
4
4
|
|
|
5
5
|
Reusable React chat UI, runtime orchestration, and the canonical source-owned Agents24 chat scaffold.
|
|
6
6
|
|
|
@@ -30,6 +30,36 @@ Agent/model selector in the composer, preserves the active controller,
|
|
|
30
30
|
transcript, thread, and draft while switching, and adopts the durable thread
|
|
31
31
|
ID from the first accepted runtime event.
|
|
32
32
|
|
|
33
|
+
## Fill the default composer with a suggestion
|
|
34
|
+
|
|
35
|
+
Keep the default composer and its attachment handling using a public ref:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useRef } from "react";
|
|
39
|
+
import { AgentChatComposer, type AgentChatComposerHandle, type AgentChatComposerSubmit } from "@agents24/chat-react/ui";
|
|
40
|
+
|
|
41
|
+
function SuggestedComposer({ scopeKey, onSubmit }: {
|
|
42
|
+
scopeKey: string;
|
|
43
|
+
onSubmit: (message: AgentChatComposerSubmit) => void | Promise<void>;
|
|
44
|
+
}) {
|
|
45
|
+
const composerRef = useRef<AgentChatComposerHandle>(null);
|
|
46
|
+
const prompt = "Find a patient with [prescription details].";
|
|
47
|
+
return <>
|
|
48
|
+
<button type="button" onClick={() => composerRef.current?.setText(prompt, {
|
|
49
|
+
focus: true,
|
|
50
|
+
selection: { start: prompt.indexOf("["), end: prompt.indexOf("]") + 1 },
|
|
51
|
+
})}>Find a patient</button>
|
|
52
|
+
<AgentChatComposer ref={composerRef} scopeKey={scopeKey} onSubmit={onSubmit} />
|
|
53
|
+
</>;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`setText` preserves attached files and previews, updates validation/expansion, and never submits. Focus is optional and defaults to false; selection uses UTF-16 offsets into the new text and does not imply focus. Both apply after text commits. Disabled composers still accept text but skip focus and selection. Existing `textareaRef` remains available independently.
|
|
58
|
+
|
|
59
|
+
Every accepted update advances the draft revision, protecting suggestions and newer user edits from failed-send recovery. Use the current ref and change `scopeKey` with conversation/identity boundaries. Saved handles return false after a scope change or unmount; accepted updates return true. Scope changes invalidate operations; hosts continue to own conversation draft restoration through `restoredDraft`.
|
|
60
|
+
|
|
61
|
+
This API is pending the coordinated package release; it is not available in the already published `0.5.37`.
|
|
62
|
+
|
|
33
63
|
## Timeline customization
|
|
34
64
|
|
|
35
65
|
`@agents24/react` projects backend response blocks into canonical ordered `message.parts`. The default scaffold renders that sequence through `AgentResponseTimeline`, preserving interleaved text, tools, HITL, and resumed text.
|
package/compatibility.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"contractHash": "
|
|
2
|
+
"contractHash": "6d6ceb78b4285f987a5635b4d4d0b407732bf5e61abd1aca1369a97a7d6336e6",
|
|
3
3
|
"contractVersion": "0.3.0",
|
|
4
4
|
"corpus": "conformance/v1/corpus.json",
|
|
5
5
|
"generatedBy": "@agents24/sdk-contract",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"reactDom": ">=19 <20"
|
|
16
16
|
},
|
|
17
17
|
"@agents24/client": {
|
|
18
|
-
"contract": "
|
|
18
|
+
"contract": "6d6ceb78b4285f987a5635b4d4d0b407732bf5e61abd1aca1369a97a7d6336e6",
|
|
19
19
|
"runtime": "run-stream.v3"
|
|
20
20
|
},
|
|
21
21
|
"@agents24/node": {
|
|
22
|
-
"contract": "
|
|
22
|
+
"contract": "6d6ceb78b4285f987a5635b4d4d0b407732bf5e61abd1aca1369a97a7d6336e6",
|
|
23
23
|
"node": "^22.0.0 || ^24.0.0"
|
|
24
24
|
},
|
|
25
25
|
"@agents24/react": {
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"react": ">=19 <20"
|
|
28
28
|
},
|
|
29
29
|
"@talmudpedia/runtime-sdk": {
|
|
30
|
-
"contract": "
|
|
30
|
+
"contract": "6d6ceb78b4285f987a5635b4d4d0b407732bf5e61abd1aca1369a97a7d6336e6",
|
|
31
31
|
"protocolOwner": "@agents24/client"
|
|
32
32
|
},
|
|
33
33
|
"agents24": {
|
|
34
|
-
"contract": "
|
|
34
|
+
"contract": "6d6ceb78b4285f987a5635b4d4d0b407732bf5e61abd1aca1369a97a7d6336e6",
|
|
35
35
|
"python": ">=3.10"
|
|
36
36
|
}
|
|
37
37
|
},
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
"title": "Agents24 Chat",
|
|
6
6
|
"description": "Complete editable Agents24 chat, inheriting your application's theme.",
|
|
7
7
|
"dependencies": [
|
|
8
|
-
"@agents24/chat-react@0.5.
|
|
9
|
-
"@agents24/client@0.2.
|
|
10
|
-
"@agents24/react@0.2.
|
|
8
|
+
"@agents24/chat-react@0.5.38",
|
|
9
|
+
"@agents24/client@0.2.20",
|
|
10
|
+
"@agents24/react@0.2.25",
|
|
11
11
|
"@shadcn/react@^0.2.1",
|
|
12
12
|
"class-variance-authority@^0.7.1",
|
|
13
13
|
"clsx@^2.1.1",
|
|
@@ -254,13 +254,13 @@
|
|
|
254
254
|
}
|
|
255
255
|
],
|
|
256
256
|
"meta": {
|
|
257
|
-
"version": "0.5.
|
|
257
|
+
"version": "0.5.38",
|
|
258
258
|
"source": "packages/agents24-chat-react/scaffold",
|
|
259
259
|
"source_sha256": "411a13a78466b84a923ae81a16d1b032deb758e7f44c1df5915cfdff71df6d62",
|
|
260
260
|
"agents24": {
|
|
261
|
-
"@agents24/client": "0.2.
|
|
262
|
-
"@agents24/react": "0.2.
|
|
263
|
-
"@agents24/chat-react": "0.5.
|
|
261
|
+
"@agents24/client": "0.2.20",
|
|
262
|
+
"@agents24/react": "0.2.25",
|
|
263
|
+
"@agents24/chat-react": "0.5.38"
|
|
264
264
|
}
|
|
265
265
|
},
|
|
266
266
|
"docs": "Import the installed styles.css from your application stylesheet. Pass your configured AgentClient to Agents24ChatScaffold. Authentication and URLs remain host-owned. Installed UI is editable; review changes before reinstalling and do not use --overwrite on customized files."
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { AgentChatComposerFile } from "./composer-attachments";
|
|
3
|
+
import { type AgentChatComposerTextOptions } from "./use-agent-chat-composer";
|
|
3
4
|
import type { AgentChatInputContract } from "../runtime/input-contract";
|
|
4
5
|
import type { ContextWindow } from "@agents24/client/protocol";
|
|
5
6
|
export type { AgentChatComposerFile } from "./composer-attachments";
|
|
@@ -8,7 +9,13 @@ export type AgentChatComposerSubmit = {
|
|
|
8
9
|
files: AgentChatComposerFile[];
|
|
9
10
|
attachmentIds?: string[];
|
|
10
11
|
};
|
|
12
|
+
export type AgentChatComposerHandle = {
|
|
13
|
+
/** Replace only text, preserving attachments. Returns false after scope change or unmount. */
|
|
14
|
+
setText(text: string, options?: AgentChatComposerTextOptions): boolean;
|
|
15
|
+
};
|
|
11
16
|
export type AgentChatComposerProps = {
|
|
17
|
+
/** Public draft controls for the default composer. */
|
|
18
|
+
ref?: React.Ref<AgentChatComposerHandle>;
|
|
12
19
|
inputContract?: AgentChatInputContract | null;
|
|
13
20
|
recordingEnabled?: boolean;
|
|
14
21
|
contextWindow?: ContextWindow | null;
|
|
@@ -35,4 +42,4 @@ export type AgentChatComposerProps = {
|
|
|
35
42
|
} | null;
|
|
36
43
|
textareaRef?: React.Ref<HTMLTextAreaElement>;
|
|
37
44
|
};
|
|
38
|
-
export declare function AgentChatComposer({ inputContract, recordingEnabled, contextWindow, accept, allowAttachments, className, disabled, forceExpanded, focusKey, scopeKey, inputToolbarContent, isRunning, isCancelling, onAttachmentCountChange, onStop, onSubmit, placeholder, restoredDraft, textareaRef, }: AgentChatComposerProps): import("react/jsx-runtime").JSX.Element;
|
|
45
|
+
export declare function AgentChatComposer({ ref, inputContract, recordingEnabled, contextWindow, accept, allowAttachments, className, disabled, forceExpanded, focusKey, scopeKey, inputToolbarContent, isRunning, isCancelling, onAttachmentCountChange, onStop, onSubmit, placeholder, restoredDraft, textareaRef, }: AgentChatComposerProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/ui/index.cjs
CHANGED
|
@@ -1099,15 +1099,33 @@ function useAgentChatComposer(options) {
|
|
|
1099
1099
|
const generationRef = React4.useRef(0);
|
|
1100
1100
|
const mountedRef = React4.useRef(true);
|
|
1101
1101
|
const textRef = React4.useRef(text);
|
|
1102
|
+
const scope = React4.useMemo(() => ({ active: false }), [options.scopeKey]);
|
|
1103
|
+
const [textInteraction, setTextInteraction] = React4.useState(null);
|
|
1104
|
+
const handledTextInteractionRef = React4.useRef(textInteraction);
|
|
1102
1105
|
React4.useLayoutEffect(() => {
|
|
1106
|
+
scope.active = true;
|
|
1103
1107
|
mountedRef.current = true;
|
|
1104
1108
|
submittingRef.current = false;
|
|
1105
1109
|
setIsSubmitting(false);
|
|
1106
1110
|
return () => {
|
|
1111
|
+
scope.active = false;
|
|
1107
1112
|
mountedRef.current = false;
|
|
1108
1113
|
generationRef.current += 1;
|
|
1109
1114
|
};
|
|
1110
|
-
}, [
|
|
1115
|
+
}, [scope]);
|
|
1116
|
+
React4.useLayoutEffect(() => {
|
|
1117
|
+
if (handledTextInteractionRef.current === textInteraction) return;
|
|
1118
|
+
handledTextInteractionRef.current = textInteraction;
|
|
1119
|
+
if (!textInteraction || textInteraction.scope !== scope || !scope.active || textInteraction.revision !== revisionRef.current || options.disabled) return;
|
|
1120
|
+
const textarea = textareaRef.current;
|
|
1121
|
+
if (!textarea) return;
|
|
1122
|
+
if (textInteraction.options.focus) textarea.focus({ preventScroll: true });
|
|
1123
|
+
const selection = textInteraction.options.selection;
|
|
1124
|
+
if (selection) {
|
|
1125
|
+
const clamp = (offset) => Math.max(0, Math.min(textRef.current.length, offset));
|
|
1126
|
+
textarea.setSelectionRange(clamp(selection.start), clamp(selection.end), selection.direction);
|
|
1127
|
+
}
|
|
1128
|
+
}, [textInteraction, scope, textareaRef, options.disabled]);
|
|
1111
1129
|
React4.useEffect(() => () => {
|
|
1112
1130
|
revokeAgentChatComposerFiles(filesRef.current);
|
|
1113
1131
|
revokeAgentChatComposerFiles([...inFlightFilesRef.current.values()].flat());
|
|
@@ -1132,11 +1150,14 @@ function useAgentChatComposer(options) {
|
|
|
1132
1150
|
}
|
|
1133
1151
|
});
|
|
1134
1152
|
const attachmentMimeTypes = inputMimeTypes(options.inputContract);
|
|
1135
|
-
const setText = (value) => {
|
|
1136
|
-
|
|
1153
|
+
const setText = React4.useCallback((value, interaction = {}) => {
|
|
1154
|
+
if (!scope.active) return false;
|
|
1155
|
+
const revision = ++revisionRef.current;
|
|
1137
1156
|
textRef.current = value;
|
|
1138
1157
|
setTextState(value);
|
|
1139
|
-
|
|
1158
|
+
setTextInteraction({ scope, revision, options: interaction });
|
|
1159
|
+
return true;
|
|
1160
|
+
}, [scope]);
|
|
1140
1161
|
const addFiles = (selected) => {
|
|
1141
1162
|
const accepted = selected.filter((file) => attachmentMimeTypes.includes(canonicalInputMimeType(file.type, file.name)));
|
|
1142
1163
|
revisionRef.current += 1;
|
|
@@ -1349,6 +1370,7 @@ function formatFileCount(files) {
|
|
|
1349
1370
|
var pillButtonClass = "inline-flex size-8 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-muted text-sm text-muted-foreground shadow-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
1350
1371
|
var submitButtonClass = "inline-flex size-9 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-primary text-sm text-primary-foreground shadow-none transition-[background-color,color,opacity,transform] duration-200 hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
1351
1372
|
function AgentChatComposer({
|
|
1373
|
+
ref,
|
|
1352
1374
|
inputContract,
|
|
1353
1375
|
recordingEnabled,
|
|
1354
1376
|
contextWindow,
|
|
@@ -1384,6 +1406,7 @@ function AgentChatComposer({
|
|
|
1384
1406
|
onSubmit: (text2, files2) => onSubmit({ text: text2.trim(), files: files2, attachmentIds: files2.flatMap((file) => file.id ? [file.id] : []) })
|
|
1385
1407
|
});
|
|
1386
1408
|
const { text, files, setText, isSubmitting, canSubmit, action, submit } = composer;
|
|
1409
|
+
React6.useImperativeHandle(ref, () => ({ setText }), [setText]);
|
|
1387
1410
|
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
|
|
1388
1411
|
const setTextareaRef = React6.useCallback(
|
|
1389
1412
|
(node) => {
|