@agent-native/core 0.148.1 → 0.149.0
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/changelog/2026-08-10-wide-tables-in-analytics-chat-now-scroll-horizontally-within.md +6 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +13 -53
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -70
- package/corpus/templates/calendar/app/lib/event-layout.ts +122 -0
- package/corpus/templates/calendar/app/routes/_app.availability.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-08-10-opening-availability-settings-from-the-agent-lands-on-the-av.md +6 -0
- package/corpus/templates/calendar/vitest.config.ts +6 -1
- package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +3 -1
- package/corpus/templates/clips/app/components/player/video-player.tsx +44 -4
- package/corpus/templates/clips/changelog/2026-08-10-fixed-transcription-failures-for-recordings-without-captured.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-10-mobile-clips-can-now-enter-fullscreen-on-iphone-safari.md +6 -0
- package/corpus/templates/content/actions/_property-utils.ts +11 -1
- package/corpus/templates/content/actions/view-screen.ts +62 -12
- package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +22 -0
- package/corpus/templates/design/AGENTS.md +3 -0
- package/corpus/templates/design/actions/create-design-from-template.ts +19 -0
- package/corpus/templates/design/actions/get-design-snapshot.ts +18 -0
- package/corpus/templates/design/actions/get-design-template.ts +178 -0
- package/corpus/templates/design/actions/list-designs.ts +3 -1
- package/corpus/templates/design/actions/view-screen.ts +31 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +225 -214
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +78 -0
- package/corpus/templates/design/app/lib/design-authors.ts +64 -0
- package/corpus/templates/design/app/pages/Index.tsx +114 -4
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +1 -0
- package/corpus/templates/design/changelog/2026-08-07-alt-dragging-an-element-to-copy-it-now-keeps-the-copy-visibl.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-design-cards-now-show-who-created-each-design-with-a-filter-.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-drawing-a-shape-on-the-overview-canvas-now-keeps-the-shape-s.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-hold-option-alt-while-dragging-a-frame-on-the-overview-canva.md +6 -0
- package/corpus/templates/design/server/lib/design-template-data.ts +143 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/AGENTS.md +2 -2
- package/corpus/templates/forms/actions/create-form.ts +1 -1
- package/corpus/templates/forms/actions/update-form.ts +5 -5
- package/corpus/templates/forms/changelog/2026-08-10-forms-now-keep-agent-applied-field-changes-visible-after-an-.md +6 -0
- package/corpus/templates/forms/changelog/2026-08-10-published-form-links-always-use-the-working-public-form-url.md +6 -0
- package/corpus/templates/forms/server/plugins/agent-chat.ts +2 -2
- package/corpus/templates/slides/AGENTS.md +4 -3
- package/corpus/templates/slides/actions/import-google-slides-reference.ts +56 -10
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +12 -74
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -1
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +15 -4
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +116 -55
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +143 -18
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
- package/corpus/templates/slides/app/lib/generation-state.ts +7 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +32 -14
- package/corpus/templates/slides/app/pages/Index.tsx +110 -128
- package/corpus/templates/slides/changelog/2026-08-10-imported-reference-decks-now-show-clear-success-and-stay-sel.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-pasted-google-slides-links-import-after-reconnecting-google-.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -0
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +13 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
- package/dist/agent/engine/registry.d.ts +8 -2
- package/dist/agent/engine/registry.js +63 -29
- package/dist/agent/production-agent.js +7 -0
- package/dist/agent/run-manager.d.ts +5 -4
- package/dist/agent/run-manager.js +6 -5
- package/dist/cli/claude-code-participant.d.ts +2 -1
- package/dist/cli/claude-code-participant.js +19 -0
- package/dist/cli/code-agent-executor.js +223 -0
- package/dist/cli/create-workspace.js +3 -3
- package/dist/cli/create.d.ts +3 -2
- package/dist/cli/create.js +20 -24
- package/dist/client/AgentPanel.js +8 -4
- package/dist/client/AssistantChat.js +34 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -0
- package/dist/client/agent-chat-adapter.js +10 -2
- package/dist/client/chat/markdown-renderer.d.ts +1 -0
- package/dist/client/chat/markdown-renderer.js +4 -0
- package/dist/client/chat/message-components.d.ts +1 -0
- package/dist/client/chat/message-components.js +9 -1
- package/dist/client/chat/tool-call-display.d.ts +8 -0
- package/dist/client/chat/tool-call-display.js +30 -21
- package/dist/client/chat-first/app-pane.js +1 -1
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +11 -11
- package/dist/client/chat-first/chat-history.d.ts +1 -1
- package/dist/client/chat-first/chat-history.js +2 -0
- package/dist/client/chat-first/primary-nav.js +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +2 -2
- package/dist/client/settings/SettingsTabsPage.d.ts +3 -1
- package/dist/client/settings/SettingsTabsPage.js +2 -2
- package/dist/client/sse-event-processor.d.ts +8 -0
- package/dist/client/sse-event-processor.js +13 -6
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +5 -7
- package/dist/deploy/build.js +19 -12
- package/dist/localization/default-messages.js +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/call-agent.js +3 -3
- package/dist/scripts/describe-workspace-apps.js +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-capabilities.js +0 -0
- package/dist/server/agent-chat/context-tools.js +1 -1
- package/dist/server/agent-chat/prompt-resources.js +1 -1
- package/dist/server/agent-chat-plugin.js +1 -1
- package/dist/server/auth.js +190 -42
- package/dist/server/better-auth-migrations.js +53 -0
- package/dist/server/http-response-telemetry.js +7 -1
- package/dist/server/onboarding-html.js +119 -33
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/password-policy.d.ts +2 -1
- package/dist/shared/password-policy.js +2 -1
- package/dist/styles/agent-conversation.css +20 -3
- package/dist/styles/agent-native.css +14 -2
- package/dist/templates/default/app/i18n/ar-SA.ts +1 -1
- package/dist/templates/default/app/i18n/de-DE.ts +1 -1
- package/dist/templates/default/app/i18n/en-US.ts +1 -1
- package/dist/templates/default/app/i18n/es-ES.ts +1 -1
- package/dist/templates/default/app/i18n/fr-FR.ts +1 -1
- package/dist/templates/default/app/i18n/hi-IN.ts +1 -1
- package/dist/templates/default/app/i18n/ja-JP.ts +1 -1
- package/dist/templates/default/app/i18n/ko-KR.ts +1 -1
- package/dist/templates/default/app/i18n/pt-BR.ts +1 -1
- package/dist/templates/default/app/i18n/zh-CN.ts +1 -1
- package/dist/templates/default/app/i18n/zh-TW.ts +1 -1
- package/dist/templates/default/pnpm-workspace.yaml +1 -0
- package/dist/templates/workspace-root/pnpm-workspace.yaml +2 -0
- package/docs/content/cloneable-saas.mdx +6 -6
- package/docs/content/deployment.mdx +10 -8
- package/docs/content/environment-variables.mdx +4 -3
- package/package.json +2 -2
- package/src/templates/default/app/i18n/ar-SA.ts +1 -1
- package/src/templates/default/app/i18n/de-DE.ts +1 -1
- package/src/templates/default/app/i18n/en-US.ts +1 -1
- package/src/templates/default/app/i18n/es-ES.ts +1 -1
- package/src/templates/default/app/i18n/fr-FR.ts +1 -1
- package/src/templates/default/app/i18n/hi-IN.ts +1 -1
- package/src/templates/default/app/i18n/ja-JP.ts +1 -1
- package/src/templates/default/app/i18n/ko-KR.ts +1 -1
- package/src/templates/default/app/i18n/pt-BR.ts +1 -1
- package/src/templates/default/app/i18n/zh-CN.ts +1 -1
- package/src/templates/default/app/i18n/zh-TW.ts +1 -1
- package/src/templates/default/pnpm-workspace.yaml +1 -0
- package/src/templates/workspace-root/pnpm-workspace.yaml +2 -0
|
@@ -287,6 +287,7 @@ export default function DeckEditor() {
|
|
|
287
287
|
generating,
|
|
288
288
|
isNewDeckCreation: wasNewDeckCreation.current,
|
|
289
289
|
slideCount,
|
|
290
|
+
generationStarted: newDeckGenerationStarted.current,
|
|
290
291
|
});
|
|
291
292
|
const { designSystem, imageStyleReferenceUrls } = useDeckDesignSystem(
|
|
292
293
|
deck?.designSystemId,
|
|
@@ -326,7 +327,9 @@ export default function DeckEditor() {
|
|
|
326
327
|
|
|
327
328
|
const showQuestionFlow = Boolean(questionFlowQuestions?.length);
|
|
328
329
|
const generatingSlideVisible =
|
|
329
|
-
canEdit &&
|
|
330
|
+
canEdit &&
|
|
331
|
+
!showQuestionFlow &&
|
|
332
|
+
(isNewDeckGenerating || addSlideGenerating || showNewDeckGeneratingOverlay);
|
|
330
333
|
const showCurrentSlideEditor =
|
|
331
334
|
!generatingSlideSelected &&
|
|
332
335
|
!showNewDeckGeneratingOverlay &&
|
|
@@ -342,12 +345,35 @@ export default function DeckEditor() {
|
|
|
342
345
|
if (!generating) setAddSlideGenerating(false);
|
|
343
346
|
}, [generating]);
|
|
344
347
|
|
|
345
|
-
const
|
|
348
|
+
const previousSlideIdsRef = useRef<string[]>([]);
|
|
346
349
|
useEffect(() => {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
350
|
+
const currentSlideIds = deck?.slides.map((slide) => slide.id) ?? [];
|
|
351
|
+
const previousSlideIds = previousSlideIdsRef.current;
|
|
352
|
+
const addedSlide = deck?.slides.find(
|
|
353
|
+
(slide) => !previousSlideIds.includes(slide.id),
|
|
354
|
+
);
|
|
355
|
+
const slideWasAdded = currentSlideIds.length > previousSlideIds.length;
|
|
356
|
+
|
|
357
|
+
if (
|
|
358
|
+
slideWasAdded &&
|
|
359
|
+
addedSlide &&
|
|
360
|
+
(generating ||
|
|
361
|
+
isNewDeckGenerating ||
|
|
362
|
+
addSlideGenerating ||
|
|
363
|
+
generatingSlideSelected)
|
|
364
|
+
) {
|
|
365
|
+
setActiveSlideId(addedSlide.id);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
previousSlideIdsRef.current = currentSlideIds;
|
|
369
|
+
if (slideWasAdded) setGeneratingSlideSelected(false);
|
|
370
|
+
}, [
|
|
371
|
+
addSlideGenerating,
|
|
372
|
+
deck,
|
|
373
|
+
generating,
|
|
374
|
+
generatingSlideSelected,
|
|
375
|
+
isNewDeckGenerating,
|
|
376
|
+
]);
|
|
351
377
|
|
|
352
378
|
useEffect(() => {
|
|
353
379
|
if (
|
|
@@ -1128,14 +1154,6 @@ export default function DeckEditor() {
|
|
|
1128
1154
|
setActiveSlideId(slideId);
|
|
1129
1155
|
if (window.innerWidth < 768) setSidebarOpen(false);
|
|
1130
1156
|
}}
|
|
1131
|
-
onDuplicateSlide={(slideId) => duplicateSlide(id, slideId)}
|
|
1132
|
-
onDeleteSlide={(slideId) => {
|
|
1133
|
-
const idx = deck.slides.findIndex((s) => s.id === slideId);
|
|
1134
|
-
const nextSlide =
|
|
1135
|
-
deck.slides[idx + 1] || deck.slides[idx - 1];
|
|
1136
|
-
deleteSlideWithUndo(id, slideId);
|
|
1137
|
-
if (nextSlide) setActiveSlideId(nextSlide.id);
|
|
1138
|
-
}}
|
|
1139
1157
|
readOnly={!canEdit}
|
|
1140
1158
|
slidePresence={slidePresence}
|
|
1141
1159
|
recentEdits={deckRecentEdits}
|
|
@@ -27,12 +27,13 @@ import { toast } from "sonner";
|
|
|
27
27
|
import DeckCard from "@/components/deck/DeckCard";
|
|
28
28
|
import {
|
|
29
29
|
NewDeckReferenceStep,
|
|
30
|
+
type ImportedReference,
|
|
30
31
|
type NewDeckReferenceSelection,
|
|
32
|
+
type NewDeckReferenceSource,
|
|
31
33
|
} from "@/components/editor/NewDeckReferenceStep";
|
|
32
34
|
import PromptPopover, {
|
|
33
35
|
uploadPromptFiles,
|
|
34
36
|
type UploadedFile,
|
|
35
|
-
type PromptImportSelection,
|
|
36
37
|
} from "@/components/editor/PromptDialog";
|
|
37
38
|
import {
|
|
38
39
|
AlertDialog,
|
|
@@ -772,95 +773,6 @@ export default function Index() {
|
|
|
772
773
|
[setNewDeckPromptOpen],
|
|
773
774
|
);
|
|
774
775
|
|
|
775
|
-
const handleDirectImport = useCallback(
|
|
776
|
-
async (selection: PromptImportSelection): Promise<boolean> => {
|
|
777
|
-
if (!session) {
|
|
778
|
-
setSignInPromptHadFiles(selection.kind !== "google-slides");
|
|
779
|
-
setShowSignInDialog(true);
|
|
780
|
-
return false;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
if (selection.kind === "google-slides") {
|
|
784
|
-
const imported = (await callAction("import-google-slides-reference", {
|
|
785
|
-
presentationUrl: selection.url,
|
|
786
|
-
})) as { id?: unknown };
|
|
787
|
-
if (typeof imported.id !== "string" || !imported.id) {
|
|
788
|
-
throw new Error(
|
|
789
|
-
"The Google Slides presentation did not create a deck.",
|
|
790
|
-
);
|
|
791
|
-
}
|
|
792
|
-
await reloadDecks();
|
|
793
|
-
navigate(`/deck/${imported.id}`, { flushSync: true });
|
|
794
|
-
return true;
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
const uploaded = await uploadPromptFiles(selection.files);
|
|
798
|
-
const file = uploaded[0];
|
|
799
|
-
if (!file) throw new Error("The selected file could not be uploaded.");
|
|
800
|
-
|
|
801
|
-
if (selection.kind === "pptx") {
|
|
802
|
-
const imported = (await callAction("import-pptx", {
|
|
803
|
-
filePath: file.path,
|
|
804
|
-
designSystemId: initialDesignSystemId,
|
|
805
|
-
})) as { id?: unknown };
|
|
806
|
-
if (typeof imported.id !== "string" || !imported.id) {
|
|
807
|
-
throw new Error("The PowerPoint presentation did not create a deck.");
|
|
808
|
-
}
|
|
809
|
-
await reloadDecks();
|
|
810
|
-
navigate(`/deck/${imported.id}`, { flushSync: true });
|
|
811
|
-
return true;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
let deck: ReturnType<typeof createDeck> | undefined;
|
|
815
|
-
flushSync(() => {
|
|
816
|
-
deck = createDeck(undefined, {
|
|
817
|
-
noDefaultSlides: true,
|
|
818
|
-
designSystemId: initialDesignSystemId,
|
|
819
|
-
});
|
|
820
|
-
});
|
|
821
|
-
if (!deck) throw new Error("The PDF deck could not be created.");
|
|
822
|
-
|
|
823
|
-
const persisted = await ensureDeckPersisted(deck.id);
|
|
824
|
-
if (!persisted.persisted) {
|
|
825
|
-
deleteDeck(deck.id);
|
|
826
|
-
throw new Error(
|
|
827
|
-
describeDeckPersistenceFailure(
|
|
828
|
-
persisted,
|
|
829
|
-
"The PDF deck could not be saved.",
|
|
830
|
-
),
|
|
831
|
-
);
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
try {
|
|
835
|
-
const imported = (await callAction("import-file", {
|
|
836
|
-
filePath: file.path,
|
|
837
|
-
format: "pdf",
|
|
838
|
-
deckId: deck.id,
|
|
839
|
-
importIntoDeck: true,
|
|
840
|
-
})) as { imported?: unknown; deckId?: unknown };
|
|
841
|
-
if (imported.imported !== true || imported.deckId !== deck.id) {
|
|
842
|
-
throw new Error("The PDF could not be imported into the new deck.");
|
|
843
|
-
}
|
|
844
|
-
await reloadDecks();
|
|
845
|
-
navigate(`/deck/${deck.id}`, { flushSync: true });
|
|
846
|
-
return true;
|
|
847
|
-
} catch (error) {
|
|
848
|
-
deleteDeck(deck.id);
|
|
849
|
-
throw error;
|
|
850
|
-
}
|
|
851
|
-
},
|
|
852
|
-
[
|
|
853
|
-
callAction,
|
|
854
|
-
createDeck,
|
|
855
|
-
deleteDeck,
|
|
856
|
-
ensureDeckPersisted,
|
|
857
|
-
navigate,
|
|
858
|
-
reloadDecks,
|
|
859
|
-
session,
|
|
860
|
-
initialDesignSystemId,
|
|
861
|
-
],
|
|
862
|
-
);
|
|
863
|
-
|
|
864
776
|
const handleReferenceSelect = useCallback(
|
|
865
777
|
async (selection: NewDeckReferenceSelection) => {
|
|
866
778
|
const pending = pendingDeck;
|
|
@@ -886,9 +798,9 @@ export default function Index() {
|
|
|
886
798
|
);
|
|
887
799
|
|
|
888
800
|
const handleReferenceImport = useCallback(
|
|
889
|
-
async (files: File[]) => {
|
|
801
|
+
async (files: File[]): Promise<ImportedReference | null> => {
|
|
890
802
|
const pending = pendingDeck;
|
|
891
|
-
if (!pending) return;
|
|
803
|
+
if (!pending) return null;
|
|
892
804
|
setReferenceImporting(true);
|
|
893
805
|
try {
|
|
894
806
|
const uploaded = await uploadPromptFiles(files);
|
|
@@ -898,26 +810,36 @@ export default function Index() {
|
|
|
898
810
|
const pdfReference = uploaded.find((file) =>
|
|
899
811
|
file.originalName.toLowerCase().endsWith(".pdf"),
|
|
900
812
|
);
|
|
901
|
-
let
|
|
902
|
-
|
|
903
|
-
referenceDeckId: null,
|
|
904
|
-
};
|
|
813
|
+
let importedReference: ImportedReference | null = null;
|
|
814
|
+
let generationFiles = uploaded;
|
|
905
815
|
if (pptxReference) {
|
|
906
816
|
const imported = (await callAction("import-pptx", {
|
|
907
817
|
filePath: pptxReference.path,
|
|
908
|
-
})) as {
|
|
909
|
-
|
|
818
|
+
})) as {
|
|
819
|
+
id?: unknown;
|
|
820
|
+
imported?: unknown;
|
|
821
|
+
slideCount?: unknown;
|
|
822
|
+
title?: unknown;
|
|
823
|
+
};
|
|
824
|
+
if (
|
|
825
|
+
typeof imported.id !== "string" ||
|
|
826
|
+
!imported.id ||
|
|
827
|
+
imported.imported !== true ||
|
|
828
|
+
typeof imported.slideCount !== "number" ||
|
|
829
|
+
imported.slideCount < 1
|
|
830
|
+
) {
|
|
910
831
|
throw new Error("The imported presentation did not create a deck.");
|
|
911
832
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
833
|
+
importedReference = {
|
|
834
|
+
id: imported.id,
|
|
835
|
+
title:
|
|
836
|
+
typeof imported.title === "string" && imported.title
|
|
837
|
+
? imported.title
|
|
838
|
+
: t("home.importedReferenceDeck"),
|
|
839
|
+
source: "pptx",
|
|
918
840
|
};
|
|
919
|
-
|
|
920
|
-
if (
|
|
841
|
+
generationFiles = uploaded.filter((file) => file !== pptxReference);
|
|
842
|
+
} else if (pdfReference) {
|
|
921
843
|
const referenceDeck = createDeck(undefined, {
|
|
922
844
|
noDefaultSlides: true,
|
|
923
845
|
});
|
|
@@ -937,36 +859,45 @@ export default function Index() {
|
|
|
937
859
|
format: "pdf",
|
|
938
860
|
deckId: referenceDeck.id,
|
|
939
861
|
importIntoDeck: true,
|
|
940
|
-
})) as {
|
|
862
|
+
})) as {
|
|
863
|
+
imported?: unknown;
|
|
864
|
+
deckId?: unknown;
|
|
865
|
+
pageCount?: unknown;
|
|
866
|
+
title?: unknown;
|
|
867
|
+
};
|
|
941
868
|
if (
|
|
942
869
|
imported.imported !== true ||
|
|
943
|
-
imported.deckId !== referenceDeck.id
|
|
870
|
+
imported.deckId !== referenceDeck.id ||
|
|
871
|
+
typeof imported.pageCount !== "number" ||
|
|
872
|
+
imported.pageCount < 1
|
|
944
873
|
) {
|
|
945
874
|
throw new Error("The PDF reference deck could not be imported.");
|
|
946
875
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
876
|
+
importedReference = {
|
|
877
|
+
id: referenceDeck.id,
|
|
878
|
+
title:
|
|
879
|
+
typeof imported.title === "string" && imported.title
|
|
880
|
+
? imported.title
|
|
881
|
+
: t("home.importedReferenceDeck"),
|
|
882
|
+
source: "pdf",
|
|
953
883
|
};
|
|
884
|
+
generationFiles = uploaded.filter((file) => file !== pdfReference);
|
|
954
885
|
} catch (error) {
|
|
955
886
|
deleteDeck(referenceDeck.id);
|
|
956
887
|
throw error;
|
|
957
888
|
}
|
|
958
889
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
setShowNewDeckReferenceStep(false);
|
|
964
|
-
setPendingDeck(null);
|
|
965
|
-
await handleCreateDeckWithPrompt(
|
|
966
|
-
pending.prompt,
|
|
967
|
-
[...pending.files, ...generationFiles],
|
|
968
|
-
referenceSelection,
|
|
890
|
+
setPendingDeck((current) =>
|
|
891
|
+
current
|
|
892
|
+
? { ...current, files: [...current.files, ...generationFiles] }
|
|
893
|
+
: current,
|
|
969
894
|
);
|
|
895
|
+
if (importedReference) {
|
|
896
|
+
await reloadDecks();
|
|
897
|
+
rememberReference({ id: importedReference.id, kind: "deck" });
|
|
898
|
+
setSelectedReferenceDeckId(importedReference.id);
|
|
899
|
+
}
|
|
900
|
+
return importedReference;
|
|
970
901
|
} catch (error) {
|
|
971
902
|
toast.error(t("editorToolbar.uploadFailed"), {
|
|
972
903
|
description:
|
|
@@ -974,6 +905,7 @@ export default function Index() {
|
|
|
974
905
|
? error.message
|
|
975
906
|
: t("editorToolbar.importFailedDescription"),
|
|
976
907
|
});
|
|
908
|
+
return null;
|
|
977
909
|
} finally {
|
|
978
910
|
setReferenceImporting(false);
|
|
979
911
|
}
|
|
@@ -983,7 +915,6 @@ export default function Index() {
|
|
|
983
915
|
createDeck,
|
|
984
916
|
deleteDeck,
|
|
985
917
|
ensureDeckPersisted,
|
|
986
|
-
handleCreateDeckWithPrompt,
|
|
987
918
|
pendingDeck,
|
|
988
919
|
reloadDecks,
|
|
989
920
|
rememberReference,
|
|
@@ -991,6 +922,59 @@ export default function Index() {
|
|
|
991
922
|
],
|
|
992
923
|
);
|
|
993
924
|
|
|
925
|
+
const handleReferenceSourceImport = useCallback(
|
|
926
|
+
async (
|
|
927
|
+
source: NewDeckReferenceSource,
|
|
928
|
+
): Promise<ImportedReference | null> => {
|
|
929
|
+
if (source.kind !== "google-docs") return null;
|
|
930
|
+
setReferenceImporting(true);
|
|
931
|
+
try {
|
|
932
|
+
const imported = (await callAction("import-google-slides-reference", {
|
|
933
|
+
presentationUrl: source.value,
|
|
934
|
+
})) as {
|
|
935
|
+
id?: unknown;
|
|
936
|
+
imported?: unknown;
|
|
937
|
+
slideCount?: unknown;
|
|
938
|
+
title?: unknown;
|
|
939
|
+
};
|
|
940
|
+
if (
|
|
941
|
+
typeof imported.id !== "string" ||
|
|
942
|
+
!imported.id ||
|
|
943
|
+
imported.imported !== true ||
|
|
944
|
+
typeof imported.slideCount !== "number" ||
|
|
945
|
+
imported.slideCount < 1
|
|
946
|
+
) {
|
|
947
|
+
throw new Error(
|
|
948
|
+
"The Google Slides presentation did not create a deck.",
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
const importedReference: ImportedReference = {
|
|
952
|
+
id: imported.id,
|
|
953
|
+
title:
|
|
954
|
+
typeof imported.title === "string" && imported.title
|
|
955
|
+
? imported.title
|
|
956
|
+
: t("home.importedReferenceDeck"),
|
|
957
|
+
source: "google-slides",
|
|
958
|
+
};
|
|
959
|
+
await reloadDecks();
|
|
960
|
+
rememberReference({ id: importedReference.id, kind: "deck" });
|
|
961
|
+
setSelectedReferenceDeckId(importedReference.id);
|
|
962
|
+
return importedReference;
|
|
963
|
+
} catch (error) {
|
|
964
|
+
toast.error(t("editorToolbar.uploadFailed"), {
|
|
965
|
+
description:
|
|
966
|
+
error instanceof Error
|
|
967
|
+
? error.message
|
|
968
|
+
: t("editorToolbar.importFailedDescription"),
|
|
969
|
+
});
|
|
970
|
+
return null;
|
|
971
|
+
} finally {
|
|
972
|
+
setReferenceImporting(false);
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
[callAction, reloadDecks, rememberReference, t],
|
|
976
|
+
);
|
|
977
|
+
|
|
994
978
|
const handleReferenceSkip = useCallback(() => {
|
|
995
979
|
const pending = pendingDeck;
|
|
996
980
|
if (!pending) {
|
|
@@ -1329,8 +1313,6 @@ export default function Index() {
|
|
|
1329
1313
|
onSkip={handleCreateDeckBlank}
|
|
1330
1314
|
skipLabel={t("home.skipPrompt")}
|
|
1331
1315
|
onSubmit={handlePromptSubmit}
|
|
1332
|
-
onImport={handleDirectImport}
|
|
1333
|
-
importFromLabel={t("home.importFrom")}
|
|
1334
1316
|
importingLabel={t("editorToolbar.importing")}
|
|
1335
1317
|
onBeforeUpload={(prompt, files) => {
|
|
1336
1318
|
if (session) return true;
|
|
@@ -1364,6 +1346,7 @@ export default function Index() {
|
|
|
1364
1346
|
recentReferences={recentReferences}
|
|
1365
1347
|
onSelect={(selection) => void handleReferenceSelect(selection)}
|
|
1366
1348
|
onImport={handleReferenceImport}
|
|
1349
|
+
onImportSource={handleReferenceSourceImport}
|
|
1367
1350
|
onSkip={handleReferenceSkip}
|
|
1368
1351
|
importing={referenceImporting}
|
|
1369
1352
|
title={t("home.newDeckPromptTitle")}
|
|
@@ -1372,7 +1355,6 @@ export default function Index() {
|
|
|
1372
1355
|
chooseDeckLabel={t("home.referenceDeckPlaceholder")}
|
|
1373
1356
|
importingLabel={t("editorToolbar.importing")}
|
|
1374
1357
|
skipLabel={t("home.referenceDeckNone")}
|
|
1375
|
-
defaultSuffix={t("home.defaultSuffix")}
|
|
1376
1358
|
starredLabel={t("home.referenceDeckStarredGroup")}
|
|
1377
1359
|
otherDecksLabel={t("home.referenceDeckOtherGroup")}
|
|
1378
1360
|
searchDecksLabel={t("root.searchDecks")}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
getGoogleDocsAccessToken,
|
|
24
24
|
getGoogleDocsAuthUrl,
|
|
25
25
|
getGooglePickerConfig,
|
|
26
|
+
hasGoogleDriveExportScope,
|
|
26
27
|
isGoogleDocsOAuthConfigured,
|
|
27
28
|
listGoogleDocsAccounts,
|
|
28
29
|
} from "../lib/google-docs-oauth.js";
|
|
@@ -163,6 +164,9 @@ export const getGoogleDocsStatus = defineEventHandler(
|
|
|
163
164
|
return {
|
|
164
165
|
configured: await isGoogleDocsOAuthConfigured(owner),
|
|
165
166
|
connected: accounts.length > 0,
|
|
167
|
+
googleSlidesUrlImportReady: accounts.some((account) =>
|
|
168
|
+
hasGoogleDriveExportScope(account.scope),
|
|
169
|
+
),
|
|
166
170
|
accounts,
|
|
167
171
|
pickerConfigured: !!(picker.apiKey && picker.appId),
|
|
168
172
|
pickerApiKey: picker.apiKey,
|
|
@@ -15,13 +15,26 @@ const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
|
15
15
|
const USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
|
|
16
16
|
|
|
17
17
|
export const GOOGLE_DOCS_PROVIDER = "google-docs";
|
|
18
|
+
export const GOOGLE_DRIVE_READONLY_SCOPE =
|
|
19
|
+
"https://www.googleapis.com/auth/drive.readonly";
|
|
18
20
|
export const GOOGLE_DOCS_SCOPES = [
|
|
19
21
|
"https://www.googleapis.com/auth/drive.file",
|
|
22
|
+
GOOGLE_DRIVE_READONLY_SCOPE,
|
|
20
23
|
"https://www.googleapis.com/auth/presentations.readonly",
|
|
21
24
|
"https://www.googleapis.com/auth/userinfo.email",
|
|
22
25
|
"https://www.googleapis.com/auth/userinfo.profile",
|
|
23
26
|
];
|
|
24
27
|
|
|
28
|
+
export function hasGoogleDriveExportScope(scope?: string): boolean {
|
|
29
|
+
const grantedScopes = new Set(
|
|
30
|
+
(scope ?? "").split(/\s+/).filter((value) => value.length > 0),
|
|
31
|
+
);
|
|
32
|
+
return (
|
|
33
|
+
grantedScopes.has("https://www.googleapis.com/auth/drive") ||
|
|
34
|
+
grantedScopes.has(GOOGLE_DRIVE_READONLY_SCOPE)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
interface GoogleDocsTokens {
|
|
26
39
|
access_token: string;
|
|
27
40
|
refresh_token?: string;
|
|
@@ -66,7 +66,7 @@ Image workflow is strict. For direct insertion, call generate-image-api with ins
|
|
|
66
66
|
|
|
67
67
|
Provider-specific Slides actions are shortcuts, not limits. If a first-class action cannot express the exact Google Drive endpoint, file metadata field, export format, query, request body, pagination mode, payload shape, or API version needed, call provider-api-catalog and provider-api-docs as needed, then call provider-api-request against the real provider API. Use the raw provider API escape hatch instead of weakening the answer or claiming Slides cannot do something the underlying Google Drive API can do.
|
|
68
68
|
|
|
69
|
-
Slides' Google Drive provider API uses the user's connected Google Docs OAuth account.
|
|
69
|
+
Slides' Google Drive provider API uses the user's connected Google Docs OAuth account. Picker imports keep the per-file drive.file path, while pasted Google Slides links use Drive export access and may ask the user to reconnect Google. For large Drive file lists or metadata sweeps, pass stageAs and pagination options to provider-api-request, then use query-staged-dataset to count, filter, group, or project the staged rows.
|
|
70
70
|
|
|
71
71
|
When a Google Drive or Google Slides request needs authentication, tell the user to use the Connect Google button shown in Slides. Do not expose internal routes, API endpoints, OAuth setup instructions, client IDs, or keys in the response, and do not ask the user to configure an API manually.`,
|
|
72
72
|
mentionProviders: async () => {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Built-in engines (anthropic, ai-sdk) are auto-registered by builtin.ts.
|
|
9
9
|
*/
|
|
10
|
+
import { type BuilderCredentialLookupIdentity } from "../../server/credential-provider.js";
|
|
10
11
|
import type { AgentEngine, EngineCapabilities } from "./types.js";
|
|
11
12
|
export interface AgentEngineEntry {
|
|
12
13
|
/** Unique name, e.g. "anthropic", "ai-sdk:anthropic", "ai-sdk:openai" */
|
|
@@ -131,7 +132,7 @@ export declare function detectEngineFromEnvForRequest(): Promise<AgentEngineEntr
|
|
|
131
132
|
* `/builder/status` resolves them via the same request org context, and the
|
|
132
133
|
* chat engine picker must not disagree with that card.
|
|
133
134
|
*/
|
|
134
|
-
export declare function detectEngineFromUserSecrets(): Promise<AgentEngineEntry | null>;
|
|
135
|
+
export declare function detectEngineFromUserSecrets(identity?: BuilderCredentialLookupIdentity): Promise<AgentEngineEntry | null>;
|
|
135
136
|
/**
|
|
136
137
|
* Legacy inline API keys on the global `agent-engine` settings row are
|
|
137
138
|
* intentionally ignored. That row is deployment-wide, so treating
|
|
@@ -158,7 +159,9 @@ export declare function isStoredEngineUsable(stored: unknown, entry: AgentEngine
|
|
|
158
159
|
* deploy env vars; this async helper is what request-time routes should use
|
|
159
160
|
* when deciding whether a stored engine can actually run for the current user.
|
|
160
161
|
*/
|
|
161
|
-
export declare function isStoredEngineUsableForRequest(stored: unknown, entry: AgentEngineEntry
|
|
162
|
+
export declare function isStoredEngineUsableForRequest(stored: unknown, entry: AgentEngineEntry, options?: {
|
|
163
|
+
credentialIdentity?: BuilderCredentialLookupIdentity;
|
|
164
|
+
}): Promise<boolean>;
|
|
162
165
|
/**
|
|
163
166
|
* Request-aware credential preflight for an already-resolved engine instance.
|
|
164
167
|
* `resolveEngine()` may still return a default or explicitly requested engine
|
|
@@ -167,6 +170,7 @@ export declare function isStoredEngineUsableForRequest(stored: unknown, entry: A
|
|
|
167
170
|
*/
|
|
168
171
|
export declare function isResolvedEngineUsableForRequest(engine: AgentEngine, options?: {
|
|
169
172
|
apiKey?: string;
|
|
173
|
+
credentialIdentity?: BuilderCredentialLookupIdentity;
|
|
170
174
|
}): Promise<boolean>;
|
|
171
175
|
export interface ResolveEngineConfig {
|
|
172
176
|
/** Explicit engine name or instance from createAgentChatPlugin options */
|
|
@@ -186,6 +190,8 @@ export interface ResolveEngineConfig {
|
|
|
186
190
|
model?: string;
|
|
187
191
|
/** App/template id used for org-scoped per-app model defaults. */
|
|
188
192
|
appId?: string;
|
|
193
|
+
/** Verified owner identity for request-scoped Builder credential capture. */
|
|
194
|
+
credentialIdentity?: BuilderCredentialLookupIdentity;
|
|
189
195
|
}
|
|
190
196
|
/**
|
|
191
197
|
* Engine name a caller explicitly selected, when {@link resolveEngine} will
|