@agent-native/core 0.149.6 → 0.151.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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +83 -45
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-dashboards-keep-the-selected-tab-when-reopened.md +6 -0
- package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/clips/changelog/2026-08-11-clips-recovers-from-transient-updates-and-avoids-macos-priva.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +32 -176
- package/corpus/templates/clips/desktop/src/lib/updater.ts +52 -37
- package/corpus/templates/clips/desktop/src/styles.css +1 -166
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -0
- package/corpus/templates/clips/desktop/src-tauri/Info.plist +4 -0
- package/corpus/templates/clips/desktop/src-tauri/src/eventkit.rs +28 -1
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +48 -1
- package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +5 -0
- package/corpus/templates/clips/server/plugins/auth.ts +3 -0
- package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +2 -1
- package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -1
- package/corpus/templates/design/changelog/2026-08-11-design-agent-prompts-copy-successfully-in-the-desktop-app.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/dispatch/app/global.css +2 -2
- package/corpus/templates/dispatch/changelog/2026-08-10-desktop-workspace-sign-in.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-shows-the-generic-chat-starter-among-defa.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-integrations-and-scheduled-work-open-as-first-class-electron-pages.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-sidebar-text-is-slightly-easier-to-read-in-light-and-dark-mo.md +6 -0
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +57 -31
- package/corpus/templates/dispatch/server/plugins/feature-flags.ts +5 -0
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +82 -33
- package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +7 -15
- package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +9 -1
- package/corpus/templates/dispatch/shared/feature-flags.ts +15 -0
- package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +8 -7
- package/corpus/templates/forms/app/hooks/use-forms.ts +8 -1
- package/corpus/templates/forms/changelog/2026-08-11-fixed-public-forms-so-conditional-questions-stay-hidden-unti.md +6 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +2 -1
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +37 -2
- package/corpus/templates/forms/shared/page-url.ts +54 -0
- package/corpus/templates/forms/shared/types.ts +4 -4
- package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/slides/actions/add-slide.ts +0 -27
- package/corpus/templates/slides/actions/import-file.ts +28 -9
- package/corpus/templates/slides/actions/view-screen.ts +4 -4
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +8 -1
- package/corpus/templates/slides/app/components/editor/AiEditingMarker.tsx +25 -0
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +43 -48
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +9 -6
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -9
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +2 -0
- package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +0 -26
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +904 -217
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +11 -0
- package/corpus/templates/slides/app/components/editor/inline-edit-session.ts +16 -0
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +22 -6
- package/corpus/templates/slides/app/components/editor/style-clipboard.ts +61 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +26 -2
- package/corpus/templates/slides/app/components/ui/context-menu.tsx +1 -0
- package/corpus/templates/slides/app/context/DeckContext.tsx +75 -25
- package/corpus/templates/slides/app/global.css +1 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -1
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
- package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +12 -12
- package/corpus/templates/slides/app/lib/slide-agent-context.ts +52 -0
- package/corpus/templates/slides/app/lib/slide-image-replacement.ts +61 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +55 -116
- package/corpus/templates/slides/app/pages/Index.tsx +28 -3
- package/corpus/templates/slides/changelog/2026-08-11-agent-chat-now-follows-the-current-slide-and-selection.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-deck-titles-now-use-slide-content-instead-of-placeholder-filenames.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-history-now-opens-from-the-deck-overflow-menu.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-images-can-be-dropped-directly-onto-slides-and-resized-or-re.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-new-decks-now-transition-directly-to-a-full-page-editor-load.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-selected-slide-layers-can-be-nudged-precisely-with-arrow-key.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-can-copy-and-paste-element-styles-with-cmd-option-c-v.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-shows-one-compact-ai-marker-and-keeps-your-current-sl.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-text-edits-preserve-slide-layout-when-no-changes-are-made.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-the-style-toolbar-now-uses-the-full-available-width.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-undo-and-redo-now-move-one-visible-slide-state-at-a-time.md +6 -0
- package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -2
- package/corpus/templates/slides/shared/deck-title.ts +56 -0
- package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/agent/run-store.js +6 -1
- package/dist/agent/thread-data-builder.js +19 -44
- package/dist/agent/types.d.ts +13 -0
- package/dist/catalog.json +1 -0
- package/dist/cli/create.js +1 -1
- package/dist/client/AssistantChat.js +58 -10
- package/dist/client/FileStorageSetupCard.d.ts +6 -0
- package/dist/client/FileStorageSetupCard.js +18 -0
- package/dist/client/NewWorkspaceAppFlow.js +10 -0
- package/dist/client/agent-chat-adapter.js +87 -11
- package/dist/client/agent-chat.d.ts +1 -1
- package/dist/client/agent-chat.js +8 -3
- package/dist/client/chat/attachment-adapters.js +10 -0
- package/dist/client/chat/message-components.d.ts +4 -1
- package/dist/client/chat/message-components.js +37 -13
- package/dist/client/chat/tool-call-display.js +14 -0
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +6 -6
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/client/chat-first/types.d.ts +1 -0
- package/dist/client/extensions/EmbeddedExtension.js +1 -1
- package/dist/client/extensions/ExtensionViewer.js +1 -1
- package/dist/client/extensions/InlineExtensionFrame.js +1 -1
- package/dist/client/onboarding/FirstRunOnboarding.js +17 -2
- package/dist/client/settings/FileStorageSettingsForm.d.ts +2 -0
- package/dist/client/settings/FileStorageSettingsForm.js +113 -0
- package/dist/client/settings/SettingsPanel.js +2 -1
- package/dist/client/sse-event-processor.js +21 -0
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/extensions/actions.js +2 -2
- package/dist/extensions/html-shell.js +3 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +15 -10
- package/dist/file-upload/pre-upload-attachments.js +68 -24
- package/dist/file-upload/registry.d.ts +4 -5
- package/dist/file-upload/registry.js +7 -14
- package/dist/file-upload/s3.d.ts +9 -0
- package/dist/file-upload/s3.js +168 -0
- package/dist/file-upload/types.d.ts +3 -3
- package/dist/file-upload/types.js +2 -2
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/onboarding/app-profile.js +23 -1
- package/dist/onboarding/default-steps.js +69 -0
- package/dist/onboarding/types.d.ts +2 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-chat/browser-team-tools.js +20 -0
- package/dist/server/agent-chat-plugin.js +12 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +45 -21
- package/dist/server/better-auth-instance.d.ts +2 -0
- package/dist/server/better-auth-instance.js +2 -2
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.js +26 -16
- package/dist/server/identity-sso-store.d.ts +3 -0
- package/dist/server/identity-sso-store.js +45 -0
- package/dist/server/identity-sso.d.ts +20 -10
- package/dist/server/identity-sso.js +79 -31
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
- package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
- package/docs/content/authentication.mdx +27 -0
- package/docs/content/cross-app-sso.mdx +38 -8
- package/docs/content/extensions.mdx +15 -15
- package/docs/content/file-uploads.mdx +10 -12
- package/docs/content/generative-ui.mdx +6 -1
- package/docs/content/key-concepts.mdx +141 -83
- package/package.json +2 -2
- package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
- package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
- package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
|
@@ -49,6 +49,34 @@ sendToAgentChat({
|
|
|
49
49
|
});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
### Keep user text separate from injected context
|
|
53
|
+
|
|
54
|
+
Treat the visible `message` as the user's request or the shortest clear
|
|
55
|
+
description of an app-initiated operation. Put context the user did not type —
|
|
56
|
+
IDs, URLs, filenames, current selection or screen state, serialized records,
|
|
57
|
+
upload instructions, and bounded source excerpts — in the `context` field.
|
|
58
|
+
`context` is model input carried through the agent turn; it is stripped from
|
|
59
|
+
the rendered user message, so do not concatenate it into `message` with labels,
|
|
60
|
+
blank-line sections, or `<context>` tags yourself.
|
|
61
|
+
|
|
62
|
+
Use the related surface for each kind of supporting input:
|
|
63
|
+
|
|
64
|
+
| Surface | Use for |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `message` | User-authored intent or a concise app action description |
|
|
67
|
+
| `context` | Derived metadata and bounded text needed to carry out that intent |
|
|
68
|
+
| `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
|
|
69
|
+
| `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
|
|
70
|
+
|
|
71
|
+
If an app builds a prompt from a form, selection, upload, or editor state, keep
|
|
72
|
+
the visible message short and pass the assembled details as `context`. Preserve
|
|
73
|
+
the user's actual freeform text in `message` when it is the request; do not
|
|
74
|
+
restate it as metadata in `context` unless the agent needs a structured copy.
|
|
75
|
+
|
|
76
|
+
This boundary applies to auto-submitted turns and prefills. A hidden context
|
|
77
|
+
field is not a license to send unbounded records or secrets: cap excerpts,
|
|
78
|
+
prefer stable IDs and URLs, and use an action or resource lookup for full data.
|
|
79
|
+
|
|
52
80
|
**From the UI, in the background:**
|
|
53
81
|
|
|
54
82
|
```ts
|
package/dist/agent/run-store.js
CHANGED
|
@@ -1792,6 +1792,7 @@ export async function getRunStatus(runId) {
|
|
|
1792
1792
|
* a second, competing truth.
|
|
1793
1793
|
*/
|
|
1794
1794
|
const TURN_ENDING_ABORT_REASONS = new Set(["user", "displaced"]);
|
|
1795
|
+
const USER_INITIATED_ABORT_REASONS = new Set(["user", "abort"]);
|
|
1795
1796
|
// Only infrastructure interruptions that the client can safely resume are
|
|
1796
1797
|
// recoverable. An arbitrary caller-supplied abort reason must never become an
|
|
1797
1798
|
// auto-continue signal, because that turns an explicit stop into new work.
|
|
@@ -1816,8 +1817,12 @@ export function terminalEventForAbortReason(reason) {
|
|
|
1816
1817
|
};
|
|
1817
1818
|
}
|
|
1818
1819
|
if (TURN_ENDING_ABORT_REASONS.has(normalized) ||
|
|
1820
|
+
USER_INITIATED_ABORT_REASONS.has(normalized) ||
|
|
1819
1821
|
normalized.startsWith("user_")) {
|
|
1820
|
-
return {
|
|
1822
|
+
return {
|
|
1823
|
+
type: "done",
|
|
1824
|
+
...(normalized !== "displaced" ? { reason: "user" } : {}),
|
|
1825
|
+
};
|
|
1821
1826
|
}
|
|
1822
1827
|
return {
|
|
1823
1828
|
type: "error",
|
|
@@ -3,13 +3,6 @@ import { isCredentialGapCodeAgentEvent, normalizeCodeAgentTranscript, } from "..
|
|
|
3
3
|
const INTERRUPTED_TOOL_RESULT = "Interrupted before this tool returned a result.";
|
|
4
4
|
export const ASSISTANT_RUN_DURATION_METADATA_KEY = "agentNativeRunDurationMs";
|
|
5
5
|
const MAX_STORED_ATTACHMENT_CHARS = 60_000;
|
|
6
|
-
/**
|
|
7
|
-
* When no file-upload provider is configured we fall back to storing base64
|
|
8
|
-
* directly in the SQL thread_data column. Cap the raw base64 per attachment to
|
|
9
|
-
* avoid unbounded row growth. Attachments larger than this get a '[truncated]'
|
|
10
|
-
* marker so the transcript still renders but the column stays sane.
|
|
11
|
-
*/
|
|
12
|
-
const MAX_STORED_BASE64_BYTES = 2 * 1024 * 1024; // 2 MB per attachment
|
|
13
6
|
function isInternalContinuationError(event) {
|
|
14
7
|
const code = String(event.errorCode ?? "").toLowerCase();
|
|
15
8
|
const msg = event.error.toLowerCase();
|
|
@@ -949,23 +942,6 @@ function textAttachmentEnvelope(att, text) {
|
|
|
949
942
|
].filter(Boolean);
|
|
950
943
|
return `<attachment ${attrs.join(" ")}>\n${truncateStoredAttachment(text)}\n</attachment>`;
|
|
951
944
|
}
|
|
952
|
-
/**
|
|
953
|
-
* Cap a base64 data-URL string for storage. When the encoded string is over
|
|
954
|
-
* the limit we replace the base64 payload with a truncation marker so the
|
|
955
|
-
* transcript still renders the attachment chip but doesn't bloat SQL.
|
|
956
|
-
*/
|
|
957
|
-
function capBase64DataUrl(dataUrl) {
|
|
958
|
-
const commaIdx = dataUrl.indexOf(",");
|
|
959
|
-
if (commaIdx === -1)
|
|
960
|
-
return dataUrl;
|
|
961
|
-
const header = dataUrl.slice(0, commaIdx + 1);
|
|
962
|
-
const b64 = dataUrl.slice(commaIdx + 1);
|
|
963
|
-
// Each base64 char encodes 6 bits; 4 chars = 3 bytes.
|
|
964
|
-
const approxBytes = Math.floor((b64.length * 3) / 4);
|
|
965
|
-
if (approxBytes <= MAX_STORED_BASE64_BYTES)
|
|
966
|
-
return dataUrl;
|
|
967
|
-
return `${header}[base64 truncated — ${approxBytes.toLocaleString()} bytes exceeds storage limit]`;
|
|
968
|
-
}
|
|
969
945
|
function buildStoredAttachments(attachments, runId) {
|
|
970
946
|
return (attachments ?? [])
|
|
971
947
|
.map((att, index) => {
|
|
@@ -1008,17 +984,7 @@ function buildStoredAttachments(attachments, runId) {
|
|
|
1008
984
|
},
|
|
1009
985
|
};
|
|
1010
986
|
}
|
|
1011
|
-
if (att.
|
|
1012
|
-
return {
|
|
1013
|
-
id,
|
|
1014
|
-
type: "image",
|
|
1015
|
-
name: att.name,
|
|
1016
|
-
contentType: att.contentType,
|
|
1017
|
-
status: { type: "complete" },
|
|
1018
|
-
content: [{ type: "image", image: capBase64DataUrl(att.data) }],
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
if (att.data) {
|
|
987
|
+
if (typeof att.text === "string" && att.text.length > 0) {
|
|
1022
988
|
return {
|
|
1023
989
|
id,
|
|
1024
990
|
type: "file",
|
|
@@ -1026,25 +992,34 @@ function buildStoredAttachments(attachments, runId) {
|
|
|
1026
992
|
contentType: att.contentType,
|
|
1027
993
|
status: { type: "complete" },
|
|
1028
994
|
content: [
|
|
1029
|
-
{
|
|
1030
|
-
type: "file",
|
|
1031
|
-
data: capBase64DataUrl(att.data),
|
|
1032
|
-
mimeType: att.contentType,
|
|
1033
|
-
filename: att.name,
|
|
1034
|
-
},
|
|
995
|
+
{ type: "text", text: textAttachmentEnvelope(att, att.text) },
|
|
1035
996
|
],
|
|
1036
997
|
};
|
|
1037
998
|
}
|
|
1038
|
-
|
|
999
|
+
// Binary attachment data is request-scoped input, not thread state. If
|
|
1000
|
+
// the provider was unavailable or failed, retain only a visible marker
|
|
1001
|
+
// so the transcript can explain why the attachment needs storage setup
|
|
1002
|
+
// without putting base64 bytes in SQL.
|
|
1003
|
+
if (att.storageRequired === true || typeof att.data === "string") {
|
|
1004
|
+
const uploadFailed = att.storageUploadFailed === true;
|
|
1039
1005
|
return {
|
|
1040
1006
|
id,
|
|
1041
|
-
type: "file",
|
|
1007
|
+
type: att.type === "image" ? "image" : "file",
|
|
1042
1008
|
name: att.name,
|
|
1043
1009
|
contentType: att.contentType,
|
|
1044
1010
|
status: { type: "complete" },
|
|
1045
1011
|
content: [
|
|
1046
|
-
{
|
|
1012
|
+
{
|
|
1013
|
+
type: "text",
|
|
1014
|
+
text: uploadFailed
|
|
1015
|
+
? "Attachment not retained: the configured object-storage upload failed. Retry the upload to keep files available throughout this thread."
|
|
1016
|
+
: "Attachment not retained: connect object storage to keep files available throughout this thread.",
|
|
1017
|
+
},
|
|
1047
1018
|
],
|
|
1019
|
+
metadata: {
|
|
1020
|
+
storageRequired: true,
|
|
1021
|
+
...(uploadFailed ? { storageUploadFailed: true } : {}),
|
|
1022
|
+
},
|
|
1048
1023
|
};
|
|
1049
1024
|
}
|
|
1050
1025
|
return null;
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -110,6 +110,17 @@ export interface AgentChatAttachment {
|
|
|
110
110
|
type: string;
|
|
111
111
|
name: string;
|
|
112
112
|
data?: string;
|
|
113
|
+
/** Stable object-storage URL for this attachment, when uploaded. */
|
|
114
|
+
url?: string;
|
|
115
|
+
/** Provider that owns `url` (for example Builder or S3). */
|
|
116
|
+
uploadProvider?: string;
|
|
117
|
+
/** SVG and other unsafe files must stay references, never inline content. */
|
|
118
|
+
referenceOnly?: boolean;
|
|
119
|
+
securityNote?: string;
|
|
120
|
+
/** Set when the current turn could not create a durable object-storage URL. */
|
|
121
|
+
storageRequired?: boolean;
|
|
122
|
+
/** Set when a configured object-storage provider rejected or failed the upload. */
|
|
123
|
+
storageUploadFailed?: boolean;
|
|
113
124
|
contentType?: string;
|
|
114
125
|
text?: string;
|
|
115
126
|
}
|
|
@@ -332,6 +343,8 @@ export type AgentChatEvent = {
|
|
|
332
343
|
summary: string;
|
|
333
344
|
} | {
|
|
334
345
|
type: "done";
|
|
346
|
+
/** Set when a human explicitly stopped the current turn. */
|
|
347
|
+
reason?: "user";
|
|
335
348
|
} | {
|
|
336
349
|
type: "error";
|
|
337
350
|
error: string;
|
package/dist/catalog.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"@tiptap/extension-horizontal-rule": "3.28.0",
|
|
18
18
|
"@tiptap/extension-image": "3.28.0",
|
|
19
19
|
"@tiptap/extension-link": "3.28.0",
|
|
20
|
+
"@tiptap/extension-list": "3.28.0",
|
|
20
21
|
"@tiptap/extension-placeholder": "3.28.0",
|
|
21
22
|
"@tiptap/extension-table": "3.28.0",
|
|
22
23
|
"@tiptap/extension-table-cell": "3.28.0",
|
package/dist/cli/create.js
CHANGED
|
@@ -554,7 +554,7 @@ async function scaffoldWorkspaceRoot(targetDir, name) {
|
|
|
554
554
|
const existing = fs.existsSync(wsPath)
|
|
555
555
|
? fs.readFileSync(wsPath, "utf-8")
|
|
556
556
|
: "";
|
|
557
|
-
if (
|
|
557
|
+
if (!/^catalog:\s*$/m.test(existing)) {
|
|
558
558
|
const catalogYaml = Object.entries(catalog)
|
|
559
559
|
.map(([k, v]) => ` "${k}": "${v}"`)
|
|
560
560
|
.join("\n");
|
|
@@ -19,7 +19,7 @@ import { AGENT_CHAT_VIEW_TRANSITION_PREPARE_EVENT } from "./chat-view-transition
|
|
|
19
19
|
// ─── chat/ module imports ─────────────────────────────────────────────────────
|
|
20
20
|
import { DownscalingImageAttachmentAdapter, BinaryDocumentAttachmentAdapter, MAX_ESTIMATED_BODY_BYTES, AGGRESSIVE_MAX_IMAGE_DIMENSION, AGGRESSIVE_JPEG_QUALITY, transcodeImageToDataURL, createAgentImageAttachments, serializeQueuedAttachments, estimateAttachmentBodyBytes, } from "./chat/attachment-adapters.js";
|
|
21
21
|
import { TextStreamingContext } from "./chat/markdown-renderer.js";
|
|
22
|
-
import { CheckpointContext, MessageActionsContext, assistantMessageRunId, UserMessage, AssistantMessage, SelectionAttachedPill, RunningActivityStatus, displayableUserMessageText, isHiddenUserMessage, ServerRunActiveContext, } from "./chat/message-components.js";
|
|
22
|
+
import { CheckpointContext, MessageActionsContext, assistantMessageRunId, UserMessage, AssistantMessage, SelectionAttachedPill, RunningActivityStatus, displayableUserMessageText, isHiddenUserMessage, ServerRunActiveContext, UserStoppedRunContext, } from "./chat/message-components.js";
|
|
23
23
|
import { repoHasAssistantMessage, getRepoMessages, getRepoMessage, shouldImportServerThreadData, dedupeRepoMessagesById, dropEmptyAssistantMessages, withLastAssistantRunDuration, } from "./chat/repo-helpers.js";
|
|
24
24
|
import { BuilderSetupCard, BuilderSetupContent, LoopLimitContinueCard, RunErrorRecoveryCard, PlanModeCallout, getLoopLimitMetadata, getRunErrorMetadata, getRequestModeMetadata, runErrorKey, } from "./chat/run-recovery.js";
|
|
25
25
|
import { createAgentChatRuntimeAdapter, } from "./chat/runtime.js";
|
|
@@ -3400,6 +3400,50 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3400
3400
|
});
|
|
3401
3401
|
}
|
|
3402
3402
|
}, [tabId, threadId, threadRuntime]);
|
|
3403
|
+
const markVisibleRunStopped = useCallback(() => {
|
|
3404
|
+
try {
|
|
3405
|
+
const repo = normalizeThreadRepository(threadRuntime.export());
|
|
3406
|
+
const messages = getRepoMessages(repo);
|
|
3407
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
3408
|
+
const entry = messages[index];
|
|
3409
|
+
const message = getRepoMessage(entry);
|
|
3410
|
+
if (message?.role !== "assistant")
|
|
3411
|
+
continue;
|
|
3412
|
+
const metadata = message.metadata ?? {};
|
|
3413
|
+
const custom = metadata.custom && typeof metadata.custom === "object"
|
|
3414
|
+
? metadata.custom
|
|
3415
|
+
: {};
|
|
3416
|
+
if (custom.userStopped === true)
|
|
3417
|
+
return;
|
|
3418
|
+
const nextMessage = {
|
|
3419
|
+
...message,
|
|
3420
|
+
metadata: {
|
|
3421
|
+
...metadata,
|
|
3422
|
+
custom: { ...custom, userStopped: true },
|
|
3423
|
+
},
|
|
3424
|
+
};
|
|
3425
|
+
const nextMessages = messages.slice();
|
|
3426
|
+
nextMessages[index] =
|
|
3427
|
+
entry.message === undefined
|
|
3428
|
+
? { ...entry, ...nextMessage }
|
|
3429
|
+
: { ...entry, message: nextMessage };
|
|
3430
|
+
threadRuntime.import(ensureMessageMetadata({ ...repo, messages: nextMessages }));
|
|
3431
|
+
return;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
catch (err) {
|
|
3435
|
+
captureError(err, {
|
|
3436
|
+
tags: {
|
|
3437
|
+
source: "agent-chat-client",
|
|
3438
|
+
phase: "mark-user-stopped-run",
|
|
3439
|
+
},
|
|
3440
|
+
extra: {
|
|
3441
|
+
threadId: threadId ?? null,
|
|
3442
|
+
tabId: tabId ?? null,
|
|
3443
|
+
},
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3446
|
+
}, [tabId, threadId, threadRuntime]);
|
|
3403
3447
|
// Abort the active server run (identical to what the Stop button does) so
|
|
3404
3448
|
// an immediate-while-running send can proceed cleanly without a 409 race.
|
|
3405
3449
|
// Captured in a stable ref so addToQueue can call it without listing
|
|
@@ -3467,6 +3511,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3467
3511
|
reconnectTailOnlyRef.current = false;
|
|
3468
3512
|
}
|
|
3469
3513
|
settleVisibleInterruptedTools();
|
|
3514
|
+
markVisibleRunStopped();
|
|
3470
3515
|
threadRuntime.cancelRun();
|
|
3471
3516
|
if (typeof window !== "undefined") {
|
|
3472
3517
|
window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
|
|
@@ -3482,6 +3527,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3482
3527
|
applyLocalQueuedMessages,
|
|
3483
3528
|
clearAutoResume,
|
|
3484
3529
|
isReconnecting,
|
|
3530
|
+
markVisibleRunStopped,
|
|
3485
3531
|
resetRunningActivity,
|
|
3486
3532
|
reconnectContent,
|
|
3487
3533
|
settleVisibleInterruptedTools,
|
|
@@ -4142,14 +4188,14 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4142
4188
|
return;
|
|
4143
4189
|
}
|
|
4144
4190
|
void addToQueue(suggestion);
|
|
4145
|
-
}, className: "w-full px-2 py-1 text-center text-[13px] text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: suggestion }, suggestion))) })) : null, showInlineEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--empty", children: resolvedThreadFooterSlot })) : null] })) : (_jsxs(MessageScrollerContent, { className: "agent-thread-content gap-4 px-4 py-4", children: [_jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(ThreadPrimitive.Messages
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4191
|
+
}, className: "w-full px-2 py-1 text-center text-[13px] text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: suggestion }, suggestion))) })) : null, showInlineEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--empty", children: resolvedThreadFooterSlot })) : null] })) : (_jsxs(MessageScrollerContent, { className: "agent-thread-content gap-4 px-4 py-4", children: [_jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(UserStoppedRunContext.Provider, { value: wasRecentlyStoppedRun, children: _jsx(ThreadPrimitive.Messages
|
|
4192
|
+
// assistant-ui indexes message parts through tap resources.
|
|
4193
|
+
// Keep this key tied to that shape so clear/add transitions remount stale
|
|
4194
|
+
// lookups without remounting on every streamed text update.
|
|
4195
|
+
, { components: {
|
|
4196
|
+
UserMessage: AssistantChatUserMessageItem,
|
|
4197
|
+
AssistantMessage: AssistantChatAssistantMessageItem,
|
|
4198
|
+
} }, messageListResetKey) }) }), visibleLoopLimit && !showRunningInUI && (_jsx(MessageScrollerItem, { children: _jsx(LoopLimitContinueCard, { info: visibleLoopLimit, onContinue: () => {
|
|
4153
4199
|
setShowContinue(false);
|
|
4154
4200
|
setLoopLimitInfo(null);
|
|
4155
4201
|
addToQueue("Continue from where you left off.", undefined, undefined, undefined, undefined, "queued", "continue");
|
|
@@ -4216,7 +4262,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4216
4262
|
isRunning,
|
|
4217
4263
|
requestedIntent: options?.intent,
|
|
4218
4264
|
}), undefined, true)
|
|
4219
|
-
: undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, modelListLoading: modelListLoading, onModelChange: onModelChange, onEffortChange: onEffortChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => stopActiveRun(
|
|
4265
|
+
: undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, modelListLoading: modelListLoading, onModelChange: onModelChange, onEffortChange: onEffortChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => stopActiveRun({
|
|
4266
|
+
preserveQueuedMessages: true,
|
|
4267
|
+
}), "aria-label": "Stop response", "data-agent-composer-slot": "stop-button", className: "shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(IconPlayerStopFilled, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: "Stop response" })] })) : undefined })] })) }), showMissingKeySetup &&
|
|
4220
4268
|
!showInlineMissingKeySetup ? (_jsx(PopoverContent, { side: missingApiKeySetupLayout === "sidebar"
|
|
4221
4269
|
? "top"
|
|
4222
4270
|
: "bottom", align: "center", sideOffset: 8, collisionPadding: 12, "data-agent-native-composer-popover": "true", className: "z-[260] box-border w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,26rem),26rem)] rounded-lg border-border p-3 shadow-lg", children: _jsx(BuilderSetupContent, { onConnected: handleBuilderConnected, layout: missingApiKeySetupLayout }) })) : null] })] })] }) }) }) }) }) }) }) }) }) }));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline storage setup shown when an attachment cannot be made durable.
|
|
3
|
+
* Builder connect and the custom-key path intentionally share the same
|
|
4
|
+
* surface so the user does not have to understand provider internals first.
|
|
5
|
+
*/
|
|
6
|
+
export declare function FileStorageSetupCard(): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ActionButton } from "@agent-native/toolkit/design-system";
|
|
3
|
+
import { BuilderConnectCard, DefaultBuilderConnectCardView, } from "./setup-connections/BuilderConnectCard.js";
|
|
4
|
+
/**
|
|
5
|
+
* Inline storage setup shown when an attachment cannot be made durable.
|
|
6
|
+
* Builder connect and the custom-key path intentionally share the same
|
|
7
|
+
* surface so the user does not have to understand provider internals first.
|
|
8
|
+
*/
|
|
9
|
+
export function FileStorageSetupCard() {
|
|
10
|
+
const openCustomStorage = () => {
|
|
11
|
+
if (typeof window === "undefined")
|
|
12
|
+
return;
|
|
13
|
+
window.dispatchEvent(new CustomEvent("agent-panel:open-settings", {
|
|
14
|
+
detail: { section: "uploads" },
|
|
15
|
+
}));
|
|
16
|
+
};
|
|
17
|
+
return (_jsx("div", { className: "space-y-2", "data-testid": "file-storage-setup-card", children: _jsx(BuilderConnectCard, { title: "Connect Builder for file storage", description: "Builder keeps uploaded images and files on durable object storage so the agent can reuse them throughout this thread.", trackingSource: "file_upload_chat_card", render: ({ viewModel, className }) => (_jsxs("div", { className: "space-y-2", children: [_jsx(DefaultBuilderConnectCardView, { viewModel: viewModel, className: className }), _jsxs(ActionButton, { type: "button", intent: "neutral", emphasis: "outline", size: "compact", onPress: openCustomStorage, className: "h-auto w-full justify-start rounded-md px-3 py-2 text-left text-xs font-medium text-foreground", children: ["Use custom storage keys", _jsx("span", { className: "mt-0.5 block text-[11px] font-normal text-muted-foreground", children: "Configure an S3-compatible bucket with a stable public URL." })] })] })) }) }));
|
|
18
|
+
}
|
|
@@ -241,6 +241,16 @@ export function NewWorkspaceAppFlow({ sourceApp = "chat", className = "", dispat
|
|
|
241
241
|
setBranchUrl(result?.url || null);
|
|
242
242
|
setStatusMessage("Builder branch created.");
|
|
243
243
|
}
|
|
244
|
+
else if (result?.mode === "local-agent") {
|
|
245
|
+
sendToAgentChat({
|
|
246
|
+
message: result.prompt ?? message,
|
|
247
|
+
submit: true,
|
|
248
|
+
type: "code",
|
|
249
|
+
newTab: true,
|
|
250
|
+
reuseEmptyTab: true,
|
|
251
|
+
});
|
|
252
|
+
setStatusMessage("Sent to the local agent.");
|
|
253
|
+
}
|
|
244
254
|
else {
|
|
245
255
|
setStatusMessage(result?.message ||
|
|
246
256
|
"This requires a code change. Edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way you like.");
|
|
@@ -217,6 +217,11 @@ const BACKGROUND_CONTINUATION_TERMINAL_REASONS = new Set([
|
|
|
217
217
|
"network_interrupted",
|
|
218
218
|
"no_progress",
|
|
219
219
|
]);
|
|
220
|
+
function isUserInitiatedTerminalReason(reason) {
|
|
221
|
+
return (reason === "aborted:user" ||
|
|
222
|
+
reason === "aborted:abort" ||
|
|
223
|
+
reason.startsWith("aborted:user_"));
|
|
224
|
+
}
|
|
220
225
|
/**
|
|
221
226
|
* True when the given `/runs/active` snapshot, if surfaced as a background
|
|
222
227
|
* terminal outcome right now, would render as an error rather than a
|
|
@@ -230,6 +235,8 @@ function isBackgroundTerminalErrorOutcome(lastKnown) {
|
|
|
230
235
|
const rawTerminalReason = typeof lastKnown?.terminalReason === "string"
|
|
231
236
|
? lastKnown.terminalReason
|
|
232
237
|
: "";
|
|
238
|
+
if (isUserInitiatedTerminalReason(rawTerminalReason))
|
|
239
|
+
return false;
|
|
233
240
|
const hasErrorTerminalReason = rawTerminalReason.startsWith("error:");
|
|
234
241
|
const terminalReason = rawTerminalReason.replace(/^error:/, "");
|
|
235
242
|
const mappedMessage = terminalReason
|
|
@@ -360,37 +367,73 @@ function extractAttachmentsFromMessage(message) {
|
|
|
360
367
|
for (const att of message.attachments ?? []) {
|
|
361
368
|
for (const part of att.content) {
|
|
362
369
|
if (part.type === "image" && typeof part.image === "string") {
|
|
370
|
+
const persistedMetadata = att && typeof att.metadata === "object"
|
|
371
|
+
? att.metadata
|
|
372
|
+
: undefined;
|
|
373
|
+
const imageIsDataUrl = part.image.startsWith("data:");
|
|
363
374
|
attachments.push({
|
|
364
375
|
type: "image",
|
|
365
376
|
name: att.name,
|
|
366
377
|
contentType: att.contentType,
|
|
367
|
-
data: part.image,
|
|
378
|
+
...(imageIsDataUrl ? { data: part.image } : { url: part.image }),
|
|
379
|
+
...(typeof persistedMetadata?.uploadProvider === "string"
|
|
380
|
+
? { uploadProvider: persistedMetadata.uploadProvider }
|
|
381
|
+
: {}),
|
|
382
|
+
...(persistedMetadata?.referenceOnly === true
|
|
383
|
+
? {
|
|
384
|
+
referenceOnly: true,
|
|
385
|
+
...(typeof persistedMetadata.securityNote === "string"
|
|
386
|
+
? { securityNote: persistedMetadata.securityNote }
|
|
387
|
+
: {}),
|
|
388
|
+
}
|
|
389
|
+
: {}),
|
|
368
390
|
});
|
|
369
391
|
}
|
|
370
|
-
else if (part.type === "file" &&
|
|
392
|
+
else if (part.type === "file" &&
|
|
393
|
+
(typeof part.data === "string" || typeof part.url === "string")) {
|
|
371
394
|
const contentType = att.contentType ??
|
|
372
395
|
(typeof part.mimeType === "string" ? part.mimeType : undefined);
|
|
373
|
-
const
|
|
396
|
+
const data = typeof part.data === "string" ? part.data : undefined;
|
|
397
|
+
const url = typeof part.url === "string" ? part.url : undefined;
|
|
398
|
+
const persistedMetadata = att && typeof att.metadata === "object"
|
|
399
|
+
? att.metadata
|
|
400
|
+
: undefined;
|
|
401
|
+
const preserveDataUrl = typeof data === "string" &&
|
|
402
|
+
data.startsWith("data:") &&
|
|
374
403
|
shouldPreserveFileDataUrl({ name: att.name, contentType });
|
|
375
|
-
const decodedText =
|
|
376
|
-
? decodeTextDataUrl(
|
|
404
|
+
const decodedText = data?.startsWith("data:")
|
|
405
|
+
? decodeTextDataUrl(data)
|
|
377
406
|
: null;
|
|
378
407
|
attachments.push({
|
|
379
408
|
type: "file",
|
|
380
409
|
name: att.name,
|
|
381
410
|
contentType,
|
|
411
|
+
...(url ? { url } : {}),
|
|
382
412
|
...(preserveDataUrl
|
|
383
413
|
? {
|
|
384
|
-
data
|
|
414
|
+
data,
|
|
385
415
|
...(decodedText !== null
|
|
386
416
|
? { text: truncateOutboundAttachment(decodedText) }
|
|
387
417
|
: {}),
|
|
388
418
|
}
|
|
389
419
|
: decodedText !== null
|
|
390
420
|
? { text: truncateOutboundAttachment(decodedText) }
|
|
391
|
-
:
|
|
392
|
-
? { data
|
|
393
|
-
:
|
|
421
|
+
: data?.startsWith("data:")
|
|
422
|
+
? { data }
|
|
423
|
+
: url
|
|
424
|
+
? {}
|
|
425
|
+
: { text: truncateOutboundAttachment(data ?? "") }),
|
|
426
|
+
...(typeof persistedMetadata?.uploadProvider === "string"
|
|
427
|
+
? { uploadProvider: persistedMetadata.uploadProvider }
|
|
428
|
+
: {}),
|
|
429
|
+
...(persistedMetadata?.referenceOnly === true
|
|
430
|
+
? {
|
|
431
|
+
referenceOnly: true,
|
|
432
|
+
...(typeof persistedMetadata.securityNote === "string"
|
|
433
|
+
? { securityNote: persistedMetadata.securityNote }
|
|
434
|
+
: {}),
|
|
435
|
+
}
|
|
436
|
+
: {}),
|
|
394
437
|
});
|
|
395
438
|
}
|
|
396
439
|
else if (part.type === "text" && typeof part.text === "string") {
|
|
@@ -405,11 +448,12 @@ function extractAttachmentsFromMessage(message) {
|
|
|
405
448
|
}
|
|
406
449
|
for (const part of message.content ?? []) {
|
|
407
450
|
if (part.type === "image" && typeof part.image === "string") {
|
|
451
|
+
const imageIsDataUrl = part.image.startsWith("data:");
|
|
408
452
|
attachments.push({
|
|
409
453
|
type: "image",
|
|
410
454
|
name: "image",
|
|
411
455
|
contentType: /^data:([^;,]+)/.exec(part.image)?.[1],
|
|
412
|
-
data: part.image,
|
|
456
|
+
...(imageIsDataUrl ? { data: part.image } : { url: part.image }),
|
|
413
457
|
});
|
|
414
458
|
}
|
|
415
459
|
}
|
|
@@ -447,11 +491,15 @@ function attachmentHistoryText(attachment) {
|
|
|
447
491
|
attachment.type
|
|
448
492
|
? `type="${escapeAttachmentAttribute(attachment.type)}"`
|
|
449
493
|
: null,
|
|
494
|
+
attachment.url
|
|
495
|
+
? `url="${escapeAttachmentAttribute(attachment.url)}"`
|
|
496
|
+
: null,
|
|
450
497
|
].filter(Boolean);
|
|
451
498
|
return `<attachment ${attrs.join(" ")}>\n${truncateHistoryAttachment(attachment.text)}\n</attachment>`;
|
|
452
499
|
}
|
|
453
500
|
if (attachment.name) {
|
|
454
|
-
|
|
501
|
+
const hostedUrl = attachment.url ? `; hosted URL: ${attachment.url}` : "";
|
|
502
|
+
return `[Attached ${attachment.type || "file"}: ${attachment.name}${attachment.contentType ? ` (${attachment.contentType})` : ""}${hostedUrl}]`;
|
|
455
503
|
}
|
|
456
504
|
return null;
|
|
457
505
|
}
|
|
@@ -2071,6 +2119,27 @@ export function createAgentChatAdapter(options) {
|
|
|
2071
2119
|
const rawTerminalReason = typeof lastKnown?.terminalReason === "string"
|
|
2072
2120
|
? lastKnown.terminalReason
|
|
2073
2121
|
: "";
|
|
2122
|
+
if (isUserInitiatedTerminalReason(rawTerminalReason)) {
|
|
2123
|
+
settleInterruptedToolCalls(content, undefined, {
|
|
2124
|
+
includeActivity: true,
|
|
2125
|
+
});
|
|
2126
|
+
settleTerminalChatRun();
|
|
2127
|
+
yield {
|
|
2128
|
+
content: [...content],
|
|
2129
|
+
status: {
|
|
2130
|
+
type: "complete",
|
|
2131
|
+
reason: "stop",
|
|
2132
|
+
},
|
|
2133
|
+
metadata: {
|
|
2134
|
+
custom: {
|
|
2135
|
+
...(runId ? { runId } : {}),
|
|
2136
|
+
userStopped: true,
|
|
2137
|
+
},
|
|
2138
|
+
},
|
|
2139
|
+
};
|
|
2140
|
+
clearActiveRun();
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2074
2143
|
// terminal_reason is either a bare reason ("dispatch_payload_missing")
|
|
2075
2144
|
// or "error:<errorCode>" when derived from a terminal error event.
|
|
2076
2145
|
const hasErrorTerminalReason = rawTerminalReason.startsWith("error:");
|
|
@@ -2406,6 +2475,13 @@ export function createAgentChatAdapter(options) {
|
|
|
2406
2475
|
if (activeRunId && isOurRun) {
|
|
2407
2476
|
lastSeenActive = active;
|
|
2408
2477
|
updateCurrentRunDispatchMode(active?.dispatchMode);
|
|
2478
|
+
const activeTerminalReason = typeof active?.terminalReason === "string"
|
|
2479
|
+
? active.terminalReason
|
|
2480
|
+
: "";
|
|
2481
|
+
if (isUserInitiatedTerminalReason(activeTerminalReason)) {
|
|
2482
|
+
yield* emitBackgroundTerminalOutcome(active);
|
|
2483
|
+
return "completed";
|
|
2484
|
+
}
|
|
2409
2485
|
const isTerminal = activeStatus === "completed" || activeStatus === "errored";
|
|
2410
2486
|
if (isTerminal && replayedTerminalRunIds.has(activeRunId)) {
|
|
2411
2487
|
if (lastAutoContinueReason === "stale_run" &&
|
|
@@ -23,7 +23,7 @@ export interface AgentChatMessage {
|
|
|
23
23
|
referenceImagePaths?: string[];
|
|
24
24
|
/** Optional uploaded reference images */
|
|
25
25
|
uploadedReferenceImages?: string[];
|
|
26
|
-
/** Optional image data URLs to include in the submitted chat message */
|
|
26
|
+
/** Optional image data URLs or durable image URLs to include in the submitted chat message */
|
|
27
27
|
images?: string[];
|
|
28
28
|
/** Stable tab identifier — auto-generated if omitted */
|
|
29
29
|
tabId?: string;
|
|
@@ -561,9 +561,14 @@ export function parseSubmitChatMessage(event) {
|
|
|
561
561
|
const message = typeof raw.message === "string" ? raw.message : "";
|
|
562
562
|
if (!message)
|
|
563
563
|
return null;
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
:
|
|
564
|
+
const imageSources = [
|
|
565
|
+
...(Array.isArray(raw.images) ? raw.images : []),
|
|
566
|
+
...(Array.isArray(raw.referenceImagePaths) ? raw.referenceImagePaths : []),
|
|
567
|
+
...(Array.isArray(raw.uploadedReferenceImages)
|
|
568
|
+
? raw.uploadedReferenceImages
|
|
569
|
+
: []),
|
|
570
|
+
].filter((image) => typeof image === "string" && image.trim().length > 0);
|
|
571
|
+
const images = imageSources.length > 0 ? [...new Set(imageSources)] : undefined;
|
|
567
572
|
return {
|
|
568
573
|
message,
|
|
569
574
|
context: typeof raw.context === "string" ? raw.context : undefined,
|
|
@@ -267,6 +267,16 @@ export function serializeAttachmentContentPart(part) {
|
|
|
267
267
|
...(typeof part.filename === "string" ? { filename: part.filename } : {}),
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
|
+
if (part.type === "file" && typeof part.url === "string") {
|
|
271
|
+
return {
|
|
272
|
+
type: "file",
|
|
273
|
+
url: part.url,
|
|
274
|
+
mimeType: typeof part.mimeType === "string"
|
|
275
|
+
? part.mimeType
|
|
276
|
+
: "application/octet-stream",
|
|
277
|
+
...(typeof part.filename === "string" ? { filename: part.filename } : {}),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
270
280
|
return null;
|
|
271
281
|
}
|
|
272
282
|
export async function serializeQueuedAttachments(attachments) {
|
|
@@ -51,6 +51,7 @@ export declare function shouldOfferRestore(args: {
|
|
|
51
51
|
export declare function assistantMessageRunId(message: unknown): string | undefined;
|
|
52
52
|
/** Stable logical-turn identity shared by chained continuation run IDs. */
|
|
53
53
|
export declare function assistantMessageTurnId(message: unknown): string | undefined;
|
|
54
|
+
export declare function assistantMessageWasUserStopped(message: unknown): boolean;
|
|
54
55
|
export declare function resolveAssistantRequestId(message: unknown, activeRun: {
|
|
55
56
|
threadId: string;
|
|
56
57
|
runId: string;
|
|
@@ -91,13 +92,15 @@ export declare function shouldShowAssistantMessageFooter({ isLast, chatRunning,
|
|
|
91
92
|
* that the agent stopped.
|
|
92
93
|
*/
|
|
93
94
|
export declare const ServerRunActiveContext: React.Context<boolean>;
|
|
94
|
-
export declare
|
|
95
|
+
export declare const UserStoppedRunContext: React.Context<(runId?: string) => boolean>;
|
|
96
|
+
export declare function shouldShowMissingFinalResponse({ isCurrentTurnRunning, serverRunActive, statusIsTerminal, hasAssistantText, hasUnresolvedTool, hasCompletedCustomUi, userStoppedRun, }: {
|
|
95
97
|
isCurrentTurnRunning: boolean;
|
|
96
98
|
serverRunActive?: boolean;
|
|
97
99
|
statusIsTerminal: boolean;
|
|
98
100
|
hasAssistantText: boolean;
|
|
99
101
|
hasUnresolvedTool: boolean;
|
|
100
102
|
hasCompletedCustomUi?: boolean;
|
|
103
|
+
userStoppedRun?: boolean;
|
|
101
104
|
}): boolean;
|
|
102
105
|
export declare function useSettledFlag(active: boolean, delayMs: number): boolean;
|
|
103
106
|
export declare function shouldShowAssistantWorkSummary({ isLast, isComplete, hasCollapsibleWork, hasUnresolvedTool, chatRunning, }: {
|