@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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// Owns: message-timestamp helpers, SelectionAttachedPill, UserMessage,
|
|
3
3
|
// AssistantMessage, MessageBranchPicker, CheckpointContext, MessageActionsContext,
|
|
4
|
-
// RunningActivityStatus, ThinkingIndicator, and
|
|
4
|
+
// UserStoppedRunContext, RunningActivityStatus, ThinkingIndicator, and
|
|
5
|
+
// displayableUserMessageText.
|
|
5
6
|
import { isPastedTextAttachmentName } from "@agent-native/toolkit/composer/pasted-text";
|
|
6
7
|
import { PastedTextChip } from "@agent-native/toolkit/composer/PastedTextChip";
|
|
7
8
|
import { useThread, useMessageRuntime, useComposer, MessagePrimitive, ActionBarPrimitive, BranchPickerPrimitive, ComposerPrimitive, useMessagePartReasoning, useMessagePartRuntime, useAuiState, } from "@assistant-ui/react";
|
|
@@ -181,6 +182,10 @@ export function assistantMessageTurnId(message) {
|
|
|
181
182
|
? metadata.turnId
|
|
182
183
|
: undefined;
|
|
183
184
|
}
|
|
185
|
+
export function assistantMessageWasUserStopped(message) {
|
|
186
|
+
const metadata = message?.metadata;
|
|
187
|
+
return (metadata?.custom?.userStopped === true || metadata?.userStopped === true);
|
|
188
|
+
}
|
|
184
189
|
export function resolveAssistantRequestId(message, activeRun, threadId) {
|
|
185
190
|
return (assistantMessageRunId(message) ||
|
|
186
191
|
(activeRun?.threadId === threadId ? activeRun.runId : undefined));
|
|
@@ -630,7 +635,10 @@ export function shouldShowAssistantMessageFooter({ isLast, chatRunning, activeRu
|
|
|
630
635
|
* that the agent stopped.
|
|
631
636
|
*/
|
|
632
637
|
export const ServerRunActiveContext = React.createContext(false);
|
|
633
|
-
export
|
|
638
|
+
export const UserStoppedRunContext = React.createContext(() => false);
|
|
639
|
+
export function shouldShowMissingFinalResponse({ isCurrentTurnRunning, serverRunActive, statusIsTerminal, hasAssistantText, hasUnresolvedTool, hasCompletedCustomUi, userStoppedRun, }) {
|
|
640
|
+
if (userStoppedRun)
|
|
641
|
+
return false;
|
|
634
642
|
if (serverRunActive)
|
|
635
643
|
return false;
|
|
636
644
|
// A completed tool can make the latest message look terminal before the
|
|
@@ -703,7 +711,10 @@ function ReasoningMessagePart() {
|
|
|
703
711
|
}, [isStreaming]);
|
|
704
712
|
return (_jsx(ReasoningCell, { text: part.text, isStreaming: isStreaming, resetKey: `message-reasoning-${partIndex}`, durationMs: durationMs, defaultOpen: partIndex === latestReasoningPartIndex, collapseWhenReplaced: partIndex < latestReasoningPartIndex }));
|
|
705
713
|
}
|
|
706
|
-
const ALWAYS_VISIBLE_ASSISTANT_TOOLS = new Set([
|
|
714
|
+
const ALWAYS_VISIBLE_ASSISTANT_TOOLS = new Set([
|
|
715
|
+
"connect-builder",
|
|
716
|
+
"connect-file-storage",
|
|
717
|
+
]);
|
|
707
718
|
export function isAlwaysVisibleAssistantTool(part) {
|
|
708
719
|
return (part.type === "tool-call" &&
|
|
709
720
|
typeof part.toolName === "string" &&
|
|
@@ -805,6 +816,10 @@ export function AssistantMessage() {
|
|
|
805
816
|
const isLast = thread.messages.length > 0 &&
|
|
806
817
|
thread.messages[thread.messages.length - 1].id === msg.id;
|
|
807
818
|
const wasLiveRef = useRef(false);
|
|
819
|
+
const messageRunId = assistantMessageRunId(msg);
|
|
820
|
+
const messageTurnId = assistantMessageTurnId(msg);
|
|
821
|
+
const userStoppedRun = React.useContext(UserStoppedRunContext);
|
|
822
|
+
const isUserStoppedRun = assistantMessageWasUserStopped(msg) || userStoppedRun(messageRunId);
|
|
808
823
|
const hasRenderableContent = assistantMessageHasRenderableContent(msg);
|
|
809
824
|
const hasUnresolvedTool = assistantMessageHasUnresolvedTool(msg.content);
|
|
810
825
|
const missingWarningText = missingFinalResponseWarningText(msg.content);
|
|
@@ -812,15 +827,14 @@ export function AssistantMessage() {
|
|
|
812
827
|
const statusIsTerminal = assistantMessageStatusIsTerminal(msg);
|
|
813
828
|
const hasCompletedCustomUi = assistantMessageHasCompletedCustomUi(msg.content);
|
|
814
829
|
const hasCustomUi = assistantMessageHasCustomUi(msg.content);
|
|
815
|
-
const messageRunId = assistantMessageRunId(msg);
|
|
816
|
-
const messageTurnId = assistantMessageTurnId(msg);
|
|
817
830
|
const serverRunActive = React.useContext(ServerRunActiveContext);
|
|
818
831
|
const messageRunError = getRunErrorMetadata(msg);
|
|
819
832
|
const messageActions = React.useContext(MessageActionsContext);
|
|
820
|
-
const showInlineRunError =
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
833
|
+
const showInlineRunError = !isUserStoppedRun &&
|
|
834
|
+
shouldShowInlineRunError({
|
|
835
|
+
runError: messageRunError,
|
|
836
|
+
bannerRunErrorKey: messageActions?.bannerRunErrorKey,
|
|
837
|
+
});
|
|
824
838
|
const missingFinalResponseCandidate = missingWarningText == null &&
|
|
825
839
|
shouldShowMissingFinalResponse({
|
|
826
840
|
isCurrentTurnRunning: isLast && chatRunning,
|
|
@@ -829,12 +843,18 @@ export function AssistantMessage() {
|
|
|
829
843
|
hasAssistantText: responseConnectionText.trim().length > 0,
|
|
830
844
|
hasUnresolvedTool,
|
|
831
845
|
hasCompletedCustomUi,
|
|
846
|
+
userStoppedRun: isUserStoppedRun,
|
|
832
847
|
});
|
|
833
848
|
const showMissingFinalResponse = useSettledFlag(missingFinalResponseCandidate, isLast ? MISSING_FINAL_RESPONSE_SETTLE_MS : 0);
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
849
|
+
const shouldShowUserStoppedNotice = isUserStoppedRun && isLast && responseConnectionText.trim().length === 0;
|
|
850
|
+
const missingFinalResponseNoticeText = shouldShowUserStoppedNotice
|
|
851
|
+
? "Stopped"
|
|
852
|
+
: isUserStoppedRun
|
|
853
|
+
? null
|
|
854
|
+
: (missingWarningText ??
|
|
855
|
+
(showMissingFinalResponse
|
|
856
|
+
? "The agent stopped without sending a final message. Ask the agent to continue or retry."
|
|
857
|
+
: null));
|
|
838
858
|
const animateMissingFinalResponse = Boolean(isLast && missingFinalResponseNoticeText && wasLiveRef.current);
|
|
839
859
|
const missingFinalResponseAnimationKey = animateMissingFinalResponse
|
|
840
860
|
? missingFinalResponseNoticeText
|
|
@@ -999,6 +1019,10 @@ export function AssistantMessage() {
|
|
|
999
1019
|
return (_jsx(RanToolsSummary, { toolCount: toolCount, children: children }));
|
|
1000
1020
|
}
|
|
1001
1021
|
case "text":
|
|
1022
|
+
if (isUserStoppedRun &&
|
|
1023
|
+
isMissingFinalResponseWarningText(part.text)) {
|
|
1024
|
+
return shouldShowUserStoppedNotice ? (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: "Stopped", animate: false })) : null;
|
|
1025
|
+
}
|
|
1002
1026
|
if (missingWarningText != null &&
|
|
1003
1027
|
part.text === missingWarningText) {
|
|
1004
1028
|
return (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: part.text, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal }));
|
|
@@ -5,6 +5,7 @@ import { AgentTaskCard } from "../AgentTaskCard.js";
|
|
|
5
5
|
import { writeClipboardText } from "../clipboard.js";
|
|
6
6
|
import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
|
|
7
7
|
import { ConnectBuilderCard } from "../ConnectBuilderCard.js";
|
|
8
|
+
import { FileStorageSetupCard } from "../FileStorageSetupCard.js";
|
|
8
9
|
import { useT } from "../i18n.js";
|
|
9
10
|
import { McpAppRenderer } from "../mcp-apps/McpAppRenderer.js";
|
|
10
11
|
import { findMcpIntegrationForToolName } from "../resources/mcp-integration-catalog.js";
|
|
@@ -323,6 +324,19 @@ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result,
|
|
|
323
324
|
}
|
|
324
325
|
}
|
|
325
326
|
catch {
|
|
327
|
+
// coercion-ok: malformed tool output should fall through to the default tool pill
|
|
328
|
+
// fall through to default pill rendering
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (toolName === "connect-file-storage" && result) {
|
|
332
|
+
try {
|
|
333
|
+
const parsed = JSON.parse(result);
|
|
334
|
+
if (parsed?.kind === "connect-file-storage-card") {
|
|
335
|
+
return _jsx(FileStorageSetupCard, {});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
// coercion-ok: malformed storage tool output should fall through to the default tool pill
|
|
326
340
|
// fall through to default pill rendering
|
|
327
341
|
}
|
|
328
342
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ChatFirstAppRailProps } from "./types.js";
|
|
2
|
-
export declare const ChatFirstAppsRail: import("react").MemoExoticComponent<({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps) => import("react").JSX.Element>;
|
|
2
|
+
export declare const ChatFirstAppsRail: import("react").MemoExoticComponent<({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onRemoveApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps) => import("react").JSX.Element>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Skeleton, Tooltip, TooltipContent, TooltipTrigger, } from "@agent-native/toolkit/ui";
|
|
3
|
-
import { IconApps, IconChevronDown, IconChevronUp, IconPin, IconPlus, } from "@tabler/icons-react";
|
|
3
|
+
import { IconApps, IconChevronDown, IconChevronUp, IconPin, IconPlus, IconTrash, } from "@tabler/icons-react";
|
|
4
4
|
import { memo, useMemo, useState } from "react";
|
|
5
5
|
import { orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
|
|
6
6
|
import { cn } from "../utils.js";
|
|
7
7
|
import { defaultChatFirstCopy } from "./copy.js";
|
|
8
|
-
function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, onOpenApp, onTogglePinned, onMove, renderIcon, copy, }) {
|
|
8
|
+
function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, onOpenApp, onRemoveApp, onTogglePinned, onMove, renderIcon, copy, }) {
|
|
9
9
|
const orderedIds = orderChatFirstAppIds(apps.map((app) => app.id), layout);
|
|
10
10
|
const appsById = new Map(apps.map((app) => [app.id, app]));
|
|
11
11
|
const orderedApps = orderedIds
|
|
@@ -33,10 +33,10 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
|
|
|
33
33
|
}
|
|
34
34
|
}, "aria-keyshortcuts": "Alt+ArrowUp Alt+ArrowDown", "aria-label": copy("openApp", { name: app.name }), children: [renderIcon(app), _jsx("span", { className: "truncate", children: app.name })] }), _jsx("button", { type: "button", className: cn("flex size-6 shrink-0 items-center justify-center rounded text-sidebar-foreground/45 opacity-0 hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100", pinned && "text-sidebar-foreground/70 opacity-100"), "aria-label": copy(pinned ? "unpinApp" : "pinApp", {
|
|
35
35
|
name: app.name,
|
|
36
|
-
}), "aria-pressed": pinned, onClick: () => onTogglePinned(app.id), children: _jsx(IconPin, { size: 13, strokeWidth: pinned ? 2.2 : 1.6, "aria-hidden": "true" }) }), _jsx("span", { className: "sr-only", children: `${index + 1} of ${orderedApps.length}` })] }) }), _jsxs(ContextMenuContent, { children: [_jsxs(ContextMenuItem, { onSelect: () => onTogglePinned(app.id), children: [_jsx(IconPin, { size: 14, "aria-hidden": "true" }), pinned ? copy("removePinned") : copy("pinTop")] }), _jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { disabled: index === 0, onSelect: () => onMove(app.id, -1), children: [_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" }), copy("moveUp")] }), _jsxs(ContextMenuItem, { disabled: index === orderedApps.length - 1, onSelect: () => onMove(app.id, 1), children: [_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }), copy("moveDown")] })] })] }, app.id));
|
|
36
|
+
}), "aria-pressed": pinned, onClick: () => onTogglePinned(app.id), children: _jsx(IconPin, { size: 13, strokeWidth: pinned ? 2.2 : 1.6, "aria-hidden": "true" }) }), _jsx("span", { className: "sr-only", children: `${index + 1} of ${orderedApps.length}` })] }) }), _jsxs(ContextMenuContent, { children: [_jsxs(ContextMenuItem, { onSelect: () => onTogglePinned(app.id), children: [_jsx(IconPin, { size: 14, "aria-hidden": "true" }), pinned ? copy("removePinned") : copy("pinTop")] }), _jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { disabled: index === 0, onSelect: () => onMove(app.id, -1), children: [_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" }), copy("moveUp")] }), _jsxs(ContextMenuItem, { disabled: index === orderedApps.length - 1, onSelect: () => onMove(app.id, 1), children: [_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }), copy("moveDown")] }), onRemoveApp ? (_jsxs(_Fragment, { children: [_jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { onSelect: () => onRemoveApp(app), className: "text-destructive focus:text-destructive", children: [_jsx(IconTrash, { size: 14, "aria-hidden": "true" }), copy("removeApp")] })] })) : null] })] }, app.id));
|
|
37
37
|
}) }));
|
|
38
38
|
}
|
|
39
|
-
export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
|
|
39
|
+
export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onRemoveApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
|
|
40
40
|
const [localLayout, setLocalLayout] = useState(() => readChatFirstAppLayout());
|
|
41
41
|
const layout = controlledLayout ?? localLayout;
|
|
42
42
|
const [draggedAppId, setDraggedAppId] = useState(null);
|
|
@@ -99,5 +99,5 @@ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeA
|
|
|
99
99
|
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
100
100
|
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), onOpenAllApps ? (_jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), title: copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
|
|
101
101
|
}
|
|
102
|
-
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-2", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-2", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-2 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
|
|
102
|
+
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-2", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-2", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onRemoveApp: onRemoveApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-2 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
|
|
103
103
|
});
|
|
@@ -18,6 +18,7 @@ const DEFAULT_COPY = {
|
|
|
18
18
|
pinTop: "Pin app to the top",
|
|
19
19
|
moveUp: "Move up",
|
|
20
20
|
moveDown: "Move down",
|
|
21
|
+
removeApp: "Remove app from workspace",
|
|
21
22
|
showMore: "Show more",
|
|
22
23
|
showLess: "Show less",
|
|
23
24
|
appOrderUnavailable: "App order could not be saved on this device.",
|
|
@@ -47,6 +47,7 @@ export interface ChatFirstAppRailProps {
|
|
|
47
47
|
onLayoutError?: (reason: "unavailable" | "write-failed") => void;
|
|
48
48
|
onRetry?: () => void;
|
|
49
49
|
onOpenApp: (app: ChatFirstAppItem) => void;
|
|
50
|
+
onRemoveApp?: (app: ChatFirstAppItem) => void;
|
|
50
51
|
onOpenAllApps?: () => void;
|
|
51
52
|
onCreateApp?: () => void;
|
|
52
53
|
createAppTrigger?: ReactNode;
|
|
@@ -235,7 +235,7 @@ export function EmbeddedExtension({ extensionId, slotId, context, className, ini
|
|
|
235
235
|
sendToAgentChat({
|
|
236
236
|
message: text,
|
|
237
237
|
context: serializeChatValue(message.context),
|
|
238
|
-
submit: message.submit
|
|
238
|
+
submit: message.submit === true,
|
|
239
239
|
openSidebar: message.openSidebar !== false,
|
|
240
240
|
});
|
|
241
241
|
return;
|
|
@@ -476,7 +476,7 @@ export function ExtensionViewer({ extensionId }) {
|
|
|
476
476
|
sendToAgentChat({
|
|
477
477
|
message: text,
|
|
478
478
|
context: serializeChatValue(message.context),
|
|
479
|
-
submit: message.submit
|
|
479
|
+
submit: message.submit === true,
|
|
480
480
|
openSidebar: message.openSidebar !== false,
|
|
481
481
|
});
|
|
482
482
|
return;
|
|
@@ -361,7 +361,7 @@ export function InlineExtensionFrame({ extensionId, extension: providedExtension
|
|
|
361
361
|
sendToAgentChat({
|
|
362
362
|
message: text,
|
|
363
363
|
context: serializeChatValue(message.context),
|
|
364
|
-
submit: message.submit
|
|
364
|
+
submit: message.submit === true,
|
|
365
365
|
openSidebar: message.openSidebar !== false,
|
|
366
366
|
});
|
|
367
367
|
return;
|
|
@@ -226,7 +226,16 @@ function OnboardingSkeleton() {
|
|
|
226
226
|
return (_jsxs("div", { className: "fixed inset-0 z-[100] flex h-full min-h-0 flex-col bg-background", "data-onboarding-loading": "true", "aria-busy": "true", children: [_jsx(Skeleton, { className: "h-0.5 w-full shrink-0" }), _jsxs("div", { className: "mx-auto flex w-full max-w-lg flex-1 flex-col items-center justify-center px-5 sm:px-8", children: [_jsx(Skeleton, { className: "h-10 w-64" }), _jsx(Skeleton, { className: "mt-3 h-10 w-52" }), _jsx(Skeleton, { className: "mt-7 h-3 w-44" }), _jsx(Skeleton, { className: "mt-7 h-10 w-24 rounded-lg" })] })] }));
|
|
227
227
|
}
|
|
228
228
|
function CapabilityList({ capabilities, compact = false, className, }) {
|
|
229
|
-
const visibleCapabilities = useMemo(() =>
|
|
229
|
+
const visibleCapabilities = useMemo(() => {
|
|
230
|
+
if (!compact)
|
|
231
|
+
return capabilities;
|
|
232
|
+
const suggested = capabilities.filter((capability) => capability.suggested);
|
|
233
|
+
const leading = capabilities.filter((capability) => !capability.suggested);
|
|
234
|
+
return [
|
|
235
|
+
...leading.slice(0, Math.max(0, 4 - suggested.length)),
|
|
236
|
+
...suggested,
|
|
237
|
+
].slice(0, 4);
|
|
238
|
+
}, [capabilities, compact]);
|
|
230
239
|
return (_jsxs("div", { className: cn("grid", className), children: [!compact && (_jsx("p", { className: "mb-2 text-xs font-medium text-muted-foreground", children: "Keys and integrations" })), _jsx("div", { className: "divide-y divide-border", children: visibleCapabilities.map((capability) => (_jsx(CapabilityRow, { capability: capability, compact: compact }, capability.id))) })] }));
|
|
231
240
|
}
|
|
232
241
|
function McpIntegrationCard({ integration, connected, busy, onConnect, }) {
|
|
@@ -234,7 +243,13 @@ function McpIntegrationCard({ integration, connected, busy, onConnect, }) {
|
|
|
234
243
|
return (_jsxs("article", { className: "flex min-w-0 items-center gap-3 border-b border-border/70 py-3.5 sm:pe-4", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [_jsx(McpIntegrationLogo, { name: integration.name, logoUrl: integration.logoUrl, integrationId: integration.id, className: "size-8 rounded-md", imageClassName: "size-full p-1" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "truncate text-sm font-medium text-foreground", children: integration.name }), connected ? (_jsx("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground", children: "Connected" })) : null] }), _jsx("p", { className: "mt-1 line-clamp-2 text-[11px] leading-4 text-muted-foreground", children: integration.description })] })] }), _jsxs("button", { type: "button", onClick: () => onConnect(integration), disabled: connected || busy, "aria-label": `${actionLabel} ${integration.name}`, className: "inline-flex min-h-8 shrink-0 items-center justify-center gap-1.5 rounded-md border border-border bg-background px-3 text-[11px] font-medium text-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", children: [busy ? _jsx(IconLoader2, { className: "animate-spin", size: 13 }) : null, connected ? "Connected" : actionLabel] })] }));
|
|
235
244
|
}
|
|
236
245
|
function CapabilityRow({ capability, compact, }) {
|
|
237
|
-
return (_jsxs("div", { className: cn("flex items-start justify-between gap-3", compact ? "py-2" : "py-3"), children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: cn("font-medium", compact ? "text-[11px]" : "text-sm"), children: capability.label }), _jsx(CapabilityInfoButton, { capability: capability, ariaLabel: `Why ${capability.label} is needed` })] }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: capability.keySummary })] }), _jsx("span", { className: cn("shrink-0 text-[10px] uppercase tracking-[0.08em]", capability.required
|
|
246
|
+
return (_jsxs("div", { className: cn("flex items-start justify-between gap-3", compact ? "py-2" : "py-3"), children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: cn("font-medium", compact ? "text-[11px]" : "text-sm"), children: capability.label }), _jsx(CapabilityInfoButton, { capability: capability, ariaLabel: `Why ${capability.label} is needed` })] }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: capability.keySummary })] }), _jsx("span", { className: cn("shrink-0 text-[10px] uppercase tracking-[0.08em]", capability.required || capability.suggested
|
|
247
|
+
? "text-primary"
|
|
248
|
+
: "text-muted-foreground"), children: capability.required
|
|
249
|
+
? "Required"
|
|
250
|
+
: capability.suggested
|
|
251
|
+
? "Suggested"
|
|
252
|
+
: "Optional" })] }));
|
|
238
253
|
}
|
|
239
254
|
function CapabilityInfoButton({ capability, ariaLabel, }) {
|
|
240
255
|
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": ariaLabel, className: "inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: _jsx(IconInfoCircle, { size: 13 }) }) }), _jsx(TooltipContent, { side: "top", className: "max-w-xs text-xs", children: capability.why })] }));
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TextField } from "@agent-native/toolkit/design-system";
|
|
3
|
+
import { Button as ToolkitButton } from "@agent-native/toolkit/ui/button";
|
|
4
|
+
import { IconCheck, IconLoader2 } from "@tabler/icons-react";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { agentNativePath } from "../api-path.js";
|
|
7
|
+
const STATUS_ENDPOINT = agentNativePath("/_agent-native/file-upload/status");
|
|
8
|
+
const SECRET_ENDPOINT = agentNativePath("/_agent-native/secrets/adhoc");
|
|
9
|
+
const STORAGE_FIELDS = [
|
|
10
|
+
{
|
|
11
|
+
key: "S3_ENDPOINT",
|
|
12
|
+
label: "Endpoint URL",
|
|
13
|
+
placeholder: "https://s3.us-east-1.amazonaws.com",
|
|
14
|
+
},
|
|
15
|
+
{ key: "S3_BUCKET", label: "Bucket name", placeholder: "my-uploads-bucket" },
|
|
16
|
+
{
|
|
17
|
+
key: "S3_ACCESS_KEY_ID",
|
|
18
|
+
label: "Access key ID",
|
|
19
|
+
placeholder: "AKIA...",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
key: "S3_SECRET_ACCESS_KEY",
|
|
23
|
+
label: "Secret access key",
|
|
24
|
+
placeholder: "Paste your secret access key",
|
|
25
|
+
secret: true,
|
|
26
|
+
},
|
|
27
|
+
{ key: "S3_REGION", label: "Region", placeholder: "auto" },
|
|
28
|
+
{
|
|
29
|
+
key: "S3_PUBLIC_BASE_URL",
|
|
30
|
+
label: "Public base URL",
|
|
31
|
+
placeholder: "https://cdn.example.com",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
export function FileStorageSettingsForm() {
|
|
35
|
+
const [values, setValues] = useState({});
|
|
36
|
+
const [configured, setConfigured] = useState(null);
|
|
37
|
+
const [saving, setSaving] = useState(false);
|
|
38
|
+
const [error, setError] = useState(null);
|
|
39
|
+
const [saved, setSaved] = useState(false);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
let cancelled = false;
|
|
42
|
+
fetch(STATUS_ENDPOINT)
|
|
43
|
+
.then(async (response) => {
|
|
44
|
+
if (!response.ok)
|
|
45
|
+
throw new Error("Storage status unavailable");
|
|
46
|
+
return (await response.json());
|
|
47
|
+
})
|
|
48
|
+
.then((status) => {
|
|
49
|
+
if (!cancelled)
|
|
50
|
+
setConfigured(status.configured === true);
|
|
51
|
+
})
|
|
52
|
+
.catch(() => {
|
|
53
|
+
if (!cancelled)
|
|
54
|
+
setConfigured(null);
|
|
55
|
+
});
|
|
56
|
+
return () => {
|
|
57
|
+
cancelled = true;
|
|
58
|
+
};
|
|
59
|
+
}, [saved]);
|
|
60
|
+
const setValue = (key, value) => {
|
|
61
|
+
setValues((current) => ({ ...current, [key]: value }));
|
|
62
|
+
setSaved(false);
|
|
63
|
+
setError(null);
|
|
64
|
+
};
|
|
65
|
+
const handleSubmit = async (event) => {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
if (saving)
|
|
68
|
+
return;
|
|
69
|
+
const missing = STORAGE_FIELDS.filter((field) => field.key !== "S3_REGION" && !(values[field.key] ?? "").trim());
|
|
70
|
+
if (missing.length > 0) {
|
|
71
|
+
setError(`Enter ${missing[0]?.label.toLowerCase() ?? "all required values"}.`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setSaving(true);
|
|
75
|
+
setError(null);
|
|
76
|
+
try {
|
|
77
|
+
for (const field of STORAGE_FIELDS) {
|
|
78
|
+
const value = values[field.key]?.trim();
|
|
79
|
+
if (!value)
|
|
80
|
+
continue;
|
|
81
|
+
const response = await fetch(SECRET_ENDPOINT, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
name: field.key,
|
|
86
|
+
value,
|
|
87
|
+
scope: "workspace",
|
|
88
|
+
description: "S3-compatible object storage for file uploads",
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
const body = (await response.json());
|
|
93
|
+
throw new Error(typeof body?.error === "string"
|
|
94
|
+
? body.error
|
|
95
|
+
: `Could not save ${field.label}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
setValues({});
|
|
99
|
+
setSaved(true);
|
|
100
|
+
setConfigured(true);
|
|
101
|
+
window.dispatchEvent(new CustomEvent("agent-engine:configured-changed"));
|
|
102
|
+
}
|
|
103
|
+
catch (cause) {
|
|
104
|
+
setError(cause instanceof Error ? cause.message : "Could not save storage keys");
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
setSaving(false);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
return (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-2.5", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [configured ? (_jsx(IconCheck, { size: 13, className: "text-emerald-500" })) : null, configured
|
|
111
|
+
? "Object storage is connected. Re-enter keys below only to rotate them."
|
|
112
|
+
: "Use a public base URL so attachment links remain stable throughout the thread."] }), _jsx("div", { className: "grid gap-2 sm:grid-cols-2", children: STORAGE_FIELDS.map((field) => (_jsx(TextField, { type: "secret" in field && field.secret ? "password" : "text", value: values[field.key] ?? "", onChange: (value) => setValue(field.key, value), "aria-label": field.label, placeholder: field.placeholder, className: "w-full text-[11px]" }, field.key))) }), error ? _jsx("p", { className: "text-[11px] text-destructive", children: error }) : null, saved ? (_jsx("p", { className: "text-[11px] text-emerald-600", children: "Storage keys saved." })) : null, _jsxs(ToolkitButton, { type: "submit", variant: "default", disabled: saving, className: "inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-[11px] font-medium", children: [saving ? _jsx(IconLoader2, { size: 13, className: "animate-spin" }) : null, saving ? "Saving…" : "Save storage keys"] })] }));
|
|
113
|
+
}
|
|
@@ -25,6 +25,7 @@ import { AgentsSection } from "./AgentsSection.js";
|
|
|
25
25
|
import { AutomationsSection } from "./AutomationsSection.js";
|
|
26
26
|
import { DemoModeSection } from "./DemoModeSection.js";
|
|
27
27
|
import { ExtensionsSettingsContent } from "./ExtensionsSettingsContent.js";
|
|
28
|
+
import { FileStorageSettingsForm } from "./FileStorageSettingsForm.js";
|
|
28
29
|
import { AgentProviderPicker } from "./ProviderSetupForm.js";
|
|
29
30
|
import { SecretsSection } from "./SecretsSection.js";
|
|
30
31
|
import { SettingsGroup, SettingsRow } from "./SettingsRow.js";
|
|
@@ -1054,7 +1055,7 @@ function SettingsPanelContent({ isDevMode, initialSection, sectionRequestKey, se
|
|
|
1054
1055
|
const isWorkspacePage = isPage && sections.includes("hosting");
|
|
1055
1056
|
return (_jsx(SettingsSurfaceProvider, { surface: surface, children: _jsxs("div", { className: cn(isPage ? "space-y-8" : "flex-1 min-h-0 overflow-y-auto p-3 space-y-2", className), style: isPage ? undefined : { overflowY: "auto" }, children: [showCapabilityStrip && (_jsx(CapabilityStatusStrip, { isDevMode: isDevMode, builderConnected: connected, builderLoading: builderLoading, builderBranchesAvailable: builderBranchesAvailable, onOpenLlm: () => openSettingsSection("llm", true) })), isPage &&
|
|
1056
1057
|
["llm", "app-models", "limits", "voice"].some((section) => shouldShowSection(section)) && (_jsxs(SettingsGroup, { title: "Agent", children: [shouldShowSection("llm") && (_jsx(LLMSectionInner, { builderFlow: builderFlow, builderLoading: builderLoading, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, grouped: true, open: openSection === "llm", onToggle: () => toggle("llm") })), shouldShowSection("app-models") && (_jsx(AppModelDefaultsSectionInner, { grouped: true, open: openSection === "app-models", onToggle: () => toggle("app-models") })), shouldShowSection("limits") && (_jsx(AgentLimitsSectionInner, { grouped: true, open: openSection === "limits", onToggle: () => toggle("limits") })), shouldShowSection("voice") && (_jsx(SettingsSection, { id: settingsSectionDomId("voice"), icon: _jsx(IconMicrophone, { size: 14 }), title: "Voice Transcription", subtitle: "Choose how voice input is transcribed.", grouped: true, flat: true, open: openSection === "voice", onToggle: () => toggle("voice"), children: _jsx(VoiceTranscriptionSection, { compact: true }) }))] })), isPage && agentAdditionalContent ? (_jsx(SettingsGroup, { title: "Notifications", children: agentAdditionalContent })) : null, isPage &&
|
|
1057
|
-
["automations", "background"].some((section) => shouldShowSection(section)) && (_jsxs(SettingsGroup, { title: "Agent workflows", children: [shouldShowSection("automations") && (_jsx(SettingsSection, { id: settingsSectionDomId("automations"), icon: _jsx(IconBolt, { size: 14 }), title: "Automations", subtitle: "Scheduled and event-triggered agent tasks.", grouped: true, flat: true, open: openSection === "automations", onToggle: () => toggle("automations"), children: _jsx(SettingsRow, { label: "Automations", description: "Schedule agent tasks or run them from events.", control: _jsxs("a", { href: "/settings/agent/automations", className: "inline-flex items-center gap-1 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground no-underline transition-colors hover:bg-accent/40", children: ["Open automations", _jsx(IconExternalLink, { size: 14 })] }) }) })), shouldShowSection("background") && (_jsx(SettingsSection, { id: settingsSectionDomId("background"), icon: _jsx(IconGitBranch, { size: 14 }), title: "Background Agent", subtitle: "Make code changes from production mode via Builder.", grouped: true, flat: true, connected: connected, open: openSection === "background", onToggle: () => toggle("background"), children: _jsx(SettingsRow, { label: "Background agent", description: "Make code changes from production mode via Builder.", control: _jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "background_agent_settings", trackingFlow: "background_agent", compact: true }) }) }))] })), isWorkspacePage && (_jsxs(SettingsGroup, { title: "Workspace", children: [shouldShowSection("demo-mode") && (_jsx(SettingsRow, { id: settingsSectionDomId("demo-mode"), label: "Demo mode", description: "Use sample data in this browser for presentations.", control: _jsx(DemoModeSection, { compact: true }) })), shouldShowSection("hosting") && (_jsx(SettingsRow, { id: settingsSectionDomId("hosting"), label: "Hosting", description: "Deploy the app to the cloud.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "hosting_settings", trackingFlow: "hosting", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Deploy manually to Netlify, Vercel, Cloudflare, or any Nitro-supported target.", docsUrl: "https://www.builder.io/c/docs/agent-native-deployment?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=deployment_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage" })] }) })), shouldShowSection("database") && (_jsx(SettingsRow, { id: settingsSectionDomId("database"), label: "Database", description: "Connect persistent app storage.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "database_settings", trackingFlow: "database", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Set DATABASE_URL in your .env to connect a supported database.", docsUrl: "https://www.builder.io/c/docs/agent-native-database?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=database_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage" })] }) })), shouldShowSection("uploads") && (_jsx(SettingsRow, { id: settingsSectionDomId("uploads"), label: "File uploads", description: "Store avatars and chat attachments.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "file_upload_settings", trackingFlow: "file_upload", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Configure your own file or object storage provider.", docsUrl: "https://www.builder.io/c/docs/agent-native-file-uploads?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=file_upload_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage" })] }) })), shouldShowSection("auth") && (_jsx(SettingsRow, { id: settingsSectionDomId("auth"), label: "Authentication", description: "Set up sign-in and access control.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "auth_settings", trackingFlow: "auth", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Configure Better Auth and optional Google or GitHub providers.", docsUrl: "https://www.builder.io/c/docs/agent-native-authentication?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=authentication_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage" })] }) }))] })), !isPage && shouldShowSection("llm") && (_jsx(LLMSectionInner, { builderFlow: builderFlow, builderLoading: builderLoading, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, open: openSection === "llm", onToggle: () => toggle("llm") })), !isPage && shouldShowSection("app-models") && (_jsx(AppModelDefaultsSectionInner, { open: openSection === "app-models", onToggle: () => toggle("app-models") })), !isPage && shouldShowSection("limits") && (_jsx(AgentLimitsSectionInner, { open: openSection === "limits", onToggle: () => toggle("limits") })), !isPage && shouldShowSection("voice") && (_jsx(SettingsSection, { id: settingsSectionDomId("voice"), icon: _jsx(IconMicrophone, { size: 14 }), title: "Voice Transcription", subtitle: "How the composer microphone turns your voice into text.", flat: true, open: openSection === "voice", onToggle: () => toggle("voice"), children: _jsx(VoiceTranscriptionSection, {}) })), !isPage && shouldShowSection("demo-mode") && (_jsx(SettingsSection, { id: settingsSectionDomId("demo-mode"), icon: _jsx(IconEyeOff, { size: 14 }), title: "Demo mode", subtitle: "Replace displayed emails with realistic fake data in this browser and reshape supported charts for presentations. Backend, agent integrations, and agent results stay real and access-scoped.", flat: true, open: openSection === "demo-mode", onToggle: () => toggle("demo-mode"), children: _jsx(DemoModeSection, {}) })), !isPage && shouldShowSection("automations") && (_jsx(SettingsSection, { id: settingsSectionDomId("automations"), icon: _jsx(IconBolt, { size: 14 }), title: "Automations", subtitle: "Scheduled and event-triggered agent tasks.", flat: true, open: openSection === "automations", onToggle: () => toggle("automations"), children: _jsx(AutomationsSection, {}) })), shouldShowSection("secrets") && (_jsx(SettingsSection, { id: settingsSectionDomId("secrets"), icon: _jsx(IconKey, { size: 14 }), title: "API keys", subtitle: "Service credentials and automation keys.", flat: true, open: openSection === "secrets", onToggle: () => toggle("secrets"), children: _jsx(SecretsSection, { focusKey: focusSecretKey }) })), !isPage && shouldShowSection("hosting") && (_jsx(SettingsSection, { id: settingsSectionDomId("hosting"), icon: _jsx(IconCloud, { size: 14 }), title: "Hosting", subtitle: "Deploy your app to the cloud.", flat: true, connected: connected, open: openSection === "hosting", onToggle: () => toggle("hosting"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "hosting_settings", trackingFlow: "hosting" }), _jsx(ManualSetupCard, { hint: "Deploy manually to Netlify, Vercel, Cloudflare, or any Nitro-supported target.", docsUrl: "https://www.builder.io/c/docs/agent-native-deployment?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=deployment_settings", dim: connected })] }) })), !isPage && shouldShowSection("database") && (_jsx(SettingsSection, { id: settingsSectionDomId("database"), icon: _jsx(IconDatabase, { size: 14 }), title: "Database", subtitle: "Connect a cloud database for persistent storage.", flat: true, connected: connected, open: openSection === "database", onToggle: () => toggle("database"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "database_settings", trackingFlow: "database" }), _jsx(ManualSetupCard, { hint: "Set DATABASE_URL in your .env to connect Neon, Supabase, Turso, any Postgres/SQLite database, or local PGlite with pglite:./data/pglite.", docsUrl: "https://www.builder.io/c/docs/agent-native-database?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=database_settings", dim: connected })] }) })), !isPage && shouldShowSection("uploads") && (_jsx(SettingsSection, { id: settingsSectionDomId("uploads"), icon: _jsx(IconUpload, { size: 14 }), title: "File uploads", subtitle: "Where user-uploaded files (avatars, chat attachments) are stored.", flat: true, connected: connected, open: openSection === "uploads", onToggle: () => toggle("uploads"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "file_upload_settings", trackingFlow: "file_upload" }), _jsx(ManualSetupCard, { hint: "Without a provider, files are stored as base64 in your database. Fine for dev, not recommended for production.", docsUrl: "https://www.builder.io/c/docs/agent-native-file-uploads?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=file_upload_settings", dim: connected })] }) })), !isPage && shouldShowSection("auth") && (_jsx(SettingsSection, { id: settingsSectionDomId("auth"), icon: _jsx(IconShield, { size: 14 }), title: "Authentication", subtitle: "Set up user authentication and access control.", flat: true, connected: connected, open: openSection === "auth", onToggle: () => toggle("auth"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "auth_settings", trackingFlow: "auth" }), _jsx(ManualSetupCard, { hint: "Configure Better Auth with BETTER_AUTH_SECRET and optional Google/GitHub OAuth providers.", docsUrl: "https://www.builder.io/c/docs/agent-native-authentication?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=authentication_settings", dim: connected })] }) })), shouldShowSection("email") && (_jsx(EmailSectionInner, { open: openSection === "email", onToggle: () => toggle("email") })), shouldShowSection("browser") && (_jsx(SettingsSection, { id: settingsSectionDomId("browser"), icon: _jsx(IconBrowser, { size: 14 }), title: "Browser Automation", subtitle: "Let agents control a real browser for web tasks.", flat: true, connected: builderConnectionOwnedExternally ? undefined : connected, open: openSection === "browser", onToggle: () => toggle("browser"), children: !builderConnectionOwnedExternally ? (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "browser_settings", trackingFlow: "browser_automation" })) : null })), !isPage &&
|
|
1058
|
+
["automations", "background"].some((section) => shouldShowSection(section)) && (_jsxs(SettingsGroup, { title: "Agent workflows", children: [shouldShowSection("automations") && (_jsx(SettingsSection, { id: settingsSectionDomId("automations"), icon: _jsx(IconBolt, { size: 14 }), title: "Automations", subtitle: "Scheduled and event-triggered agent tasks.", grouped: true, flat: true, open: openSection === "automations", onToggle: () => toggle("automations"), children: _jsx(SettingsRow, { label: "Automations", description: "Schedule agent tasks or run them from events.", control: _jsxs("a", { href: "/settings/agent/automations", className: "inline-flex items-center gap-1 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground no-underline transition-colors hover:bg-accent/40", children: ["Open automations", _jsx(IconExternalLink, { size: 14 })] }) }) })), shouldShowSection("background") && (_jsx(SettingsSection, { id: settingsSectionDomId("background"), icon: _jsx(IconGitBranch, { size: 14 }), title: "Background Agent", subtitle: "Make code changes from production mode via Builder.", grouped: true, flat: true, connected: connected, open: openSection === "background", onToggle: () => toggle("background"), children: _jsx(SettingsRow, { label: "Background agent", description: "Make code changes from production mode via Builder.", control: _jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "background_agent_settings", trackingFlow: "background_agent", compact: true }) }) }))] })), isWorkspacePage && (_jsxs(SettingsGroup, { title: "Workspace", children: [shouldShowSection("demo-mode") && (_jsx(SettingsRow, { id: settingsSectionDomId("demo-mode"), label: "Demo mode", description: "Use sample data in this browser for presentations.", control: _jsx(DemoModeSection, { compact: true }) })), shouldShowSection("hosting") && (_jsx(SettingsRow, { id: settingsSectionDomId("hosting"), label: "Hosting", description: "Deploy the app to the cloud.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "hosting_settings", trackingFlow: "hosting", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Deploy manually to Netlify, Vercel, Cloudflare, or any Nitro-supported target.", docsUrl: "https://www.builder.io/c/docs/agent-native-deployment?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=deployment_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage" })] }) })), shouldShowSection("database") && (_jsx(SettingsRow, { id: settingsSectionDomId("database"), label: "Database", description: "Connect persistent app storage.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "database_settings", trackingFlow: "database", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Set DATABASE_URL in your .env to connect a supported database.", docsUrl: "https://www.builder.io/c/docs/agent-native-database?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=database_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage" })] }) })), shouldShowSection("uploads") && (_jsx(SettingsRow, { id: settingsSectionDomId("uploads"), label: "File uploads", description: "Store avatars and chat attachments.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "file_upload_settings", trackingFlow: "file_upload", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Use an S3-compatible bucket with a stable public URL for durable chat attachments.", docsUrl: "https://www.builder.io/c/docs/agent-native-file-uploads?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=file_upload_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage", children: _jsx(FileStorageSettingsForm, {}) })] }) })), shouldShowSection("auth") && (_jsx(SettingsRow, { id: settingsSectionDomId("auth"), label: "Authentication", description: "Set up sign-in and access control.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "auth_settings", trackingFlow: "auth", compact: true }), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Configure Better Auth and optional Google or GitHub providers.", docsUrl: "https://www.builder.io/c/docs/agent-native-authentication?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=authentication_settings", dim: connected, bare: true, popover: true, popoverLabel: "Manage" })] }) }))] })), !isPage && shouldShowSection("llm") && (_jsx(LLMSectionInner, { builderFlow: builderFlow, builderLoading: builderLoading, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, open: openSection === "llm", onToggle: () => toggle("llm") })), !isPage && shouldShowSection("app-models") && (_jsx(AppModelDefaultsSectionInner, { open: openSection === "app-models", onToggle: () => toggle("app-models") })), !isPage && shouldShowSection("limits") && (_jsx(AgentLimitsSectionInner, { open: openSection === "limits", onToggle: () => toggle("limits") })), !isPage && shouldShowSection("voice") && (_jsx(SettingsSection, { id: settingsSectionDomId("voice"), icon: _jsx(IconMicrophone, { size: 14 }), title: "Voice Transcription", subtitle: "How the composer microphone turns your voice into text.", flat: true, open: openSection === "voice", onToggle: () => toggle("voice"), children: _jsx(VoiceTranscriptionSection, {}) })), !isPage && shouldShowSection("demo-mode") && (_jsx(SettingsSection, { id: settingsSectionDomId("demo-mode"), icon: _jsx(IconEyeOff, { size: 14 }), title: "Demo mode", subtitle: "Replace displayed emails with realistic fake data in this browser and reshape supported charts for presentations. Backend, agent integrations, and agent results stay real and access-scoped.", flat: true, open: openSection === "demo-mode", onToggle: () => toggle("demo-mode"), children: _jsx(DemoModeSection, {}) })), !isPage && shouldShowSection("automations") && (_jsx(SettingsSection, { id: settingsSectionDomId("automations"), icon: _jsx(IconBolt, { size: 14 }), title: "Automations", subtitle: "Scheduled and event-triggered agent tasks.", flat: true, open: openSection === "automations", onToggle: () => toggle("automations"), children: _jsx(AutomationsSection, {}) })), shouldShowSection("secrets") && (_jsx(SettingsSection, { id: settingsSectionDomId("secrets"), icon: _jsx(IconKey, { size: 14 }), title: "API keys", subtitle: "Service credentials and automation keys.", flat: true, open: openSection === "secrets", onToggle: () => toggle("secrets"), children: _jsx(SecretsSection, { focusKey: focusSecretKey }) })), !isPage && shouldShowSection("hosting") && (_jsx(SettingsSection, { id: settingsSectionDomId("hosting"), icon: _jsx(IconCloud, { size: 14 }), title: "Hosting", subtitle: "Deploy your app to the cloud.", flat: true, connected: connected, open: openSection === "hosting", onToggle: () => toggle("hosting"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "hosting_settings", trackingFlow: "hosting" }), _jsx(ManualSetupCard, { hint: "Deploy manually to Netlify, Vercel, Cloudflare, or any Nitro-supported target.", docsUrl: "https://www.builder.io/c/docs/agent-native-deployment?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=deployment_settings", dim: connected })] }) })), !isPage && shouldShowSection("database") && (_jsx(SettingsSection, { id: settingsSectionDomId("database"), icon: _jsx(IconDatabase, { size: 14 }), title: "Database", subtitle: "Connect a cloud database for persistent storage.", flat: true, connected: connected, open: openSection === "database", onToggle: () => toggle("database"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "database_settings", trackingFlow: "database" }), _jsx(ManualSetupCard, { hint: "Set DATABASE_URL in your .env to connect Neon, Supabase, Turso, any Postgres/SQLite database, or local PGlite with pglite:./data/pglite.", docsUrl: "https://www.builder.io/c/docs/agent-native-database?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=database_settings", dim: connected })] }) })), !isPage && shouldShowSection("uploads") && (_jsx(SettingsSection, { id: settingsSectionDomId("uploads"), icon: _jsx(IconUpload, { size: 14 }), title: "File uploads", subtitle: "Where user-uploaded files (avatars, chat attachments) are stored.", flat: true, connected: connected, open: openSection === "uploads", onToggle: () => toggle("uploads"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "file_upload_settings", trackingFlow: "file_upload" }), _jsx(ManualSetupCard, { hint: "Object storage keeps uploaded files durable and their URLs reusable throughout the thread. Connect Builder or use an S3-compatible bucket below.", docsUrl: "https://www.builder.io/c/docs/agent-native-file-uploads?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=file_upload_settings", dim: connected, children: _jsx(FileStorageSettingsForm, {}) })] }) })), !isPage && shouldShowSection("auth") && (_jsx(SettingsSection, { id: settingsSectionDomId("auth"), icon: _jsx(IconShield, { size: 14 }), title: "Authentication", subtitle: "Set up user authentication and access control.", flat: true, connected: connected, open: openSection === "auth", onToggle: () => toggle("auth"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "auth_settings", trackingFlow: "auth" }), _jsx(ManualSetupCard, { hint: "Configure Better Auth with BETTER_AUTH_SECRET and optional Google/GitHub OAuth providers.", docsUrl: "https://www.builder.io/c/docs/agent-native-authentication?utm_source=agent-native&utm_medium=product&utm_campaign=onboarding&utm_content=authentication_settings", dim: connected })] }) })), shouldShowSection("email") && (_jsx(EmailSectionInner, { open: openSection === "email", onToggle: () => toggle("email") })), shouldShowSection("browser") && (_jsx(SettingsSection, { id: settingsSectionDomId("browser"), icon: _jsx(IconBrowser, { size: 14 }), title: "Browser Automation", subtitle: "Let agents control a real browser for web tasks.", flat: true, connected: builderConnectionOwnedExternally ? undefined : connected, open: openSection === "browser", onToggle: () => toggle("browser"), children: !builderConnectionOwnedExternally ? (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "browser_settings", trackingFlow: "browser_automation" })) : null })), !isPage &&
|
|
1058
1059
|
builderBranchesAvailable &&
|
|
1059
1060
|
shouldShowSection("background") && (_jsx(SettingsSection, { id: settingsSectionDomId("background"), icon: _jsx(IconGitBranch, { size: 14 }), title: "Background Agent", subtitle: "Make code changes from production mode via Builder.", flat: true, connected: connected, open: openSection === "background", onToggle: () => toggle("background"), children: _jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "background_agent_settings", trackingFlow: "background_agent" }) })), shouldShowSection("integrations") && (_jsx(SettingsSection, { id: settingsSectionDomId("integrations"), icon: _jsx(IconPlugConnected, { size: 14 }), title: "Integrations", subtitle: "Connect messaging platforms and external services.", flat: true, open: openSection === "integrations", onToggle: () => toggle("integrations"), children: _jsx(Suspense, { fallback: null, children: _jsx(IntegrationsPanel, {}) }) })), shouldShowSection("usage") && (_jsx(SettingsSection, { id: settingsSectionDomId("usage"), icon: _jsx(IconCoin, { size: 14 }), title: "Usage", subtitle: "Track token consumption and estimated cost \u2014 broken down by chat, automations, and background jobs.", flat: true, open: openSection === "usage", onToggle: () => toggle("usage"), children: _jsx(UsageSection, {}) })), shouldShowSection("a2a") && (_jsx(SettingsSection, { id: settingsSectionDomId("a2a"), icon: _jsx(IconTopologyRing2, { size: 14 }), title: "Connected Agents (A2A)", subtitle: "Manage remote agents connected via the A2A protocol.", flat: true, open: openSection === "a2a", onToggle: () => toggle("a2a"), children: _jsx(AgentsSection, {}) }))] }) }));
|
|
1060
1061
|
}
|
|
@@ -1430,6 +1430,7 @@ export function processEvent(ev, content, toolCallCounter, tabId, state) {
|
|
|
1430
1430
|
// a wrap-up marker. Clear any preparation label before inspecting pending
|
|
1431
1431
|
// tools so both success and interrupted-terminal paths settle the UI.
|
|
1432
1432
|
dispatchActivityClear(tabId);
|
|
1433
|
+
const userStoppedRun = ev.reason === "user";
|
|
1433
1434
|
const interruptedTools = pendingToolNames(content);
|
|
1434
1435
|
const allInterruptedTools = [
|
|
1435
1436
|
...interruptedTools.running,
|
|
@@ -1437,6 +1438,16 @@ export function processEvent(ev, content, toolCallCounter, tabId, state) {
|
|
|
1437
1438
|
];
|
|
1438
1439
|
if (allInterruptedTools.length > 0) {
|
|
1439
1440
|
settleInterruptedToolCalls(content, undefined, { includeActivity: true });
|
|
1441
|
+
if (userStoppedRun) {
|
|
1442
|
+
return {
|
|
1443
|
+
action: "done",
|
|
1444
|
+
result: {
|
|
1445
|
+
content: contentSnapshot(content),
|
|
1446
|
+
status: { type: "complete", reason: "stop" },
|
|
1447
|
+
metadata: { custom: { userStopped: true } },
|
|
1448
|
+
},
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1440
1451
|
const message = interruptedToolMessage(interruptedTools);
|
|
1441
1452
|
const runError = {
|
|
1442
1453
|
message,
|
|
@@ -1462,6 +1473,16 @@ export function processEvent(ev, content, toolCallCounter, tabId, state) {
|
|
|
1462
1473
|
},
|
|
1463
1474
|
};
|
|
1464
1475
|
}
|
|
1476
|
+
if (userStoppedRun) {
|
|
1477
|
+
return {
|
|
1478
|
+
action: "done",
|
|
1479
|
+
result: {
|
|
1480
|
+
content: contentSnapshot(content),
|
|
1481
|
+
status: { type: "complete", reason: "stop" },
|
|
1482
|
+
metadata: { custom: { userStopped: true } },
|
|
1483
|
+
},
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1465
1486
|
const runWarning = appendMissingFinalResponseWarning(content, state ? state.completedToolsAfterLastAssistantText : undefined);
|
|
1466
1487
|
if (runWarning) {
|
|
1467
1488
|
return {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
error: string;
|
|
17
16
|
ok?: undefined;
|
|
17
|
+
error: string;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -243,7 +243,7 @@ export function createExtensionActionEntries() {
|
|
|
243
243
|
},
|
|
244
244
|
"render-inline-extension": {
|
|
245
245
|
tool: {
|
|
246
|
-
description: "Render a one-time, transient sandboxed Alpine.js mini-app directly inside the chat. Use this for generated UI that should answer the current turn inline without saving anything to the Extensions view: calculators, adjustable controls, knobs, pickers, visualizers, temporary dashboards, and interactive results. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send()/sendToAgentChat(). Use appAction() or extensionData for writes; dbQuery() is for read-only inspection of known app SQL tables. Use agentNative.ui.output for passive current values from knobs, sliders, and selections; it writes application state at inline-ui:<inline extension id>:output, which the agent can read later with readAppState when the user says to use that value. Use agentNative.chat.send for visible submit/apply actions. For transient UIs, extensionData is browser-local throwaway state; use application_state/appFetch, appAction, ui.output, or chat.send for anything the agent or app must observe. Use semantic Tailwind colors (bg-background, text-foreground, bg-primary, etc.) so it inherits the parent app theme. Use create-extension instead when the user wants the UI saved or reusable.",
|
|
246
|
+
description: "Render a one-time, transient sandboxed Alpine.js mini-app directly inside the chat. Use this for generated UI that should answer the current turn inline without saving anything to the Extensions view: calculators, adjustable controls, knobs, pickers, visualizers, temporary dashboards, and interactive results. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send()/sendToAgentChat(). Use appAction() or extensionData for writes; dbQuery() is for read-only inspection of known app SQL tables. Use agentNative.ui.output for passive current values from knobs, sliders, and selections; it writes application state at inline-ui:<inline extension id>:output, which the agent can read later with readAppState when the user says to use that value. Use agentNative.chat.send(message, { submit: true }) only for visible user-triggered submit/apply actions; omit submit or set it to false for a draft, and never call chat.send from polling, refresh, or error handlers. For transient UIs, extensionData is browser-local throwaway state; use application_state/appFetch, appAction, ui.output, or chat.send for anything the agent or app must observe. Use semantic Tailwind colors (bg-background, text-foreground, bg-primary, etc.) so it inherits the parent app theme. Use create-extension instead when the user wants the UI saved or reusable.",
|
|
247
247
|
parameters: {
|
|
248
248
|
type: "object",
|
|
249
249
|
properties: {
|
|
@@ -412,7 +412,7 @@ export function createExtensionActionEntries() {
|
|
|
412
412
|
},
|
|
413
413
|
"create-extension": {
|
|
414
414
|
tool: {
|
|
415
|
-
description: 'Create a persisted sandboxed Alpine.js mini-app extension and render it inline in the chat. Use this when the user wants generated UI that should be saved, reusable, or visible in the Extensions view: extensions, widgets, dashboards, calculators, mini-apps, and reusable interactive utilities. For one-time chat-only UI, use render-inline-extension instead. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send()/sendToAgentChat(). Use appAction() for app data writes and extensionData for extension-owned persisted UI state; dbQuery() is for read-only inspection of known app SQL tables. Use agentNative.ui.output for passive current values from knobs, sliders, and selections; it writes application state at inline-ui:<extension id>:output, which the agent can read later with readAppState when the user says to use that value. Use agentNative.chat.send for visible submit/apply actions. Persist reusable user-edited state with extensionData: if the extension has checkboxes, todos, notes, filters, preferences, or any control whose value should survive reload/reopen, load that state on init and save changes with extensionData, usually at user scope, instead of keeping it only in Alpine state. IMPORTANT — hosting a pasted file: if the user pasted a large HTML/Alpine file (it appears in your context as an <attachment name="pasted-text-…"> block) and asked you to host it as-is, do NOT copy that file into `content`. Instead leave `content` empty and pass `contentFromAttachment` set to that attachment\'s name (or the literal "latest" for the most recent pasted block) — the server reads the file verbatim. Re-emitting a large pasted file as `content` regularly gets cut off mid-stream and stalls the turn. IMPORTANT — cloning a large extension that lives as a workspace resource (not a chat attachment): leave `content` empty and pass `contentFromWorkspaceFile` set to the resource path (e.g. "intuit-analytics-extension.html"); the server reads the full file. Do NOT try to reconstruct the body with run-code or route create-extension through run-code (mutating actions are not callable there). Prefer appAction(name, params) for app data and actions, including read actions mounted as GET; do not call template /api/* routes from appFetch because the extension bridge only allows framework /_agent-native/* paths. Parse JSON string action results before aggregating; use dbQuery() only for known existing SQL tables and never for writes. Keep the initial create-extension payload compact and working; for complex extensions, create a useful v1 first, then use focused update-extension edits for refinements rather than assembling one enormous initial tool input. For any non-trivial component (more than a couple of state fields, any methods, any string formatting, any branching) put the component in a <script> block via Alpine.data(\'name\', () => ({...})) and reference it with x-data="name" — do NOT cram methods, template literals, or branching logic into an inline x-data="{...}" attribute (HTML parser pitfalls cause ReferenceError failures). Define every variable referenced from x-text/x-show/x-if/x-for on the data object\'s initial state. If the extension\'s value depends on an LLM call, require a real key via \\${keys.OPENAI_API_KEY}/\\${keys.ANTHROPIC_API_KEY} (and tell the user to add it in the Dispatch Vault, or in app Settings → API Keys & Connections for standalone apps, if missing) or route the AI work to the agent chat — never ship a stubbed analysis step that renders a placeholder/boolean as the result.',
|
|
415
|
+
description: 'Create a persisted sandboxed Alpine.js mini-app extension and render it inline in the chat. Use this when the user wants generated UI that should be saved, reusable, or visible in the Extensions view: extensions, widgets, dashboards, calculators, mini-apps, and reusable interactive utilities. For one-time chat-only UI, use render-inline-extension instead. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send()/sendToAgentChat(). Use appAction() for app data writes and extensionData for extension-owned persisted UI state; dbQuery() is for read-only inspection of known app SQL tables. Use agentNative.ui.output for passive current values from knobs, sliders, and selections; it writes application state at inline-ui:<extension id>:output, which the agent can read later with readAppState when the user says to use that value. Use agentNative.chat.send(message, { submit: true }) only for visible user-triggered submit/apply actions; omit submit or set it to false for a draft, and never call chat.send from polling, refresh, or error handlers. Persist reusable user-edited state with extensionData: if the extension has checkboxes, todos, notes, filters, preferences, or any control whose value should survive reload/reopen, load that state on init and save changes with extensionData, usually at user scope, instead of keeping it only in Alpine state. IMPORTANT — hosting a pasted file: if the user pasted a large HTML/Alpine file (it appears in your context as an <attachment name="pasted-text-…"> block) and asked you to host it as-is, do NOT copy that file into `content`. Instead leave `content` empty and pass `contentFromAttachment` set to that attachment\'s name (or the literal "latest" for the most recent pasted block) — the server reads the file verbatim. Re-emitting a large pasted file as `content` regularly gets cut off mid-stream and stalls the turn. IMPORTANT — cloning a large extension that lives as a workspace resource (not a chat attachment): leave `content` empty and pass `contentFromWorkspaceFile` set to the resource path (e.g. "intuit-analytics-extension.html"); the server reads the full file. Do NOT try to reconstruct the body with run-code or route create-extension through run-code (mutating actions are not callable there). Prefer appAction(name, params) for app data and actions, including read actions mounted as GET; do not call template /api/* routes from appFetch because the extension bridge only allows framework /_agent-native/* paths. Parse JSON string action results before aggregating; use dbQuery() only for known existing SQL tables and never for writes. Keep the initial create-extension payload compact and working; for complex extensions, create a useful v1 first, then use focused update-extension edits for refinements rather than assembling one enormous initial tool input. For any non-trivial component (more than a couple of state fields, any methods, any string formatting, any branching) put the component in a <script> block via Alpine.data(\'name\', () => ({...})) and reference it with x-data="name" — do NOT cram methods, template literals, or branching logic into an inline x-data="{...}" attribute (HTML parser pitfalls cause ReferenceError failures). Define every variable referenced from x-text/x-show/x-if/x-for on the data object\'s initial state. If the extension\'s value depends on an LLM call, require a real key via \\${keys.OPENAI_API_KEY}/\\${keys.ANTHROPIC_API_KEY} (and tell the user to add it in the Dispatch Vault, or in app Settings → API Keys & Connections for standalone apps, if missing) or route the AI work to the agent chat — never ship a stubbed analysis step that renders a placeholder/boolean as the result.',
|
|
416
416
|
parameters: {
|
|
417
417
|
type: "object",
|
|
418
418
|
properties: {
|
|
@@ -393,7 +393,9 @@ export function buildExtensionHtml(content, themeVars, isDark, extensionId, bind
|
|
|
393
393
|
type: 'agent-native-send-to-chat',
|
|
394
394
|
message: text,
|
|
395
395
|
context: options.context,
|
|
396
|
-
|
|
396
|
+
// Extension code can run polling and error handlers without a user
|
|
397
|
+
// gesture; submitting is opt-in for an explicit user action.
|
|
398
|
+
submit: options.submit === true,
|
|
397
399
|
openSidebar: options.openSidebar !== false,
|
|
398
400
|
}, '*');
|
|
399
401
|
return { ok: true };
|