@agent-native/core 0.164.3 → 0.164.11
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/calendar/app/components/calendar/CreateEventDialog.tsx +36 -47
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +18 -1
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +256 -38
- package/corpus/templates/calendar/app/hooks/use-google-auth.ts +43 -3
- package/corpus/templates/calendar/app/i18n-data.ts +120 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +113 -0
- package/corpus/templates/calendar/server/handlers/google-auth.ts +119 -3
- package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +1 -1
- package/corpus/templates/clips/app/components/player/video-player.tsx +12 -0
- package/corpus/templates/clips/app/routes/_app.dictate.tsx +18 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +114 -20
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -78
- package/corpus/templates/clips/changelog/2026-08-19-clips-nightly-builds-are-available-for-trying-the-latest-upd.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +6 -0
- package/corpus/templates/clips/desktop/README.md +19 -4
- package/corpus/templates/clips/desktop/src/app.tsx +78 -16
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -2
- package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -0
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +7 -13
- package/corpus/templates/clips/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +41 -3
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +22 -1
- package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +41 -22
- package/corpus/templates/clips/shared/share-meta.ts +67 -3
- package/corpus/templates/content/actions/list-content-databases.ts +95 -1
- package/corpus/templates/content/changelog/2026-08-11-content-mutations-delegated-from-verified-slack-dms-now-use-.md +6 -0
- package/corpus/templates/content/shared/api.ts +11 -0
- package/corpus/templates/factory/AGENTS.md +1 -1
- package/corpus/templates/factory/actions/list-factory-automations.ts +4 -0
- package/corpus/templates/factory/actions/start-builder-for-item.ts +19 -17
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +100 -36
- package/corpus/templates/factory/app/i18n/en-US.ts +4 -0
- package/corpus/templates/factory/app/routes/factory.tsx +209 -46
- package/corpus/templates/factory/server/connectors/slack.ts +44 -2
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -7
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +13 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +5 -0
- package/corpus/templates/factory/server/triage/slack-poller.ts +72 -4
- package/corpus/templates/mail/actions/view-screen.ts +1 -1
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -18
- package/corpus/templates/mail/server/handlers/google-auth.ts +125 -24
- package/corpus/templates/mail/server/lib/gmail-query.ts +5 -2
- package/corpus/templates/mail/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +0 -12
- package/corpus/templates/plan/changelog/2026-08-19-plan-prompts-now-guide-you-to-connect-ai-before-sending-and-.md +6 -0
- package/corpus/templates/slides/actions/approve-deck-access-request.ts +234 -0
- package/corpus/templates/slides/actions/get-deck-access-status.ts +35 -2
- package/corpus/templates/slides/actions/request-deck-access.ts +263 -64
- package/corpus/templates/slides/app/components/editor/MissingDeckAccessPane.tsx +196 -0
- package/corpus/templates/slides/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
- package/corpus/templates/slides/app/hooks/use-deck-access.ts +7 -4
- package/corpus/templates/slides/app/i18n/en-US.ts +29 -6
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +147 -169
- package/corpus/templates/slides/app/routes/access-request.approve.tsx +250 -0
- package/corpus/templates/slides/server/db/schema.ts +8 -0
- package/corpus/templates/slides/server/lib/access-request-email.ts +36 -0
- package/corpus/templates/slides/server/lib/emails.ts +26 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +7 -0
- package/corpus/templates/slides/server/plugins/db.ts +50 -0
- package/corpus/templates/slides/shared/deck-access.ts +31 -0
- package/dist/a2a/artifact-response.d.ts +50 -0
- package/dist/a2a/artifact-response.js +266 -39
- package/dist/a2a/index.d.ts +2 -0
- package/dist/a2a/index.js +1 -0
- package/dist/a2a/task-store.d.ts +1 -0
- package/dist/a2a/task-store.js +1 -1
- package/dist/agent/observational-memory/store.d.ts +1 -0
- package/dist/agent/observational-memory/store.js +1 -1
- package/dist/agent/run-store.d.ts +1 -0
- package/dist/agent/run-store.js +1 -1
- package/dist/agent/tool-approval-store.d.ts +1 -0
- package/dist/agent/tool-approval-store.js +1 -1
- package/dist/application-state/store.d.ts +1 -0
- package/dist/application-state/store.js +1 -1
- package/dist/browser-sessions/store.d.ts +1 -0
- package/dist/browser-sessions/store.js +1 -1
- package/dist/checkpoints/store.d.ts +1 -0
- package/dist/checkpoints/store.js +1 -1
- package/dist/cli/code-agent-schedules.d.ts +11 -0
- package/dist/cli/code-agent-schedules.js +44 -5
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AssistantChat.d.ts +5 -1
- package/dist/client/AssistantChat.js +40 -6
- package/dist/client/EnvironmentBadge.d.ts +7 -13
- package/dist/client/EnvironmentBadge.js +43 -92
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/agent-chat-adapter.js +76 -50
- package/dist/client/app-providers.js +4 -4
- package/dist/client/chat/message-components.d.ts +4 -0
- package/dist/client/chat/message-components.js +28 -1
- package/dist/client/chat/run-recovery.js +26 -0
- package/dist/client/chat-first/apps-rail.d.ts +1 -0
- package/dist/client/chat-first/apps-rail.js +28 -2
- package/dist/client/sse-event-processor.js +13 -8
- package/dist/client/use-active-agent-chat-run.d.ts +2 -0
- package/dist/client/use-active-agent-chat-run.js +22 -0
- package/dist/coding-tools/sandbox/executions-store.d.ts +1 -0
- package/dist/coding-tools/sandbox/executions-store.js +1 -1
- package/dist/collab/awareness-store.d.ts +1 -0
- package/dist/collab/awareness-store.js +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/storage.d.ts +1 -0
- package/dist/collab/storage.js +1 -1
- package/dist/db/client.js +3 -3
- package/dist/db/ddl-guard.js +8 -0
- package/dist/db/migration-runtime.d.ts +23 -0
- package/dist/db/migration-runtime.js +41 -0
- package/dist/db/migrations.d.ts +1 -2
- package/dist/db/migrations.js +2 -24
- package/dist/email-catalog/log.d.ts +1 -0
- package/dist/email-catalog/log.js +1 -1
- package/dist/guards/index.d.ts +1 -0
- package/dist/guards/index.js +1 -0
- package/dist/guards/release-schema-complete.d.ts +22 -0
- package/dist/guards/release-schema-complete.js +144 -0
- package/dist/integrations/a2a-continuations-store.d.ts +1 -0
- package/dist/integrations/a2a-continuations-store.js +1 -1
- package/dist/integrations/adapters/slack.js +58 -7
- package/dist/integrations/awaiting-input-store.d.ts +1 -0
- package/dist/integrations/awaiting-input-store.js +1 -1
- package/dist/integrations/config-store.d.ts +1 -0
- package/dist/integrations/config-store.js +1 -1
- package/dist/integrations/controls-store.d.ts +1 -0
- package/dist/integrations/controls-store.js +1 -1
- package/dist/integrations/identity-links-store.d.ts +1 -0
- package/dist/integrations/identity-links-store.js +1 -1
- package/dist/integrations/installations-store.d.ts +1 -0
- package/dist/integrations/installations-store.js +1 -1
- package/dist/integrations/remote-commands-store.d.ts +1 -0
- package/dist/integrations/remote-commands-store.js +1 -1
- package/dist/integrations/remote-devices-store.d.ts +1 -0
- package/dist/integrations/remote-devices-store.js +1 -1
- package/dist/integrations/remote-push-store.d.ts +1 -0
- package/dist/integrations/remote-push-store.js +1 -1
- package/dist/integrations/remote-run-events-store.d.ts +1 -0
- package/dist/integrations/remote-run-events-store.js +1 -1
- package/dist/integrations/scope-store.d.ts +1 -0
- package/dist/integrations/scope-store.js +1 -1
- package/dist/integrations/thread-mapping-store.d.ts +1 -0
- package/dist/integrations/thread-mapping-store.js +1 -1
- package/dist/integrations/usage-budget-store.d.ts +1 -0
- package/dist/integrations/usage-budget-store.js +1 -1
- package/dist/jobs/background-automation-runner.js +111 -9
- package/dist/jobs/run-history.d.ts +1 -0
- package/dist/jobs/run-history.js +1 -1
- package/dist/jobs/scheduler-health.d.ts +1 -0
- package/dist/jobs/scheduler-health.js +1 -1
- package/dist/mcp/approval-store.d.ts +1 -0
- package/dist/mcp/approval-store.js +1 -1
- package/dist/mcp/connect-store.d.ts +1 -0
- package/dist/mcp/connect-store.js +1 -1
- package/dist/mcp/oauth-store.d.ts +1 -0
- package/dist/mcp/oauth-store.js +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/notifications/store.d.ts +1 -0
- package/dist/notifications/store.js +1 -1
- package/dist/oauth-tokens/store.d.ts +1 -0
- package/dist/oauth-tokens/store.js +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/progress/store.d.ts +1 -0
- package/dist/progress/store.js +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/provider-api/corpus-jobs-store.d.ts +1 -0
- package/dist/provider-api/corpus-jobs-store.js +1 -1
- package/dist/provider-api/custom-registry.d.ts +1 -0
- package/dist/provider-api/custom-registry.js +1 -1
- package/dist/provider-api/quota-governor.d.ts +1 -0
- package/dist/provider-api/quota-governor.js +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts +1 -0
- package/dist/provider-api/staged-datasets-store.js +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/resources/store.d.ts +1 -0
- package/dist/resources/store.js +1 -1
- package/dist/scripts/dev/index.js +15 -1
- package/dist/search/index.js +3 -0
- package/dist/secrets/storage.d.ts +1 -0
- package/dist/secrets/storage.js +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +2 -1
- package/dist/server/agent-chat/action-filters-a2a.js +10 -2
- package/dist/server/agent-chat-plugin.js +71 -20
- package/dist/server/agent-teams-run-queue.d.ts +1 -0
- package/dist/server/agent-teams-run-queue.js +1 -1
- package/dist/server/auth-marketing.js +3 -1
- package/dist/server/auth.d.ts +18 -1
- package/dist/server/auth.js +370 -154
- package/dist/server/embed-session.d.ts +1 -0
- package/dist/server/embed-session.js +1 -1
- package/dist/server/google-auth-plugin.js +3 -1
- package/dist/server/google-oauth.d.ts +10 -0
- package/dist/server/google-oauth.js +25 -1
- package/dist/server/identity-sso-store.d.ts +1 -0
- package/dist/server/identity-sso-store.js +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/onboarding-html.js +200 -31
- package/dist/server/poll.d.ts +2 -0
- package/dist/server/poll.js +19 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +5 -0
- package/dist/server/release-schema.d.ts +46 -0
- package/dist/server/release-schema.js +307 -0
- package/dist/settings/store.d.ts +1 -0
- package/dist/settings/store.js +1 -1
- package/dist/shared/environment-lanes.d.ts +28 -0
- package/dist/shared/environment-lanes.js +61 -0
- package/dist/sharing/actions/share-resource.js +22 -2
- package/dist/styles/agent-native.css +17 -15
- package/dist/triggers/routes.d.ts +11 -0
- package/dist/triggers/routes.js +10 -0
- package/dist/usage/alerts-store.d.ts +1 -0
- package/dist/usage/alerts-store.js +1 -1
- package/dist/usage/store.d.ts +1 -0
- package/dist/usage/store.js +1 -1
- package/docs/content/drop-in-agent.mdx +4 -4
- package/docs/content/faq.mdx +15 -15
- package/docs/content/getting-started.mdx +173 -41
- package/docs/content/key-concepts.mdx +10 -10
- package/docs/content/using-your-agent.mdx +2 -2
- package/docs/content/what-is-agent-native.mdx +79 -228
- package/package.json +2 -2
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { callAction, useSession } from "@agent-native/core/client/hooks";
|
|
2
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
3
|
+
import { buildSignInReturnHref } from "@agent-native/core/client/ui";
|
|
4
|
+
import { IconAlertTriangle, IconCheck, IconLock } from "@tabler/icons-react";
|
|
5
|
+
import { useEffect, useMemo, useState } from "react";
|
|
6
|
+
import { Link, useSearchParams } from "react-router";
|
|
7
|
+
|
|
8
|
+
import { Button } from "@/components/ui/button";
|
|
9
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
10
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
11
|
+
import enMessages from "@/i18n/en-US";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
deckAccessApprovalContinuationPath,
|
|
15
|
+
deckAccessApprovalSessionKey,
|
|
16
|
+
} from "../../shared/deck-access";
|
|
17
|
+
|
|
18
|
+
interface ApprovalResult {
|
|
19
|
+
ok: true;
|
|
20
|
+
alreadyAllowed: boolean;
|
|
21
|
+
requesterEmail: string;
|
|
22
|
+
deckId: string;
|
|
23
|
+
deckTitle: string;
|
|
24
|
+
shareId: string;
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type ApprovalState =
|
|
29
|
+
| { kind: "loading" }
|
|
30
|
+
| { kind: "sign-in" }
|
|
31
|
+
| { kind: "success"; result: ApprovalResult }
|
|
32
|
+
| { kind: "error"; message: string };
|
|
33
|
+
|
|
34
|
+
export function meta() {
|
|
35
|
+
return [{ title: enMessages.deckEditor.privateDeckTitle }];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default function ApproveDeckAccessRequestRoute() {
|
|
39
|
+
const t = useT();
|
|
40
|
+
const [searchParams] = useSearchParams();
|
|
41
|
+
const { session, isLoading: sessionLoading } = useSession();
|
|
42
|
+
const deckId = searchParams.get("deckId") ?? "";
|
|
43
|
+
const approvalTokenFromUrl = searchParams.get("token") ?? "";
|
|
44
|
+
const approvalTokenStorageKey = deckAccessApprovalSessionKey(deckId);
|
|
45
|
+
const [approvalToken, setApprovalToken] = useState(() => {
|
|
46
|
+
if (approvalTokenFromUrl) return approvalTokenFromUrl;
|
|
47
|
+
if (typeof window === "undefined" || !deckId) return "";
|
|
48
|
+
try {
|
|
49
|
+
return sessionStorage.getItem(approvalTokenStorageKey) ?? "";
|
|
50
|
+
} catch {
|
|
51
|
+
// coercion-ok: unavailable tab storage is an absent continuation.
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
const [state, setState] = useState<ApprovalState>({ kind: "loading" });
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!deckId) {
|
|
59
|
+
setApprovalToken("");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!approvalTokenFromUrl) {
|
|
63
|
+
try {
|
|
64
|
+
setApprovalToken(sessionStorage.getItem(approvalTokenStorageKey) ?? "");
|
|
65
|
+
} catch {
|
|
66
|
+
setApprovalToken("");
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
sessionStorage.setItem(approvalTokenStorageKey, approvalTokenFromUrl);
|
|
72
|
+
} catch {
|
|
73
|
+
// coercion-ok: never fall back to putting the bearer token in the URL.
|
|
74
|
+
// Session storage may be unavailable in hardened browser contexts.
|
|
75
|
+
}
|
|
76
|
+
setApprovalToken(approvalTokenFromUrl);
|
|
77
|
+
|
|
78
|
+
// The email link must contain the capability, but it should not remain in
|
|
79
|
+
// the address bar or be copied into the sign-in continuation URL.
|
|
80
|
+
const params = new URLSearchParams(window.location.search);
|
|
81
|
+
params.delete("token");
|
|
82
|
+
const nextSearch = params.toString();
|
|
83
|
+
window.history.replaceState(
|
|
84
|
+
window.history.state,
|
|
85
|
+
"",
|
|
86
|
+
`${window.location.pathname}${nextSearch ? `?${nextSearch}` : ""}${window.location.hash}`,
|
|
87
|
+
);
|
|
88
|
+
}, [approvalTokenFromUrl, approvalTokenStorageKey, deckId]);
|
|
89
|
+
|
|
90
|
+
const signInReturnTo = useMemo(
|
|
91
|
+
() => deckAccessApprovalContinuationPath(deckId),
|
|
92
|
+
[deckId],
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (sessionLoading) return;
|
|
97
|
+
|
|
98
|
+
let cancelled = false;
|
|
99
|
+
if (!session?.email) {
|
|
100
|
+
setState({ kind: "sign-in" });
|
|
101
|
+
return () => {
|
|
102
|
+
cancelled = true;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (!deckId || !approvalToken) {
|
|
106
|
+
setState({
|
|
107
|
+
kind: "error",
|
|
108
|
+
message: t("deckEditor.accessApprovalInvalid"),
|
|
109
|
+
});
|
|
110
|
+
return () => {
|
|
111
|
+
cancelled = true;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setState({ kind: "loading" });
|
|
116
|
+
void callAction<ApprovalResult>("approve-deck-access-request", {
|
|
117
|
+
deckId,
|
|
118
|
+
approvalToken,
|
|
119
|
+
})
|
|
120
|
+
.then((result) => {
|
|
121
|
+
try {
|
|
122
|
+
sessionStorage.removeItem(approvalTokenStorageKey);
|
|
123
|
+
} catch {
|
|
124
|
+
// coercion-ok: cleanup failure cannot authorize or expose the token.
|
|
125
|
+
// Session storage may be unavailable in hardened browser contexts.
|
|
126
|
+
}
|
|
127
|
+
if (!cancelled) setState({ kind: "success", result });
|
|
128
|
+
})
|
|
129
|
+
.catch((error: unknown) => {
|
|
130
|
+
if (cancelled) return;
|
|
131
|
+
setState({
|
|
132
|
+
kind: "error",
|
|
133
|
+
message:
|
|
134
|
+
error instanceof Error
|
|
135
|
+
? error.message
|
|
136
|
+
: t("deckEditor.accessApprovalInvalid"),
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return () => {
|
|
141
|
+
cancelled = true;
|
|
142
|
+
};
|
|
143
|
+
}, [
|
|
144
|
+
approvalToken,
|
|
145
|
+
approvalTokenStorageKey,
|
|
146
|
+
deckId,
|
|
147
|
+
session?.email,
|
|
148
|
+
sessionLoading,
|
|
149
|
+
t,
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
const signInHref = buildSignInReturnHref({ returnTo: signInReturnTo });
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<div className="flex min-h-screen items-center justify-center bg-background px-6 py-12 text-foreground">
|
|
156
|
+
<Card className="w-full max-w-md">
|
|
157
|
+
<CardHeader>
|
|
158
|
+
<CardTitle className="flex items-center gap-2">
|
|
159
|
+
<IconLock
|
|
160
|
+
className="size-5 text-muted-foreground"
|
|
161
|
+
aria-hidden="true"
|
|
162
|
+
/>
|
|
163
|
+
{state.kind === "success"
|
|
164
|
+
? state.result.alreadyAllowed
|
|
165
|
+
? t("deckEditor.accessApprovalAlreadyTitle")
|
|
166
|
+
: t("deckEditor.accessApprovalTitle")
|
|
167
|
+
: state.kind === "error"
|
|
168
|
+
? t("deckEditor.accessApprovalErrorTitle")
|
|
169
|
+
: state.kind === "sign-in"
|
|
170
|
+
? t("deckEditor.accessApprovalSignInTitle")
|
|
171
|
+
: t("deckEditor.accessApprovalLoading")}
|
|
172
|
+
</CardTitle>
|
|
173
|
+
</CardHeader>
|
|
174
|
+
<CardContent aria-live="polite">
|
|
175
|
+
{state.kind === "loading" ? (
|
|
176
|
+
<div className="space-y-3 py-2">
|
|
177
|
+
<Skeleton className="h-4 w-full" />
|
|
178
|
+
<Skeleton className="h-4 w-4/5" />
|
|
179
|
+
</div>
|
|
180
|
+
) : state.kind === "sign-in" ? (
|
|
181
|
+
<div className="space-y-4">
|
|
182
|
+
<p className="text-sm text-muted-foreground">
|
|
183
|
+
{t("deckEditor.accessApprovalSignInMessage")}
|
|
184
|
+
</p>
|
|
185
|
+
<Button asChild>
|
|
186
|
+
<a
|
|
187
|
+
href={signInHref}
|
|
188
|
+
onClick={() => {
|
|
189
|
+
if (!approvalToken) return;
|
|
190
|
+
try {
|
|
191
|
+
sessionStorage.setItem(
|
|
192
|
+
approvalTokenStorageKey,
|
|
193
|
+
approvalToken,
|
|
194
|
+
);
|
|
195
|
+
} catch {
|
|
196
|
+
// coercion-ok: never fall back to putting the bearer token in the URL.
|
|
197
|
+
// Session storage may be unavailable in hardened browser contexts.
|
|
198
|
+
}
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
{t("deckEditor.accessApprovalSignIn")}
|
|
202
|
+
</a>
|
|
203
|
+
</Button>
|
|
204
|
+
</div>
|
|
205
|
+
) : state.kind === "error" ? (
|
|
206
|
+
<div className="space-y-4">
|
|
207
|
+
<div className="flex items-start gap-3 rounded-md border border-destructive/30 bg-destructive/10 p-3 text-sm text-destructive">
|
|
208
|
+
<IconAlertTriangle
|
|
209
|
+
className="mt-0.5 size-4 shrink-0"
|
|
210
|
+
aria-hidden="true"
|
|
211
|
+
/>
|
|
212
|
+
<span>{state.message}</span>
|
|
213
|
+
</div>
|
|
214
|
+
{deckId ? (
|
|
215
|
+
<Button asChild variant="outline">
|
|
216
|
+
<Link to={`/deck/${encodeURIComponent(deckId)}`}>
|
|
217
|
+
{t("deckEditor.accessApprovalOpenDeck")}
|
|
218
|
+
</Link>
|
|
219
|
+
</Button>
|
|
220
|
+
) : null}
|
|
221
|
+
</div>
|
|
222
|
+
) : (
|
|
223
|
+
<div className="space-y-4">
|
|
224
|
+
<div className="flex items-start gap-3 rounded-md border border-primary/30 bg-primary/10 p-3 text-sm">
|
|
225
|
+
<IconCheck
|
|
226
|
+
className="mt-0.5 size-4 shrink-0 text-primary"
|
|
227
|
+
aria-hidden="true"
|
|
228
|
+
/>
|
|
229
|
+
<span>
|
|
230
|
+
{state.result.alreadyAllowed
|
|
231
|
+
? t("deckEditor.accessApprovalAlreadyMessage", {
|
|
232
|
+
email: state.result.requesterEmail,
|
|
233
|
+
})
|
|
234
|
+
: t("deckEditor.accessApprovalMessage", {
|
|
235
|
+
email: state.result.requesterEmail,
|
|
236
|
+
})}
|
|
237
|
+
</span>
|
|
238
|
+
</div>
|
|
239
|
+
<Button asChild variant="outline">
|
|
240
|
+
<Link to={`/deck/${encodeURIComponent(state.result.deckId)}`}>
|
|
241
|
+
{t("deckEditor.accessApprovalOpenDeck")}
|
|
242
|
+
</Link>
|
|
243
|
+
</Button>
|
|
244
|
+
</div>
|
|
245
|
+
)}
|
|
246
|
+
</CardContent>
|
|
247
|
+
</Card>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
@@ -93,3 +93,11 @@ export const deckEvents = table("deck_events", {
|
|
|
93
93
|
createdBy: text("created_by").notNull().default("human"),
|
|
94
94
|
createdAt: text("created_at").notNull().default(now()),
|
|
95
95
|
});
|
|
96
|
+
|
|
97
|
+
// One durable bucket per deck keeps anonymous access-request throttling
|
|
98
|
+
// consistent across serverless instances and cold starts.
|
|
99
|
+
export const deckAccessRequestLimits = table("deck_access_request_limits", {
|
|
100
|
+
deckId: text("deck_id").primaryKey(),
|
|
101
|
+
windowStartedAt: text("window_started_at").notNull(),
|
|
102
|
+
requestCount: integer("request_count").notNull().default(0),
|
|
103
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { emailStrong, renderEmail } from "@agent-native/core/server";
|
|
2
|
+
|
|
3
|
+
export const SLIDES_DECK_ACCESS_REQUEST_EMAIL_ID = "slides.deck-access-request";
|
|
4
|
+
|
|
5
|
+
function cleanSubjectPart(value: string): string {
|
|
6
|
+
return value.replace(/[\r\n]+/g, " ").trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function renderDeckAccessRequestEmail(input: {
|
|
10
|
+
requesterName: string;
|
|
11
|
+
requesterEmail: string;
|
|
12
|
+
deckTitle: string;
|
|
13
|
+
url: string;
|
|
14
|
+
allowAccessUrl: string;
|
|
15
|
+
}) {
|
|
16
|
+
const subject = `Access request for "${cleanSubjectPart(input.deckTitle)}"`;
|
|
17
|
+
return {
|
|
18
|
+
subject,
|
|
19
|
+
...renderEmail({
|
|
20
|
+
brandName: "Slides",
|
|
21
|
+
preheader: subject,
|
|
22
|
+
heading: "Access requested",
|
|
23
|
+
paragraphs: [
|
|
24
|
+
`${emailStrong(input.requesterName)} (${emailStrong(input.requesterEmail)}) requested access to ${emailStrong(input.deckTitle)}.`,
|
|
25
|
+
"Select Allow access to add them to this deck's sharing list. You can also open the deck to review sharing first.",
|
|
26
|
+
],
|
|
27
|
+
cta: { label: "Allow access", url: input.allowAccessUrl },
|
|
28
|
+
secondaryCta: { label: "Open deck", url: input.url },
|
|
29
|
+
closingParagraphs: [
|
|
30
|
+
"This approval link expires in 7 days and requires you to be signed in as a deck owner or admin.",
|
|
31
|
+
],
|
|
32
|
+
footer:
|
|
33
|
+
"You received this because you own this deck. If you do not recognize the requester, you can ignore this email.",
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
getTransactionalEmail,
|
|
11
11
|
} from "@agent-native/core/email-catalog";
|
|
12
12
|
|
|
13
|
+
import {
|
|
14
|
+
renderDeckAccessRequestEmail,
|
|
15
|
+
SLIDES_DECK_ACCESS_REQUEST_EMAIL_ID,
|
|
16
|
+
} from "./access-request-email.js";
|
|
13
17
|
import { renderDeckCommentEmail } from "./comment-notifications.js";
|
|
14
18
|
|
|
15
19
|
export const SLIDES_DECK_COMMENT_EMAIL_ID = "slides.deck-comment";
|
|
@@ -42,4 +46,26 @@ export function registerSlidesEmails(): void {
|
|
|
42
46
|
isReply: false,
|
|
43
47
|
}),
|
|
44
48
|
});
|
|
49
|
+
|
|
50
|
+
defineTransactionalEmail({
|
|
51
|
+
id: SLIDES_DECK_ACCESS_REQUEST_EMAIL_ID,
|
|
52
|
+
name: "Deck access request",
|
|
53
|
+
trigger:
|
|
54
|
+
"A signed-in viewer requests access to a private Slides deck. One request is recorded per viewer and deck.",
|
|
55
|
+
recipientLabel: "Deck owner",
|
|
56
|
+
recipient:
|
|
57
|
+
"The owner of the private deck. The in-app notification is stored even when email delivery is unavailable.",
|
|
58
|
+
senderLabel: "Agent-Native Slides",
|
|
59
|
+
sender:
|
|
60
|
+
"The configured default sender. This call site sets no custom from or app sender.",
|
|
61
|
+
preview: () =>
|
|
62
|
+
renderDeckAccessRequestEmail({
|
|
63
|
+
requesterName: "Sam Rivera",
|
|
64
|
+
requesterEmail: "sam.rivera@example.com",
|
|
65
|
+
deckTitle: "Quarterly review",
|
|
66
|
+
url: "https://example.com/deck/deck_sample",
|
|
67
|
+
allowAccessUrl:
|
|
68
|
+
"https://example.com/access-request/approve?deckId=deck_sample&token=preview-token",
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
45
71
|
}
|
|
@@ -56,6 +56,13 @@ export default createAgentChatPlugin({
|
|
|
56
56
|
const authContext = await resolveSlidesRequestAuthContext(event);
|
|
57
57
|
return authContext.orgId === undefined ? null : authContext.orgId;
|
|
58
58
|
},
|
|
59
|
+
// Guest access requests authenticate with a signed deck capability and the
|
|
60
|
+
// requester email, so this action must reach its own validation without a
|
|
61
|
+
// browser session.
|
|
62
|
+
actionRoutePublicPaths: [
|
|
63
|
+
"/_agent-native/actions/get-deck-access-status",
|
|
64
|
+
"/_agent-native/actions/request-deck-access",
|
|
65
|
+
],
|
|
59
66
|
prepareRequest: prepareSlidesChatAttachments,
|
|
60
67
|
systemPrompt: `You are an AI deck assistant. You create, edit, import, export, style, share, and navigate decks through actions and shared application state. For a newly created presentation, use create-deck with slides: [] only when you are creating the deck yourself, then add-slide sequentially with full rendered HTML. The legacy generate-slides-ai action returns Markdown drafts and is not part of the persisted presentation workflow. When speaker notes are requested, keep presenter-only text in each slide's notes field rather than the slide HTML, and preserve notes during source-preserving edits.
|
|
61
68
|
|
|
@@ -252,6 +252,56 @@ export const runSlidesMigrations = runMigrations(
|
|
|
252
252
|
);
|
|
253
253
|
CREATE INDEX IF NOT EXISTS deck_events_deck_created_idx ON deck_events (deck_id, created_at)`,
|
|
254
254
|
},
|
|
255
|
+
{
|
|
256
|
+
version: 23,
|
|
257
|
+
name: "slides-deck-access-request-rate-limits",
|
|
258
|
+
sql: `CREATE TABLE IF NOT EXISTS deck_access_request_limits (
|
|
259
|
+
deck_id TEXT PRIMARY KEY,
|
|
260
|
+
window_started_at TEXT NOT NULL,
|
|
261
|
+
request_count INTEGER NOT NULL DEFAULT 0
|
|
262
|
+
)`,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
version: 24,
|
|
266
|
+
name: "slides-deck-shares-user-principal-unique",
|
|
267
|
+
// Reconcile legacy rows before adding the constraint. Older databases
|
|
268
|
+
// could contain case variants or concurrent duplicate user shares, and
|
|
269
|
+
// CREATE UNIQUE INDEX would otherwise fail before this migration is
|
|
270
|
+
// recorded. Keep the strongest grant; id/created_at make equal-role
|
|
271
|
+
// survivors deterministic, and lower the retained principal so the
|
|
272
|
+
// repaired row is canonical as well as uniquely indexed.
|
|
273
|
+
sql: `DELETE FROM deck_shares
|
|
274
|
+
WHERE principal_type = 'user'
|
|
275
|
+
AND id NOT IN (
|
|
276
|
+
SELECT id
|
|
277
|
+
FROM (
|
|
278
|
+
SELECT id,
|
|
279
|
+
ROW_NUMBER() OVER (
|
|
280
|
+
PARTITION BY resource_id, LOWER(principal_id)
|
|
281
|
+
ORDER BY CASE LOWER(role)
|
|
282
|
+
WHEN 'owner' THEN 5
|
|
283
|
+
WHEN 'admin' THEN 4
|
|
284
|
+
WHEN 'editor' THEN 3
|
|
285
|
+
WHEN 'commenter' THEN 2
|
|
286
|
+
WHEN 'viewer' THEN 1
|
|
287
|
+
ELSE 0
|
|
288
|
+
END DESC,
|
|
289
|
+
created_at ASC,
|
|
290
|
+
id ASC
|
|
291
|
+
) AS row_number
|
|
292
|
+
FROM deck_shares
|
|
293
|
+
WHERE principal_type = 'user'
|
|
294
|
+
) AS ranked
|
|
295
|
+
WHERE row_number = 1
|
|
296
|
+
);
|
|
297
|
+
UPDATE deck_shares
|
|
298
|
+
SET principal_id = LOWER(principal_id)
|
|
299
|
+
WHERE principal_type = 'user'
|
|
300
|
+
AND principal_id <> LOWER(principal_id);
|
|
301
|
+
CREATE UNIQUE INDEX IF NOT EXISTS deck_shares_resource_user_principal_uidx
|
|
302
|
+
ON deck_shares (resource_id, LOWER(principal_id))
|
|
303
|
+
WHERE principal_type = 'user'`,
|
|
304
|
+
},
|
|
255
305
|
],
|
|
256
306
|
{ table: "slides_migrations" },
|
|
257
307
|
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Short-lived capability used by a private deck viewer to request access. */
|
|
2
|
+
export const SLIDES_ACCESS_REQUEST_TOKEN_PREFIX = "slides-access-request";
|
|
3
|
+
/** Capability used when the private-deck access probe is unavailable. */
|
|
4
|
+
export const SLIDES_ACCESS_REQUEST_FALLBACK_TOKEN_PREFIX =
|
|
5
|
+
"slides-access-request-fallback";
|
|
6
|
+
export const SLIDES_ACCESS_REQUEST_TOKEN_TTL_SECONDS = 10 * 60;
|
|
7
|
+
|
|
8
|
+
/** Signed capability used by an owner to approve a private deck access request. */
|
|
9
|
+
export const SLIDES_ACCESS_APPROVAL_TOKEN_PREFIX = "slides-access-approval";
|
|
10
|
+
export const SLIDES_ACCESS_APPROVAL_TOKEN_TTL_SECONDS = 7 * 24 * 60 * 60;
|
|
11
|
+
export const SLIDES_ACCESS_APPROVAL_SESSION_KEY_PREFIX =
|
|
12
|
+
"slides-access-approval-token:";
|
|
13
|
+
|
|
14
|
+
/** Owner-only route used by the one-click approval button in access emails. */
|
|
15
|
+
export function deckAccessApprovalPath(
|
|
16
|
+
deckId: string,
|
|
17
|
+
approvalToken: string,
|
|
18
|
+
): string {
|
|
19
|
+
const params = new URLSearchParams({ deckId, token: approvalToken });
|
|
20
|
+
return `/access-request/approve?${params.toString()}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Token-free continuation used after the approver signs in. */
|
|
24
|
+
export function deckAccessApprovalContinuationPath(deckId: string): string {
|
|
25
|
+
return `/access-request/approve?${new URLSearchParams({ deckId }).toString()}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Tab-scoped key for carrying an approval token across the sign-in redirect. */
|
|
29
|
+
export function deckAccessApprovalSessionKey(deckId: string): string {
|
|
30
|
+
return `${SLIDES_ACCESS_APPROVAL_SESSION_KEY_PREFIX}${encodeURIComponent(deckId)}`;
|
|
31
|
+
}
|
|
@@ -9,6 +9,7 @@ export interface A2AArtifactResponseOptions {
|
|
|
9
9
|
includeReferencedArtifacts?: boolean;
|
|
10
10
|
includePersistedArtifactMarker?: boolean;
|
|
11
11
|
persistedArtifactSecret?: string;
|
|
12
|
+
delegatedTaskId?: string;
|
|
12
13
|
}
|
|
13
14
|
export interface GuardedA2AArtifactResponse {
|
|
14
15
|
text: string;
|
|
@@ -16,6 +17,7 @@ export interface GuardedA2AArtifactResponse {
|
|
|
16
17
|
}
|
|
17
18
|
export interface A2AArtifactIdentityOptions {
|
|
18
19
|
persistedArtifactSecrets?: readonly string[];
|
|
20
|
+
expectedDelegatedTaskId?: string;
|
|
19
21
|
}
|
|
20
22
|
export interface A2AArtifactIdentity {
|
|
21
23
|
resourceType: "document" | "deck" | "dashboard" | "analysis" | "image" | "design" | "monitor" | "form";
|
|
@@ -24,6 +26,48 @@ export interface A2AArtifactIdentity {
|
|
|
24
26
|
titleAtAction?: string;
|
|
25
27
|
url?: string;
|
|
26
28
|
}
|
|
29
|
+
export interface A2APersistedMutationReceipt {
|
|
30
|
+
receiptId: string;
|
|
31
|
+
sourceAction: string;
|
|
32
|
+
operation: string;
|
|
33
|
+
outcome: string;
|
|
34
|
+
target: {
|
|
35
|
+
authorityScopeKind: "personal" | "organization";
|
|
36
|
+
authorityScopeId: string;
|
|
37
|
+
spaceId: string;
|
|
38
|
+
databaseId: string;
|
|
39
|
+
databaseDocumentId: string;
|
|
40
|
+
itemId?: string;
|
|
41
|
+
rowDocumentId?: string;
|
|
42
|
+
propertyId?: string;
|
|
43
|
+
};
|
|
44
|
+
row: {
|
|
45
|
+
itemId?: string;
|
|
46
|
+
documentId: string;
|
|
47
|
+
urlPath: string;
|
|
48
|
+
};
|
|
49
|
+
idempotency: {
|
|
50
|
+
key: string;
|
|
51
|
+
result: "applied" | "replayed";
|
|
52
|
+
payloadDigest: string;
|
|
53
|
+
};
|
|
54
|
+
revisions: {
|
|
55
|
+
before?: string | null;
|
|
56
|
+
after?: string;
|
|
57
|
+
rowBefore?: string;
|
|
58
|
+
rowAfter?: string;
|
|
59
|
+
fieldBefore?: number;
|
|
60
|
+
fieldAfter?: number;
|
|
61
|
+
};
|
|
62
|
+
affected?: {
|
|
63
|
+
title?: boolean;
|
|
64
|
+
propertyIds?: string[];
|
|
65
|
+
blockIds?: string[];
|
|
66
|
+
deletedBlockIds?: string[];
|
|
67
|
+
order?: string[];
|
|
68
|
+
};
|
|
69
|
+
readbackVerified: true;
|
|
70
|
+
}
|
|
27
71
|
export declare function stripA2APersistedArtifactMarkers(text: string): string;
|
|
28
72
|
/**
|
|
29
73
|
* Extract a compact, verified identity ledger from successful artifact tools.
|
|
@@ -31,6 +75,12 @@ export declare function stripA2APersistedArtifactMarkers(text: string): string;
|
|
|
31
75
|
* long-lived thread context and stable even when a resource is later renamed.
|
|
32
76
|
*/
|
|
33
77
|
export declare function extractA2AArtifactIdentities(results: A2AToolResultSummary[], options?: A2AArtifactIdentityOptions): A2AArtifactIdentity[];
|
|
78
|
+
/**
|
|
79
|
+
* Extract bounded, read-back-verified mutation receipts. Nested agent results
|
|
80
|
+
* are accepted only through the signed persisted ledger.
|
|
81
|
+
*/
|
|
82
|
+
export declare function extractA2APersistedMutationReceipts(results: A2AToolResultSummary[], options?: A2AArtifactIdentityOptions): A2APersistedMutationReceipt[];
|
|
83
|
+
export declare function appendA2APersistedMutationReceipts(text: string, toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): string;
|
|
34
84
|
export declare function guardA2AArtifactResponse(responseText: string, toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): GuardedA2AArtifactResponse;
|
|
35
85
|
export declare function appendA2AArtifactLinks(responseText: string, toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): string;
|
|
36
86
|
export declare function buildA2ARecoverableArtifactMessage(toolResults: A2AToolResultSummary[], options?: A2AArtifactResponseOptions): string | null;
|