@agent-native/core 0.107.0 → 0.107.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/core/CHANGELOG.md +10 -0
- package/corpus/core/docs/content/integrations.mdx +27 -27
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +19 -0
- package/corpus/core/src/agent/run-store.ts +98 -2
- package/corpus/core/src/client/AssistantChat.tsx +13 -0
- package/corpus/core/src/client/CommandMenu.tsx +5 -0
- package/corpus/core/src/client/active-run-state.ts +34 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +4 -0
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +41 -11
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -9
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +45 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +30 -9
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +2 -2
- package/corpus/core/src/localization/default-messages.ts +4 -1
- package/corpus/core/src/secrets/register-framework-secrets.ts +28 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +29 -1
- package/corpus/core/src/vite/client.ts +24 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +29 -21
- package/corpus/templates/analytics/AGENTS.md +8 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +6 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +35 -7
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/extension-slot.ts +11 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +27 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-dashboard-extension-boxes.md +6 -0
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +8 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/assets/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/brain/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/brain/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/content/actions/_content-space-access.ts +45 -2
- package/corpus/templates/content/actions/create-content-database.ts +41 -7
- package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/content/app/root.tsx +1 -10
- package/corpus/templates/content/changelog/2026-07-17-creating-a-database-now-completes-reliably-in-hosted-workspa.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/dispatch/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +2 -4
- package/corpus/templates/forms/app/components/ui/popover.tsx +25 -1
- package/corpus/templates/forms/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +13 -10
- package/corpus/templates/forms/app/root.tsx +6 -1
- package/corpus/templates/forms/changelog/2026-07-17-form-builder-popovers-stay-within-the-viewport.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-form-editor-loading-matches-current-sidebar-free-layout.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +14 -3
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +8 -1
- package/corpus/templates/plan/app/root.tsx +0 -2
- package/corpus/templates/plan/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +23 -23
- package/corpus/templates/slides/changelog/2026-07-17-duplicated-slides-now-stay-in-the-deck.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/tasks/app/components/layout/Layout.tsx +2 -13
- package/corpus/templates/tasks/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts +17 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +84 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +10 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +4 -0
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/active-run-state.d.ts +7 -0
- package/dist/client/active-run-state.d.ts.map +1 -1
- package/dist/client/active-run-state.js +28 -0
- package/dist/client/active-run-state.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +4 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +27 -8
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +4 -2
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +6 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +33 -0
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +15 -12
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +1 -1
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +3 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +25 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +23 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/dist/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +20 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/integrations.mdx +27 -27
- package/package.json +1 -1
- package/src/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
|
@@ -24,6 +24,7 @@ export type McpIntegrationVerification =
|
|
|
24
24
|
declare const __AGENT_NATIVE_MCP_INTEGRATIONS_CONFIG__:
|
|
25
25
|
| NormalizedMcpIntegrationsConfig
|
|
26
26
|
| undefined;
|
|
27
|
+
declare const __AGENT_NATIVE_TEMPLATE__: string | undefined;
|
|
27
28
|
|
|
28
29
|
export interface DefaultMcpIntegration {
|
|
29
30
|
id: string;
|
|
@@ -41,6 +42,11 @@ export interface DefaultMcpIntegration {
|
|
|
41
42
|
logoUrl: string;
|
|
42
43
|
docsUrl?: string;
|
|
43
44
|
setupNoteKey?: string;
|
|
45
|
+
apiFallback?: {
|
|
46
|
+
secretKey: string;
|
|
47
|
+
docsUrl: string;
|
|
48
|
+
templateUses?: readonly string[];
|
|
49
|
+
};
|
|
44
50
|
headerPlaceholder?: string;
|
|
45
51
|
keywords: string[];
|
|
46
52
|
}
|
|
@@ -272,6 +278,12 @@ export const DEFAULT_MCP_INTEGRATIONS: DefaultMcpIntegration[] = [
|
|
|
272
278
|
logoUrl: mcpIntegrationLogo("figma"),
|
|
273
279
|
docsUrl: "https://developers.figma.com/docs/figma-mcp-server/",
|
|
274
280
|
setupNoteKey: "mcpIntegrations.catalog.figma.setupNote",
|
|
281
|
+
apiFallback: {
|
|
282
|
+
secretKey: "FIGMA_ACCESS_TOKEN",
|
|
283
|
+
docsUrl:
|
|
284
|
+
"https://developers.figma.com/docs/rest-api/personal-access-tokens/",
|
|
285
|
+
templateUses: ["design"],
|
|
286
|
+
},
|
|
275
287
|
keywords: ["design", "figjam", "components", "variables", "canvas"],
|
|
276
288
|
},
|
|
277
289
|
{
|
|
@@ -532,6 +544,39 @@ function readRuntimeMcpIntegrationsConfig(): NormalizedMcpIntegrationsConfig {
|
|
|
532
544
|
return normalizeMcpIntegrationsConfig();
|
|
533
545
|
}
|
|
534
546
|
|
|
547
|
+
function normalizeTemplateName(value: unknown): string | null {
|
|
548
|
+
if (typeof value !== "string") return null;
|
|
549
|
+
const normalized = value.trim().toLowerCase();
|
|
550
|
+
return normalized || null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function getActiveTemplateName(): string | null {
|
|
554
|
+
try {
|
|
555
|
+
const compiledTemplate = normalizeTemplateName(__AGENT_NATIVE_TEMPLATE__);
|
|
556
|
+
if (compiledTemplate) return compiledTemplate;
|
|
557
|
+
} catch {
|
|
558
|
+
// Test and non-Vite contexts may not define the compile-time constant.
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const runtimeConfig = (
|
|
562
|
+
globalThis as typeof globalThis & {
|
|
563
|
+
__AGENT_NATIVE_CONFIG__?: { template?: unknown };
|
|
564
|
+
}
|
|
565
|
+
).__AGENT_NATIVE_CONFIG__;
|
|
566
|
+
return normalizeTemplateName(runtimeConfig?.template);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export function getMcpIntegrationApiFallback(
|
|
570
|
+
integration: DefaultMcpIntegration,
|
|
571
|
+
templateName = getActiveTemplateName(),
|
|
572
|
+
): DefaultMcpIntegration["apiFallback"] | null {
|
|
573
|
+
const fallback = integration.apiFallback;
|
|
574
|
+
if (!fallback) return null;
|
|
575
|
+
if (!fallback.templateUses?.length) return fallback;
|
|
576
|
+
if (!templateName) return null;
|
|
577
|
+
return fallback.templateUses.includes(templateName) ? fallback : null;
|
|
578
|
+
}
|
|
579
|
+
|
|
535
580
|
function normalizePresetConfig(
|
|
536
581
|
config?: McpIntegrationsConfigInput | NormalizedMcpIntegrationsConfig,
|
|
537
582
|
): NormalizedMcpIntegrationsConfig {
|
|
@@ -261,6 +261,7 @@ function handleSharePopoverInteractOutside(
|
|
|
261
261
|
*/
|
|
262
262
|
export function ShareButton(props: ShareButtonProps) {
|
|
263
263
|
const [open, setOpen] = useState(false);
|
|
264
|
+
const [inviteEmail, setInviteEmail] = useState("");
|
|
264
265
|
const shareTabDefaultValue = props.shareTabs?.defaultValue ?? "share";
|
|
265
266
|
const [activeShareTab, setActiveShareTab] = useState(shareTabDefaultValue);
|
|
266
267
|
const [pendingVisibility, setPendingVisibility] = useState<Visibility | null>(
|
|
@@ -295,6 +296,10 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
295
296
|
}
|
|
296
297
|
};
|
|
297
298
|
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
setInviteEmail("");
|
|
301
|
+
}, [props.resourceType, props.resourceId]);
|
|
302
|
+
|
|
298
303
|
const handleShareTabChange = (value: string) => {
|
|
299
304
|
setActiveShareTab(value);
|
|
300
305
|
props.shareTabs?.onValueChange?.(value);
|
|
@@ -399,6 +404,8 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
399
404
|
>
|
|
400
405
|
<SharePanel
|
|
401
406
|
{...props}
|
|
407
|
+
inviteEmail={inviteEmail}
|
|
408
|
+
onInviteEmailChange={setInviteEmail}
|
|
402
409
|
sharesQuery={sharesQuery}
|
|
403
410
|
visibilityOverride={pendingVisibility}
|
|
404
411
|
onVisibilityChange={handleVisibilityChange}
|
|
@@ -575,6 +582,8 @@ function mergeMembers(existing: OrgMember[], next: OrgMember[]): OrgMember[] {
|
|
|
575
582
|
|
|
576
583
|
function SharePanel(
|
|
577
584
|
props: ShareButtonProps & {
|
|
585
|
+
inviteEmail: string;
|
|
586
|
+
onInviteEmailChange: (email: string) => void;
|
|
578
587
|
sharesQuery: ReturnType<typeof useActionQuery<SharesResponse>>;
|
|
579
588
|
visibilityOverride: Visibility | null;
|
|
580
589
|
onVisibilityChange: (visibility: Visibility) => Promise<void>;
|
|
@@ -591,17 +600,18 @@ function SharePanel(
|
|
|
591
600
|
visibilityOverride,
|
|
592
601
|
onVisibilityChange,
|
|
593
602
|
onClose,
|
|
603
|
+
inviteEmail,
|
|
604
|
+
onInviteEmailChange,
|
|
594
605
|
} = props;
|
|
595
606
|
|
|
596
607
|
const share = useActionMutation("share-resource");
|
|
597
608
|
const unshare = useActionMutation("unshare-resource");
|
|
598
609
|
|
|
599
|
-
const [email, setEmail] = useState("");
|
|
600
610
|
const [role, setRole] = useState<Role>("viewer");
|
|
601
611
|
const [notifyPeople, setNotifyPeople] = useState(true);
|
|
602
612
|
const [shareError, setShareError] = useState<string | null>(null);
|
|
603
613
|
const [suggestionsOpen, setSuggestionsOpen] = useState(false);
|
|
604
|
-
const hasInviteEmail =
|
|
614
|
+
const hasInviteEmail = inviteEmail.trim().length > 0;
|
|
605
615
|
|
|
606
616
|
// Optimistic overlays so clicks feel instant.
|
|
607
617
|
const [pendingAdds, setPendingAdds] = useState<Share[]>([]);
|
|
@@ -685,7 +695,10 @@ function SharePanel(
|
|
|
685
695
|
.map((s) => ({ ...s, role: roleOverrides[keyOf(s)] ?? s.role })),
|
|
686
696
|
...pendingAdds,
|
|
687
697
|
];
|
|
688
|
-
const memberSearch = useOrgMemberSearch(
|
|
698
|
+
const memberSearch = useOrgMemberSearch(
|
|
699
|
+
inviteEmail,
|
|
700
|
+
canManage && suggestionsOpen,
|
|
701
|
+
);
|
|
689
702
|
const excludedMemberEmails = new Set<string>();
|
|
690
703
|
if (data?.ownerEmail) excludedMemberEmails.add(data.ownerEmail.toLowerCase());
|
|
691
704
|
for (const s of shares) {
|
|
@@ -726,7 +739,7 @@ function SharePanel(
|
|
|
726
739
|
};
|
|
727
740
|
|
|
728
741
|
const handleAdd = () => {
|
|
729
|
-
const trimmed =
|
|
742
|
+
const trimmed = inviteEmail.trim();
|
|
730
743
|
if (!trimmed) return;
|
|
731
744
|
const optimistic: Share = {
|
|
732
745
|
id: `pending-${trimmed}`,
|
|
@@ -739,7 +752,7 @@ function SharePanel(
|
|
|
739
752
|
if (inFlight.has(k)) return;
|
|
740
753
|
setShareError(null);
|
|
741
754
|
setPendingAdds((p) => [...p, optimistic]);
|
|
742
|
-
|
|
755
|
+
onInviteEmailChange("");
|
|
743
756
|
setSuggestionsOpen(false);
|
|
744
757
|
addInFlight(k);
|
|
745
758
|
share.mutate(
|
|
@@ -762,7 +775,7 @@ function SharePanel(
|
|
|
762
775
|
onError: (err: any) => {
|
|
763
776
|
setPendingAdds((p) => p.filter((s) => s.id !== optimistic.id));
|
|
764
777
|
clearInFlight(k);
|
|
765
|
-
|
|
778
|
+
onInviteEmailChange(trimmed);
|
|
766
779
|
setShareError(extractShareErrorMessage(err));
|
|
767
780
|
},
|
|
768
781
|
},
|
|
@@ -898,15 +911,15 @@ function SharePanel(
|
|
|
898
911
|
<div className="mb-4 space-y-2">
|
|
899
912
|
<div className="flex items-stretch gap-2">
|
|
900
913
|
<MemberAutocomplete
|
|
901
|
-
value={
|
|
914
|
+
value={inviteEmail}
|
|
902
915
|
open={suggestionsOpen}
|
|
903
916
|
onOpenChange={setSuggestionsOpen}
|
|
904
917
|
onValueChange={(next) => {
|
|
905
|
-
|
|
918
|
+
onInviteEmailChange(next);
|
|
906
919
|
if (shareError) setShareError(null);
|
|
907
920
|
}}
|
|
908
921
|
onSelectMember={(member) => {
|
|
909
|
-
|
|
922
|
+
onInviteEmailChange(member.email);
|
|
910
923
|
setSuggestionsOpen(false);
|
|
911
924
|
if (shareError) setShareError(null);
|
|
912
925
|
}}
|
|
@@ -920,6 +933,14 @@ function SharePanel(
|
|
|
920
933
|
search={memberSearch}
|
|
921
934
|
/>
|
|
922
935
|
<RoleSelect value={role} onChange={setRole} />
|
|
936
|
+
<button
|
|
937
|
+
type="button"
|
|
938
|
+
onClick={handleAdd}
|
|
939
|
+
disabled={!hasInviteEmail}
|
|
940
|
+
className={BUTTON_PRIMARY_SM}
|
|
941
|
+
>
|
|
942
|
+
Add
|
|
943
|
+
</button>
|
|
923
944
|
</div>
|
|
924
945
|
{shareError ? (
|
|
925
946
|
<div
|
|
@@ -35,7 +35,7 @@ export interface UseAgentChatHomeHandoffLinksOptions {
|
|
|
35
35
|
enabled?: boolean;
|
|
36
36
|
/** How long the handoff marker remains valid. Defaults to 6 hours. */
|
|
37
37
|
ttlMs?: number;
|
|
38
|
-
/** Only intercept links if a recent handoff marker already exists. */
|
|
38
|
+
/** Only intercept links if a recent handoff marker already exists. Defaults to true. */
|
|
39
39
|
requireActiveHandoff?: boolean;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -148,7 +148,7 @@ export function useAgentChatHomeHandoffLinks({
|
|
|
148
148
|
isChatPath,
|
|
149
149
|
enabled = true,
|
|
150
150
|
ttlMs,
|
|
151
|
-
requireActiveHandoff =
|
|
151
|
+
requireActiveHandoff = true,
|
|
152
152
|
}: UseAgentChatHomeHandoffLinksOptions): void {
|
|
153
153
|
const location = useLocation();
|
|
154
154
|
const navigate = useNavigate();
|
|
@@ -697,7 +697,10 @@ const messages = {
|
|
|
697
697
|
configure: "Configure",
|
|
698
698
|
connect: "Connect",
|
|
699
699
|
connectWithOAuth: "Connect with OAuth",
|
|
700
|
+
useApiToken: "Use API token",
|
|
700
701
|
connectSuggestion: "Connect {{name}} to use this link in chat",
|
|
702
|
+
connectSuggestionWithApiToken:
|
|
703
|
+
"Connect {{name}} with an API token to use this link in chat",
|
|
701
704
|
dismissSuggestion: "Dismiss integration suggestion",
|
|
702
705
|
backToIntegrations: "Back to integrations",
|
|
703
706
|
customTitle: "Add custom MCP server",
|
|
@@ -792,7 +795,7 @@ const messages = {
|
|
|
792
795
|
"Bring Figma design context and canvas actions into an agent.",
|
|
793
796
|
useCase: "Design files, components, variables, design systems, canvas",
|
|
794
797
|
setupNote:
|
|
795
|
-
"Figma only allows clients listed in
|
|
798
|
+
"Figma MCP only allows clients listed in Figma's MCP Catalog, so this remote endpoint cannot connect from Agent Native yet. Use the Figma REST API fallback with a personal access token for reading file and node context; MCP canvas actions remain unavailable until Figma approves Agent Native.",
|
|
796
799
|
},
|
|
797
800
|
canva: {
|
|
798
801
|
description: "Search, create, and update Canva designs and assets.",
|
|
@@ -197,4 +197,32 @@ export function registerFrameworkSecrets(): void {
|
|
|
197
197
|
required: false,
|
|
198
198
|
});
|
|
199
199
|
}
|
|
200
|
+
|
|
201
|
+
if (!getRequiredSecret("FIGMA_ACCESS_TOKEN")) {
|
|
202
|
+
registerRequiredSecret({
|
|
203
|
+
key: "FIGMA_ACCESS_TOKEN",
|
|
204
|
+
label: "Figma access token",
|
|
205
|
+
description:
|
|
206
|
+
"Optional fallback for reading Figma file and node context when the hosted Figma MCP server is unavailable. Generate a personal access token with current_user:read and file_content:read.",
|
|
207
|
+
docsUrl:
|
|
208
|
+
"https://developers.figma.com/docs/rest-api/personal-access-tokens/",
|
|
209
|
+
scope: "user",
|
|
210
|
+
kind: "api-key",
|
|
211
|
+
required: false,
|
|
212
|
+
validator: async (value) => {
|
|
213
|
+
const response = await fetch("https://api.figma.com/v1/me", {
|
|
214
|
+
headers: {
|
|
215
|
+
"X-Figma-Token": value,
|
|
216
|
+
"User-Agent": "AgentNative/1.0",
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
return response.ok
|
|
220
|
+
? { ok: true }
|
|
221
|
+
: {
|
|
222
|
+
ok: false,
|
|
223
|
+
error: `Figma rejected the token (HTTP ${response.status}).`,
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
}
|
|
200
228
|
}
|
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
callerHasRunAccess,
|
|
70
70
|
callerHasThreadAccess,
|
|
71
71
|
} from "../agent/run-ownership.js";
|
|
72
|
-
import { readBackgroundRunClaim } from "../agent/run-store.js";
|
|
72
|
+
import { markTurnAborted, readBackgroundRunClaim } from "../agent/run-store.js";
|
|
73
73
|
import {
|
|
74
74
|
buildCurrentTimeUserContext,
|
|
75
75
|
buildRuntimeContextPrompt,
|
|
@@ -4137,6 +4137,31 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4137
4137
|
callerHasRunAccess(owner, runId, "viewer", { orgId });
|
|
4138
4138
|
const canEditRun = (runId: string) =>
|
|
4139
4139
|
callerHasRunAccess(owner, runId, "editor", { orgId });
|
|
4140
|
+
const canEditThread = (threadId: string) =>
|
|
4141
|
+
callerHasThreadAccess(owner, threadId, "editor", { orgId });
|
|
4142
|
+
|
|
4143
|
+
// Route: POST /runs/turn/:turnId/abort
|
|
4144
|
+
// Covers the short window where the client has sent a turn but the
|
|
4145
|
+
// server has not yet created its background run id.
|
|
4146
|
+
const turnAbortMatch =
|
|
4147
|
+
url.match(/\/runs\/turn\/([^/?]+)\/abort/) ||
|
|
4148
|
+
url.match(/^\/turn\/([^/?]+)\/abort/);
|
|
4149
|
+
if (turnAbortMatch && method === "POST") {
|
|
4150
|
+
const turnId = decodeURIComponent(turnAbortMatch[1]);
|
|
4151
|
+
const body = await readBody(event).catch(() => null);
|
|
4152
|
+
const threadId =
|
|
4153
|
+
typeof body?.threadId === "string" ? body.threadId : "";
|
|
4154
|
+
if (
|
|
4155
|
+
!/^[a-zA-Z0-9_-]{1,160}$/.test(turnId) ||
|
|
4156
|
+
!threadId ||
|
|
4157
|
+
!(await canEditThread(threadId))
|
|
4158
|
+
) {
|
|
4159
|
+
setResponseStatus(event, 404);
|
|
4160
|
+
return { error: "Run not found" };
|
|
4161
|
+
}
|
|
4162
|
+
await markTurnAborted(threadId, turnId);
|
|
4163
|
+
return { ok: true };
|
|
4164
|
+
}
|
|
4140
4165
|
|
|
4141
4166
|
// Route: GET /runs/list?goalId=agent-team|agent-harness
|
|
4142
4167
|
// Returns background agents in the Code hub-compatible run shape.
|
|
@@ -5180,6 +5205,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5180
5205
|
}
|
|
5181
5206
|
workerBody = {
|
|
5182
5207
|
...parsedPayload,
|
|
5208
|
+
...(prepared.body.internalContinuation === true
|
|
5209
|
+
? { internalContinuation: true }
|
|
5210
|
+
: {}),
|
|
5183
5211
|
[AGENT_CHAT_BACKGROUND_RUN_FIELD]: preparedMarker,
|
|
5184
5212
|
};
|
|
5185
5213
|
}
|
|
@@ -2579,6 +2579,27 @@ function createAgentNativePlugins(
|
|
|
2579
2579
|
].filter(Boolean);
|
|
2580
2580
|
}
|
|
2581
2581
|
|
|
2582
|
+
function resolveAgentNativeTemplate(cwd: string): string {
|
|
2583
|
+
const configured = [
|
|
2584
|
+
process.env.AGENT_NATIVE_TEMPLATE,
|
|
2585
|
+
process.env.VITE_AGENT_NATIVE_TEMPLATE,
|
|
2586
|
+
process.env.VITE_APP_TEMPLATE,
|
|
2587
|
+
].find((value) => value?.trim());
|
|
2588
|
+
if (configured) return configured.trim().toLowerCase();
|
|
2589
|
+
|
|
2590
|
+
const normalizedCwd = cwd.replaceAll("\\", "/");
|
|
2591
|
+
const marker = "/templates/";
|
|
2592
|
+
const markerIndex = normalizedCwd.lastIndexOf(marker);
|
|
2593
|
+
if (markerIndex === -1) return "";
|
|
2594
|
+
return (
|
|
2595
|
+
normalizedCwd
|
|
2596
|
+
.slice(markerIndex + marker.length)
|
|
2597
|
+
.split("/")[0]
|
|
2598
|
+
?.trim()
|
|
2599
|
+
.toLowerCase() ?? ""
|
|
2600
|
+
);
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2582
2603
|
function createAgentNativeConfig(
|
|
2583
2604
|
options: ClientConfigOptions | AgentNativeVitePluginOptions = {},
|
|
2584
2605
|
command?: AgentNativeViteCommand,
|
|
@@ -2683,6 +2704,9 @@ function createAgentNativeConfig(
|
|
|
2683
2704
|
__AGENT_NATIVE_MCP_INTEGRATIONS_CONFIG__: JSON.stringify(
|
|
2684
2705
|
normalizeMcpIntegrationsConfig(options.mcpIntegrations),
|
|
2685
2706
|
),
|
|
2707
|
+
__AGENT_NATIVE_TEMPLATE__: JSON.stringify(
|
|
2708
|
+
resolveAgentNativeTemplate(cwd),
|
|
2709
|
+
),
|
|
2686
2710
|
},
|
|
2687
2711
|
server: {
|
|
2688
2712
|
...(userConfig.server ?? {}),
|
|
@@ -83,7 +83,7 @@ When the user asks for a dashboard:
|
|
|
83
83
|
2. If a metric definition, date range, or grain is ambiguous and the choice would change the panel's numbers, use the `ask-question` clarifying tool once before building. Skip it when the dictionary or the user already settled it.
|
|
84
84
|
3. If a metric is not documented, do not guess column names. Ask for the table/columns or introspect the provider schema, then propose a dictionary entry with `save-data-dictionary-entry`.
|
|
85
85
|
4. Build a complete `SqlDashboardConfig` with `name` and `panels`. Optionally set top-level `columns` (1–6, default 2) to control how many grid columns the panels before any section use.
|
|
86
|
-
5. Every panel needs `id`, `title`, `source`, `chartType`, `width`, and `sql`. `width` is the number of grid columns the panel spans (1..6, clamped to the active section's column count). Section panels skip `source` and `sql` and may set their own `columns` (1–6) to override the dashboard default for the panels following the section. Extension panels (`chartType: "extension"`) also skip `source` and `sql`;
|
|
86
|
+
5. Every panel needs `id`, `title`, `source`, `chartType`, `width`, and `sql`. `width` is the number of grid columns the panel spans (1..6, clamped to the active section's column count). Section panels skip `source` and `sql` and may set their own `columns` (1–6) to override the dashboard default for the panels following the section. Extension panels (`chartType: "extension"`) also skip `source` and `sql`; use `config.extensionSlotId` for new panels, with legacy `config.extensionId` supported for existing direct embeds (see "Embedding An Extension As A Panel").
|
|
87
87
|
6. Persist with `update-dashboard`, not raw SQL or settings writes.
|
|
88
88
|
7. Navigate to it with `pnpm action navigate --view=adhoc --dashboardId=<id>`.
|
|
89
89
|
|
|
@@ -114,14 +114,18 @@ rather than forcing it into a native dashboard config.
|
|
|
114
114
|
|
|
115
115
|
## Embedding An Extension As A Panel
|
|
116
116
|
|
|
117
|
-
Use `chartType: "extension"` to
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
117
|
+
Use `chartType: "extension"` to add an extension box alongside normal SQL
|
|
118
|
+
charts. The panel skips `source` and `sql`. New boxes use a stable slot id in
|
|
119
|
+
`config.extensionSlotId`:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
analytics.dashboard.<dashboard-id>.panel.<panel-id>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Create or choose the extension, call `add-extension-slot-target` with the
|
|
126
|
+
extension id and slot id, then call `install-extension` with the same values.
|
|
127
|
+
The dashboard panel is shared, while the installed extension is per-user.
|
|
128
|
+
Empty slots show the normal install affordance instead of a broken iframe.
|
|
125
129
|
|
|
126
130
|
```jsonc
|
|
127
131
|
{
|
|
@@ -129,21 +133,22 @@ extension panel without a non-empty `config.extensionId`.
|
|
|
129
133
|
"title": "Pipeline Widget",
|
|
130
134
|
"chartType": "extension",
|
|
131
135
|
"width": 3,
|
|
132
|
-
"config": {
|
|
136
|
+
"config": {
|
|
137
|
+
"extensionSlotId": "analytics.dashboard.weekly-metrics.panel.pipeline-widget",
|
|
138
|
+
},
|
|
133
139
|
}
|
|
134
140
|
```
|
|
135
141
|
|
|
136
142
|
Notes:
|
|
137
143
|
|
|
138
|
-
-
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
the same audience as the dashboard so all viewers can see it.
|
|
144
|
+
- Slot-backed extensions receive the dashboard id, name, description, current
|
|
145
|
+
filters, and panel id/title/slot id as live context.
|
|
146
|
+
- Installs and extension access are per viewer. Sharing the dashboard does not
|
|
147
|
+
automatically install or grant access to its extension for other viewers.
|
|
148
|
+
- `config.extensionId` remains supported for legacy direct embeds. Use it only
|
|
149
|
+
when preserving an existing dashboard; new boxes should use a slot.
|
|
145
150
|
|
|
146
|
-
## Cloning
|
|
151
|
+
## Cloning A Legacy Direct-Extension Dashboard (e.g. per-customer copies)
|
|
147
152
|
|
|
148
153
|
When the user asks for a copy of an existing extension-backed dashboard for a
|
|
149
154
|
different customer/org (for example "make an Intuit version of the Roku usage
|
|
@@ -154,8 +159,11 @@ from that written file — never by pulling the body into chat context first or
|
|
|
154
159
|
re-typing it as a `content` argument.
|
|
155
160
|
|
|
156
161
|
1. `get-sql-dashboard` with `includeConfig: true` on the source dashboard and
|
|
157
|
-
confirm the target panel is `chartType: "extension"
|
|
158
|
-
`config.extensionId
|
|
162
|
+
confirm the target panel is a legacy `chartType: "extension"` panel with
|
|
163
|
+
`config.extensionId`; grab that extension id. For a slot-backed panel, clone
|
|
164
|
+
the dashboard panel with a new stable `extensionSlotId`, then target and
|
|
165
|
+
install the desired extension into that slot instead of using this body-copy
|
|
166
|
+
playbook.
|
|
159
167
|
2. `get-extension` for that id with `forceContent: true` **exactly once**. Reuse
|
|
160
168
|
that body for the rest of the turn — a second same-run read intentionally
|
|
161
169
|
omits `content` and returns `contentOmitted` instead. That is not the content
|
|
@@ -397,7 +405,7 @@ type PanelInput = PanelPatch & {
|
|
|
397
405
|
chartType: NonNullable<PanelPatch["chartType"]>;
|
|
398
406
|
source?: PanelPatch["source"]; // required for non-section / non-extension panels
|
|
399
407
|
sql?: string; // required for non-section / non-extension panels
|
|
400
|
-
// For chartType "extension": config.extensionId is
|
|
408
|
+
// For chartType "extension": use config.extensionSlotId; extensionId is legacy direct embed compatibility.
|
|
401
409
|
};
|
|
402
410
|
|
|
403
411
|
type PanelFilter = {
|
|
@@ -163,6 +163,14 @@ membership id when its native update status reports `update-available`.
|
|
|
163
163
|
serialization traps. The script is constrained: only documented dashboard
|
|
164
164
|
method calls with JSON-compatible arguments are parsed; variables, imports,
|
|
165
165
|
loops, functions, network, filesystem, and DB access are not available.
|
|
166
|
+
- Dashboard extension boxes use `chartType: "extension"` with
|
|
167
|
+
`config.extensionSlotId`. The stable per-box slot is
|
|
168
|
+
`analytics.dashboard.<dashboard-id>.panel.<panel-id>`. To add an extension,
|
|
169
|
+
create/choose it, call `add-extension-slot-target` with that slot id, then
|
|
170
|
+
`install-extension` with the same slot id. Installs are per-user; the
|
|
171
|
+
dashboard panel remains shared. Slot-backed extensions receive dashboard id,
|
|
172
|
+
name, description, current filters, and the panel id/title/slot id as context.
|
|
173
|
+
`config.extensionId` is legacy direct embedding for existing dashboards.
|
|
166
174
|
- Dashboard saves keep bounded history in SQL. Use
|
|
167
175
|
`list-dashboard-revisions` to inspect undo points and
|
|
168
176
|
`restore-dashboard-revision` to restore one instead of hand-editing history
|
|
@@ -132,7 +132,7 @@ export const DASHBOARD_MUTATION_EXAMPLES = [
|
|
|
132
132
|
`dashboard.insertPanel({"id":"new-kpi","title":"New KPI","source":"first-party","chartType":"metric","width":1,"config":{"timeScope":"dashboard"},"sql":"SELECT COUNT(*) AS value FROM analytics_events WHERE event_date >= '{{dateStart}}' AND event_date < '{{dateEnd}}'"}).atTop();`,
|
|
133
133
|
`dashboard.insertPanel({"id":"new-chart","title":"New Chart","source":"first-party","chartType":"line","width":1,"config":{"timeScope":"dashboard"},"sql":"SELECT event_date AS date, COUNT(*) AS value FROM analytics_events WHERE event_date >= '{{dateStart}}' AND event_date < '{{dateEnd}}' GROUP BY event_date ORDER BY event_date"}).nextTo("retention-over-time");`,
|
|
134
134
|
`dashboard.insertPanel({"id":"row-chart","title":"Row Chart","source":"first-party","chartType":"bar","width":1,"config":{"timeScope":"dashboard"},"sql":"SELECT event_name AS name, COUNT(*) AS value FROM analytics_events WHERE event_date >= '{{dateStart}}' AND event_date < '{{dateEnd}}' GROUP BY event_name"}).atRow(2);`,
|
|
135
|
-
'dashboard.insertPanel({"id":"pipeline-widget","title":"Pipeline Widget","chartType":"extension","width":3,"config":{"
|
|
135
|
+
'dashboard.insertPanel({"id":"pipeline-widget","title":"Pipeline Widget","chartType":"extension","width":3,"config":{"extensionSlotId":"analytics.dashboard.<dashboard-id>.panel.pipeline-widget"}}).atBottom();',
|
|
136
136
|
] as const;
|
|
137
137
|
|
|
138
138
|
export type DashboardMutationOperation =
|
|
@@ -369,8 +369,12 @@ export function validateDashboardConfig(
|
|
|
369
369
|
cfg && typeof cfg.extensionId === "string"
|
|
370
370
|
? cfg.extensionId.trim()
|
|
371
371
|
: "";
|
|
372
|
-
|
|
373
|
-
|
|
372
|
+
const extensionSlotId =
|
|
373
|
+
cfg && typeof cfg.extensionSlotId === "string"
|
|
374
|
+
? cfg.extensionSlotId.trim()
|
|
375
|
+
: "";
|
|
376
|
+
if (!extensionId && !extensionSlotId) {
|
|
377
|
+
return `panel[${i}].config.extensionId or config.extensionSlotId is required for extension panels`;
|
|
374
378
|
}
|
|
375
379
|
}
|
|
376
380
|
if (
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { useDemoModeStatus, useT } from "@agent-native/core/client";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
EmbeddedExtension,
|
|
4
|
+
ExtensionSlot,
|
|
5
|
+
} from "@agent-native/core/client/extensions";
|
|
3
6
|
import {
|
|
4
7
|
IconArrowsSort,
|
|
5
8
|
IconSortAscending,
|
|
@@ -1185,6 +1188,8 @@ interface SqlChartProps {
|
|
|
1185
1188
|
className?: string;
|
|
1186
1189
|
loadData?: boolean;
|
|
1187
1190
|
onExportCsvChange?: (handler: (() => void) | null) => void;
|
|
1191
|
+
/** Dashboard/panel state sent to slot-backed extension boxes. */
|
|
1192
|
+
extensionContext?: Record<string, unknown> | null;
|
|
1188
1193
|
}
|
|
1189
1194
|
|
|
1190
1195
|
export function SqlChart({
|
|
@@ -1192,6 +1197,7 @@ export function SqlChart({
|
|
|
1192
1197
|
resolvedSql,
|
|
1193
1198
|
loadData = true,
|
|
1194
1199
|
onExportCsvChange,
|
|
1200
|
+
extensionContext,
|
|
1195
1201
|
}: SqlChartProps) {
|
|
1196
1202
|
const t = useT();
|
|
1197
1203
|
const { enabled: demoModeEnabled } = useDemoModeStatus();
|
|
@@ -1266,11 +1272,12 @@ export function SqlChart({
|
|
|
1266
1272
|
);
|
|
1267
1273
|
}
|
|
1268
1274
|
|
|
1269
|
-
// Extension panels render a
|
|
1270
|
-
//
|
|
1275
|
+
// Extension panels render either a named extension-point slot or a legacy
|
|
1276
|
+
// direct extension iframe instead of querying a data source.
|
|
1271
1277
|
if (isExtension) {
|
|
1272
1278
|
const extensionId = panel.config?.extensionId;
|
|
1273
|
-
|
|
1279
|
+
const slotId = panel.config?.extensionSlotId;
|
|
1280
|
+
if (!extensionId && !slotId) {
|
|
1274
1281
|
return (
|
|
1275
1282
|
<div className="flex flex-1 items-center justify-center px-4 py-8 min-h-[120px]">
|
|
1276
1283
|
<p className="text-sm text-muted-foreground text-center">
|
|
@@ -1280,7 +1287,12 @@ export function SqlChart({
|
|
|
1280
1287
|
);
|
|
1281
1288
|
}
|
|
1282
1289
|
return (
|
|
1283
|
-
<DashboardExtensionPanel
|
|
1290
|
+
<DashboardExtensionPanel
|
|
1291
|
+
extensionId={extensionId}
|
|
1292
|
+
panelId={panel.id}
|
|
1293
|
+
slotId={slotId}
|
|
1294
|
+
context={extensionContext}
|
|
1295
|
+
/>
|
|
1284
1296
|
);
|
|
1285
1297
|
}
|
|
1286
1298
|
const colors = panel.config?.colors || DEFAULT_COLORS;
|
|
@@ -1409,9 +1421,13 @@ export function SqlChart({
|
|
|
1409
1421
|
function DashboardExtensionPanel({
|
|
1410
1422
|
extensionId,
|
|
1411
1423
|
panelId,
|
|
1424
|
+
slotId,
|
|
1425
|
+
context,
|
|
1412
1426
|
}: {
|
|
1413
|
-
extensionId
|
|
1427
|
+
extensionId?: string;
|
|
1414
1428
|
panelId: string;
|
|
1429
|
+
slotId?: string;
|
|
1430
|
+
context?: Record<string, unknown> | null;
|
|
1415
1431
|
}) {
|
|
1416
1432
|
const t = useT();
|
|
1417
1433
|
// Hold the report-readiness marker until the extension iframe paints so
|
|
@@ -1419,6 +1435,18 @@ function DashboardExtensionPanel({
|
|
|
1419
1435
|
const [ready, setReady] = useState(false);
|
|
1420
1436
|
const [unavailable, setUnavailable] = useState(false);
|
|
1421
1437
|
|
|
1438
|
+
if (slotId) {
|
|
1439
|
+
return (
|
|
1440
|
+
<ExtensionSlot
|
|
1441
|
+
id={slotId}
|
|
1442
|
+
context={context}
|
|
1443
|
+
showEmptyAffordance
|
|
1444
|
+
className="min-h-[120px] w-full"
|
|
1445
|
+
toolClassName="w-full"
|
|
1446
|
+
/>
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1422
1450
|
// Embedding never grants access to the extension itself (same model as
|
|
1423
1451
|
// ExtensionSlots). A viewer with dashboard-only access who can't see the
|
|
1424
1452
|
// referenced extension gets a clear message instead of a blank panel.
|
|
@@ -1438,7 +1466,7 @@ function DashboardExtensionPanel({
|
|
|
1438
1466
|
data-dashboard-report-loading={ready ? undefined : "true"}
|
|
1439
1467
|
>
|
|
1440
1468
|
<EmbeddedExtension
|
|
1441
|
-
extensionId={extensionId}
|
|
1469
|
+
extensionId={extensionId!}
|
|
1442
1470
|
slotId={`dashboard-panel-${panelId}`}
|
|
1443
1471
|
className="w-full"
|
|
1444
1472
|
// Intentional for v1: extension panels are standalone widgets and do not
|
|
@@ -163,8 +163,6 @@ export function Layout({ children }: LayoutProps) {
|
|
|
163
163
|
activePath: location.pathname,
|
|
164
164
|
enabled: !isAskRoute && !reportScreenshot,
|
|
165
165
|
});
|
|
166
|
-
const sidebarScope = chatHomeHandoffActive ? null : analyticsScope;
|
|
167
|
-
|
|
168
166
|
useEffect(() => {
|
|
169
167
|
function handleChatRunning(event: Event) {
|
|
170
168
|
const detail = (event as CustomEvent).detail;
|
|
@@ -294,7 +292,7 @@ export function Layout({ children }: LayoutProps) {
|
|
|
294
292
|
t("chat.suggestionAnomalies"),
|
|
295
293
|
t("chat.suggestionMrr"),
|
|
296
294
|
]}
|
|
297
|
-
scope={
|
|
295
|
+
scope={analyticsScope}
|
|
298
296
|
composerSlot={<CreativeContextComposerChip />}
|
|
299
297
|
>
|
|
300
298
|
{contentFrame}
|