@agent-native/core 0.148.0 → 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.
Files changed (165) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/changelog/2026-08-10-wide-tables-in-analytics-chat-now-scroll-horizontally-within.md +6 -0
  3. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +13 -53
  4. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -70
  5. package/corpus/templates/calendar/app/lib/event-layout.ts +122 -0
  6. package/corpus/templates/calendar/app/routes/_app.availability.tsx +1 -1
  7. package/corpus/templates/calendar/changelog/2026-08-10-opening-availability-settings-from-the-agent-lands-on-the-av.md +6 -0
  8. package/corpus/templates/calendar/vitest.config.ts +6 -1
  9. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +3 -1
  10. package/corpus/templates/clips/app/components/player/video-player.tsx +44 -4
  11. package/corpus/templates/clips/changelog/2026-08-10-fixed-transcription-failures-for-recordings-without-captured.md +6 -0
  12. package/corpus/templates/clips/changelog/2026-08-10-mobile-clips-can-now-enter-fullscreen-on-iphone-safari.md +6 -0
  13. package/corpus/templates/content/actions/_property-utils.ts +11 -1
  14. package/corpus/templates/content/actions/view-screen.ts +62 -12
  15. package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
  16. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +22 -0
  17. package/corpus/templates/design/AGENTS.md +3 -0
  18. package/corpus/templates/design/actions/create-design-from-template.ts +19 -0
  19. package/corpus/templates/design/actions/get-design-snapshot.ts +18 -0
  20. package/corpus/templates/design/actions/get-design-template.ts +178 -0
  21. package/corpus/templates/design/actions/list-designs.ts +3 -1
  22. package/corpus/templates/design/actions/view-screen.ts +31 -0
  23. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +225 -214
  24. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  25. package/corpus/templates/design/app/i18n-data.ts +78 -0
  26. package/corpus/templates/design/app/lib/design-authors.ts +64 -0
  27. package/corpus/templates/design/app/pages/Index.tsx +114 -4
  28. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +1 -0
  29. package/corpus/templates/design/changelog/2026-08-07-alt-dragging-an-element-to-copy-it-now-keeps-the-copy-visibl.md +6 -0
  30. package/corpus/templates/design/changelog/2026-08-07-design-cards-now-show-who-created-each-design-with-a-filter-.md +6 -0
  31. package/corpus/templates/design/changelog/2026-08-07-drawing-a-shape-on-the-overview-canvas-now-keeps-the-shape-s.md +6 -0
  32. package/corpus/templates/design/changelog/2026-08-07-hold-option-alt-while-dragging-a-frame-on-the-overview-canva.md +6 -0
  33. package/corpus/templates/design/server/lib/design-template-data.ts +143 -0
  34. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -0
  35. package/corpus/templates/forms/AGENTS.md +2 -2
  36. package/corpus/templates/forms/actions/create-form.ts +1 -1
  37. package/corpus/templates/forms/actions/update-form.ts +5 -5
  38. package/corpus/templates/forms/changelog/2026-08-10-forms-now-keep-agent-applied-field-changes-visible-after-an-.md +6 -0
  39. package/corpus/templates/forms/changelog/2026-08-10-published-form-links-always-use-the-working-public-form-url.md +6 -0
  40. package/corpus/templates/forms/server/plugins/agent-chat.ts +2 -2
  41. package/corpus/templates/slides/AGENTS.md +4 -3
  42. package/corpus/templates/slides/actions/import-google-slides-reference.ts +56 -10
  43. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +12 -74
  44. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -1
  45. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +15 -4
  46. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +116 -55
  47. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
  48. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +143 -18
  49. package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
  50. package/corpus/templates/slides/app/lib/generation-state.ts +7 -1
  51. package/corpus/templates/slides/app/pages/DeckEditor.tsx +32 -14
  52. package/corpus/templates/slides/app/pages/Index.tsx +110 -128
  53. package/corpus/templates/slides/changelog/2026-08-10-imported-reference-decks-now-show-clear-success-and-stay-sel.md +6 -0
  54. package/corpus/templates/slides/changelog/2026-08-10-pasted-google-slides-links-import-after-reconnecting-google-.md +6 -0
  55. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -0
  56. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +13 -0
  57. package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
  58. package/dist/agent/engine/registry.d.ts +8 -2
  59. package/dist/agent/engine/registry.js +63 -29
  60. package/dist/agent/production-agent.js +7 -0
  61. package/dist/agent/run-manager.d.ts +5 -4
  62. package/dist/agent/run-manager.js +6 -5
  63. package/dist/cli/claude-code-participant.d.ts +2 -1
  64. package/dist/cli/claude-code-participant.js +19 -0
  65. package/dist/cli/code-agent-executor.js +223 -0
  66. package/dist/cli/create-workspace.js +3 -3
  67. package/dist/cli/create.d.ts +3 -2
  68. package/dist/cli/create.js +20 -24
  69. package/dist/client/AgentPanel.js +8 -4
  70. package/dist/client/AssistantChat.js +34 -1
  71. package/dist/client/agent-chat-adapter.d.ts +1 -0
  72. package/dist/client/agent-chat-adapter.js +10 -2
  73. package/dist/client/blocks/library/diagram.js +1 -1
  74. package/dist/client/blocks/library/sanitize-html.js +66 -2
  75. package/dist/client/blocks/library/wireframe-kit.js +69 -0
  76. package/dist/client/chat/markdown-renderer.d.ts +1 -0
  77. package/dist/client/chat/markdown-renderer.js +4 -0
  78. package/dist/client/chat/message-components.d.ts +1 -0
  79. package/dist/client/chat/message-components.js +9 -1
  80. package/dist/client/chat/tool-call-display.d.ts +8 -0
  81. package/dist/client/chat/tool-call-display.js +30 -21
  82. package/dist/client/chat-first/app-pane.js +1 -1
  83. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  84. package/dist/client/chat-first/apps-rail.js +12 -12
  85. package/dist/client/chat-first/chat-history.d.ts +1 -1
  86. package/dist/client/chat-first/chat-history.js +2 -0
  87. package/dist/client/chat-first/copy.js +1 -0
  88. package/dist/client/chat-first/primary-nav.js +1 -1
  89. package/dist/client/chat-first/types.d.ts +1 -0
  90. package/dist/client/resources/McpConnectionSuggestion.js +2 -2
  91. package/dist/client/settings/SettingsTabsPage.d.ts +3 -1
  92. package/dist/client/settings/SettingsTabsPage.js +2 -2
  93. package/dist/client/sse-event-processor.d.ts +8 -0
  94. package/dist/client/sse-event-processor.js +13 -6
  95. package/dist/collab/struct-routes.d.ts +1 -1
  96. package/dist/deploy/build.d.ts +5 -7
  97. package/dist/deploy/build.js +19 -12
  98. package/dist/localization/default-messages.js +1 -1
  99. package/dist/observability/routes.d.ts +3 -3
  100. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  101. package/dist/scripts/call-agent.js +3 -3
  102. package/dist/scripts/describe-workspace-apps.js +1 -1
  103. package/dist/secrets/routes.d.ts +6 -6
  104. package/dist/server/agent-capabilities.js +0 -0
  105. package/dist/server/agent-chat/context-tools.js +1 -1
  106. package/dist/server/agent-chat/prompt-resources.js +1 -1
  107. package/dist/server/agent-chat-plugin.js +1 -1
  108. package/dist/server/auth.js +190 -42
  109. package/dist/server/better-auth-migrations.js +53 -0
  110. package/dist/server/http-response-telemetry.js +7 -1
  111. package/dist/server/onboarding-html.js +119 -33
  112. package/dist/server/transcribe-voice.d.ts +1 -1
  113. package/dist/shared/password-policy.d.ts +2 -1
  114. package/dist/shared/password-policy.js +2 -1
  115. package/dist/styles/agent-conversation.css +20 -3
  116. package/dist/styles/agent-native.css +14 -2
  117. package/dist/styles/blocks.css +132 -0
  118. package/dist/templates/default/app/i18n/ar-SA.ts +1 -1
  119. package/dist/templates/default/app/i18n/de-DE.ts +1 -1
  120. package/dist/templates/default/app/i18n/en-US.ts +1 -1
  121. package/dist/templates/default/app/i18n/es-ES.ts +1 -1
  122. package/dist/templates/default/app/i18n/fr-FR.ts +1 -1
  123. package/dist/templates/default/app/i18n/hi-IN.ts +1 -1
  124. package/dist/templates/default/app/i18n/ja-JP.ts +1 -1
  125. package/dist/templates/default/app/i18n/ko-KR.ts +1 -1
  126. package/dist/templates/default/app/i18n/pt-BR.ts +1 -1
  127. package/dist/templates/default/app/i18n/zh-CN.ts +1 -1
  128. package/dist/templates/default/app/i18n/zh-TW.ts +1 -1
  129. package/dist/templates/default/pnpm-workspace.yaml +1 -0
  130. package/dist/templates/workspace-root/pnpm-workspace.yaml +2 -0
  131. package/docs/content/actions.mdx +4 -1
  132. package/docs/content/agent-surfaces.mdx +8 -6
  133. package/docs/content/automations.mdx +1 -1
  134. package/docs/content/cloneable-saas.mdx +6 -6
  135. package/docs/content/database.mdx +6 -1
  136. package/docs/content/deployment.mdx +10 -8
  137. package/docs/content/docs-components.mdx +22 -14
  138. package/docs/content/drop-in-agent.mdx +1 -1
  139. package/docs/content/environment-variables.mdx +4 -3
  140. package/docs/content/getting-started-actions.mdx +5 -4
  141. package/docs/content/getting-started-pages.mdx +7 -6
  142. package/docs/content/getting-started.mdx +4 -3
  143. package/docs/content/notifications.mdx +6 -6
  144. package/docs/content/progress.mdx +5 -5
  145. package/docs/content/routing.mdx +3 -1
  146. package/docs/content/server-database.mdx +13 -4
  147. package/docs/content/server-overview.mdx +9 -9
  148. package/docs/content/server-plugins.mdx +10 -4
  149. package/docs/content/server-routes.mdx +7 -2
  150. package/docs/content/tracking.mdx +4 -4
  151. package/docs/content/what-is-agent-native.mdx +4 -4
  152. package/package.json +2 -2
  153. package/src/templates/default/app/i18n/ar-SA.ts +1 -1
  154. package/src/templates/default/app/i18n/de-DE.ts +1 -1
  155. package/src/templates/default/app/i18n/en-US.ts +1 -1
  156. package/src/templates/default/app/i18n/es-ES.ts +1 -1
  157. package/src/templates/default/app/i18n/fr-FR.ts +1 -1
  158. package/src/templates/default/app/i18n/hi-IN.ts +1 -1
  159. package/src/templates/default/app/i18n/ja-JP.ts +1 -1
  160. package/src/templates/default/app/i18n/ko-KR.ts +1 -1
  161. package/src/templates/default/app/i18n/pt-BR.ts +1 -1
  162. package/src/templates/default/app/i18n/zh-CN.ts +1 -1
  163. package/src/templates/default/app/i18n/zh-TW.ts +1 -1
  164. package/src/templates/default/pnpm-workspace.yaml +1 -0
  165. 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 && !showQuestionFlow && (isNewDeckGenerating || addSlideGenerating);
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 previousSlideCountRef = useRef(slideCount);
348
+ const previousSlideIdsRef = useRef<string[]>([]);
346
349
  useEffect(() => {
347
- if (previousSlideCountRef.current === slideCount) return;
348
- previousSlideCountRef.current = slideCount;
349
- setGeneratingSlideSelected(false);
350
- }, [slideCount]);
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 referenceSelection: NewDeckReferenceSelection = {
902
- designSystemId: null,
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 { id?: unknown };
909
- if (typeof imported.id !== "string" || !imported.id) {
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
- await reloadDecks();
913
- rememberReference({ id: imported.id, kind: "deck" });
914
- setSelectedReferenceDeckId(imported.id);
915
- referenceSelection = {
916
- designSystemId: null,
917
- referenceDeckId: imported.id,
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 (!pptxReference && pdfReference) {
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 { imported?: unknown; deckId?: unknown };
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
- await reloadDecks();
948
- rememberReference({ id: referenceDeck.id, kind: "deck" });
949
- setSelectedReferenceDeckId(referenceDeck.id);
950
- referenceSelection = {
951
- designSystemId: null,
952
- referenceDeckId: referenceDeck.id,
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
- const importedReference = pptxReference ?? pdfReference;
960
- const generationFiles = uploaded.filter(
961
- (file) => file !== importedReference,
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")}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ Imported reference decks now show clear success and stay selected for new deck generation
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ Pasted Google Slides links import after reconnecting Google Drive when needed.
@@ -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. The drive.file scope is intentionally limited to files the user selected or the app created. 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.
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): Promise<boolean>;
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