@agent-native/toolkit 0.206.0 → 0.206.1-nightly-20261009213000
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/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +3 -3
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +1 -0
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +1 -0
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +43 -0
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +219 -75
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts +18 -0
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.js +12 -4
- package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts +6 -1
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +6 -3
- package/dist/app/chat/chat/run-recovery.js.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +8 -2
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +8 -2
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +8 -2
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +8 -2
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +8 -2
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +8 -2
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +8 -2
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +8 -2
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +8 -2
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +8 -2
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +8 -2
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +8 -2
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/sharing/ShareButton.d.ts +9 -0
- package/dist/app/sharing/ShareButton.d.ts.map +1 -1
- package/dist/app/sharing/ShareButton.js +23 -11
- package/dist/app/sharing/ShareButton.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentkit/react/components.tsx +6 -3
- package/src/app/agentkit/react/context.tsx +2 -0
- package/src/app/agentkit/react/styles.css +43 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +425 -15
- package/src/app/chat/AgentKitAssistantChat.tsx +306 -95
- package/src/app/chat/agentkit-chat/guided-questions.flow.spec.tsx +89 -5
- package/src/app/chat/agentkit-chat/guided-questions.tsx +17 -6
- package/src/app/chat/chat/run-recovery.spec.tsx +48 -0
- package/src/app/chat/chat/run-recovery.tsx +23 -1
- package/src/app/i18n/catalogs/ar-SA.ts +14 -2
- package/src/app/i18n/catalogs/de-DE.ts +12 -2
- package/src/app/i18n/catalogs/en-US.ts +13 -2
- package/src/app/i18n/catalogs/es-ES.ts +12 -2
- package/src/app/i18n/catalogs/fr-FR.ts +12 -2
- package/src/app/i18n/catalogs/hi-IN.ts +12 -2
- package/src/app/i18n/catalogs/ja-JP.ts +12 -2
- package/src/app/i18n/catalogs/ko-KR.ts +12 -2
- package/src/app/i18n/catalogs/pt-BR.ts +12 -2
- package/src/app/i18n/catalogs/zh-CN.ts +8 -2
- package/src/app/i18n/catalogs/zh-TW.ts +8 -2
- package/src/app/settings/model/ProviderDialog.spec.tsx +1 -0
- package/src/app/sharing/ShareButton.spec.tsx +148 -0
- package/src/app/sharing/ShareButton.tsx +44 -15
- package/src/app/sharing/useShareButtonController.spec.tsx +25 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { hasActiveAgentRuns, selectAgentSuggestions, isCurrentAgentSuggestion, } from "@agent-native/agentkit";
|
|
3
|
-
import { MAX_AGENT_REQUEST_ATTACHMENT_DATA_CHARS } from "@agent-native/agentkit/protocol";
|
|
2
|
+
import { AgentKitUploadError, hasActiveAgentRuns, selectAgentSuggestions, isCurrentAgentSuggestion, } from "@agent-native/agentkit";
|
|
3
|
+
import { MAX_AGENT_REQUEST_ATTACHMENT_DATA_CHARS, isInlineDataUrl, persistableFilePart, } from "@agent-native/agentkit/protocol";
|
|
4
4
|
import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "@agent-native/core/agent/chatgpt-subscription-contract";
|
|
5
5
|
import { isCreditsLimitErrorCode } from "@agent-native/core/agent/engine/error-detail";
|
|
6
6
|
import { appendAgentChatContextToMessage, filterAgentChatContextItems, formatAgentChatContextItemsForPrompt, getAgentChatContextState, normalizeAgentChatContextItem, publishAgentChatContextItems, reportAgentChatSubmitResult, refreshAgentChatContext, subscribeAgentChatContext, } from "@agent-native/core/client/agent-chat";
|
|
@@ -26,8 +26,9 @@ import { writeClipboardText } from "@agent-native/toolkit/clipboard";
|
|
|
26
26
|
import { AgentSuggestionBar, agentSuggestionPrompt, snapshotComposerContextItems, AGENT_PROMPT_MAX_INLINE_IMAGE_BYTES, readAgentPromptAttachment, } from "@agent-native/toolkit/composer";
|
|
27
27
|
import { appendRealtimeVoiceTranscriptToRepository, realtimeVoiceTranscriptRegistry, } from "@agent-native/toolkit/composer/realtime-voice-transcript";
|
|
28
28
|
import { IconButton } from "@agent-native/toolkit/design-system";
|
|
29
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@agent-native/toolkit/ui/dropdown-menu";
|
|
29
30
|
import { cn } from "@agent-native/toolkit/utils";
|
|
30
|
-
import { IconAlertTriangle, IconCircleCheck, IconCopy, IconMessage, IconPlayerStopFilled, IconQuote, IconRefresh, IconX, } from "@tabler/icons-react";
|
|
31
|
+
import { IconAlertTriangle, IconCircleCheck, IconCopy, IconDotsVertical, IconMessage, IconPlayerStopFilled, IconQuote, IconRefresh, IconX, } from "@tabler/icons-react";
|
|
31
32
|
import React, { createContext, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
|
|
32
33
|
import { AgentKitChat, AgentKitComposer, AgentApprovalPrompt, AgentMessageView, useAgentKit, useAgentKitControl, useAgentThread, } from "../agentkit/react/index.js";
|
|
33
34
|
import { AgentKitDevCheckpointProvider, AgentKitDevCheckpointRestore, useOptionalAgentKitHistory, } from "./agentkit-chat/history.js";
|
|
@@ -507,6 +508,7 @@ export const AgentKitAssistantChat = forwardRef(function AgentKitAssistantChat(p
|
|
|
507
508
|
name: "{{name}}",
|
|
508
509
|
}),
|
|
509
510
|
pastedText: t("agentChat.pastedText.title"),
|
|
511
|
+
attachmentNotSaved: t("agentChat.composer.attachmentNotSaved"),
|
|
510
512
|
imagePreview: t("agentChat.composer.imagePreview"),
|
|
511
513
|
closePreview: t("agentChat.composer.closePreview"),
|
|
512
514
|
dropFilesToAttach: t("agentChat.composer.dropToAttach"),
|
|
@@ -546,43 +548,28 @@ export const AgentKitAssistantChat = forwardRef(function AgentKitAssistantChat(p
|
|
|
546
548
|
form.set(key, value);
|
|
547
549
|
}
|
|
548
550
|
form.set("file", file.body, file.name);
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const messageKey = response.status === 415
|
|
557
|
-
? "agentChat.composer.unsupportedFileType"
|
|
558
|
-
: response.status === 413
|
|
559
|
-
? "agentChat.composer.fileTooLarge"
|
|
560
|
-
: response.status === 401
|
|
561
|
-
? "agentChat.composer.sessionExpired"
|
|
562
|
-
: undefined;
|
|
563
|
-
const message = messageKey
|
|
564
|
-
? translatorRef.current(messageKey)
|
|
565
|
-
: translatorRef.current("agentChat.composer.uploadFailed");
|
|
566
|
-
const error = new Error(message);
|
|
567
|
-
Object.assign(error, {
|
|
568
|
-
code: response.status === 413
|
|
569
|
-
? "upload_too_large"
|
|
570
|
-
: response.status === 401
|
|
571
|
-
? "upload_session_expired"
|
|
572
|
-
: response.status === 415
|
|
573
|
-
? "upload_unsupported_type"
|
|
574
|
-
: `upload_http_${response.status}`,
|
|
575
|
-
status: response.status,
|
|
576
|
-
retryable: response.status === 408 ||
|
|
577
|
-
response.status === 429 ||
|
|
578
|
-
response.status >= 500,
|
|
551
|
+
let response;
|
|
552
|
+
try {
|
|
553
|
+
response = await fetch(target.url, {
|
|
554
|
+
method: target.method,
|
|
555
|
+
headers: target.headers,
|
|
556
|
+
body: form,
|
|
557
|
+
signal: context?.signal,
|
|
579
558
|
});
|
|
580
|
-
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
if (context?.signal?.aborted)
|
|
562
|
+
throw error;
|
|
563
|
+
throw Object.assign(new Error(translatorRef.current("agentChat.composer.uploadOffline")), { code: "upload_network_error", retryable: true, cause: error });
|
|
564
|
+
}
|
|
565
|
+
// The server's error body is never shown: its text is untranslated.
|
|
566
|
+
if (!response.ok) {
|
|
567
|
+
throw uploadHttpError(response.status, translatorRef.current);
|
|
581
568
|
}
|
|
582
569
|
const result = await response.json();
|
|
583
570
|
const uploaded = asRecord(result);
|
|
584
571
|
if (typeof uploaded?.url !== "string" || !uploaded.url) {
|
|
585
|
-
throw new
|
|
572
|
+
throw Object.assign(new Error(translatorRef.current("agentChat.composer.uploadFailed")), { code: "upload_invalid_response", retryable: false });
|
|
586
573
|
}
|
|
587
574
|
uploadedFilesRef.current.set(target.uploadId, {
|
|
588
575
|
type: "file",
|
|
@@ -1422,6 +1409,7 @@ const AgentKitAssistantChatBody = forwardRef(function AgentKitAssistantChatBody(
|
|
|
1422
1409
|
: await uploadAgentChatAttachments(control, attachments, files, {
|
|
1423
1410
|
storageConfigured: fileStorageConfigured,
|
|
1424
1411
|
storageUnavailableMessage: t("onboarding.fileStorage.title"),
|
|
1412
|
+
fileTooLargeMessage: uploadTooLargeMessage(t),
|
|
1425
1413
|
});
|
|
1426
1414
|
const fileParts = uploadedAttachments.fileParts;
|
|
1427
1415
|
let requestAttachments = options.deferredRequestAttachments ??
|
|
@@ -1458,7 +1446,9 @@ const AgentKitAssistantChatBody = forwardRef(function AgentKitAssistantChatBody(
|
|
|
1458
1446
|
if (inlineImageDataChars > MAX_AGENT_REQUEST_ATTACHMENT_DATA_CHARS) {
|
|
1459
1447
|
throw new Error(t("agentChat.composer.requestTooLarge"));
|
|
1460
1448
|
}
|
|
1461
|
-
|
|
1449
|
+
// Readiness was gated before the upload; a provider status refresh during
|
|
1450
|
+
// it must not discard the upload, only a change of thread or scope.
|
|
1451
|
+
composerOptions.validateSubmissionScope?.();
|
|
1462
1452
|
const selectionChangedDuringSubmission = selectionRevision !== selectionRevisionRef.current ||
|
|
1463
1453
|
(options.pendingSelectionCapturedAt !== undefined &&
|
|
1464
1454
|
options.pendingSelectionCapturedAt !==
|
|
@@ -1626,8 +1616,12 @@ const AgentKitAssistantChatBody = forwardRef(function AgentKitAssistantChatBody(
|
|
|
1626
1616
|
: await uploadAgentChatAttachments(control, attachments, files, {
|
|
1627
1617
|
storageConfigured: fileStorageConfigured,
|
|
1628
1618
|
storageUnavailableMessage: t("onboarding.fileStorage.title"),
|
|
1619
|
+
fileTooLargeMessage: uploadTooLargeMessage(t),
|
|
1629
1620
|
});
|
|
1630
|
-
const fileParts = uploadedAttachments.fileParts
|
|
1621
|
+
const fileParts = await durableFileParts(control, uploadedAttachments.fileParts, {
|
|
1622
|
+
storageConfigured: fileStorageConfigured,
|
|
1623
|
+
storageUnavailableMessage: t("onboarding.fileStorage.title"),
|
|
1624
|
+
});
|
|
1631
1625
|
if (!fileStorageConfigured &&
|
|
1632
1626
|
uploadedAttachments.requestAttachments.some((attachment) => attachment.data)) {
|
|
1633
1627
|
throw new Error(t("onboarding.fileStorage.title"));
|
|
@@ -1668,6 +1662,7 @@ const AgentKitAssistantChatBody = forwardRef(function AgentKitAssistantChatBody(
|
|
|
1668
1662
|
delete deferredComposerOptions.attachments;
|
|
1669
1663
|
delete deferredComposerOptions.onLocalSubmit;
|
|
1670
1664
|
delete deferredComposerOptions.validateSubmission;
|
|
1665
|
+
delete deferredComposerOptions.validateSubmissionScope;
|
|
1671
1666
|
deferredComposerOptions.model ??= props.selectedModel;
|
|
1672
1667
|
deferredComposerOptions.engine ??= props.selectedEngine;
|
|
1673
1668
|
deferredComposerOptions.effort ??= props.selectedEffort;
|
|
@@ -2347,13 +2342,10 @@ function AgentKitTranscript({ children, threadId }) {
|
|
|
2347
2342
|
}
|
|
2348
2343
|
return (_jsxs(_Fragment, { children: [thread.messages.length > 0 || surface.hasRenderedMessages ? (_jsx(AgentKitHistoryBeginningRevert, {})) : null, surface.threadRestore.status === "error" ? (_jsxs("div", { className: "mx-4 mt-3 flex items-center justify-between gap-3 rounded-md border border-border px-3 py-2 text-xs text-muted-foreground", role: "alert", children: [_jsx("span", { children: surface.threadRestore.notFound
|
|
2349
2344
|
? t("agentChat.message.threadNotFound")
|
|
2350
|
-
: t("agentChat.message.restoreRequestFailed") }), _jsx("button", { type: "button", onClick: surface.retryThreadRestore, className: "shrink-0 rounded px-2 py-1 hover:bg-accent hover:text-foreground", children: t("agentChat.common.retry") })] })) : null, renderThreadSlot(surface.props.threadContentSlot, threadId, surface.props.tabId), children, activeRunId ? (_jsxs("div", { className: "agentkit-activities-static", "data-agentkit-active-run-id-copy": "true", children: [
|
|
2351
|
-
?
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
: t("agentChat.message.copyRequestId"), title: t("agentChat.message.copyRequestId"), icon: activeRunRequestIdCopyStatus === "copied" ? (_jsx(IconCircleCheck, { "aria-hidden": "true" })) : activeRunRequestIdCopyStatus === "failed" ? (_jsx(IconAlertTriangle, { "aria-hidden": "true" })) : (_jsx(IconCopy, { "aria-hidden": "true" })), size: "compact", onPress: () => void copyActiveRunRequestId() }), activeRunRequestIdCopyStatus ? (_jsx("span", { className: "sr-only", role: activeRunRequestIdCopyStatus === "failed" ? "alert" : "status", children: activeRunRequestIdCopyStatus === "copied"
|
|
2355
|
-
? t("agentChat.common.copied")
|
|
2356
|
-
: t("agentChat.recovery.copyFailed") })) : null] })) : null, handoffMessages.map((message) => (_jsx(AgentMessageView, { value: message, threadId: threadId }, message.id))), pendingVoiceMessages.map((message) => (_jsx(AgentMessageView, { value: message, threadId: threadId }, message.id))), showThinking ? (_jsx("div", { className: "agentkit-activities agentkit-activities-summary-content", role: "status", "aria-live": "polite", children: _jsx("span", { className: "agentkit-activities-current", "data-running": "true", "data-agentkit-current-activity": "true", children: _jsx("span", { className: "agentkit-activities-current-label agent-running-shimmer", children: t("agentChat.status.thinking") }) }) })) : null, surface.props.showGuidedQuestions !== false &&
|
|
2345
|
+
: t("agentChat.message.restoreRequestFailed") }), _jsx("button", { type: "button", onClick: surface.retryThreadRestore, className: "shrink-0 rounded px-2 py-1 hover:bg-accent hover:text-foreground", children: t("agentChat.common.retry") })] })) : null, renderThreadSlot(surface.props.threadContentSlot, threadId, surface.props.tabId), children, activeRunId ? (_jsxs("div", { className: "agentkit-activities-static agentkit-active-run-actions", "data-agentkit-active-run-id-copy": "true", children: [activeRunRequestIdCopyStatus ? (_jsxs("span", { className: cn("agentkit-active-run-actions-feedback", activeRunRequestIdCopyStatus === "failed" &&
|
|
2346
|
+
"agentkit-active-run-actions-feedback-error"), role: activeRunRequestIdCopyStatus === "failed" ? "alert" : "status", children: [activeRunRequestIdCopyStatus === "copied" ? (_jsx(IconCircleCheck, { size: 14, "aria-hidden": "true" })) : (_jsx(IconAlertTriangle, { size: 14, "aria-hidden": "true" })), activeRunRequestIdCopyStatus === "copied"
|
|
2347
|
+
? t("agentChat.common.copied")
|
|
2348
|
+
: t("agentChat.recovery.copyFailed")] })) : null, _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(IconButton, { label: t("agentChat.message.actions"), title: t("agentChat.message.actions"), icon: _jsx(IconDotsVertical, { "aria-hidden": "true" }), size: "compact", className: "agentkit-active-run-actions-trigger" }) }), _jsx(DropdownMenuContent, { align: "end", sideOffset: 6, children: _jsxs(DropdownMenuItem, { onSelect: () => void copyActiveRunRequestId(), children: [activeRunRequestIdCopyStatus === "copied" ? (_jsx(IconCircleCheck, { size: 14, "aria-hidden": "true" })) : activeRunRequestIdCopyStatus === "failed" ? (_jsx(IconAlertTriangle, { size: 14, "aria-hidden": "true" })) : (_jsx(IconCopy, { size: 14, "aria-hidden": "true" })), t("agentChat.message.copyRequestId")] }) })] })] })) : null, handoffMessages.map((message) => (_jsx(AgentMessageView, { value: message, threadId: threadId }, message.id))), pendingVoiceMessages.map((message) => (_jsx(AgentMessageView, { value: message, threadId: threadId }, message.id))), showThinking ? (_jsx("div", { className: "agentkit-activities agentkit-activities-summary-content", role: "status", "aria-live": "polite", children: _jsx("span", { className: "agentkit-activities-current", "data-running": "true", "data-agentkit-current-activity": "true", children: _jsx("span", { className: "agentkit-activities-current-label agent-running-shimmer", children: t("agentChat.status.thinking") }) }) })) : null, surface.props.showGuidedQuestions !== false &&
|
|
2357
2349
|
guided.questions?.length ? (_jsx("div", { className: "px-3 pb-3", children: _jsx(GuidedQuestionFlow, { questions: guided.questions, onSubmit: guided.handleSubmit, onSkip: guided.handleSkip, isSubmitting: guided.isSubmitting, isSubmissionBlocked: !surface.canChat, providerStatus: surface.providerStatus, modelListUnavailable: surface.modelListUnavailable, showProviderStatusGate: !composerShowsSetupCard(surface), onRetryProviderStatus: surface.modelListUnavailable
|
|
2358
2350
|
? surface.retryModelList
|
|
2359
2351
|
: surface.retryProviderStatus, ...(guided.title ? { title: guided.title } : {}), ...(guided.description ? { description: guided.description } : {}), ...(guided.skipLabel ? { skipLabel: guided.skipLabel } : {}), ...(guided.submitLabel ? { submitLabel: guided.submitLabel } : {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) })) : null, suggestionBar, renderThreadSlot(surface.props.threadFooterSlot, threadId, surface.props.tabId)] }));
|
|
@@ -2452,20 +2444,28 @@ function AgentKitComposerSurface({ threadId, props, composerContext, canChat, se
|
|
|
2452
2444
|
: {}),
|
|
2453
2445
|
};
|
|
2454
2446
|
setComposerError(null);
|
|
2447
|
+
const assertSubmissionScope = () => {
|
|
2448
|
+
const latest = controller.getThread(threadId);
|
|
2449
|
+
if (!mounted.current ||
|
|
2450
|
+
currentSubmissionScope.current !== submissionScope ||
|
|
2451
|
+
(options.suggestion &&
|
|
2452
|
+
(latest.messages.length || Object.keys(latest.runs).length) &&
|
|
2453
|
+
!isCurrentAgentSuggestion(latest, options.suggestion))) {
|
|
2454
|
+
throw Object.assign(new Error(t("agentChat.composer.submissionScopeChanged")), { code: "submission_scope_changed" });
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2455
2457
|
const assertCurrentSubmission = () => {
|
|
2456
|
-
if (!submissionAllowed.current
|
|
2457
|
-
|
|
2458
|
-
currentSubmissionScope.current !== submissionScope) {
|
|
2459
|
-
throw new Error(t("agentChat.error.failed"));
|
|
2458
|
+
if (!submissionAllowed.current) {
|
|
2459
|
+
throw Object.assign(new Error(t("agentChat.composer.submissionNotReady")), { code: "submission_not_ready" });
|
|
2460
2460
|
}
|
|
2461
|
-
|
|
2462
|
-
if (options.suggestion &&
|
|
2463
|
-
(latest.messages.length || Object.keys(latest.runs).length) &&
|
|
2464
|
-
!isCurrentAgentSuggestion(latest, options.suggestion))
|
|
2465
|
-
throw new Error(t("agentChat.error.failed"));
|
|
2461
|
+
assertSubmissionScope();
|
|
2466
2462
|
};
|
|
2467
2463
|
assertCurrentSubmission();
|
|
2468
|
-
options = {
|
|
2464
|
+
options = {
|
|
2465
|
+
...options,
|
|
2466
|
+
validateSubmission: assertCurrentSubmission,
|
|
2467
|
+
validateSubmissionScope: assertSubmissionScope,
|
|
2468
|
+
};
|
|
2469
2469
|
const captured = snapshotComposerContextItems(options.contextItems ?? visibleContextItems);
|
|
2470
2470
|
let prepared;
|
|
2471
2471
|
await onSubmit(message, files, references, options, async () => {
|
|
@@ -2826,6 +2826,15 @@ function AgentKitRunFailure({ error, runId, threadId, }) {
|
|
|
2826
2826
|
const retryRequest = retryRequestFrom(failedPrompt ?? lastUserMessage);
|
|
2827
2827
|
const alreadyRetried = wasRetried(thread.messages, runId, failedPrompt?.id, lastUserMessage?.id);
|
|
2828
2828
|
const retryFailedTurn = () => sendRetryRequest(surface, retryRequest, runId);
|
|
2829
|
+
const retryWithoutAttachments = retryRequest.fileParts.length || retryRequest.requestAttachments.length
|
|
2830
|
+
? () => void sendRetryRequest(surface, {
|
|
2831
|
+
...retryRequest,
|
|
2832
|
+
text: retryRequest.textWithContext,
|
|
2833
|
+
fileParts: [],
|
|
2834
|
+
requestAttachments: [],
|
|
2835
|
+
hasUnavailableAttachment: false,
|
|
2836
|
+
}, runId)
|
|
2837
|
+
: undefined;
|
|
2829
2838
|
const resumeAfterSetup = useResumeAfterAiSetup(`${threadId}:${(failedPrompt ?? lastUserMessage)?.id ?? runId}`, setupFailure &&
|
|
2830
2839
|
!superseded &&
|
|
2831
2840
|
!alreadyRetried &&
|
|
@@ -2874,7 +2883,7 @@ function AgentKitRunFailure({ error, runId, threadId, }) {
|
|
|
2874
2883
|
runId,
|
|
2875
2884
|
recoverable: error.retryable,
|
|
2876
2885
|
};
|
|
2877
|
-
return (_jsx(RunErrorRecoveryCard, { info: info, onContinue: continueStoppedRun, continueError: continueFailed ? t("agentChat.recovery.continueUnavailable") : null, onRetry: () => void retryFailedTurn(), retryHasUnavailableAttachment: retryRequest.hasUnavailableAttachment, onFork: async () => {
|
|
2886
|
+
return (_jsx(RunErrorRecoveryCard, { info: info, onContinue: continueStoppedRun, continueError: continueFailed ? t("agentChat.recovery.continueUnavailable") : null, onRetry: () => void retryFailedTurn(), onRetryWithoutAttachments: retryWithoutAttachments, retryHasUnavailableAttachment: retryRequest.hasUnavailableAttachment, onFork: async () => {
|
|
2878
2887
|
if (!lastUserMessage)
|
|
2879
2888
|
return surface.props.onForkChat?.();
|
|
2880
2889
|
const fork = await control.fork(lastUserMessage.id);
|
|
@@ -2932,6 +2941,7 @@ function retryRequestFrom(message) {
|
|
|
2932
2941
|
const requestMode = mode === "plan" || mode === "act" ? mode : undefined;
|
|
2933
2942
|
return {
|
|
2934
2943
|
text: message ? agentMessageText(message) : "",
|
|
2944
|
+
textWithContext: message ? rawAgentMessageText(message) : "",
|
|
2935
2945
|
fileParts,
|
|
2936
2946
|
requestAttachments: retryRequestAttachmentsFrom(custom?.agentNativeRetryRequestAttachments),
|
|
2937
2947
|
hasUnavailableAttachment: custom?.agentNativeRetryAttachmentsUnavailable === true ||
|
|
@@ -3272,6 +3282,47 @@ function setupSubmissionResult(error) {
|
|
|
3272
3282
|
: "submission-unavailable",
|
|
3273
3283
|
};
|
|
3274
3284
|
}
|
|
3285
|
+
/** The `/_agent-native/file-upload` ceiling (core's `DEFAULT_UPLOAD_MAX_FILE_BYTES`). */
|
|
3286
|
+
const AGENT_CHAT_UPLOAD_MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
3287
|
+
function uploadTooLargeMessage(t) {
|
|
3288
|
+
return t("agentChat.composer.fileTooLarge", {
|
|
3289
|
+
size: AGENT_CHAT_UPLOAD_MAX_FILE_BYTES / 1024 / 1024,
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
function uploadHttpError(status, t) {
|
|
3293
|
+
const [code, message, retryable] = status === 413
|
|
3294
|
+
? ["upload_too_large", uploadTooLargeMessage(t), false]
|
|
3295
|
+
: status === 401 || status === 403
|
|
3296
|
+
? [
|
|
3297
|
+
"upload_session_expired",
|
|
3298
|
+
t("agentChat.composer.sessionExpired"),
|
|
3299
|
+
false,
|
|
3300
|
+
]
|
|
3301
|
+
: status === 415
|
|
3302
|
+
? [
|
|
3303
|
+
"upload_unsupported_type",
|
|
3304
|
+
t("agentChat.composer.unsupportedFileType"),
|
|
3305
|
+
false,
|
|
3306
|
+
]
|
|
3307
|
+
: status === 408 || status === 429 || status >= 500
|
|
3308
|
+
? [
|
|
3309
|
+
"upload_unavailable",
|
|
3310
|
+
t("agentChat.composer.uploadUnavailable"),
|
|
3311
|
+
true,
|
|
3312
|
+
]
|
|
3313
|
+
: [
|
|
3314
|
+
`upload_http_${status}`,
|
|
3315
|
+
t("agentChat.composer.uploadFailed"),
|
|
3316
|
+
false,
|
|
3317
|
+
];
|
|
3318
|
+
return Object.assign(new Error(message), { code, status, retryable });
|
|
3319
|
+
}
|
|
3320
|
+
/**
|
|
3321
|
+
* Durable uploads keyed by the attachment or file they came from, so a send
|
|
3322
|
+
* that failed on one file reuses its siblings' uploads instead of orphaning
|
|
3323
|
+
* them and uploading again.
|
|
3324
|
+
*/
|
|
3325
|
+
const uploadedAgentChatSources = new WeakMap();
|
|
3275
3326
|
const OPTIMIZABLE_RASTER_IMAGE_TYPES = new Set([
|
|
3276
3327
|
"image/gif",
|
|
3277
3328
|
"image/jpeg",
|
|
@@ -3307,6 +3358,7 @@ function requiresDurableAttachmentUpload(attachments, files) {
|
|
|
3307
3358
|
}
|
|
3308
3359
|
async function uploadAgentChatAttachments(control, attachments, files, options) {
|
|
3309
3360
|
const entries = [];
|
|
3361
|
+
const sourceByFile = new Map();
|
|
3310
3362
|
for (const attachment of attachments) {
|
|
3311
3363
|
if (attachment.displayOnly)
|
|
3312
3364
|
continue;
|
|
@@ -3321,12 +3373,15 @@ async function uploadAgentChatAttachments(control, attachments, files, options)
|
|
|
3321
3373
|
}
|
|
3322
3374
|
else {
|
|
3323
3375
|
const file = await attachmentToFile(attachment);
|
|
3324
|
-
if (file)
|
|
3376
|
+
if (file) {
|
|
3325
3377
|
entries.push(file);
|
|
3378
|
+
sourceByFile.set(file, attachment);
|
|
3379
|
+
}
|
|
3326
3380
|
}
|
|
3327
3381
|
}
|
|
3328
3382
|
entries.push(...files);
|
|
3329
3383
|
const pending = entries.filter((entry) => entry instanceof File);
|
|
3384
|
+
const sourceOf = (file) => sourceByFile.get(file) ?? file;
|
|
3330
3385
|
const optimizedImages = new Map();
|
|
3331
3386
|
for (const file of pending) {
|
|
3332
3387
|
if (!file.type.startsWith("image/") ||
|
|
@@ -3349,24 +3404,59 @@ async function uploadAgentChatAttachments(control, attachments, files, options)
|
|
|
3349
3404
|
if (!options.storageConfigured && filesToUploadNormally.length > 0) {
|
|
3350
3405
|
throw new Error(options.storageUnavailableMessage);
|
|
3351
3406
|
}
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3407
|
+
const oversized = filesToUploadNormally.flatMap((file, index) => file.size > AGENT_CHAT_UPLOAD_MAX_FILE_BYTES
|
|
3408
|
+
? [
|
|
3409
|
+
{
|
|
3410
|
+
index,
|
|
3411
|
+
name: file.name,
|
|
3412
|
+
error: Object.assign(new Error(options.fileTooLargeMessage), {
|
|
3413
|
+
code: "upload_too_large",
|
|
3414
|
+
retryable: false,
|
|
3415
|
+
}),
|
|
3416
|
+
},
|
|
3417
|
+
]
|
|
3418
|
+
: []);
|
|
3419
|
+
if (oversized.length)
|
|
3420
|
+
throw new AgentKitUploadError(oversized, []);
|
|
3421
|
+
const filesToUpload = filesToUploadNormally.filter((file) => {
|
|
3422
|
+
const reused = uploadedAgentChatSources.get(sourceOf(file));
|
|
3423
|
+
if (reused)
|
|
3424
|
+
uploadedByFile.set(file, reused);
|
|
3425
|
+
return !reused;
|
|
3426
|
+
});
|
|
3427
|
+
if (filesToUpload.length) {
|
|
3428
|
+
let uploaded;
|
|
3429
|
+
try {
|
|
3430
|
+
uploaded = await control.uploadFiles(filesToUpload.map((file) => ({
|
|
3431
|
+
name: file.name,
|
|
3432
|
+
mediaType: file.type || "application/octet-stream",
|
|
3433
|
+
size: file.size,
|
|
3434
|
+
body: file,
|
|
3435
|
+
})));
|
|
3436
|
+
}
|
|
3437
|
+
catch (error) {
|
|
3438
|
+
if (error instanceof AgentKitUploadError) {
|
|
3439
|
+
for (const { index, part } of error.uploaded) {
|
|
3440
|
+
uploadedAgentChatSources.set(sourceOf(filesToUpload[index]), part);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
throw error;
|
|
3444
|
+
}
|
|
3445
|
+
filesToUpload.forEach((file, index) => {
|
|
3360
3446
|
const part = uploaded[index];
|
|
3361
3447
|
if (!part) {
|
|
3362
3448
|
throw new TypeError("File upload did not return every uploaded file.");
|
|
3363
3449
|
}
|
|
3364
3450
|
uploadedByFile.set(file, part);
|
|
3451
|
+
uploadedAgentChatSources.set(sourceOf(file), part);
|
|
3365
3452
|
});
|
|
3366
3453
|
}
|
|
3367
3454
|
for (const file of pending) {
|
|
3368
|
-
if (!optimizedImages.has(file) ||
|
|
3455
|
+
if (!optimizedImages.has(file) ||
|
|
3456
|
+
!options.storageConfigured ||
|
|
3457
|
+
file.size > AGENT_CHAT_UPLOAD_MAX_FILE_BYTES) {
|
|
3369
3458
|
continue;
|
|
3459
|
+
}
|
|
3370
3460
|
try {
|
|
3371
3461
|
const [part] = await control.uploadFiles([
|
|
3372
3462
|
{
|
|
@@ -3439,6 +3529,34 @@ async function uploadRequestAttachments(control, attachments) {
|
|
|
3439
3529
|
return { ...reference, url: part.url };
|
|
3440
3530
|
});
|
|
3441
3531
|
}
|
|
3532
|
+
/** Stored submissions carry durable references, so inline file bytes upload first. */
|
|
3533
|
+
async function durableFileParts(control, parts, options) {
|
|
3534
|
+
const inline = parts.filter((part) => isInlineDataUrl(part.url));
|
|
3535
|
+
if (inline.length === 0)
|
|
3536
|
+
return [...parts];
|
|
3537
|
+
if (!options.storageConfigured) {
|
|
3538
|
+
throw new Error(options.storageUnavailableMessage);
|
|
3539
|
+
}
|
|
3540
|
+
const uploaded = await control.uploadFiles(await Promise.all(inline.map(async (part) => {
|
|
3541
|
+
const body = await (await fetch(part.url)).blob();
|
|
3542
|
+
return {
|
|
3543
|
+
name: part.name,
|
|
3544
|
+
mediaType: part.mediaType ?? (body.type || "application/octet-stream"),
|
|
3545
|
+
size: body.size,
|
|
3546
|
+
body,
|
|
3547
|
+
};
|
|
3548
|
+
})));
|
|
3549
|
+
let uploadIndex = 0;
|
|
3550
|
+
return parts.map((part) => {
|
|
3551
|
+
if (!isInlineDataUrl(part.url))
|
|
3552
|
+
return part;
|
|
3553
|
+
const durable = uploaded[uploadIndex++];
|
|
3554
|
+
if (!durable?.url && !durable?.fileId) {
|
|
3555
|
+
throw new TypeError(`${part.name} did not receive a durable reference.`);
|
|
3556
|
+
}
|
|
3557
|
+
return durable;
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3442
3560
|
function rawAgentMessageTextFromParts(parts) {
|
|
3443
3561
|
return parts
|
|
3444
3562
|
.filter((part) => part.type === "text")
|
|
@@ -3454,21 +3572,47 @@ function rawAgentMessageText(message) {
|
|
|
3454
3572
|
function agentMessageText(message) {
|
|
3455
3573
|
return agentMessageTextFromParts(message.parts);
|
|
3456
3574
|
}
|
|
3575
|
+
/**
|
|
3576
|
+
* The live message keeps inline image bytes for display; what is stored keeps
|
|
3577
|
+
* only a durable reference, or a named `omitted` marker when there is none.
|
|
3578
|
+
*/
|
|
3457
3579
|
function persistedAgentMessage(message) {
|
|
3458
3580
|
return {
|
|
3459
3581
|
...message,
|
|
3460
3582
|
parts: message.parts.map((part) => {
|
|
3461
3583
|
const record = asRecord(part);
|
|
3462
|
-
if (
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
const { data: _data, ...withoutInlineBody } = record;
|
|
3466
|
-
return withoutInlineBody;
|
|
3584
|
+
if (!record ||
|
|
3585
|
+
(!isInlineDataUrl(record.data) && !isInlineDataUrl(record.url))) {
|
|
3586
|
+
return part;
|
|
3467
3587
|
}
|
|
3468
|
-
|
|
3588
|
+
const { data: _data, url, ...rest } = record;
|
|
3589
|
+
const durableUrl = isInlineDataUrl(url) ? undefined : url;
|
|
3590
|
+
return {
|
|
3591
|
+
...rest,
|
|
3592
|
+
...(durableUrl ? { url: durableUrl } : {}),
|
|
3593
|
+
...(durableUrl || rest.fileId ? {} : { omitted: "inline-bytes" }),
|
|
3594
|
+
};
|
|
3469
3595
|
}),
|
|
3470
3596
|
};
|
|
3471
3597
|
}
|
|
3598
|
+
function persistedAgentEvent(event) {
|
|
3599
|
+
return "message" in event && event.message
|
|
3600
|
+
? { ...event, message: persistedAgentMessage(event.message) }
|
|
3601
|
+
: event;
|
|
3602
|
+
}
|
|
3603
|
+
function persistedQueuedMessage(message) {
|
|
3604
|
+
return {
|
|
3605
|
+
...message,
|
|
3606
|
+
...(message.attachments
|
|
3607
|
+
? { attachments: message.attachments.map(persistableFilePart) }
|
|
3608
|
+
: {}),
|
|
3609
|
+
...(message.requestAttachments
|
|
3610
|
+
? {
|
|
3611
|
+
requestAttachments: message.requestAttachments.flatMap(({ data: _data, ...reference }) => reference.url ? [reference] : []),
|
|
3612
|
+
}
|
|
3613
|
+
: {}),
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3472
3616
|
function createAgentKitThreadSnapshot(thread) {
|
|
3473
3617
|
const messages = thread.messages.map(persistedAgentMessage);
|
|
3474
3618
|
const repositoryMessages = messages.map((message, index) => ({
|
|
@@ -3501,7 +3645,7 @@ function createAgentKitThreadSnapshot(thread) {
|
|
|
3501
3645
|
}));
|
|
3502
3646
|
const agentKit = {
|
|
3503
3647
|
messages,
|
|
3504
|
-
events: thread.events,
|
|
3648
|
+
events: thread.events.map(persistedAgentEvent),
|
|
3505
3649
|
runs,
|
|
3506
3650
|
activeRunIds: thread.activeRunIds,
|
|
3507
3651
|
toolCalls: Object.values(thread.tools),
|
|
@@ -3511,7 +3655,7 @@ function createAgentKitThreadSnapshot(thread) {
|
|
|
3511
3655
|
threadData: JSON.stringify({
|
|
3512
3656
|
headId: messages.at(-1)?.id ?? null,
|
|
3513
3657
|
messages: repositoryMessages,
|
|
3514
|
-
queuedMessages: thread.queuedMessages,
|
|
3658
|
+
queuedMessages: thread.queuedMessages.map(persistedQueuedMessage),
|
|
3515
3659
|
agentKit,
|
|
3516
3660
|
}),
|
|
3517
3661
|
title,
|
|
@@ -3569,8 +3713,8 @@ function storeAgentKitThreadHandoffSnapshot(key, thread, snapshot) {
|
|
|
3569
3713
|
createdAt: thread.thread?.createdAt ?? messages[0]?.createdAt ?? now,
|
|
3570
3714
|
updatedAt: thread.thread?.updatedAt ?? messages.at(-1)?.createdAt ?? now,
|
|
3571
3715
|
messages,
|
|
3572
|
-
queuedMessages: thread.queuedMessages,
|
|
3573
|
-
events: thread.events,
|
|
3716
|
+
queuedMessages: thread.queuedMessages.map(persistedQueuedMessage),
|
|
3717
|
+
events: thread.events.map(persistedAgentEvent),
|
|
3574
3718
|
runs: Object.entries(thread.runs).map(([id, run]) => ({
|
|
3575
3719
|
...run,
|
|
3576
3720
|
id,
|