@agents24/chat-react 0.5.0 → 0.5.1

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 (133) hide show
  1. package/README.md +90 -21
  2. package/compatibility.json +10 -10
  3. package/dist/chunk-SSUO4EPD.js +189 -0
  4. package/dist/chunk-SSUO4EPD.js.map +1 -0
  5. package/dist/chunk-ZCNHLFRB.js +108 -0
  6. package/dist/chunk-ZCNHLFRB.js.map +1 -0
  7. package/dist/index.cjs +112 -95
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +0 -1
  10. package/dist/index.js +362 -68
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp-oauth.d.ts +13 -0
  13. package/dist/runtime/index.cjs +594 -0
  14. package/dist/runtime/index.cjs.map +1 -0
  15. package/dist/runtime/index.d.ts +2 -0
  16. package/dist/runtime/index.js +458 -0
  17. package/dist/runtime/index.js.map +1 -0
  18. package/dist/runtime/input-contract.d.ts +20 -0
  19. package/dist/runtime/use-agent-chat-runtime.d.ts +83 -0
  20. package/dist/scaffold/app.d.ts +30 -0
  21. package/dist/scaffold/components/chat/chat-composer.d.ts +21 -0
  22. package/dist/scaffold/components/chat/chat-message.d.ts +21 -0
  23. package/dist/scaffold/components/chat/chat-shell.d.ts +8 -0
  24. package/dist/scaffold/components/chat/collapsible-user-message.d.ts +3 -0
  25. package/dist/scaffold/components/chat/external-link-dialog.d.ts +2 -0
  26. package/dist/scaffold/components/chat/identity-control.d.ts +4 -0
  27. package/dist/scaffold/components/chat/message-parts.d.ts +12 -0
  28. package/dist/scaffold/components/chat/theme-menu.d.ts +1 -0
  29. package/dist/scaffold/components/chat/thread-sidebar.d.ts +16 -0
  30. package/dist/scaffold/components/chat/timeline-loading.d.ts +7 -0
  31. package/dist/scaffold/components/chat/turn-outline.d.ts +4 -0
  32. package/dist/scaffold/components/theme-provider.d.ts +20 -0
  33. package/dist/scaffold/components/ui/attachment.d.ts +24 -0
  34. package/dist/scaffold/components/ui/bubble.d.ts +17 -0
  35. package/dist/scaffold/components/ui/button-group.d.ts +11 -0
  36. package/dist/scaffold/components/ui/button.d.ts +10 -0
  37. package/dist/scaffold/components/ui/dialog.d.ts +17 -0
  38. package/dist/scaffold/components/ui/dropdown-menu.d.ts +29 -0
  39. package/dist/scaffold/components/ui/input.d.ts +3 -0
  40. package/dist/scaffold/components/ui/marker.d.ts +11 -0
  41. package/dist/scaffold/components/ui/message-scroller.d.ts +10 -0
  42. package/dist/scaffold/components/ui/message.d.ts +10 -0
  43. package/dist/scaffold/components/ui/popover.d.ts +6 -0
  44. package/dist/scaffold/components/ui/select.d.ts +8 -0
  45. package/dist/scaffold/components/ui/separator.d.ts +4 -0
  46. package/dist/scaffold/components/ui/sheet.d.ts +14 -0
  47. package/dist/scaffold/components/ui/sidebar.d.ts +69 -0
  48. package/dist/scaffold/components/ui/skeleton.d.ts +2 -0
  49. package/dist/scaffold/components/ui/textarea.d.ts +3 -0
  50. package/dist/scaffold/components/ui/tooltip.d.ts +15 -0
  51. package/dist/scaffold/hooks/use-mobile.d.ts +1 -0
  52. package/dist/scaffold/identity.d.ts +10 -0
  53. package/dist/scaffold/index.cjs +2270 -0
  54. package/dist/scaffold/index.cjs.map +1 -0
  55. package/dist/scaffold/index.d.ts +14 -0
  56. package/dist/scaffold/index.js +2244 -0
  57. package/dist/scaffold/index.js.map +1 -0
  58. package/dist/scaffold/lib/utils.d.ts +2 -0
  59. package/dist/styles.css +137 -378
  60. package/dist/types.d.ts +3 -13
  61. package/dist/ui/adapters.d.ts +1 -1
  62. package/dist/ui/agent-chat-actions.d.ts +12 -5
  63. package/dist/ui/agent-chat-composer.d.ts +2 -1
  64. package/dist/ui/agent-chat-context-compression-row.d.ts +5 -0
  65. package/dist/ui/agent-chat-context-status.d.ts +13 -0
  66. package/dist/ui/agent-chat-message.d.ts +7 -5
  67. package/dist/ui/agent-response-timeline.d.ts +29 -7
  68. package/dist/ui/ask-user-interaction-state.d.ts +20 -0
  69. package/dist/ui/ask-user-interaction.d.ts +8 -0
  70. package/dist/ui/index.cjs +1391 -271
  71. package/dist/ui/index.cjs.map +1 -1
  72. package/dist/ui/index.d.ts +5 -0
  73. package/dist/ui/index.js +2380 -65
  74. package/dist/ui/index.js.map +1 -1
  75. package/dist/ui/tool-sessions.d.ts +70 -0
  76. package/dist/ui/use-audio-recorder.d.ts +8 -0
  77. package/package.json +27 -12
  78. package/scaffold/components.json +25 -0
  79. package/scaffold/manifest.json +28 -0
  80. package/scaffold/src/app.tsx +71 -0
  81. package/scaffold/src/chat.css +54 -0
  82. package/scaffold/src/components/chat/chat-composer.tsx +237 -0
  83. package/scaffold/src/components/chat/chat-message.tsx +76 -0
  84. package/scaffold/src/components/chat/chat-shell.tsx +257 -0
  85. package/scaffold/src/components/chat/collapsible-user-message.tsx +37 -0
  86. package/scaffold/src/components/chat/external-link-dialog.tsx +48 -0
  87. package/scaffold/src/components/chat/identity-control.tsx +69 -0
  88. package/scaffold/src/components/chat/message-parts.tsx +83 -0
  89. package/scaffold/src/components/chat/theme-menu.tsx +79 -0
  90. package/scaffold/src/components/chat/thread-sidebar.tsx +114 -0
  91. package/scaffold/src/components/chat/timeline-loading.ts +17 -0
  92. package/scaffold/src/components/chat/turn-outline.tsx +73 -0
  93. package/scaffold/src/components/theme-provider.tsx +91 -0
  94. package/scaffold/src/components/ui/attachment.tsx +204 -0
  95. package/scaffold/src/components/ui/bubble.tsx +125 -0
  96. package/scaffold/src/components/ui/button-group.tsx +83 -0
  97. package/scaffold/src/components/ui/button.tsx +65 -0
  98. package/scaffold/src/components/ui/dialog.tsx +171 -0
  99. package/scaffold/src/components/ui/dropdown-menu.tsx +271 -0
  100. package/scaffold/src/components/ui/input.tsx +19 -0
  101. package/scaffold/src/components/ui/marker.tsx +69 -0
  102. package/scaffold/src/components/ui/message-scroller.tsx +129 -0
  103. package/scaffold/src/components/ui/message.tsx +92 -0
  104. package/scaffold/src/components/ui/popover.tsx +40 -0
  105. package/scaffold/src/components/ui/select.tsx +92 -0
  106. package/scaffold/src/components/ui/separator.tsx +28 -0
  107. package/scaffold/src/components/ui/sheet.tsx +150 -0
  108. package/scaffold/src/components/ui/sidebar.tsx +677 -0
  109. package/scaffold/src/components/ui/skeleton.tsx +13 -0
  110. package/scaffold/src/components/ui/textarea.tsx +18 -0
  111. package/scaffold/src/components/ui/tooltip.tsx +56 -0
  112. package/scaffold/src/hooks/use-mobile.ts +24 -0
  113. package/scaffold/src/identity.ts +17 -0
  114. package/scaffold/src/index.css +222 -0
  115. package/scaffold/src/index.tsx +39 -0
  116. package/scaffold/src/lib/utils.ts +6 -0
  117. package/scaffold/styles.css +3 -0
  118. package/dist/chunk-CHAU7FNW.js +0 -140
  119. package/dist/chunk-CHAU7FNW.js.map +0 -1
  120. package/dist/chunk-LI67M74F.js +0 -445
  121. package/dist/chunk-LI67M74F.js.map +0 -1
  122. package/dist/chunk-R7WFL3YR.js +0 -1327
  123. package/dist/chunk-R7WFL3YR.js.map +0 -1
  124. package/dist/renderers.d.ts +0 -10
  125. package/dist/templates/agent-chat-app.d.ts +0 -18
  126. package/dist/templates/agent-chat-layout.d.ts +0 -77
  127. package/dist/templates/agent-chat-shell.d.ts +0 -37
  128. package/dist/templates/appearance-control.d.ts +0 -5
  129. package/dist/templates/index.cjs +0 -2600
  130. package/dist/templates/index.cjs.map +0 -1
  131. package/dist/templates/index.d.ts +0 -4
  132. package/dist/templates/index.js +0 -846
  133. package/dist/templates/index.js.map +0 -1
