@agent-native/core 0.214.0 → 0.217.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action.js +54 -1
- package/dist/agent/actions/disconnect-chatgpt-subscription.js +5 -0
- package/dist/agent/actions/get-agentkit-capabilities.js +3 -3
- package/dist/agent/actions/manage-builder-connection.js +5 -0
- package/dist/agent/actions/manage-provider-key-policy.js +5 -0
- package/dist/agent/actions/manage-provider-models.js +5 -0
- package/dist/agent/actions/manage-service-providers.js +5 -0
- package/dist/agent/actions/select-chatgpt-subscription-account.js +5 -0
- package/dist/agent/context-xray/actions/context-evict.js +5 -0
- package/dist/agent/context-xray/actions/context-pin.js +5 -0
- package/dist/agent/context-xray/actions/context-report.js +5 -0
- package/dist/agent/context-xray/actions/context-restore.js +5 -0
- package/dist/agent/execution-fence.d.ts +1 -0
- package/dist/agent/execution-fence.js +5 -0
- package/dist/agent/production-agent.d.ts +35 -2
- package/dist/agent/production-agent.js +629 -32
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.js +397 -115
- package/dist/agent/run-slot-lock.d.ts +2 -0
- package/dist/agent/run-slot-lock.js +6 -0
- package/dist/agent/run-store.d.ts +5 -0
- package/dist/agent/run-store.js +423 -156
- package/dist/agent/thread-data-builder.js +27 -5
- package/dist/agent/types.d.ts +6 -12
- package/dist/agentkit/capabilities.d.ts +1 -1
- package/dist/app-config/analytics.js +1 -1
- package/dist/application-state/store.d.ts +5 -0
- package/dist/application-state/store.js +26 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/chat-threads/schema-migrations.js +17 -0
- package/dist/chat-threads/store.d.ts +21 -1
- package/dist/chat-threads/store.js +223 -41
- package/dist/cli/skills-content/turn-into-app-skill.d.ts +1 -1
- package/dist/cli/skills-content/turn-into-app-skill.js +20 -18
- package/dist/cli/templates-meta.js +14 -0
- package/dist/client/analytics-session.d.ts +7 -0
- package/dist/client/analytics-session.js +27 -1
- package/dist/client/analytics.js +276 -31
- package/dist/client/chat/agentkit-agent-native.js +79 -3
- package/dist/client/chat/agentkit-protocol.js +80 -42
- package/dist/client/chat/runtime.d.ts +5 -1
- package/dist/client/chat/runtime.js +46 -3
- package/dist/client/consent.d.ts +48 -0
- package/dist/client/consent.js +413 -0
- package/dist/client/onboarding/index.d.ts +1 -0
- package/dist/client/onboarding/index.js +1 -0
- package/dist/client/onboarding/use-onboarding-checklist.d.ts +23 -0
- package/dist/client/onboarding/use-onboarding-checklist.js +115 -0
- package/dist/client/review/use-review.d.ts +2 -0
- package/dist/client/review/use-review.js +28 -1
- package/dist/client/session-replay.d.ts +1 -1
- package/dist/client/session-replay.js +142 -3
- package/dist/client/sharing/useResourceAccessGate.d.ts +3 -2
- package/dist/client/sharing/useResourceAccessGate.js +9 -1
- package/dist/client/use-chat-thread-list.d.ts +2 -0
- package/dist/client/use-chat-thread-list.js +12 -2
- package/dist/client/use-chat-threads.d.ts +1 -0
- package/dist/client/use-chat-threads.js +2 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/connections/catalog.d.ts +5 -5
- package/dist/connections/catalog.js +22 -3
- package/dist/data-widgets/index.d.ts +36 -2
- package/dist/data-widgets/index.js +94 -6
- package/dist/db/migrations.js +4 -1
- package/dist/db/schema-snapshot.generated.js +8 -1
- package/dist/feature-flags/actions/set-feature-flag.js +5 -0
- package/dist/feature-flags/index.d.ts +1 -1
- package/dist/feature-flags/index.js +1 -1
- package/dist/feature-flags/store.d.ts +12 -0
- package/dist/feature-flags/store.js +79 -7
- package/dist/file-upload/actions/manage-file-storage.js +5 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.js +5 -0
- package/dist/history/actions/create-resource-version.js +5 -0
- package/dist/history/actions/restore-resource-version.js +5 -0
- package/dist/ingestion/index.d.ts +1 -1
- package/dist/ingestion/index.js +1 -1
- package/dist/ingestion/pptx.d.ts +11 -0
- package/dist/ingestion/pptx.js +81 -1
- package/dist/integrations/actions/manage-messaging-channel.js +5 -0
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/jobs/scheduler.js +17 -1
- package/dist/labs/actions/set-lab.js +5 -0
- package/dist/localization/actions/set-localization-preference.js +5 -0
- package/dist/localization/consent-banner-messages.d.ts +16 -0
- package/dist/localization/consent-banner-messages.js +149 -0
- package/dist/localization/core-messages.js +4 -0
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +2 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/actions/save-observability-instruction-update.js +5 -0
- package/dist/observability/actions/save-observability-review-summary.js +5 -0
- package/dist/observability/routes.d.ts +6 -6
- package/dist/onboarding/index.d.ts +2 -1
- package/dist/onboarding/index.js +1 -0
- package/dist/onboarding/plugin.js +55 -46
- package/dist/onboarding/status.d.ts +15 -0
- package/dist/onboarding/status.js +59 -0
- package/dist/onboarding/types.d.ts +12 -0
- package/dist/onboarding/types.js +2 -1
- package/dist/org/workspace-app-access.js +19 -9
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/provider-api/index.js +9 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/review/actions/consume-review-feedback.js +5 -0
- package/dist/review/actions/create-review-comment.js +5 -0
- package/dist/review/actions/delete-review-comment.js +5 -0
- package/dist/review/actions/react-to-review-comment.js +5 -0
- package/dist/review/actions/reply-review-comment.js +5 -0
- package/dist/review/actions/resolve-review-thread.js +5 -0
- package/dist/review/actions/send-review-thread-to-agent.js +5 -0
- package/dist/review/actions/set-review-status.js +5 -0
- package/dist/review/actions/set-review-thread-muted.js +5 -0
- package/dist/review/actions/set-review-thread-unread.js +5 -0
- package/dist/review/actions/set-review-threads-unread.js +5 -0
- package/dist/review/actions/update-review-comment-anchor.js +5 -0
- package/dist/review/actions/update-review-comment.js +5 -0
- package/dist/review/suggestions/actions.js +60 -21
- package/dist/review/suggestions/store.d.ts +2 -0
- package/dist/review/suggestions/store.js +28 -0
- package/dist/secrets/actions/delete-api-key.js +5 -0
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-capabilities.js +13 -6
- package/dist/server/agent-chat/context-tools.js +63 -2
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.js +242 -54
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/analytics.d.ts +0 -1
- package/dist/server/analytics.js +81 -20
- package/dist/server/application-state-maintenance.d.ts +2 -0
- package/dist/server/application-state-maintenance.js +2 -0
- package/dist/server/attribution.d.ts +14 -0
- package/dist/server/attribution.js +62 -2
- package/dist/server/auth.js +7 -0
- package/dist/server/builder-api-auth.d.ts +7 -0
- package/dist/server/builder-api-auth.js +24 -0
- package/dist/server/builder-oauth.d.ts +5 -1
- package/dist/server/builder-oauth.js +5 -0
- package/dist/server/consent-route.d.ts +41 -0
- package/dist/server/consent-route.js +102 -0
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/first-touch-route.d.ts +39 -0
- package/dist/server/first-touch-route.js +115 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +2 -1
- package/dist/server/queued-submission-inbox-drain.d.ts +9 -0
- package/dist/server/queued-submission-inbox-drain.js +12 -0
- package/dist/server/request-origin.d.ts +6 -0
- package/dist/server/request-origin.js +12 -0
- package/dist/settings/org-settings.d.ts +2 -1
- package/dist/settings/org-settings.js +4 -1
- package/dist/settings/store.d.ts +8 -0
- package/dist/settings/store.js +14 -5
- package/dist/shared/consent.d.ts +33 -0
- package/dist/shared/consent.js +149 -0
- package/dist/shared/first-touch-persistence.d.ts +31 -0
- package/dist/shared/first-touch-persistence.js +53 -0
- package/dist/sharing/actions/set-resource-visibility.js +5 -0
- package/dist/sharing/actions/share-resource.js +5 -0
- package/dist/sharing/actions/unshare-resource.js +5 -0
- package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
- package/dist/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
- package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
- package/dist/triggers/actions/manage-automation.d.ts +4 -4
- package/dist/usage/actions/get-usage-metrics.js +27 -4
- package/dist/usage/actions/manage-usage-alert.js +5 -0
- package/dist/vite/client.js +8 -6
- package/dist/workspace-connections/actions/bulk-update-workspace-user-groups.js +5 -0
- package/dist/workspace-connections/actions/delete-workspace-user-group.js +5 -0
- package/dist/workspace-connections/actions/upsert-workspace-user-group.js +5 -0
- package/docs/content/actions-access-control.mdx +8 -8
- package/docs/content/actions-defining.mdx +9 -8
- package/docs/content/deployment-environment-variables.mdx +1 -1
- package/docs/content/environment-variables.mdx +1 -1
- package/docs/content/template-design-developers.mdx +9 -9
- package/docs/content/template-design-features.mdx +1 -3
- package/docs/content/turn-into-app.mdx +4 -0
- package/package.json +6 -3
- package/src/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
- package/src/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
- package/src/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
- package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { CONSENT_COOKIE_LIFETIME_SECONDS, CONSENT_COOKIE_NAME, CONSENT_PATH, CONSENT_REGION_PATH, consentCookieDomain, isConsentRegionTimeZone, parseConsent, serializeConsent, } from "../shared/consent.js";
|
|
2
|
+
import { agentNativePath } from "./api-path.js";
|
|
3
|
+
import { agentNativeApiDisabledReason } from "./api-surface.js";
|
|
4
|
+
/** Strictly necessary for the banner: which region this session is in. */
|
|
5
|
+
const REGION_STORAGE_KEY = "an_consent_region";
|
|
6
|
+
/**
|
|
7
|
+
* Scripts that run before this module loads, like the Google tags the server
|
|
8
|
+
* injects into the page, queue `[category, run]` here.
|
|
9
|
+
*/
|
|
10
|
+
const SCRIPT_QUEUE_KEY = "__AN_CONSENT_QUEUE__";
|
|
11
|
+
/** The Google data layers the server's Consent Mode default script set up. */
|
|
12
|
+
const GOOGLE_CONSENT_LAYERS_KEY = "__AN_CONSENT_LAYERS__";
|
|
13
|
+
// One state per page, however many copies of this module a bundle carries.
|
|
14
|
+
const STATE_KEY = Symbol.for("agent-native.client.consent");
|
|
15
|
+
function state() {
|
|
16
|
+
const holder = globalThis;
|
|
17
|
+
return (holder[STATE_KEY] ??= {
|
|
18
|
+
region: null,
|
|
19
|
+
regionPromise: null,
|
|
20
|
+
waiters: [],
|
|
21
|
+
listeners: new Set(),
|
|
22
|
+
settingsOpen: false,
|
|
23
|
+
pageChoice: null,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function readCookie(name) {
|
|
27
|
+
if (typeof document === "undefined")
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
for (const part of document.cookie ? document.cookie.split(";") : []) {
|
|
31
|
+
const eq = part.indexOf("=");
|
|
32
|
+
if (eq !== -1 && part.slice(0, eq).trim() === name) {
|
|
33
|
+
return part.slice(eq + 1).trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// coercion-ok: a sandboxed iframe can't read cookies, so it holds no
|
|
39
|
+
// choice and the visitor is asked.
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/** The visitor's stored choice, or `null` before they've made one. */
|
|
44
|
+
export function getConsentChoice() {
|
|
45
|
+
return (parseConsent(readCookie(CONSENT_COOKIE_NAME)) ?? state().pageChoice ?? null);
|
|
46
|
+
}
|
|
47
|
+
function cookieDomain() {
|
|
48
|
+
const host = typeof window === "undefined" ? "" : window.location.hostname;
|
|
49
|
+
const domain = consentCookieDomain(host);
|
|
50
|
+
return domain ? `; domain=${domain}` : "";
|
|
51
|
+
}
|
|
52
|
+
function writeChoiceCookie(choice) {
|
|
53
|
+
try {
|
|
54
|
+
document.cookie =
|
|
55
|
+
`${CONSENT_COOKIE_NAME}=${serializeConsent(choice)}; path=/; ` +
|
|
56
|
+
`max-age=${CONSENT_COOKIE_LIFETIME_SECONDS}; SameSite=Lax${cookieDomain()}`;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// coercion-ok: a sandboxed iframe can't store the choice; it still
|
|
60
|
+
// applies to this page.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let serverChoiceWrites = Promise.resolve();
|
|
64
|
+
let latestChoice = null;
|
|
65
|
+
/**
|
|
66
|
+
* Safari keeps a cookie page JavaScript wrote for seven days at most, so the
|
|
67
|
+
* server writes the choice again for its full year. Writes go one at a time
|
|
68
|
+
* and only the newest is sent. A response for an older choice still sets that
|
|
69
|
+
* choice's cookie, so the newest is written back over it at once.
|
|
70
|
+
*/
|
|
71
|
+
function storeChoiceOnServer(choice) {
|
|
72
|
+
latestChoice = choice;
|
|
73
|
+
if (agentNativeApiDisabledReason() || typeof fetch !== "function")
|
|
74
|
+
return;
|
|
75
|
+
serverChoiceWrites = serverChoiceWrites.then(async () => {
|
|
76
|
+
if (latestChoice !== choice)
|
|
77
|
+
return;
|
|
78
|
+
await fetch(agentNativePath(CONSENT_PATH), {
|
|
79
|
+
method: "POST",
|
|
80
|
+
credentials: "same-origin",
|
|
81
|
+
keepalive: true,
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
83
|
+
body: JSON.stringify({ choice: serializeConsent(choice) }),
|
|
84
|
+
}).catch(() => {
|
|
85
|
+
// coercion-ok: the browser's own copy still holds the choice, for
|
|
86
|
+
// seven days in Safari; the visitor is asked again after that.
|
|
87
|
+
});
|
|
88
|
+
if (latestChoice && latestChoice !== choice) {
|
|
89
|
+
writeChoiceCookie(latestChoice);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function readCachedRegion() {
|
|
94
|
+
try {
|
|
95
|
+
const value = window.sessionStorage.getItem(REGION_STORAGE_KEY);
|
|
96
|
+
return value === "consent" || value === "none" ? value : null;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// coercion-ok: without session storage the region is asked again.
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function cacheRegion(region) {
|
|
104
|
+
try {
|
|
105
|
+
window.sessionStorage.setItem(REGION_STORAGE_KEY, region);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// coercion-ok: without session storage the region is asked again.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function timeZoneRegion() {
|
|
112
|
+
try {
|
|
113
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
114
|
+
return isConsentRegionTimeZone(timeZone) ? "consent" : "none";
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// An unreadable zone could be anywhere, so it gets the banner.
|
|
118
|
+
return "consent";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function fetchRegion() {
|
|
122
|
+
if (agentNativeApiDisabledReason() || typeof fetch !== "function") {
|
|
123
|
+
return timeZoneRegion();
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(agentNativePath(CONSENT_REGION_PATH), {
|
|
127
|
+
credentials: "same-origin",
|
|
128
|
+
});
|
|
129
|
+
const body = response.ok
|
|
130
|
+
? (await response.json())
|
|
131
|
+
: {};
|
|
132
|
+
if (body.region === "consent" || body.region === "none") {
|
|
133
|
+
return body.region;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// coercion-ok: with no answer from the server, the time zone decides.
|
|
138
|
+
}
|
|
139
|
+
return timeZoneRegion();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Whether this visitor gets the banner: the server's answer from the
|
|
143
|
+
* request's country, else the browser's time zone. Asked once a session.
|
|
144
|
+
*/
|
|
145
|
+
export function resolveConsentRegion() {
|
|
146
|
+
const current = state();
|
|
147
|
+
if (current.region)
|
|
148
|
+
return Promise.resolve(current.region);
|
|
149
|
+
const cached = typeof window === "undefined" ? null : readCachedRegion();
|
|
150
|
+
if (cached) {
|
|
151
|
+
current.region = cached;
|
|
152
|
+
// Another app or tab may have stored a reject since this page's waiters
|
|
153
|
+
// last looked; let them clear what they kept.
|
|
154
|
+
settle();
|
|
155
|
+
notify();
|
|
156
|
+
return Promise.resolve(cached);
|
|
157
|
+
}
|
|
158
|
+
return (current.regionPromise ??= fetchRegion().then((region) => {
|
|
159
|
+
current.region = region;
|
|
160
|
+
cacheRegion(region);
|
|
161
|
+
settle();
|
|
162
|
+
notify();
|
|
163
|
+
return region;
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
/** The best answer available now, without waiting for the server. */
|
|
167
|
+
function regionGuess() {
|
|
168
|
+
const current = state();
|
|
169
|
+
return current.region ?? readCachedRegion() ?? timeZoneRegion();
|
|
170
|
+
}
|
|
171
|
+
function allows(category, region) {
|
|
172
|
+
const choice = getConsentChoice();
|
|
173
|
+
if (choice)
|
|
174
|
+
return choice[category];
|
|
175
|
+
return region === "none";
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Whether `category` is allowed right now, on the time zone's guess until the
|
|
179
|
+
* server answers. Pair it with `whenConsentAllows` and a `revoke` that undoes
|
|
180
|
+
* whatever this let through.
|
|
181
|
+
*/
|
|
182
|
+
export function consentAllowsNow(category) {
|
|
183
|
+
if (typeof window === "undefined")
|
|
184
|
+
return false;
|
|
185
|
+
if (!state().region)
|
|
186
|
+
void resolveConsentRegion();
|
|
187
|
+
return allows(category, regionGuess());
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Whether `category` is allowed, from the visitor's choice or the server's
|
|
191
|
+
* region; `null` until one of them is known, where `consentAllowsNow` would
|
|
192
|
+
* guess from the time zone. For what `revoke` can't undo once it's sent.
|
|
193
|
+
*/
|
|
194
|
+
export function consentDecision(category) {
|
|
195
|
+
if (typeof window === "undefined")
|
|
196
|
+
return false;
|
|
197
|
+
const choice = getConsentChoice();
|
|
198
|
+
if (choice)
|
|
199
|
+
return choice[category];
|
|
200
|
+
const region = state().region ?? readCachedRegion();
|
|
201
|
+
if (region)
|
|
202
|
+
return region === "none";
|
|
203
|
+
void resolveConsentRegion();
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Google's tags can't be unloaded, but they follow Consent Mode: a reject
|
|
208
|
+
* stops what's already on the page from storing or sending identifiers.
|
|
209
|
+
*/
|
|
210
|
+
function updateGoogleConsent(region) {
|
|
211
|
+
const holder = window;
|
|
212
|
+
const layers = holder[GOOGLE_CONSENT_LAYERS_KEY];
|
|
213
|
+
if (!Array.isArray(layers))
|
|
214
|
+
return;
|
|
215
|
+
const grant = (category) => allows(category, region) ? "granted" : "denied";
|
|
216
|
+
const update = {
|
|
217
|
+
analytics_storage: grant("performance"),
|
|
218
|
+
ad_storage: grant("targeting"),
|
|
219
|
+
ad_user_data: grant("targeting"),
|
|
220
|
+
ad_personalization: grant("targeting"),
|
|
221
|
+
};
|
|
222
|
+
for (const name of layers) {
|
|
223
|
+
if (typeof name !== "string")
|
|
224
|
+
continue;
|
|
225
|
+
const layer = (holder[name] ??= []);
|
|
226
|
+
// gtag reads an `arguments` object from the data layer, never an array.
|
|
227
|
+
(function gtag(..._args) {
|
|
228
|
+
layer.push(arguments);
|
|
229
|
+
})("consent", "update", update);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function settle() {
|
|
233
|
+
const region = state().region;
|
|
234
|
+
if (region || getConsentChoice())
|
|
235
|
+
updateGoogleConsent(region);
|
|
236
|
+
for (const waiter of state().waiters) {
|
|
237
|
+
const allowed = allows(waiter.category, region);
|
|
238
|
+
if (allowed && !waiter.ran) {
|
|
239
|
+
waiter.ran = true;
|
|
240
|
+
isolate(waiter.run);
|
|
241
|
+
}
|
|
242
|
+
else if (!allowed && (region || getConsentChoice())) {
|
|
243
|
+
// Also clears what an earlier page stored before the banner existed.
|
|
244
|
+
waiter.ran = false;
|
|
245
|
+
if (waiter.revoke)
|
|
246
|
+
isolate(waiter.revoke);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// A choice stored here or in another tab decides before the region does.
|
|
250
|
+
if (region || getConsentChoice())
|
|
251
|
+
releaseHeldForDecision?.(true);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* One callback throwing must not skip the waiters after it, or a later revoke
|
|
255
|
+
* would never clear what a rejected category stored. Rethrow it on its own task
|
|
256
|
+
* so it still surfaces.
|
|
257
|
+
*/
|
|
258
|
+
function isolate(callback) {
|
|
259
|
+
try {
|
|
260
|
+
callback();
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
setTimeout(() => {
|
|
264
|
+
throw error;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function notify() {
|
|
269
|
+
for (const listener of state().listeners)
|
|
270
|
+
listener();
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Run `run` once `category` is allowed: at once outside the consent region,
|
|
274
|
+
* and inside it when the visitor accepts. A later "reject" calls `revoke`.
|
|
275
|
+
*/
|
|
276
|
+
export function whenConsentAllows(category, run, options = {}) {
|
|
277
|
+
if (typeof window === "undefined")
|
|
278
|
+
return;
|
|
279
|
+
const waiter = { category, run, revoke: options.revoke, ran: false };
|
|
280
|
+
state().waiters.push(waiter);
|
|
281
|
+
const region = state().region ??
|
|
282
|
+
readCachedRegion() ??
|
|
283
|
+
(options.optimistic ? regionGuess() : null);
|
|
284
|
+
if (allows(category, region)) {
|
|
285
|
+
waiter.ran = true;
|
|
286
|
+
isolate(run);
|
|
287
|
+
}
|
|
288
|
+
else if (state().region || getConsentChoice()) {
|
|
289
|
+
// A reject decided before this caller asked still clears what it stored.
|
|
290
|
+
if (options.revoke)
|
|
291
|
+
isolate(options.revoke);
|
|
292
|
+
}
|
|
293
|
+
if (!state().region)
|
|
294
|
+
void resolveConsentRegion();
|
|
295
|
+
}
|
|
296
|
+
let heldForDecision = null;
|
|
297
|
+
let releaseHeldForDecision = null;
|
|
298
|
+
/**
|
|
299
|
+
* Run `send` once the visitor's choice or the server's region is known, so
|
|
300
|
+
* the time zone's guess never sends what consent covers. `decided` is false
|
|
301
|
+
* when the page closes first; send then without what consent covers.
|
|
302
|
+
*/
|
|
303
|
+
export function whenConsentDecided(send) {
|
|
304
|
+
if (consentDecision("performance") !== null) {
|
|
305
|
+
send(true);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (!heldForDecision) {
|
|
309
|
+
const held = [];
|
|
310
|
+
heldForDecision = held;
|
|
311
|
+
const release = (decided) => {
|
|
312
|
+
if (heldForDecision !== held)
|
|
313
|
+
return;
|
|
314
|
+
heldForDecision = null;
|
|
315
|
+
releaseHeldForDecision = null;
|
|
316
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
317
|
+
for (const run of held)
|
|
318
|
+
run(decided);
|
|
319
|
+
};
|
|
320
|
+
const onPageHide = () => release(false);
|
|
321
|
+
releaseHeldForDecision = release;
|
|
322
|
+
window.addEventListener("pagehide", onPageHide);
|
|
323
|
+
void resolveConsentRegion().then(() => release(true));
|
|
324
|
+
}
|
|
325
|
+
heldForDecision.push(send);
|
|
326
|
+
}
|
|
327
|
+
const CONSENT_CHANNEL_NAME = "agent-native-consent";
|
|
328
|
+
let seenChoice = null;
|
|
329
|
+
let consentChannel = null;
|
|
330
|
+
/**
|
|
331
|
+
* Apply a choice another tab or app stored: at once in other tabs of this
|
|
332
|
+
* app, and on the next focus for another app on the site.
|
|
333
|
+
*/
|
|
334
|
+
function recheckStoredChoice() {
|
|
335
|
+
const raw = readCookie(CONSENT_COOKIE_NAME);
|
|
336
|
+
if (raw === seenChoice)
|
|
337
|
+
return;
|
|
338
|
+
seenChoice = raw;
|
|
339
|
+
state().settingsOpen = false;
|
|
340
|
+
settle();
|
|
341
|
+
notify();
|
|
342
|
+
}
|
|
343
|
+
function watchOtherTabs() {
|
|
344
|
+
if (typeof window === "undefined" || typeof document === "undefined")
|
|
345
|
+
return;
|
|
346
|
+
seenChoice = readCookie(CONSENT_COOKIE_NAME);
|
|
347
|
+
window.addEventListener?.("focus", recheckStoredChoice);
|
|
348
|
+
document.addEventListener?.("visibilitychange", recheckStoredChoice);
|
|
349
|
+
if (typeof BroadcastChannel !== "function")
|
|
350
|
+
return;
|
|
351
|
+
try {
|
|
352
|
+
consentChannel = new BroadcastChannel(CONSENT_CHANNEL_NAME);
|
|
353
|
+
consentChannel.onmessage = recheckStoredChoice;
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
// coercion-ok: without the channel, other tabs recheck on focus.
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** Store the visitor's choice for every app on this site, and apply it. */
|
|
360
|
+
export function setConsentChoice(choice) {
|
|
361
|
+
if (typeof document === "undefined")
|
|
362
|
+
return;
|
|
363
|
+
writeChoiceCookie(choice);
|
|
364
|
+
storeChoiceOnServer(choice);
|
|
365
|
+
seenChoice = readCookie(CONSENT_COOKIE_NAME);
|
|
366
|
+
// Only a browser that won't keep the cookie falls back to this page's
|
|
367
|
+
// copy; a cookie the visitor clears later means they're asked again.
|
|
368
|
+
state().pageChoice = seenChoice === null ? choice : null;
|
|
369
|
+
try {
|
|
370
|
+
consentChannel?.postMessage("changed");
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// coercion-ok: other tabs still recheck on focus.
|
|
374
|
+
}
|
|
375
|
+
state().settingsOpen = false;
|
|
376
|
+
if (!state().region)
|
|
377
|
+
state().region = "consent";
|
|
378
|
+
settle();
|
|
379
|
+
notify();
|
|
380
|
+
}
|
|
381
|
+
/** Reopen the banner so the visitor can change their choice. */
|
|
382
|
+
export function openConsentSettings() {
|
|
383
|
+
state().settingsOpen = true;
|
|
384
|
+
notify();
|
|
385
|
+
}
|
|
386
|
+
/** Whether the banner should show now, for the banner component. */
|
|
387
|
+
export function shouldShowConsentBanner() {
|
|
388
|
+
const current = state();
|
|
389
|
+
if (current.settingsOpen)
|
|
390
|
+
return true;
|
|
391
|
+
return current.region === "consent" && !getConsentChoice();
|
|
392
|
+
}
|
|
393
|
+
export function subscribeConsent(listener) {
|
|
394
|
+
state().listeners.add(listener);
|
|
395
|
+
return () => state().listeners.delete(listener);
|
|
396
|
+
}
|
|
397
|
+
/** Run the scripts the page queued before this module loaded. */
|
|
398
|
+
function drainScriptQueue() {
|
|
399
|
+
if (typeof window === "undefined")
|
|
400
|
+
return;
|
|
401
|
+
const holder = window;
|
|
402
|
+
const queued = holder[SCRIPT_QUEUE_KEY];
|
|
403
|
+
const push = (...entries) => {
|
|
404
|
+
for (const [category, run] of entries)
|
|
405
|
+
whenConsentAllows(category, run);
|
|
406
|
+
return 0;
|
|
407
|
+
};
|
|
408
|
+
holder[SCRIPT_QUEUE_KEY] = { push };
|
|
409
|
+
if (Array.isArray(queued))
|
|
410
|
+
push(...queued);
|
|
411
|
+
}
|
|
412
|
+
drainScriptQueue();
|
|
413
|
+
watchOtherTabs();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { useOnboarding, type UseOnboardingResult } from "./use-onboarding.js";
|
|
2
|
+
export { useOnboardingChecklist, type UseOnboardingChecklistResult, } from "./use-onboarding-checklist.js";
|
|
2
3
|
export { isFirstRunOnboardingEnabled } from "./first-run-enabled.js";
|
|
3
4
|
export { dispatchFirstRunOnboardingStatus, fetchFirstRunOnboardingStatus, FIRST_RUN_ONBOARDING_STATUS_RESOLVED_EVENT, } from "./first-run-status.js";
|
|
4
5
|
export { useOnboardingPreviewMode, useOnboardingPreviewStep, ONBOARDING_PREVIEW_STORAGE_KEY, ONBOARDING_PREVIEW_QUERY_PARAM, ONBOARDING_PREVIEW_QUERY_VALUE, ONBOARDING_PREVIEW_STEP_QUERY_PARAM, ONBOARDING_PREVIEW_STEPS, getOnboardingPreviewStep, isOnboardingPreviewQuery, } from "./use-preview-mode.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { useOnboarding } from "./use-onboarding.js";
|
|
2
|
+
export { useOnboardingChecklist, } from "./use-onboarding-checklist.js";
|
|
2
3
|
export { isFirstRunOnboardingEnabled } from "./first-run-enabled.js";
|
|
3
4
|
export { dispatchFirstRunOnboardingStatus, fetchFirstRunOnboardingStatus, FIRST_RUN_ONBOARDING_STATUS_RESOLVED_EVENT, } from "./first-run-status.js";
|
|
4
5
|
export { useOnboardingPreviewMode, useOnboardingPreviewStep, ONBOARDING_PREVIEW_STORAGE_KEY, ONBOARDING_PREVIEW_QUERY_PARAM, ONBOARDING_PREVIEW_QUERY_VALUE, ONBOARDING_PREVIEW_STEP_QUERY_PARAM, ONBOARDING_PREVIEW_STEPS, getOnboardingPreviewStep, isOnboardingPreviewQuery, } from "./use-preview-mode.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { OnboardingStepStatus } from "../../onboarding/types.js";
|
|
2
|
+
export interface UseOnboardingChecklistResult {
|
|
3
|
+
steps: OnboardingStepStatus[];
|
|
4
|
+
completeCount: number;
|
|
5
|
+
totalCount: number;
|
|
6
|
+
dismissed: boolean;
|
|
7
|
+
collapsed: boolean;
|
|
8
|
+
/** `loading` only before the first answer; a refresh keeps the last one. */
|
|
9
|
+
status: "loading" | "ready" | "error";
|
|
10
|
+
error: Error | null;
|
|
11
|
+
refresh: () => Promise<unknown>;
|
|
12
|
+
dismiss: () => Promise<void>;
|
|
13
|
+
reopen: () => Promise<void>;
|
|
14
|
+
setCollapsed: (collapsed: boolean) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Reads one named onboarding checklist (steps registered with
|
|
18
|
+
* `registerOnboardingStep({ checklist })`). Rows are re-read whenever a local
|
|
19
|
+
* action or an agent tool finishes, so a step checks itself off after the work
|
|
20
|
+
* that completes it. Dismissal is stored by the onboarding plugin and the
|
|
21
|
+
* collapsed state in application state, so both follow the user across devices.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useOnboardingChecklist(checklist: string): UseOnboardingChecklistResult;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useMutation, useQuery, useQueryClient, } from "@tanstack/react-query";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { agentNativePath } from "../api-path.js";
|
|
4
|
+
import { readClientAppState, writeClientAppState, } from "../application-state.js";
|
|
5
|
+
import { useChangeVersions } from "../use-change-version.js";
|
|
6
|
+
const CHECKLIST_TIMEOUT_MS = 10_000;
|
|
7
|
+
function summaryKey(checklist) {
|
|
8
|
+
return ["onboarding-checklist", checklist];
|
|
9
|
+
}
|
|
10
|
+
function collapsedKey(checklist) {
|
|
11
|
+
return ["onboarding-checklist-collapsed", checklist];
|
|
12
|
+
}
|
|
13
|
+
function collapsedStateKey(checklist) {
|
|
14
|
+
return `onboarding:collapsed:${checklist}`;
|
|
15
|
+
}
|
|
16
|
+
function checklistUrl(path, checklist) {
|
|
17
|
+
return agentNativePath(`/_agent-native/onboarding/${path}?checklist=${encodeURIComponent(checklist)}`);
|
|
18
|
+
}
|
|
19
|
+
async function fetchChecklist(checklist, signal) {
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const abort = () => controller.abort();
|
|
22
|
+
signal.addEventListener("abort", abort);
|
|
23
|
+
const timer = setTimeout(abort, CHECKLIST_TIMEOUT_MS);
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(checklistUrl("summary", checklist), {
|
|
26
|
+
signal: controller.signal,
|
|
27
|
+
});
|
|
28
|
+
if (!response.ok)
|
|
29
|
+
throw new Error(`checklist: ${response.status}`);
|
|
30
|
+
const body = (await response.json());
|
|
31
|
+
return { steps: body.steps, dismissed: !!body.dismissed };
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
signal.removeEventListener("abort", abort);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function postChecklist(path, checklist) {
|
|
39
|
+
const response = await fetch(checklistUrl(path, checklist), {
|
|
40
|
+
method: "POST",
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok)
|
|
43
|
+
throw new Error(`checklist ${path}: ${response.status}`);
|
|
44
|
+
}
|
|
45
|
+
function patchSummary(queryClient, checklist, patch) {
|
|
46
|
+
const snapshot = queryClient.getQueriesData({
|
|
47
|
+
queryKey: summaryKey(checklist),
|
|
48
|
+
});
|
|
49
|
+
queryClient.setQueriesData({ queryKey: summaryKey(checklist) }, (current) => (current ? { ...current, ...patch } : current));
|
|
50
|
+
return snapshot;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Reads one named onboarding checklist (steps registered with
|
|
54
|
+
* `registerOnboardingStep({ checklist })`). Rows are re-read whenever a local
|
|
55
|
+
* action or an agent tool finishes, so a step checks itself off after the work
|
|
56
|
+
* that completes it. Dismissal is stored by the onboarding plugin and the
|
|
57
|
+
* collapsed state in application state, so both follow the user across devices.
|
|
58
|
+
*/
|
|
59
|
+
export function useOnboardingChecklist(checklist) {
|
|
60
|
+
const queryClient = useQueryClient();
|
|
61
|
+
const changeVersion = useChangeVersions(["action"]);
|
|
62
|
+
const summary = useQuery({
|
|
63
|
+
queryKey: [...summaryKey(checklist), changeVersion],
|
|
64
|
+
queryFn: ({ signal }) => fetchChecklist(checklist, signal),
|
|
65
|
+
placeholderData: (previous) => previous,
|
|
66
|
+
});
|
|
67
|
+
const collapsedQuery = useQuery({
|
|
68
|
+
queryKey: collapsedKey(checklist),
|
|
69
|
+
queryFn: async () => {
|
|
70
|
+
const value = await readClientAppState(collapsedStateKey(checklist));
|
|
71
|
+
return value?.collapsed === true;
|
|
72
|
+
},
|
|
73
|
+
staleTime: Infinity,
|
|
74
|
+
});
|
|
75
|
+
const dismissMutation = useMutation({
|
|
76
|
+
mutationFn: (dismissed) => postChecklist(dismissed ? "dismiss" : "reopen", checklist),
|
|
77
|
+
onMutate: (dismissed) => ({
|
|
78
|
+
previous: patchSummary(queryClient, checklist, { dismissed }),
|
|
79
|
+
}),
|
|
80
|
+
onError: (_error, _dismissed, context) => {
|
|
81
|
+
for (const [key, data] of context?.previous ?? []) {
|
|
82
|
+
queryClient.setQueryData(key, data);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
onSettled: () => queryClient.invalidateQueries({ queryKey: summaryKey(checklist) }),
|
|
86
|
+
});
|
|
87
|
+
const collapseMutation = useMutation({
|
|
88
|
+
mutationFn: (collapsed) => writeClientAppState(collapsedStateKey(checklist), { collapsed }),
|
|
89
|
+
onMutate: (collapsed) => {
|
|
90
|
+
const previous = queryClient.getQueryData(collapsedKey(checklist));
|
|
91
|
+
queryClient.setQueryData(collapsedKey(checklist), collapsed);
|
|
92
|
+
return { previous };
|
|
93
|
+
},
|
|
94
|
+
onError: (_error, _collapsed, context) => {
|
|
95
|
+
queryClient.setQueryData(collapsedKey(checklist), context?.previous);
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
const dismiss = useCallback(() => dismissMutation.mutateAsync(true), [dismissMutation]);
|
|
99
|
+
const reopen = useCallback(() => dismissMutation.mutateAsync(false), [dismissMutation]);
|
|
100
|
+
const setCollapsed = useCallback((collapsed) => collapseMutation.mutateAsync(collapsed).then(() => undefined), [collapseMutation]);
|
|
101
|
+
const steps = summary.data?.steps ?? [];
|
|
102
|
+
return {
|
|
103
|
+
steps,
|
|
104
|
+
completeCount: steps.filter((step) => step.complete).length,
|
|
105
|
+
totalCount: steps.length,
|
|
106
|
+
dismissed: summary.data?.dismissed ?? false,
|
|
107
|
+
collapsed: collapsedQuery.data ?? false,
|
|
108
|
+
status: summary.data ? "ready" : summary.isError ? "error" : "loading",
|
|
109
|
+
error: summary.error,
|
|
110
|
+
refresh: () => queryClient.invalidateQueries({ queryKey: summaryKey(checklist) }),
|
|
111
|
+
dismiss,
|
|
112
|
+
reopen,
|
|
113
|
+
setCollapsed,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -269,6 +269,8 @@ export declare function useResourceSuggestions(params: ListResourceSuggestionsPa
|
|
|
269
269
|
export declare function useCreateResourceSuggestion(): import("@tanstack/react-query").UseMutationResult<ResourceSuggestion, Error, CreateResourceSuggestionInput, unknown>;
|
|
270
270
|
export declare function useCreateResourceSuggestionProposal(): import("@tanstack/react-query").UseMutationResult<ResourceSuggestionProposalResult, Error, CreateResourceSuggestionProposalInput, unknown>;
|
|
271
271
|
export declare function useDecideResourceSuggestionProposal(): import("@tanstack/react-query").UseMutationResult<ResourceSuggestionProposalResult, Error, DecideResourceSuggestionProposalInput, unknown>;
|
|
272
|
+
/** Shows every listed proposal member as decided until the server answers. */
|
|
273
|
+
export declare function proposalDecisionOverlay(input: DecideResourceSuggestionProposalInput, queryClient: QueryClient): ReviewOptimisticMutation;
|
|
272
274
|
export declare function useDecideResourceSuggestion(): import("@tanstack/react-query").UseMutationResult<{
|
|
273
275
|
suggestion: ResourceSuggestion;
|
|
274
276
|
decision: unknown;
|
|
@@ -547,7 +547,34 @@ export function useCreateResourceSuggestionProposal() {
|
|
|
547
547
|
return useActionMutation("create-resource-suggestion-proposal");
|
|
548
548
|
}
|
|
549
549
|
export function useDecideResourceSuggestionProposal() {
|
|
550
|
-
return
|
|
550
|
+
return useOptimisticReviewMutation("decide-resource-suggestion-proposal", proposalDecisionOverlay);
|
|
551
|
+
}
|
|
552
|
+
/** Shows every listed proposal member as decided until the server answers. */
|
|
553
|
+
export function proposalDecisionOverlay(input, queryClient) {
|
|
554
|
+
const [first] = input.members;
|
|
555
|
+
const resource = first && resourceForSuggestion(queryClient, first.id);
|
|
556
|
+
if (!resource)
|
|
557
|
+
return noSuggestionOverlay();
|
|
558
|
+
const memberIds = new Set(input.members.map((member) => member.id));
|
|
559
|
+
return {
|
|
560
|
+
action: "list-resource-suggestions",
|
|
561
|
+
resource,
|
|
562
|
+
transform: (data, params) => updateSuggestions(data, (suggestions) => suggestions.flatMap((suggestion) => {
|
|
563
|
+
if (!memberIds.has(suggestion.id))
|
|
564
|
+
return [suggestion];
|
|
565
|
+
const next = { ...suggestion, status: input.decision };
|
|
566
|
+
return matchesSuggestionQuery(params, next) ? [next] : [];
|
|
567
|
+
})),
|
|
568
|
+
onSuccess: (result) => (data, params) => {
|
|
569
|
+
const decided = new Map(result.suggestions.map((suggestion) => [suggestion.id, suggestion]));
|
|
570
|
+
return updateSuggestions(data, (suggestions) => suggestions.flatMap((suggestion) => {
|
|
571
|
+
const next = decided.get(suggestion.id);
|
|
572
|
+
if (!next)
|
|
573
|
+
return [suggestion];
|
|
574
|
+
return matchesSuggestionQuery(params, next) ? [next] : [];
|
|
575
|
+
}));
|
|
576
|
+
},
|
|
577
|
+
};
|
|
551
578
|
}
|
|
552
579
|
export function useDecideResourceSuggestion() {
|
|
553
580
|
return useOptimisticReviewMutation("decide-resource-suggestion", (input, queryClient) => {
|
|
@@ -64,7 +64,7 @@ export interface SessionReplayOptions {
|
|
|
64
64
|
}
|
|
65
65
|
export interface SessionReplayStartResult {
|
|
66
66
|
started: boolean;
|
|
67
|
-
reason?: "disabled" | "not-browser" | "missing-public-key" | "missing-session-id" | "missing-user-id" | "sampled-out" | "url-blocked" | "already-active" | "import-failed" | "record-failed";
|
|
67
|
+
reason?: "disabled" | "not-browser" | "missing-public-key" | "missing-session-id" | "missing-user-id" | "sampled-out" | "url-blocked" | "already-active" | "import-failed" | "record-failed" | "consent-required";
|
|
68
68
|
replayId?: string;
|
|
69
69
|
sessionId?: string;
|
|
70
70
|
sampled?: boolean;
|