@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,17 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AgentKitUploadError,
|
|
2
3
|
hasActiveAgentRuns,
|
|
3
4
|
selectAgentSuggestions,
|
|
4
5
|
isCurrentAgentSuggestion,
|
|
5
6
|
type AgentKitUploadDriver,
|
|
6
7
|
type AgentThreadState,
|
|
7
8
|
} from "@agent-native/agentkit";
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
MAX_AGENT_REQUEST_ATTACHMENT_DATA_CHARS,
|
|
11
|
+
isInlineDataUrl,
|
|
12
|
+
persistableFilePart,
|
|
13
|
+
} from "@agent-native/agentkit/protocol";
|
|
9
14
|
import type {
|
|
10
15
|
AgentActionResult,
|
|
11
16
|
AgentApprovalRequest,
|
|
12
17
|
AgentConnectionRequest,
|
|
13
18
|
AgentEvent,
|
|
14
19
|
AgentMessage,
|
|
20
|
+
AgentQueuedMessage,
|
|
15
21
|
AgentRequestAttachment,
|
|
16
22
|
AgentStreamIntegrityReport,
|
|
17
23
|
AgentTransport,
|
|
@@ -94,11 +100,18 @@ import {
|
|
|
94
100
|
type RealtimeVoiceTranscriptMessage,
|
|
95
101
|
} from "@agent-native/toolkit/composer/realtime-voice-transcript";
|
|
96
102
|
import { IconButton } from "@agent-native/toolkit/design-system";
|
|
103
|
+
import {
|
|
104
|
+
DropdownMenu,
|
|
105
|
+
DropdownMenuContent,
|
|
106
|
+
DropdownMenuItem,
|
|
107
|
+
DropdownMenuTrigger,
|
|
108
|
+
} from "@agent-native/toolkit/ui/dropdown-menu";
|
|
97
109
|
import { cn } from "@agent-native/toolkit/utils";
|
|
98
110
|
import {
|
|
99
111
|
IconAlertTriangle,
|
|
100
112
|
IconCircleCheck,
|
|
101
113
|
IconCopy,
|
|
114
|
+
IconDotsVertical,
|
|
102
115
|
IconMessage,
|
|
103
116
|
IconPlayerStopFilled,
|
|
104
117
|
IconQuote,
|
|
@@ -563,6 +576,7 @@ type AgentKitComposerSubmit = (
|
|
|
563
576
|
type AgentKitSuggestionSubmitOptions = PromptComposerSubmitOptions & {
|
|
564
577
|
suggestion?: Exclude<AgentSuggestionInput, string>;
|
|
565
578
|
validateSubmission?: () => void;
|
|
579
|
+
validateSubmissionScope?: () => void;
|
|
566
580
|
queuedWhileRunActive?: boolean;
|
|
567
581
|
};
|
|
568
582
|
|
|
@@ -929,6 +943,7 @@ export const AgentKitAssistantChat = forwardRef<
|
|
|
929
943
|
name: "{{name}}",
|
|
930
944
|
}),
|
|
931
945
|
pastedText: t("agentChat.pastedText.title"),
|
|
946
|
+
attachmentNotSaved: t("agentChat.composer.attachmentNotSaved"),
|
|
932
947
|
imagePreview: t("agentChat.composer.imagePreview"),
|
|
933
948
|
closePreview: t("agentChat.composer.closePreview"),
|
|
934
949
|
dropFilesToAttach: t("agentChat.composer.dropToAttach"),
|
|
@@ -970,46 +985,32 @@ export const AgentKitAssistantChat = forwardRef<
|
|
|
970
985
|
form.set(key, value);
|
|
971
986
|
}
|
|
972
987
|
form.set("file", file.body, file.name);
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const messageKey =
|
|
981
|
-
response.status === 415
|
|
982
|
-
? "agentChat.composer.unsupportedFileType"
|
|
983
|
-
: response.status === 413
|
|
984
|
-
? "agentChat.composer.fileTooLarge"
|
|
985
|
-
: response.status === 401
|
|
986
|
-
? "agentChat.composer.sessionExpired"
|
|
987
|
-
: undefined;
|
|
988
|
-
const message = messageKey
|
|
989
|
-
? translatorRef.current(messageKey)
|
|
990
|
-
: translatorRef.current("agentChat.composer.uploadFailed");
|
|
991
|
-
const error = new Error(message);
|
|
992
|
-
Object.assign(error, {
|
|
993
|
-
code:
|
|
994
|
-
response.status === 413
|
|
995
|
-
? "upload_too_large"
|
|
996
|
-
: response.status === 401
|
|
997
|
-
? "upload_session_expired"
|
|
998
|
-
: response.status === 415
|
|
999
|
-
? "upload_unsupported_type"
|
|
1000
|
-
: `upload_http_${response.status}`,
|
|
1001
|
-
status: response.status,
|
|
1002
|
-
retryable:
|
|
1003
|
-
response.status === 408 ||
|
|
1004
|
-
response.status === 429 ||
|
|
1005
|
-
response.status >= 500,
|
|
988
|
+
let response: Response;
|
|
989
|
+
try {
|
|
990
|
+
response = await fetch(target.url, {
|
|
991
|
+
method: target.method,
|
|
992
|
+
headers: target.headers,
|
|
993
|
+
body: form,
|
|
994
|
+
signal: context?.signal,
|
|
1006
995
|
});
|
|
1007
|
-
|
|
996
|
+
} catch (error) {
|
|
997
|
+
if (context?.signal?.aborted) throw error;
|
|
998
|
+
throw Object.assign(
|
|
999
|
+
new Error(translatorRef.current("agentChat.composer.uploadOffline")),
|
|
1000
|
+
{ code: "upload_network_error", retryable: true, cause: error },
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
// The server's error body is never shown: its text is untranslated.
|
|
1004
|
+
if (!response.ok) {
|
|
1005
|
+
throw uploadHttpError(response.status, translatorRef.current);
|
|
1008
1006
|
}
|
|
1009
1007
|
const result: unknown = await response.json();
|
|
1010
1008
|
const uploaded = asRecord(result);
|
|
1011
1009
|
if (typeof uploaded?.url !== "string" || !uploaded.url) {
|
|
1012
|
-
throw
|
|
1010
|
+
throw Object.assign(
|
|
1011
|
+
new Error(translatorRef.current("agentChat.composer.uploadFailed")),
|
|
1012
|
+
{ code: "upload_invalid_response", retryable: false },
|
|
1013
|
+
);
|
|
1013
1014
|
}
|
|
1014
1015
|
uploadedFilesRef.current.set(target.uploadId, {
|
|
1015
1016
|
type: "file",
|
|
@@ -2144,6 +2145,7 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
2144
2145
|
: await uploadAgentChatAttachments(control, attachments, files, {
|
|
2145
2146
|
storageConfigured: fileStorageConfigured,
|
|
2146
2147
|
storageUnavailableMessage: t("onboarding.fileStorage.title"),
|
|
2148
|
+
fileTooLargeMessage: uploadTooLargeMessage(t),
|
|
2147
2149
|
});
|
|
2148
2150
|
const fileParts = uploadedAttachments.fileParts;
|
|
2149
2151
|
let requestAttachments =
|
|
@@ -2192,7 +2194,9 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
2192
2194
|
if (inlineImageDataChars > MAX_AGENT_REQUEST_ATTACHMENT_DATA_CHARS) {
|
|
2193
2195
|
throw new Error(t("agentChat.composer.requestTooLarge"));
|
|
2194
2196
|
}
|
|
2195
|
-
|
|
2197
|
+
// Readiness was gated before the upload; a provider status refresh during
|
|
2198
|
+
// it must not discard the upload, only a change of thread or scope.
|
|
2199
|
+
composerOptions.validateSubmissionScope?.();
|
|
2196
2200
|
const selectionChangedDuringSubmission =
|
|
2197
2201
|
selectionRevision !== selectionRevisionRef.current ||
|
|
2198
2202
|
(options.pendingSelectionCapturedAt !== undefined &&
|
|
@@ -2393,8 +2397,16 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
2393
2397
|
: await uploadAgentChatAttachments(control, attachments, files, {
|
|
2394
2398
|
storageConfigured: fileStorageConfigured,
|
|
2395
2399
|
storageUnavailableMessage: t("onboarding.fileStorage.title"),
|
|
2400
|
+
fileTooLargeMessage: uploadTooLargeMessage(t),
|
|
2396
2401
|
});
|
|
2397
|
-
const fileParts =
|
|
2402
|
+
const fileParts = await durableFileParts(
|
|
2403
|
+
control,
|
|
2404
|
+
uploadedAttachments.fileParts,
|
|
2405
|
+
{
|
|
2406
|
+
storageConfigured: fileStorageConfigured,
|
|
2407
|
+
storageUnavailableMessage: t("onboarding.fileStorage.title"),
|
|
2408
|
+
},
|
|
2409
|
+
);
|
|
2398
2410
|
if (
|
|
2399
2411
|
!fileStorageConfigured &&
|
|
2400
2412
|
uploadedAttachments.requestAttachments.some(
|
|
@@ -2448,6 +2460,7 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
2448
2460
|
delete deferredComposerOptions.attachments;
|
|
2449
2461
|
delete deferredComposerOptions.onLocalSubmit;
|
|
2450
2462
|
delete deferredComposerOptions.validateSubmission;
|
|
2463
|
+
delete deferredComposerOptions.validateSubmissionScope;
|
|
2451
2464
|
deferredComposerOptions.model ??= props.selectedModel;
|
|
2452
2465
|
deferredComposerOptions.engine ??= props.selectedEngine;
|
|
2453
2466
|
deferredComposerOptions.effort ??= props.selectedEffort;
|
|
@@ -3589,42 +3602,53 @@ function AgentKitTranscript({ children, threadId }: AgentKitRegionRenderProps) {
|
|
|
3589
3602
|
{children}
|
|
3590
3603
|
{activeRunId ? (
|
|
3591
3604
|
<div
|
|
3592
|
-
className="agentkit-activities-static"
|
|
3605
|
+
className="agentkit-activities-static agentkit-active-run-actions"
|
|
3593
3606
|
data-agentkit-active-run-id-copy="true"
|
|
3594
3607
|
>
|
|
3595
|
-
<IconButton
|
|
3596
|
-
label={
|
|
3597
|
-
activeRunRequestIdCopyStatus === "copied"
|
|
3598
|
-
? t("agentChat.common.copied")
|
|
3599
|
-
: activeRunRequestIdCopyStatus === "failed"
|
|
3600
|
-
? t("agentChat.recovery.copyFailed")
|
|
3601
|
-
: t("agentChat.message.copyRequestId")
|
|
3602
|
-
}
|
|
3603
|
-
title={t("agentChat.message.copyRequestId")}
|
|
3604
|
-
icon={
|
|
3605
|
-
activeRunRequestIdCopyStatus === "copied" ? (
|
|
3606
|
-
<IconCircleCheck aria-hidden="true" />
|
|
3607
|
-
) : activeRunRequestIdCopyStatus === "failed" ? (
|
|
3608
|
-
<IconAlertTriangle aria-hidden="true" />
|
|
3609
|
-
) : (
|
|
3610
|
-
<IconCopy aria-hidden="true" />
|
|
3611
|
-
)
|
|
3612
|
-
}
|
|
3613
|
-
size="compact"
|
|
3614
|
-
onPress={() => void copyActiveRunRequestId()}
|
|
3615
|
-
/>
|
|
3616
3608
|
{activeRunRequestIdCopyStatus ? (
|
|
3617
3609
|
<span
|
|
3618
|
-
className=
|
|
3610
|
+
className={cn(
|
|
3611
|
+
"agentkit-active-run-actions-feedback",
|
|
3612
|
+
activeRunRequestIdCopyStatus === "failed" &&
|
|
3613
|
+
"agentkit-active-run-actions-feedback-error",
|
|
3614
|
+
)}
|
|
3619
3615
|
role={
|
|
3620
3616
|
activeRunRequestIdCopyStatus === "failed" ? "alert" : "status"
|
|
3621
3617
|
}
|
|
3622
3618
|
>
|
|
3619
|
+
{activeRunRequestIdCopyStatus === "copied" ? (
|
|
3620
|
+
<IconCircleCheck size={14} aria-hidden="true" />
|
|
3621
|
+
) : (
|
|
3622
|
+
<IconAlertTriangle size={14} aria-hidden="true" />
|
|
3623
|
+
)}
|
|
3623
3624
|
{activeRunRequestIdCopyStatus === "copied"
|
|
3624
3625
|
? t("agentChat.common.copied")
|
|
3625
3626
|
: t("agentChat.recovery.copyFailed")}
|
|
3626
3627
|
</span>
|
|
3627
3628
|
) : null}
|
|
3629
|
+
<DropdownMenu>
|
|
3630
|
+
<DropdownMenuTrigger asChild>
|
|
3631
|
+
<IconButton
|
|
3632
|
+
label={t("agentChat.message.actions")}
|
|
3633
|
+
title={t("agentChat.message.actions")}
|
|
3634
|
+
icon={<IconDotsVertical aria-hidden="true" />}
|
|
3635
|
+
size="compact"
|
|
3636
|
+
className="agentkit-active-run-actions-trigger"
|
|
3637
|
+
/>
|
|
3638
|
+
</DropdownMenuTrigger>
|
|
3639
|
+
<DropdownMenuContent align="end" sideOffset={6}>
|
|
3640
|
+
<DropdownMenuItem onSelect={() => void copyActiveRunRequestId()}>
|
|
3641
|
+
{activeRunRequestIdCopyStatus === "copied" ? (
|
|
3642
|
+
<IconCircleCheck size={14} aria-hidden="true" />
|
|
3643
|
+
) : activeRunRequestIdCopyStatus === "failed" ? (
|
|
3644
|
+
<IconAlertTriangle size={14} aria-hidden="true" />
|
|
3645
|
+
) : (
|
|
3646
|
+
<IconCopy size={14} aria-hidden="true" />
|
|
3647
|
+
)}
|
|
3648
|
+
{t("agentChat.message.copyRequestId")}
|
|
3649
|
+
</DropdownMenuItem>
|
|
3650
|
+
</DropdownMenuContent>
|
|
3651
|
+
</DropdownMenu>
|
|
3628
3652
|
</div>
|
|
3629
3653
|
) : null}
|
|
3630
3654
|
{handoffMessages.map((message) => (
|
|
@@ -3916,24 +3940,36 @@ function AgentKitComposerSurface({
|
|
|
3916
3940
|
: {}),
|
|
3917
3941
|
};
|
|
3918
3942
|
setComposerError(null);
|
|
3919
|
-
const
|
|
3943
|
+
const assertSubmissionScope = () => {
|
|
3944
|
+
const latest = controller.getThread(threadId);
|
|
3920
3945
|
if (
|
|
3921
|
-
!submissionAllowed.current ||
|
|
3922
3946
|
!mounted.current ||
|
|
3923
|
-
currentSubmissionScope.current !== submissionScope
|
|
3947
|
+
currentSubmissionScope.current !== submissionScope ||
|
|
3948
|
+
(options.suggestion &&
|
|
3949
|
+
(latest.messages.length || Object.keys(latest.runs).length) &&
|
|
3950
|
+
!isCurrentAgentSuggestion(latest, options.suggestion))
|
|
3924
3951
|
) {
|
|
3925
|
-
throw
|
|
3952
|
+
throw Object.assign(
|
|
3953
|
+
new Error(t("agentChat.composer.submissionScopeChanged")),
|
|
3954
|
+
{ code: "submission_scope_changed" },
|
|
3955
|
+
);
|
|
3926
3956
|
}
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3957
|
+
};
|
|
3958
|
+
const assertCurrentSubmission = () => {
|
|
3959
|
+
if (!submissionAllowed.current) {
|
|
3960
|
+
throw Object.assign(
|
|
3961
|
+
new Error(t("agentChat.composer.submissionNotReady")),
|
|
3962
|
+
{ code: "submission_not_ready" },
|
|
3963
|
+
);
|
|
3964
|
+
}
|
|
3965
|
+
assertSubmissionScope();
|
|
3934
3966
|
};
|
|
3935
3967
|
assertCurrentSubmission();
|
|
3936
|
-
options = {
|
|
3968
|
+
options = {
|
|
3969
|
+
...options,
|
|
3970
|
+
validateSubmission: assertCurrentSubmission,
|
|
3971
|
+
validateSubmissionScope: assertSubmissionScope,
|
|
3972
|
+
};
|
|
3937
3973
|
const captured = snapshotComposerContextItems(
|
|
3938
3974
|
options.contextItems ?? visibleContextItems,
|
|
3939
3975
|
);
|
|
@@ -4728,6 +4764,21 @@ function AgentKitRunFailure({
|
|
|
4728
4764
|
lastUserMessage?.id,
|
|
4729
4765
|
);
|
|
4730
4766
|
const retryFailedTurn = () => sendRetryRequest(surface, retryRequest, runId);
|
|
4767
|
+
const retryWithoutAttachments =
|
|
4768
|
+
retryRequest.fileParts.length || retryRequest.requestAttachments.length
|
|
4769
|
+
? () =>
|
|
4770
|
+
void sendRetryRequest(
|
|
4771
|
+
surface,
|
|
4772
|
+
{
|
|
4773
|
+
...retryRequest,
|
|
4774
|
+
text: retryRequest.textWithContext,
|
|
4775
|
+
fileParts: [],
|
|
4776
|
+
requestAttachments: [],
|
|
4777
|
+
hasUnavailableAttachment: false,
|
|
4778
|
+
},
|
|
4779
|
+
runId,
|
|
4780
|
+
)
|
|
4781
|
+
: undefined;
|
|
4731
4782
|
const resumeAfterSetup = useResumeAfterAiSetup(
|
|
4732
4783
|
`${threadId}:${(failedPrompt ?? lastUserMessage)?.id ?? runId}`,
|
|
4733
4784
|
setupFailure &&
|
|
@@ -4802,6 +4853,7 @@ function AgentKitRunFailure({
|
|
|
4802
4853
|
continueFailed ? t("agentChat.recovery.continueUnavailable") : null
|
|
4803
4854
|
}
|
|
4804
4855
|
onRetry={() => void retryFailedTurn()}
|
|
4856
|
+
onRetryWithoutAttachments={retryWithoutAttachments}
|
|
4805
4857
|
retryHasUnavailableAttachment={retryRequest.hasUnavailableAttachment}
|
|
4806
4858
|
onFork={async () => {
|
|
4807
4859
|
if (!lastUserMessage) return surface.props.onForkChat?.();
|
|
@@ -4870,6 +4922,7 @@ function retryRequestFrom(message: AgentMessage | undefined) {
|
|
|
4870
4922
|
mode === "plan" || mode === "act" ? mode : undefined;
|
|
4871
4923
|
return {
|
|
4872
4924
|
text: message ? agentMessageText(message) : "",
|
|
4925
|
+
textWithContext: message ? rawAgentMessageText(message) : "",
|
|
4873
4926
|
fileParts,
|
|
4874
4927
|
requestAttachments: retryRequestAttachmentsFrom(
|
|
4875
4928
|
custom?.agentNativeRetryRequestAttachments,
|
|
@@ -5411,6 +5464,52 @@ interface UploadedAgentChatAttachments {
|
|
|
5411
5464
|
requestAttachments: AgentRequestAttachment[];
|
|
5412
5465
|
}
|
|
5413
5466
|
|
|
5467
|
+
/** The `/_agent-native/file-upload` ceiling (core's `DEFAULT_UPLOAD_MAX_FILE_BYTES`). */
|
|
5468
|
+
const AGENT_CHAT_UPLOAD_MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
5469
|
+
|
|
5470
|
+
function uploadTooLargeMessage(t: ReturnType<typeof useT>): string {
|
|
5471
|
+
return t("agentChat.composer.fileTooLarge", {
|
|
5472
|
+
size: AGENT_CHAT_UPLOAD_MAX_FILE_BYTES / 1024 / 1024,
|
|
5473
|
+
});
|
|
5474
|
+
}
|
|
5475
|
+
|
|
5476
|
+
function uploadHttpError(status: number, t: ReturnType<typeof useT>): Error {
|
|
5477
|
+
const [code, message, retryable]: [string, string, boolean] =
|
|
5478
|
+
status === 413
|
|
5479
|
+
? ["upload_too_large", uploadTooLargeMessage(t), false]
|
|
5480
|
+
: status === 401 || status === 403
|
|
5481
|
+
? [
|
|
5482
|
+
"upload_session_expired",
|
|
5483
|
+
t("agentChat.composer.sessionExpired"),
|
|
5484
|
+
false,
|
|
5485
|
+
]
|
|
5486
|
+
: status === 415
|
|
5487
|
+
? [
|
|
5488
|
+
"upload_unsupported_type",
|
|
5489
|
+
t("agentChat.composer.unsupportedFileType"),
|
|
5490
|
+
false,
|
|
5491
|
+
]
|
|
5492
|
+
: status === 408 || status === 429 || status >= 500
|
|
5493
|
+
? [
|
|
5494
|
+
"upload_unavailable",
|
|
5495
|
+
t("agentChat.composer.uploadUnavailable"),
|
|
5496
|
+
true,
|
|
5497
|
+
]
|
|
5498
|
+
: [
|
|
5499
|
+
`upload_http_${status}`,
|
|
5500
|
+
t("agentChat.composer.uploadFailed"),
|
|
5501
|
+
false,
|
|
5502
|
+
];
|
|
5503
|
+
return Object.assign(new Error(message), { code, status, retryable });
|
|
5504
|
+
}
|
|
5505
|
+
|
|
5506
|
+
/**
|
|
5507
|
+
* Durable uploads keyed by the attachment or file they came from, so a send
|
|
5508
|
+
* that failed on one file reuses its siblings' uploads instead of orphaning
|
|
5509
|
+
* them and uploading again.
|
|
5510
|
+
*/
|
|
5511
|
+
const uploadedAgentChatSources = new WeakMap<object, FilePart>();
|
|
5512
|
+
|
|
5414
5513
|
const OPTIMIZABLE_RASTER_IMAGE_TYPES = new Set([
|
|
5415
5514
|
"image/gif",
|
|
5416
5515
|
"image/jpeg",
|
|
@@ -5472,9 +5571,11 @@ async function uploadAgentChatAttachments(
|
|
|
5472
5571
|
options: {
|
|
5473
5572
|
storageConfigured: boolean;
|
|
5474
5573
|
storageUnavailableMessage: string;
|
|
5574
|
+
fileTooLargeMessage: string;
|
|
5475
5575
|
},
|
|
5476
5576
|
): Promise<UploadedAgentChatAttachments> {
|
|
5477
5577
|
const entries: Array<FilePart | File> = [];
|
|
5578
|
+
const sourceByFile = new Map<File, object>();
|
|
5478
5579
|
for (const attachment of attachments) {
|
|
5479
5580
|
if (attachment.displayOnly) continue;
|
|
5480
5581
|
if (attachment.url) {
|
|
@@ -5488,13 +5589,17 @@ async function uploadAgentChatAttachments(
|
|
|
5488
5589
|
});
|
|
5489
5590
|
} else {
|
|
5490
5591
|
const file = await attachmentToFile(attachment);
|
|
5491
|
-
if (file)
|
|
5592
|
+
if (file) {
|
|
5593
|
+
entries.push(file);
|
|
5594
|
+
sourceByFile.set(file, attachment);
|
|
5595
|
+
}
|
|
5492
5596
|
}
|
|
5493
5597
|
}
|
|
5494
5598
|
entries.push(...files);
|
|
5495
5599
|
const pending = entries.filter(
|
|
5496
5600
|
(entry): entry is File => entry instanceof File,
|
|
5497
5601
|
);
|
|
5602
|
+
const sourceOf = (file: File): object => sourceByFile.get(file) ?? file;
|
|
5498
5603
|
const optimizedImages = new Map<
|
|
5499
5604
|
File,
|
|
5500
5605
|
{ data: string; contentType: string }
|
|
@@ -5523,25 +5628,62 @@ async function uploadAgentChatAttachments(
|
|
|
5523
5628
|
if (!options.storageConfigured && filesToUploadNormally.length > 0) {
|
|
5524
5629
|
throw new Error(options.storageUnavailableMessage);
|
|
5525
5630
|
}
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5631
|
+
const oversized = filesToUploadNormally.flatMap((file, index) =>
|
|
5632
|
+
file.size > AGENT_CHAT_UPLOAD_MAX_FILE_BYTES
|
|
5633
|
+
? [
|
|
5634
|
+
{
|
|
5635
|
+
index,
|
|
5636
|
+
name: file.name,
|
|
5637
|
+
error: Object.assign(new Error(options.fileTooLargeMessage), {
|
|
5638
|
+
code: "upload_too_large",
|
|
5639
|
+
retryable: false,
|
|
5640
|
+
}),
|
|
5641
|
+
},
|
|
5642
|
+
]
|
|
5643
|
+
: [],
|
|
5644
|
+
);
|
|
5645
|
+
if (oversized.length) throw new AgentKitUploadError(oversized, []);
|
|
5646
|
+
const filesToUpload = filesToUploadNormally.filter((file) => {
|
|
5647
|
+
const reused = uploadedAgentChatSources.get(sourceOf(file));
|
|
5648
|
+
if (reused) uploadedByFile.set(file, reused);
|
|
5649
|
+
return !reused;
|
|
5650
|
+
});
|
|
5651
|
+
if (filesToUpload.length) {
|
|
5652
|
+
let uploaded: FilePart[];
|
|
5653
|
+
try {
|
|
5654
|
+
uploaded = await control.uploadFiles(
|
|
5655
|
+
filesToUpload.map((file) => ({
|
|
5656
|
+
name: file.name,
|
|
5657
|
+
mediaType: file.type || "application/octet-stream",
|
|
5658
|
+
size: file.size,
|
|
5659
|
+
body: file,
|
|
5660
|
+
})),
|
|
5661
|
+
);
|
|
5662
|
+
} catch (error) {
|
|
5663
|
+
if (error instanceof AgentKitUploadError) {
|
|
5664
|
+
for (const { index, part } of error.uploaded) {
|
|
5665
|
+
uploadedAgentChatSources.set(sourceOf(filesToUpload[index]!), part);
|
|
5666
|
+
}
|
|
5667
|
+
}
|
|
5668
|
+
throw error;
|
|
5669
|
+
}
|
|
5670
|
+
filesToUpload.forEach((file, index) => {
|
|
5536
5671
|
const part = uploaded[index];
|
|
5537
5672
|
if (!part) {
|
|
5538
5673
|
throw new TypeError("File upload did not return every uploaded file.");
|
|
5539
5674
|
}
|
|
5540
5675
|
uploadedByFile.set(file, part);
|
|
5676
|
+
uploadedAgentChatSources.set(sourceOf(file), part);
|
|
5541
5677
|
});
|
|
5542
5678
|
}
|
|
5543
5679
|
for (const file of pending) {
|
|
5544
|
-
if (
|
|
5680
|
+
if (
|
|
5681
|
+
!optimizedImages.has(file) ||
|
|
5682
|
+
!options.storageConfigured ||
|
|
5683
|
+
file.size > AGENT_CHAT_UPLOAD_MAX_FILE_BYTES
|
|
5684
|
+
) {
|
|
5685
|
+
continue;
|
|
5686
|
+
}
|
|
5545
5687
|
try {
|
|
5546
5688
|
const [part] = await control.uploadFiles([
|
|
5547
5689
|
{
|
|
@@ -5618,6 +5760,42 @@ async function uploadRequestAttachments(
|
|
|
5618
5760
|
});
|
|
5619
5761
|
}
|
|
5620
5762
|
|
|
5763
|
+
/** Stored submissions carry durable references, so inline file bytes upload first. */
|
|
5764
|
+
async function durableFileParts(
|
|
5765
|
+
control: ReturnType<typeof useAgentKitControl>,
|
|
5766
|
+
parts: readonly FilePart[],
|
|
5767
|
+
options: { storageConfigured: boolean; storageUnavailableMessage: string },
|
|
5768
|
+
): Promise<FilePart[]> {
|
|
5769
|
+
const inline = parts.filter((part) => isInlineDataUrl(part.url));
|
|
5770
|
+
if (inline.length === 0) return [...parts];
|
|
5771
|
+
if (!options.storageConfigured) {
|
|
5772
|
+
throw new Error(options.storageUnavailableMessage);
|
|
5773
|
+
}
|
|
5774
|
+
const uploaded = await control.uploadFiles(
|
|
5775
|
+
await Promise.all(
|
|
5776
|
+
inline.map(async (part) => {
|
|
5777
|
+
const body = await (await fetch(part.url!)).blob();
|
|
5778
|
+
return {
|
|
5779
|
+
name: part.name,
|
|
5780
|
+
mediaType:
|
|
5781
|
+
part.mediaType ?? (body.type || "application/octet-stream"),
|
|
5782
|
+
size: body.size,
|
|
5783
|
+
body,
|
|
5784
|
+
};
|
|
5785
|
+
}),
|
|
5786
|
+
),
|
|
5787
|
+
);
|
|
5788
|
+
let uploadIndex = 0;
|
|
5789
|
+
return parts.map((part) => {
|
|
5790
|
+
if (!isInlineDataUrl(part.url)) return part;
|
|
5791
|
+
const durable = uploaded[uploadIndex++];
|
|
5792
|
+
if (!durable?.url && !durable?.fileId) {
|
|
5793
|
+
throw new TypeError(`${part.name} did not receive a durable reference.`);
|
|
5794
|
+
}
|
|
5795
|
+
return durable;
|
|
5796
|
+
});
|
|
5797
|
+
}
|
|
5798
|
+
|
|
5621
5799
|
function rawAgentMessageTextFromParts(parts: AgentMessage["parts"]): string {
|
|
5622
5800
|
return parts
|
|
5623
5801
|
.filter((part) => part.type === "text")
|
|
@@ -5639,24 +5817,57 @@ function agentMessageText(message: AgentMessage): string {
|
|
|
5639
5817
|
return agentMessageTextFromParts(message.parts);
|
|
5640
5818
|
}
|
|
5641
5819
|
|
|
5820
|
+
/**
|
|
5821
|
+
* The live message keeps inline image bytes for display; what is stored keeps
|
|
5822
|
+
* only a durable reference, or a named `omitted` marker when there is none.
|
|
5823
|
+
*/
|
|
5642
5824
|
function persistedAgentMessage(message: AgentMessage): AgentMessage {
|
|
5643
5825
|
return {
|
|
5644
5826
|
...message,
|
|
5645
5827
|
parts: message.parts.map((part) => {
|
|
5646
5828
|
const record = asRecord(part);
|
|
5647
5829
|
if (
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
record.data.startsWith("data:")
|
|
5830
|
+
!record ||
|
|
5831
|
+
(!isInlineDataUrl(record.data) && !isInlineDataUrl(record.url))
|
|
5651
5832
|
) {
|
|
5652
|
-
|
|
5653
|
-
return withoutInlineBody as unknown as AgentMessage["parts"][number];
|
|
5833
|
+
return part;
|
|
5654
5834
|
}
|
|
5655
|
-
|
|
5835
|
+
const { data: _data, url, ...rest } = record;
|
|
5836
|
+
const durableUrl = isInlineDataUrl(url) ? undefined : url;
|
|
5837
|
+
return {
|
|
5838
|
+
...rest,
|
|
5839
|
+
...(durableUrl ? { url: durableUrl } : {}),
|
|
5840
|
+
...(durableUrl || rest.fileId ? {} : { omitted: "inline-bytes" }),
|
|
5841
|
+
} as unknown as AgentMessage["parts"][number];
|
|
5656
5842
|
}),
|
|
5657
5843
|
};
|
|
5658
5844
|
}
|
|
5659
5845
|
|
|
5846
|
+
function persistedAgentEvent(event: AgentEvent): AgentEvent {
|
|
5847
|
+
return "message" in event && event.message
|
|
5848
|
+
? { ...event, message: persistedAgentMessage(event.message) }
|
|
5849
|
+
: event;
|
|
5850
|
+
}
|
|
5851
|
+
|
|
5852
|
+
function persistedQueuedMessage(
|
|
5853
|
+
message: AgentQueuedMessage,
|
|
5854
|
+
): AgentQueuedMessage {
|
|
5855
|
+
return {
|
|
5856
|
+
...message,
|
|
5857
|
+
...(message.attachments
|
|
5858
|
+
? { attachments: message.attachments.map(persistableFilePart) }
|
|
5859
|
+
: {}),
|
|
5860
|
+
...(message.requestAttachments
|
|
5861
|
+
? {
|
|
5862
|
+
requestAttachments: message.requestAttachments.flatMap(
|
|
5863
|
+
({ data: _data, ...reference }) =>
|
|
5864
|
+
reference.url ? [reference] : [],
|
|
5865
|
+
),
|
|
5866
|
+
}
|
|
5867
|
+
: {}),
|
|
5868
|
+
};
|
|
5869
|
+
}
|
|
5870
|
+
|
|
5660
5871
|
function createAgentKitThreadSnapshot(thread: AgentThreadState) {
|
|
5661
5872
|
const messages = thread.messages.map(persistedAgentMessage);
|
|
5662
5873
|
const repositoryMessages = messages.map((message, index) => ({
|
|
@@ -5690,7 +5901,7 @@ function createAgentKitThreadSnapshot(thread: AgentThreadState) {
|
|
|
5690
5901
|
}));
|
|
5691
5902
|
const agentKit = {
|
|
5692
5903
|
messages,
|
|
5693
|
-
events: thread.events,
|
|
5904
|
+
events: thread.events.map(persistedAgentEvent),
|
|
5694
5905
|
runs,
|
|
5695
5906
|
activeRunIds: thread.activeRunIds,
|
|
5696
5907
|
toolCalls: Object.values(thread.tools),
|
|
@@ -5700,7 +5911,7 @@ function createAgentKitThreadSnapshot(thread: AgentThreadState) {
|
|
|
5700
5911
|
threadData: JSON.stringify({
|
|
5701
5912
|
headId: messages.at(-1)?.id ?? null,
|
|
5702
5913
|
messages: repositoryMessages,
|
|
5703
|
-
queuedMessages: thread.queuedMessages,
|
|
5914
|
+
queuedMessages: thread.queuedMessages.map(persistedQueuedMessage),
|
|
5704
5915
|
agentKit,
|
|
5705
5916
|
}),
|
|
5706
5917
|
title,
|
|
@@ -5772,8 +5983,8 @@ function storeAgentKitThreadHandoffSnapshot(
|
|
|
5772
5983
|
createdAt: thread.thread?.createdAt ?? messages[0]?.createdAt ?? now,
|
|
5773
5984
|
updatedAt: thread.thread?.updatedAt ?? messages.at(-1)?.createdAt ?? now,
|
|
5774
5985
|
messages,
|
|
5775
|
-
queuedMessages: thread.queuedMessages,
|
|
5776
|
-
events: thread.events,
|
|
5986
|
+
queuedMessages: thread.queuedMessages.map(persistedQueuedMessage),
|
|
5987
|
+
events: thread.events.map(persistedAgentEvent),
|
|
5777
5988
|
runs: Object.entries(thread.runs).map(([id, run]) => ({
|
|
5778
5989
|
...run,
|
|
5779
5990
|
id,
|