@@ -0,0 +1,594 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/runtime/index.ts
31
+ var runtime_exports = {};
32
+ __export(runtime_exports, {
33
+ SUPPORTED_INPUT_MIME_TYPES: () => SUPPORTED_INPUT_MIME_TYPES,
34
+ agentChatRuntimeErrorMessage: () => agentChatRuntimeErrorMessage,
35
+ agentChatRuntimeStateFromError: () => agentChatRuntimeStateFromError,
36
+ canonicalInputMimeType: () => canonicalInputMimeType,
37
+ inputMimeTypes: () => inputMimeTypes,
38
+ modelsFromAgentChatBootstrap: () => modelsFromAgentChatBootstrap,
39
+ useAgentChatRuntime: () => useAgentChatRuntime,
40
+ validateAgentChatSubmission: () => validateAgentChatSubmission,
41
+ validateWorkflowInputSubmission: () => validateWorkflowInputSubmission,
42
+ workflowInputRule: () => workflowInputRule
43
+ });
44
+ module.exports = __toCommonJS(runtime_exports);
45
+
46
+ // src/runtime/use-agent-chat-runtime.ts
47
+ var import_react = require("@agents24/react");
48
+ var React = __toESM(require("react"), 1);
49
+
50
+ // src/mcp-oauth.ts
51
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
52
+ function browserChatCrypto() {
53
+ const value = globalThis.crypto;
54
+ if (!value?.subtle || !value.randomUUID) {
55
+ throw new Error("Secure browser crypto is required for MCP authorization.");
56
+ }
57
+ return value;
58
+ }
59
+ function base64Url(bytes) {
60
+ let binary = "";
61
+ bytes.forEach((byte) => {
62
+ binary += String.fromCharCode(byte);
63
+ });
64
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
65
+ }
66
+ async function createMcpPkceProof() {
67
+ const crypto = browserChatCrypto();
68
+ const verifier = base64Url(crypto.getRandomValues(new Uint8Array(32)));
69
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
70
+ return { challenge: base64Url(new Uint8Array(digest)), verifier };
71
+ }
72
+ function isLoopback(hostname) {
73
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
74
+ }
75
+ function normalizedOrigin(value) {
76
+ try {
77
+ const url = new URL(value);
78
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback(url.hostname))) {
79
+ throw new Error("unsafe callback origin");
80
+ }
81
+ if (url.origin === "null") throw new Error("opaque callback origin");
82
+ return url.origin;
83
+ } catch {
84
+ throw new Error("MCP authorization returned an invalid callback origin.");
85
+ }
86
+ }
87
+ function validateMcpCallbackResult(result, expectedServerId) {
88
+ const serverId = String(result.server_id || "").trim();
89
+ const connectionId = String(result.connection_id || "").trim();
90
+ if (!serverId || serverId !== expectedServerId) {
91
+ throw new Error("MCP authorization completed for an unexpected server.");
92
+ }
93
+ if (!UUID_PATTERN.test(connectionId)) {
94
+ throw new Error("MCP authorization returned an invalid connection identity.");
95
+ }
96
+ return result;
97
+ }
98
+ function waitForMcpOauthComplete({
99
+ popup,
100
+ callbackOrigin,
101
+ popupNonce,
102
+ requireConnectionId = false,
103
+ serverId,
104
+ timeoutMs = 12e4
105
+ }) {
106
+ if (!popup) return Promise.reject(new Error("Could not open the MCP authorization popup."));
107
+ const expectedOrigin = normalizedOrigin(callbackOrigin);
108
+ const expectedServerId = String(serverId || "").trim();
109
+ const expectedPopupNonce = String(popupNonce || "").trim();
110
+ if (!expectedServerId) return Promise.reject(new Error("MCP authorization is missing its server identity."));
111
+ if (!expectedPopupNonce) return Promise.reject(new Error("MCP authorization is missing its popup binding."));
112
+ const returnFocusTo = typeof document !== "undefined" ? document.activeElement : null;
113
+ return new Promise((resolve, reject) => {
114
+ let settled = false;
115
+ const finish = (result) => {
116
+ if (settled) return;
117
+ settled = true;
118
+ window.clearTimeout(timeout);
119
+ window.clearInterval(closeTimer);
120
+ window.removeEventListener("message", onMessage);
121
+ returnFocusTo?.focus?.();
122
+ if (result instanceof Error) reject(result);
123
+ else resolve(result);
124
+ };
125
+ const onMessage = (event) => {
126
+ if (event.source !== popup || event.origin !== expectedOrigin) return;
127
+ const data = event.data;
128
+ if (data?.type !== "agents24.mcp.callback" || data.server_id !== expectedServerId || data.popup_nonce !== expectedPopupNonce) return;
129
+ const code = String(data.code || "").trim();
130
+ if (!code || code.length > 2048) return;
131
+ const connectionId = String(data.connection_id || "").trim();
132
+ if ((requireConnectionId || connectionId) && !UUID_PATTERN.test(connectionId)) return;
133
+ finish({
134
+ code,
135
+ ...connectionId ? { connectionId } : {},
136
+ popupNonce: expectedPopupNonce,
137
+ serverId: expectedServerId
138
+ });
139
+ };
140
+ const timeout = window.setTimeout(
141
+ () => finish(new Error("MCP authorization timed out.")),
142
+ Math.max(1, timeoutMs)
143
+ );
144
+ const closeTimer = window.setInterval(() => {
145
+ if (popup.closed) finish(new Error("MCP authorization window was closed before connecting."));
146
+ }, 500);
147
+ window.addEventListener("message", onMessage);
148
+ });
149
+ }
150
+
151
+ // src/runtime/input-contract.ts
152
+ var SUPPORTED_INPUT_MIME_TYPES = {
153
+ files: ["application/pdf", "text/plain", "text/markdown", "text/csv", "application/json"],
154
+ images: ["image/png", "image/jpeg", "image/webp"],
155
+ audio: ["audio/wav", "audio/mpeg", "audio/webm", "audio/mp4"]
156
+ };
157
+ function workflowInputRule(inputs, key) {
158
+ const input = inputs?.find((candidate) => candidate.key === key);
159
+ return {
160
+ enabled: input?.enabled !== false,
161
+ required: input?.required === true
162
+ };
163
+ }
164
+ function validateWorkflowInputSubmission(inputs, text, attachmentMimeTypes) {
165
+ const present = { files: false, images: false, audio: false };
166
+ if (text.trim() && !workflowInputRule(inputs, "text").enabled) {
167
+ return "This workflow does not accept text input.";
168
+ }
169
+ for (const rawMimeType of attachmentMimeTypes) {
170
+ const mimeType = canonicalInputMimeType(rawMimeType);
171
+ const modality = mimeType.startsWith("image/") ? "images" : mimeType.startsWith("audio/") ? "audio" : "files";
172
+ const supported = SUPPORTED_INPUT_MIME_TYPES[modality].includes(mimeType);
173
+ if (!supported || !workflowInputRule(inputs, modality).enabled) {
174
+ return `The attachment type ${mimeType || "unknown"} is not accepted.`;
175
+ }
176
+ present[modality] = true;
177
+ }
178
+ if (workflowInputRule(inputs, "text").required && !text.trim()) return "Text input is required.";
179
+ for (const modality of ["files", "images", "audio"]) {
180
+ if (workflowInputRule(inputs, modality).required && !present[modality]) {
181
+ return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;
182
+ }
183
+ }
184
+ return null;
185
+ }
186
+ function canonicalInputMimeType(value) {
187
+ const normalized = value.split(";", 1)[0]?.trim().toLowerCase() || "";
188
+ if (normalized === "image/jpg") return "image/jpeg";
189
+ if (["audio/x-wav"].includes(normalized)) return "audio/wav";
190
+ if (["audio/x-m4a", "audio/m4a"].includes(normalized)) return "audio/mp4";
191
+ return normalized;
192
+ }
193
+ function uploadByteSize(data) {
194
+ if (data instanceof Uint8Array || data instanceof ArrayBuffer) return data.byteLength;
195
+ return data.size;
196
+ }
197
+ function inputMimeTypes(contract) {
198
+ if (!contract) return [];
199
+ return [
200
+ ...contract.modalities.files.allowed_mime_types,
201
+ ...contract.modalities.images.allowed_mime_types,
202
+ ...contract.modalities.audio.allowed_mime_types
203
+ ];
204
+ }
205
+ function validateAgentChatSubmission(contract, text, files) {
206
+ const { modalities, limits } = contract;
207
+ const normalizedText = text.trim();
208
+ if (normalizedText && !modalities.text.enabled) return "This agent does not accept text input.";
209
+ if (modalities.text.required && !normalizedText) return "Text input is required.";
210
+ if (files.length > limits.max_attachments_per_turn) {
211
+ return `You can attach up to ${limits.max_attachments_per_turn} items per message.`;
212
+ }
213
+ let totalBytes = 0;
214
+ const present = { files: false, images: false, audio: false };
215
+ for (const file of files) {
216
+ const mediaType = canonicalInputMimeType(file.mediaType);
217
+ const modality = mediaType.startsWith("image/") ? "images" : mediaType.startsWith("audio/") ? "audio" : "files";
218
+ const rule = modalities[modality];
219
+ if (!rule.enabled || !rule.allowed_mime_types.includes(mediaType)) {
220
+ return `The attachment type ${mediaType || "unknown"} is not accepted.`;
221
+ }
222
+ const byteSize = uploadByteSize(file.data);
223
+ if (byteSize > limits.max_file_bytes) return "An attachment exceeds the per-file size limit.";
224
+ totalBytes += byteSize;
225
+ present[modality] = true;
226
+ }
227
+ if (totalBytes > limits.max_total_bytes) return "Attachments exceed the total size limit.";
228
+ for (const modality of ["files", "images", "audio"]) {
229
+ if (modalities[modality].required && !present[modality]) return `${modality[0].toUpperCase()}${modality.slice(1)} input is required.`;
230
+ }
231
+ return null;
232
+ }
233
+
234
+ // src/runtime/use-agent-chat-runtime.ts
235
+ function agentChatRuntimeStateFromError(error) {
236
+ const typed = error;
237
+ if (typed?.kind === "authentication") return "expired";
238
+ if (typed?.kind === "authorization") return "denied";
239
+ if (typed?.kind === "quota" || typed?.kind === "rate_limit") return "quota";
240
+ if (typed?.kind === "network") return "offline";
241
+ return "failed";
242
+ }
243
+ function agentChatRuntimeErrorMessage(error) {
244
+ if (!error) return null;
245
+ return error instanceof Error ? error.message : String(error);
246
+ }
247
+ function modelsFromAgentChatBootstrap(bootstrap) {
248
+ const modelFeatures = bootstrap.features.models;
249
+ const selectable = bootstrap.capabilities.includes("models.select") && modelFeatures?.selectable === true;
250
+ if (!selectable) return { defaultModelId: null, models: [] };
251
+ const models = Array.isArray(modelFeatures.options) ? modelFeatures.options.flatMap((option) => typeof option?.id === "string" && option.id.trim() ? [{ id: option.id, label: typeof option.label === "string" && option.label.trim() ? option.label : option.id }] : []) : [];
252
+ const configuredDefault = models.some((model) => model.id === modelFeatures.default_id) ? modelFeatures.default_id : null;
253
+ return {
254
+ defaultModelId: configuredDefault,
255
+ models
256
+ };
257
+ }
258
+ function modelsFromEffectivePolicy(policy) {
259
+ const models = policy.model_selection.selectable ? policy.model_selection.options.map((option) => ({ id: option.id, label: option.label })) : [];
260
+ return {
261
+ defaultModelId: models.some((model) => model.id === policy.model_selection.default_id) ? policy.model_selection.default_id : null,
262
+ models
263
+ };
264
+ }
265
+ function hitlKey(part) {
266
+ return part.interruptId || part.id;
267
+ }
268
+ function useAgentChatRuntime(options) {
269
+ const {
270
+ activeThreadId,
271
+ agent: agentOverride,
272
+ client,
273
+ completeMcpAuthorization,
274
+ mcpRedirectUri,
275
+ modelId,
276
+ onActiveThreadIdChange,
277
+ onModelChange
278
+ } = options;
279
+ const controller = (0, import_react.useAgentChat)(
280
+ activeThreadId === void 0 ? { client } : { activeThreadId, client }
281
+ );
282
+ const attachments = (0, import_react.useAgentAttachments)();
283
+ const mcp = (0, import_react.useAgentMcp)();
284
+ const [bootstrap, setBootstrap] = React.useState(null);
285
+ const [effectivePolicy, setEffectivePolicy] = React.useState(null);
286
+ const [isBootstrapping, setIsBootstrapping] = React.useState(true);
287
+ const [bootstrapError, setBootstrapError] = React.useState(null);
288
+ const [localOperationError, setLocalOperationError] = React.useState(null);
289
+ const [selectedModelId, setSelectedModelId] = React.useState(modelId ?? null);
290
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
291
+ const submitPromiseRef = React.useRef(null);
292
+ const [hitlStates, setHitlStates] = React.useState({});
293
+ const hitlStatesRef = React.useRef(hitlStates);
294
+ hitlStatesRef.current = hitlStates;
295
+ const mountedRef = React.useRef(true);
296
+ const clientRef = React.useRef(client);
297
+ clientRef.current = client;
298
+ const bootstrapPromiseRef = React.useRef(null);
299
+ React.useEffect(() => {
300
+ mountedRef.current = true;
301
+ return () => {
302
+ mountedRef.current = false;
303
+ };
304
+ }, []);
305
+ const previousControlledThreadRef = React.useRef(activeThreadId);
306
+ const controlledSyncTargetRef = React.useRef(null);
307
+ const previousControllerThreadRef = React.useRef(controller.activeThreadId);
308
+ const pendingThreadTransitionRef = React.useRef(null);
309
+ const lastReportedThreadRef = React.useRef(activeThreadId ?? controller.activeThreadId);
310
+ if (activeThreadId !== previousControlledThreadRef.current) {
311
+ previousControlledThreadRef.current = activeThreadId;
312
+ if (activeThreadId !== void 0) {
313
+ controlledSyncTargetRef.current = { value: activeThreadId };
314
+ lastReportedThreadRef.current = activeThreadId;
315
+ }
316
+ }
317
+ const reportActiveThread = React.useCallback((threadId, reason) => {
318
+ if (lastReportedThreadRef.current === threadId) return;
319
+ lastReportedThreadRef.current = threadId;
320
+ onActiveThreadIdChange?.({ threadId, reason });
321
+ }, [onActiveThreadIdChange]);
322
+ const retryBootstrap = React.useCallback(() => {
323
+ if (bootstrapPromiseRef.current?.client === client) return bootstrapPromiseRef.current.promise;
324
+ const requestClient = client;
325
+ const request = (async () => {
326
+ if (mountedRef.current) {
327
+ setIsBootstrapping(true);
328
+ setBootstrapError(null);
329
+ setBootstrap(null);
330
+ setEffectivePolicy(null);
331
+ }
332
+ try {
333
+ const result = await requestClient.bootstrap();
334
+ if (!mountedRef.current || clientRef.current !== requestClient) return;
335
+ setBootstrap(result);
336
+ const entitlement = await requestClient.resourcePolicies.getEffective();
337
+ if (!mountedRef.current || clientRef.current !== requestClient) return;
338
+ setEffectivePolicy(entitlement);
339
+ const availableModels = modelsFromEffectivePolicy(entitlement);
340
+ if (modelId === void 0) {
341
+ setSelectedModelId((current) => availableModels.models.some((item) => item.id === current) ? current : availableModels.defaultModelId);
342
+ }
343
+ await controller.refreshThreads().catch(() => void 0);
344
+ } catch (error) {
345
+ if (mountedRef.current && clientRef.current === requestClient) setBootstrapError(error);
346
+ } finally {
347
+ if (mountedRef.current && clientRef.current === requestClient) setIsBootstrapping(false);
348
+ }
349
+ })();
350
+ bootstrapPromiseRef.current = { client: requestClient, promise: request };
351
+ void request.finally(() => {
352
+ if (bootstrapPromiseRef.current?.promise === request) bootstrapPromiseRef.current = null;
353
+ });
354
+ return request;
355
+ }, [client, controller.refreshThreads, modelId]);
356
+ const refreshEffectivePolicy = React.useCallback(async () => {
357
+ const entitlement = await client.resourcePolicies.getEffective();
358
+ if (!mountedRef.current || clientRef.current !== client) return;
359
+ setEffectivePolicy(entitlement);
360
+ if (modelId === void 0) {
361
+ const available = modelsFromEffectivePolicy(entitlement);
362
+ setSelectedModelId((current) => available.models.some((item) => item.id === current) ? current : available.defaultModelId);
363
+ }
364
+ }, [client, modelId]);
365
+ React.useEffect(() => {
366
+ void retryBootstrap();
367
+ }, [retryBootstrap]);
368
+ React.useEffect(() => {
369
+ if (modelId !== void 0) setSelectedModelId(modelId);
370
+ }, [modelId]);
371
+ React.useEffect(() => {
372
+ hitlStatesRef.current = {};
373
+ setHitlStates({});
374
+ }, [client, controller.activeThreadId]);
375
+ React.useEffect(() => {
376
+ if (controlledSyncTargetRef.current?.value === null && controller.activeThreadId) {
377
+ controller.startNewThread();
378
+ }
379
+ }, [activeThreadId, controller.activeThreadId, controller.startNewThread]);
380
+ React.useEffect(() => {
381
+ const previous = previousControllerThreadRef.current;
382
+ const current = controller.activeThreadId;
383
+ previousControllerThreadRef.current = current;
384
+ const controlledTarget = controlledSyncTargetRef.current;
385
+ if (controlledTarget) {
386
+ if (current === controlledTarget.value) controlledSyncTargetRef.current = null;
387
+ return;
388
+ }
389
+ const pendingTransition = pendingThreadTransitionRef.current;
390
+ if (pendingTransition && current === pendingTransition.value) return;
391
+ if (previous === null && current !== null) reportActiveThread(current, "created");
392
+ }, [activeThreadId, controller.activeThreadId, reportActiveThread]);
393
+ const clearOperationError = React.useCallback(() => {
394
+ setLocalOperationError(null);
395
+ controller.clearOperationError();
396
+ }, [controller.clearOperationError]);
397
+ const submit = React.useCallback((input) => {
398
+ if (submitPromiseRef.current) return submitPromiseRef.current;
399
+ clearOperationError();
400
+ setIsSubmitting(true);
401
+ const request = (async () => {
402
+ try {
403
+ if (!bootstrap) throw new Error("The agent input contract is unavailable.");
404
+ const inputError = validateAgentChatSubmission(bootstrap.features.inputs, input.text, input.files || []);
405
+ if (inputError) throw new Error(inputError);
406
+ const uploaded = input.files?.length ? await attachments.uploadBatch({
407
+ uploads: input.files,
408
+ threadId: controller.activeThreadId || void 0
409
+ }) : [];
410
+ await controller.submit({
411
+ text: input.text,
412
+ attachmentIds: uploaded.flatMap((attachment) => attachment.id ? [attachment.id] : []),
413
+ attachments: uploaded,
414
+ requestedModelId: selectedModelId || void 0
415
+ });
416
+ } catch (error) {
417
+ setLocalOperationError(error);
418
+ const typed = error;
419
+ if (typed.kind === "quota" || typed.kind === "authorization") {
420
+ await refreshEffectivePolicy().catch(() => void 0);
421
+ }
422
+ throw error;
423
+ } finally {
424
+ setIsSubmitting(false);
425
+ }
426
+ })();
427
+ submitPromiseRef.current = request;
428
+ const clearRequest = () => {
429
+ if (submitPromiseRef.current === request) submitPromiseRef.current = null;
430
+ };
431
+ void request.then(clearRequest, clearRequest);
432
+ return request;
433
+ }, [attachments, bootstrap, clearOperationError, controller.activeThreadId, controller.submit, refreshEffectivePolicy, selectedModelId]);
434
+ const completeMcp = React.useCallback(async (part) => {
435
+ if (!mcpRedirectUri) throw new Error("mcpRedirectUri is required for MCP Connect.");
436
+ const serverId = String(part.presentation.server_id || "").trim();
437
+ if (!serverId) throw new Error("MCP authorization is missing its server identity.");
438
+ const runId = controller.activeRunId || controller.messages.find((message) => message.parts.includes(part))?.runId;
439
+ if (!runId) throw new Error("MCP authorization is missing its paused run.");
440
+ const popupNonce = browserChatCrypto().randomUUID();
441
+ const proof = await createMcpPkceProof();
442
+ const popup = window.open("about:blank", "agents24-mcp", "popup,width=560,height=720");
443
+ if (!popup) throw new Error("Allow popups to connect this MCP account.");
444
+ popup.document.title = "Connecting to MCP";
445
+ popup.document.body.textContent = "Opening secure connection\u2026";
446
+ try {
447
+ const started = await mcp.startAuthorization({
448
+ runId,
449
+ serverId,
450
+ interruptId: part.interruptId,
451
+ redirectUri: mcpRedirectUri,
452
+ popupNonce,
453
+ codeChallenge: proof.challenge
454
+ });
455
+ if (!started.callback_origin) throw new Error("MCP authorization did not return a callback origin.");
456
+ popup.location.replace(started.authorization_url);
457
+ const completion = await waitForMcpOauthComplete({
458
+ popup,
459
+ callbackOrigin: started.callback_origin,
460
+ popupNonce,
461
+ requireConnectionId: Boolean(completeMcpAuthorization),
462
+ serverId
463
+ });
464
+ if (completeMcpAuthorization) {
465
+ await completeMcpAuthorization({ completion, codeVerifier: proof.verifier, serverId });
466
+ } else {
467
+ const redeemed = await mcp.redeemCallback({ code: completion.code, codeVerifier: proof.verifier });
468
+ validateMcpCallbackResult(redeemed, serverId);
469
+ }
470
+ } finally {
471
+ popup.close();
472
+ }
473
+ }, [completeMcpAuthorization, controller.activeRunId, controller.messages, mcp, mcpRedirectUri]);
474
+ const onHitlAction = React.useCallback(async (part, response) => {
475
+ const action = response.action;
476
+ const key = hitlKey(part);
477
+ const current = hitlStatesRef.current[key];
478
+ if (current?.status === "connecting" || current?.status === "resuming") return;
479
+ clearOperationError();
480
+ const pending = {
481
+ action,
482
+ status: part.hitlKind === "mcp_auth" && action === "connect" ? "connecting" : "resuming"
483
+ };
484
+ hitlStatesRef.current = { ...hitlStatesRef.current, [key]: pending };
485
+ setHitlStates(hitlStatesRef.current);
486
+ try {
487
+ if (part.hitlKind === "mcp_auth" && action === "connect") await completeMcp(part);
488
+ await controller.resumeHitl(part, response);
489
+ setHitlStates((states) => {
490
+ const next = { ...states };
491
+ delete next[key];
492
+ hitlStatesRef.current = next;
493
+ return next;
494
+ });
495
+ } catch (error) {
496
+ setLocalOperationError(error);
497
+ const failedState = {
498
+ ...hitlStatesRef.current,
499
+ [key]: { action, error: agentChatRuntimeErrorMessage(error), status: "error" }
500
+ };
501
+ hitlStatesRef.current = failedState;
502
+ setHitlStates(failedState);
503
+ throw error;
504
+ }
505
+ }, [clearOperationError, completeMcp, controller.resumeHitl]);
506
+ const newThread = React.useCallback(() => {
507
+ clearOperationError();
508
+ pendingThreadTransitionRef.current = null;
509
+ reportActiveThread(null, "new");
510
+ controller.startNewThread();
511
+ }, [clearOperationError, controller.startNewThread, reportActiveThread]);
512
+ const openThread = React.useCallback(async (threadId) => {
513
+ clearOperationError();
514
+ pendingThreadTransitionRef.current = { reason: "opened", value: threadId };
515
+ try {
516
+ await controller.openThread(threadId);
517
+ reportActiveThread(threadId, "opened");
518
+ } catch (error) {
519
+ setLocalOperationError(error);
520
+ throw error;
521
+ } finally {
522
+ if (pendingThreadTransitionRef.current?.value === threadId) pendingThreadTransitionRef.current = null;
523
+ }
524
+ }, [clearOperationError, controller.openThread, reportActiveThread]);
525
+ const deleteThread = React.useCallback(async (threadId) => {
526
+ clearOperationError();
527
+ const wasActive = controller.activeThreadId === threadId;
528
+ if (wasActive) pendingThreadTransitionRef.current = { reason: "deleted", value: null };
529
+ try {
530
+ await controller.deleteThread(threadId);
531
+ if (wasActive) reportActiveThread(null, "deleted");
532
+ } catch (error) {
533
+ setLocalOperationError(error);
534
+ throw error;
535
+ } finally {
536
+ if (wasActive && pendingThreadTransitionRef.current?.reason === "deleted") {
537
+ pendingThreadTransitionRef.current = null;
538
+ }
539
+ }
540
+ }, [clearOperationError, controller.activeThreadId, controller.deleteThread, reportActiveThread]);
541
+ const changeModel = React.useCallback((nextModelId) => {
542
+ if (modelId === void 0) setSelectedModelId(nextModelId);
543
+ onModelChange?.(nextModelId);
544
+ }, [modelId, onModelChange]);
545
+ const modelOptions = effectivePolicy ? modelsFromEffectivePolicy(effectivePolicy).models : bootstrap ? modelsFromAgentChatBootstrap(bootstrap).models : [];
546
+ const fatalError = bootstrapError || controller.error;
547
+ const operationError = localOperationError || controller.operationError?.cause;
548
+ const state = isBootstrapping ? "loading" : fatalError ? agentChatRuntimeStateFromError(fatalError) : controller.runState === "streaming" ? "streaming" : controller.runState === "reconnecting" ? "reconnecting" : controller.runState === "paused" ? "paused" : controller.runState === "cancelling" ? "cancelling" : controller.runState === "cancelled" ? "cancelled" : "ready";
549
+ const connectionState = state === "offline" ? "offline" : state === "expired" ? "expired" : state === "reconnecting" ? "reconnecting" : isBootstrapping ? "connecting" : "connected";
550
+ return {
551
+ agent: agentOverride || { name: bootstrap?.deployment.name || "Agent" },
552
+ inputContract: bootstrap?.features.inputs || null,
553
+ allowFeedback: Boolean(bootstrap?.capabilities.includes("feedback.write")),
554
+ backgroundError: controller.backgroundError?.cause || null,
555
+ bootstrap,
556
+ capabilities: bootstrap?.capabilities || [],
557
+ changeModel,
558
+ clearOperationError,
559
+ connectionState,
560
+ contextWindow: controller.contextWindow,
561
+ contextCompression: controller.contextCompression,
562
+ controller,
563
+ deleteThread,
564
+ errorMessage: agentChatRuntimeErrorMessage(fatalError || operationError),
565
+ fatalError,
566
+ getHitlActionState: (part) => hitlStates[hitlKey(part)],
567
+ isBootstrapping,
568
+ isSubmitting: isSubmitting || controller.isSubmitting,
569
+ isUploading: attachments.isUploading,
570
+ modelId: selectedModelId,
571
+ models: modelOptions,
572
+ newThread,
573
+ onHitlAction,
574
+ openThread,
575
+ operationError,
576
+ retryBootstrap,
577
+ state,
578
+ submit
579
+ };
580
+ }
581
+ // Annotate the CommonJS export names for ESM import in node:
582
+ 0 && (module.exports = {
583
+ SUPPORTED_INPUT_MIME_TYPES,
584
+ agentChatRuntimeErrorMessage,
585
+ agentChatRuntimeStateFromError,
586
+ canonicalInputMimeType,
587
+ inputMimeTypes,
588
+ modelsFromAgentChatBootstrap,
589
+ useAgentChatRuntime,
590
+ validateAgentChatSubmission,
591
+ validateWorkflowInputSubmission,
592
+ workflowInputRule
593
+ });
594
+ //# sourceMappingURL=index.cjs.map