@agent-native/core 0.205.0 → 0.206.0-nightly-20261009011246

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/a2a/client.d.ts +2 -2
  2. package/dist/agent/engine/anthropic-engine.d.ts +1 -1
  3. package/dist/agent/engine/builder-engine.d.ts +1 -1
  4. package/dist/agent/engine/builder-engine.js +23 -8
  5. package/dist/agent/engine/error-telemetry.d.ts +5 -0
  6. package/dist/agent/engine/error-telemetry.js +41 -0
  7. package/dist/agent/engine/registry.d.ts +1 -6
  8. package/dist/agent/engine/registry.js +2 -31
  9. package/dist/agent/model-config.d.ts +26 -14
  10. package/dist/agent/model-config.js +131 -8
  11. package/dist/agent/model-version.d.ts +19 -0
  12. package/dist/agent/model-version.js +153 -0
  13. package/dist/agent/production-agent.d.ts +1 -0
  14. package/dist/agent/production-agent.js +24 -6
  15. package/dist/agent/provider-model-selection.js +6 -4
  16. package/dist/agent/run-manager.js +5 -11
  17. package/dist/agent/thread-data-builder.js +14 -6
  18. package/dist/authorization/check-action.js +8 -4
  19. package/dist/chat-threads/store.js +22 -9
  20. package/dist/client/analytics.d.ts +1 -0
  21. package/dist/client/analytics.js +10 -2
  22. package/dist/client/chat/runtime.js +18 -0
  23. package/dist/client/chat-model-groups.d.ts +3 -0
  24. package/dist/client/error-format.js +10 -7
  25. package/dist/client/onboarding/use-onboarding.d.ts +12 -0
  26. package/dist/client/onboarding/use-onboarding.js +491 -4
  27. package/dist/client/session-replay-privacy.d.ts +8 -0
  28. package/dist/client/session-replay-privacy.js +8 -0
  29. package/dist/client/session-replay.d.ts +1 -0
  30. package/dist/client/session-replay.js +18 -9
  31. package/dist/client/sse-event-processor.js +10 -6
  32. package/dist/client/use-chat-models.d.ts +5 -0
  33. package/dist/client/use-chat-models.js +51 -5
  34. package/dist/client/use-session.d.ts +8 -1
  35. package/dist/client/use-session.js +20 -1
  36. package/dist/collab/struct-routes.d.ts +1 -1
  37. package/dist/deploy/build.js +353 -3
  38. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  39. package/dist/jobs/backfill-run-history-ownership.d.ts +12 -0
  40. package/dist/jobs/backfill-run-history-ownership.js +87 -0
  41. package/dist/jobs/background-automation-runner.js +3 -9
  42. package/dist/jobs/remote-execution.js +3 -9
  43. package/dist/jobs/run-history-ownership.d.ts +5 -0
  44. package/dist/jobs/run-history-ownership.js +16 -0
  45. package/dist/jobs/run-now.js +2 -3
  46. package/dist/localization/agent-resources-messages.d.ts +3 -0
  47. package/dist/localization/agent-resources-messages.js +33 -0
  48. package/dist/mcp/actions/create-org-service-token.js +5 -7
  49. package/dist/mcp/build-server.d.ts +23 -18
  50. package/dist/mcp/build-server.js +220 -212
  51. package/dist/mcp/connect-route.d.ts +11 -2
  52. package/dist/mcp/connect-route.js +34 -24
  53. package/dist/mcp/connect-store.d.ts +9 -13
  54. package/dist/mcp/connect-store.js +11 -24
  55. package/dist/mcp/credential-membership.js +17 -6
  56. package/dist/mcp/oauth-route.d.ts +11 -1
  57. package/dist/mcp/oauth-route.js +28 -6
  58. package/dist/mcp/oauth-token.d.ts +12 -2
  59. package/dist/mcp/oauth-token.js +15 -4
  60. package/dist/mcp/server.js +9 -5
  61. package/dist/notifications/routes.d.ts +2 -2
  62. package/dist/observability/posthog-ai.d.ts +1 -0
  63. package/dist/observability/posthog-ai.js +20 -8
  64. package/dist/observability/routes.d.ts +6 -6
  65. package/dist/observability/traces.js +61 -39
  66. package/dist/org/app-roles.d.ts +8 -1
  67. package/dist/org/app-roles.js +29 -9
  68. package/dist/org/context.js +24 -8
  69. package/dist/org/index.d.ts +2 -2
  70. package/dist/org/workspace-app-identity.d.ts +1 -0
  71. package/dist/org/workspace-app-identity.js +25 -0
  72. package/dist/progress/routes.d.ts +1 -1
  73. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  74. package/dist/provider-api/actions/provider-api.d.ts +3 -3
  75. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  76. package/dist/scripts/agent-engines/list-agent-engines.js +1 -0
  77. package/dist/server/action-routes.js +8 -1
  78. package/dist/server/agent-chat-plugin.js +1 -0
  79. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  80. package/dist/server/auth.js +33 -17
  81. package/dist/server/bearer-credential-refusal.d.ts +23 -0
  82. package/dist/server/bearer-credential-refusal.js +67 -0
  83. package/dist/server/better-auth-instance.js +13 -6
  84. package/dist/server/builder-browser.d.ts +2 -0
  85. package/dist/server/builder-browser.js +16 -3
  86. package/dist/server/capture-error.d.ts +1 -0
  87. package/dist/server/capture-error.js +28 -4
  88. package/dist/server/complete-text.js +16 -3
  89. package/dist/server/core-routes-plugin.js +2 -0
  90. package/dist/server/dev-action-bridge.js +7 -1
  91. package/dist/server/google-oauth.js +6 -3
  92. package/dist/server/index.d.ts +1 -1
  93. package/dist/server/index.js +1 -1
  94. package/dist/server/realtime-token.d.ts +1 -1
  95. package/dist/server/recap-image-route.js +4 -0
  96. package/dist/server/request-context.d.ts +16 -0
  97. package/dist/server/request-context.js +17 -0
  98. package/dist/shared/agent-chat-context.d.ts +1 -0
  99. package/dist/shared/agent-chat-context.js +26 -0
  100. package/dist/shared/index.d.ts +1 -1
  101. package/dist/shared/index.js +1 -1
  102. package/dist/shared/reasoning-effort.js +10 -1
  103. package/dist/shared/ssr-session-bootstrap.d.ts +7 -0
  104. package/dist/shared/ssr-session-bootstrap.js +21 -0
  105. package/dist/sharing/access.js +86 -5
  106. package/dist/templates/builder-code-starter/server/routes/index.get.ts +2 -0
  107. package/dist/triggers/routes.js +2 -1
  108. package/docs/content/automation-connectors.mdx +5 -3
  109. package/docs/content/automations.mdx +78 -49
  110. package/docs/content/observability.mdx +16 -5
  111. package/docs/content/organizations-teams-permissions.mdx +44 -2
  112. package/docs/content/recurring-jobs.mdx +4 -8
  113. package/docs/content/template-forms.mdx +6 -0
  114. package/package.json +5 -4
  115. package/src/templates/builder-code-starter/server/routes/index.get.ts +2 -0
@@ -1,5 +1,17 @@
1
1
  import type { OnboardingAppProfile, OnboardingMethod, OnboardingStepStatus } from "../../onboarding/types.js";
2
+ type CustomKeyOnboardingSetupKind = "credential" | "local_endpoint";
3
+ type CustomKeyOnboardingOutcome = "credential_entry_started" | "credential_validated" | "credential_saved" | "credential_skipped" | "credential_abandoned" | "local_endpoint_saved" | "local_endpoint_skipped" | "local_endpoint_abandoned";
4
+ type CustomKeyOutcomeResult = "tracked" | "tracked_uncorrelated" | "tracked_storage_unavailable" | "pending" | "missing" | "unavailable" | "stale" | "identity_mismatch" | "duplicate";
5
+ export declare function createOnboardingCorrelationId(): string;
6
+ export declare function setCustomKeyOnboardingAttempt(id: string): Promise<"stored" | "no_session" | "unavailable">;
7
+ export declare function setCustomKeyOnboardingSetupKind(setupKind: CustomKeyOnboardingSetupKind): "stored" | "memory" | "missing" | "unavailable";
8
+ export declare function trackCustomKeyOnboardingOutcome(outcome: CustomKeyOnboardingOutcome): CustomKeyOutcomeResult;
9
+ export declare function withCustomKeyOnboardingCredentialSave<T>(save: () => Promise<T>): Promise<T>;
10
+ export declare function withCustomKeyOnboardingLocalEndpointSave<T>(save: () => Promise<T>): Promise<T>;
11
+ export declare function requestCustomKeyOnboardingAbandonment(): void;
12
+ export declare function useCustomKeyOnboardingAttemptLifecycle(): void;
2
13
  export declare function __resetOnboardingSummaryReadsForTests(): void;
14
+ export declare function __resetOnboardingEventDedupeForTests(): void;
3
15
  export declare function trackOnboardingEvent(name: string, properties: Record<string, unknown>): void;
4
16
  export interface UseOnboardingResult {
5
17
  steps: OnboardingStepStatus[];
@@ -1,11 +1,464 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- import { getAnalyticsIdentityKey, trackEvent } from "../analytics.js";
2
+ import { getAnalyticsIdentityKey, getAnalyticsSessionId, resolveAnalyticsIdentityKey, trackEvent, } from "../analytics.js";
3
3
  import { agentNativePath } from "../api-path.js";
4
4
  import { scheduleAfterPaint, scheduleAfterStartup, } from "../use-after-paint.js";
5
5
  import { dispatchFirstRunOnboardingStatus, fetchFirstRunOnboardingStatus, readFirstRunOnboardingCookieState, } from "./first-run-status.js";
6
+ const lastOnboardingStepViews = new Map();
6
7
  const seenOnboardingEvents = new Set();
8
+ const CUSTOM_KEY_ATTEMPT_STORAGE_KEY = "agent-native.onboarding.custom_keys_attempt";
9
+ const locallyTrackedCustomKeyOutcomes = new Set();
10
+ const pendingCustomKeyCredentialSaves = new Map();
11
+ let onboardingDocumentId = null;
12
+ let onboardingCorrelationSequence = 0;
7
13
  const ONBOARDING_SUMMARY_TIMEOUT_MS = 15_000;
8
14
  const ONBOARDING_SUMMARY_REUSE_MS = 5_000;
15
+ function isTerminalCustomKeyOnboardingOutcome(outcome) {
16
+ return (outcome === "credential_saved" ||
17
+ outcome === "credential_skipped" ||
18
+ outcome === "credential_abandoned" ||
19
+ outcome === "local_endpoint_saved" ||
20
+ outcome === "local_endpoint_skipped" ||
21
+ outcome === "local_endpoint_abandoned");
22
+ }
23
+ export function createOnboardingCorrelationId() {
24
+ const cryptoApi = globalThis.crypto;
25
+ if (typeof cryptoApi?.randomUUID === "function") {
26
+ return cryptoApi.randomUUID();
27
+ }
28
+ onboardingCorrelationSequence += 1;
29
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}-${onboardingCorrelationSequence.toString(36)}`;
30
+ }
31
+ function getOnboardingDocumentId() {
32
+ return (onboardingDocumentId ??= createOnboardingCorrelationId());
33
+ }
34
+ // Keep the handoff alive while the SPA opens settings before auth refresh settles.
35
+ let pendingCustomKeyOnboardingAttempt = null;
36
+ function flushPendingCustomKeyOnboardingOutcomes(pending) {
37
+ const outcomes = pending.outcomes.splice(0);
38
+ for (const outcome of outcomes) {
39
+ trackCustomKeyOnboardingOutcome(outcome);
40
+ }
41
+ }
42
+ function readCustomKeyOnboardingAttempt() {
43
+ if (typeof window === "undefined")
44
+ return { kind: "unavailable" };
45
+ const pending = pendingCustomKeyOnboardingAttempt;
46
+ if (pending?.attempt.documentId === getOnboardingDocumentId()) {
47
+ if (pending.status === "resolving" || pending.status === "unavailable") {
48
+ return { kind: "pending", pending };
49
+ }
50
+ }
51
+ try {
52
+ const value = window.sessionStorage.getItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY);
53
+ if (!value) {
54
+ if (pending?.attempt.documentId === getOnboardingDocumentId() &&
55
+ pending.attempt.identityKey) {
56
+ return {
57
+ kind: "available",
58
+ attempt: pending.attempt,
59
+ };
60
+ }
61
+ return { kind: "available", attempt: null };
62
+ }
63
+ const parsed = JSON.parse(value);
64
+ if (!parsed || typeof parsed !== "object") {
65
+ return { kind: "unavailable" };
66
+ }
67
+ const attempt = parsed;
68
+ if (typeof attempt.id !== "string")
69
+ return { kind: "unavailable" };
70
+ if (attempt.documentId !== getOnboardingDocumentId()) {
71
+ return { kind: "stale" };
72
+ }
73
+ if (typeof attempt.identityKey !== "string") {
74
+ return { kind: "unavailable" };
75
+ }
76
+ if (pending?.attempt.id === attempt.id &&
77
+ pending.attempt.documentId === attempt.documentId &&
78
+ pending.attempt.identityKey) {
79
+ return {
80
+ kind: "available",
81
+ attempt: pending.attempt,
82
+ };
83
+ }
84
+ return {
85
+ kind: "available",
86
+ attempt: attempt,
87
+ };
88
+ }
89
+ catch {
90
+ if (pending?.attempt.documentId === getOnboardingDocumentId() &&
91
+ pending.attempt.identityKey) {
92
+ return {
93
+ kind: "available",
94
+ attempt: pending.attempt,
95
+ };
96
+ }
97
+ return { kind: "unavailable" };
98
+ }
99
+ }
100
+ function writePendingCustomKeyOnboardingAttempt(pending, identityKey) {
101
+ pending.attempt.identityKey = identityKey;
102
+ try {
103
+ window.sessionStorage.setItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY, JSON.stringify(pending.attempt));
104
+ pending.status = "stored";
105
+ return "stored";
106
+ }
107
+ catch {
108
+ pending.status = "memory";
109
+ return "unavailable";
110
+ }
111
+ }
112
+ export function setCustomKeyOnboardingAttempt(id) {
113
+ if (typeof window === "undefined")
114
+ return Promise.resolve("unavailable");
115
+ const sessionId = getAnalyticsSessionId();
116
+ const pending = {
117
+ attempt: {
118
+ id,
119
+ documentId: getOnboardingDocumentId(),
120
+ setupKind: "credential",
121
+ },
122
+ identityKeyAtStart: getAnalyticsIdentityKey(),
123
+ status: "resolving",
124
+ outcomes: [],
125
+ };
126
+ pendingCustomKeyOnboardingAttempt = pending;
127
+ return resolveAnalyticsIdentityKey()
128
+ .then((identityKey) => {
129
+ if (!sessionId || !identityKey) {
130
+ pending.status = "unavailable";
131
+ flushPendingCustomKeyOnboardingOutcomes(pending);
132
+ return "no_session";
133
+ }
134
+ const result = writePendingCustomKeyOnboardingAttempt(pending, identityKey);
135
+ flushPendingCustomKeyOnboardingOutcomes(pending);
136
+ return result;
137
+ })
138
+ .catch(() => {
139
+ pending.status = "unavailable";
140
+ flushPendingCustomKeyOnboardingOutcomes(pending);
141
+ return "unavailable";
142
+ });
143
+ }
144
+ export function setCustomKeyOnboardingSetupKind(setupKind) {
145
+ const pending = pendingCustomKeyOnboardingAttempt;
146
+ if (pending && pending.attempt.documentId === getOnboardingDocumentId()) {
147
+ pending.attempt.setupKind = setupKind;
148
+ if (pending.status === "resolving")
149
+ return "memory";
150
+ if (pending.status === "unavailable")
151
+ return "unavailable";
152
+ }
153
+ const stored = readCustomKeyOnboardingAttempt();
154
+ if (stored.kind === "pending") {
155
+ stored.pending.attempt.setupKind = setupKind;
156
+ return stored.pending.status === "resolving" ? "memory" : "unavailable";
157
+ }
158
+ if (stored.kind !== "available" || !stored.attempt) {
159
+ return stored.kind === "available" ? "missing" : "unavailable";
160
+ }
161
+ const attempt = { ...stored.attempt, setupKind };
162
+ if (pending?.attempt.id === attempt.id)
163
+ pending.attempt.setupKind = setupKind;
164
+ try {
165
+ window.sessionStorage.setItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY, JSON.stringify(attempt));
166
+ return "stored";
167
+ }
168
+ catch {
169
+ return "unavailable";
170
+ }
171
+ }
172
+ function trackUnavailableCustomKeyOnboardingOutcome(pending, outcome) {
173
+ const identityKey = pending.attempt.identityKey ?? pending.identityKeyAtStart;
174
+ if (identityKey !== getAnalyticsIdentityKey()) {
175
+ if (pendingCustomKeyOnboardingAttempt === pending) {
176
+ pendingCustomKeyOnboardingAttempt = null;
177
+ }
178
+ try {
179
+ window.sessionStorage.removeItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY);
180
+ }
181
+ catch {
182
+ return "unavailable";
183
+ }
184
+ return "identity_mismatch";
185
+ }
186
+ const attemptOutcomeKey = `${pending.attempt.id}:${outcome}`;
187
+ if (locallyTrackedCustomKeyOutcomes.has(attemptOutcomeKey)) {
188
+ return "duplicate";
189
+ }
190
+ trackOnboardingEvent("onboarding_method_outcome", {
191
+ flow: "first_run",
192
+ step_id: "choice",
193
+ method_id: "custom_keys",
194
+ onboarding_attempt_id: pending.attempt.id,
195
+ outcome,
196
+ correlation_status: "unavailable",
197
+ });
198
+ locallyTrackedCustomKeyOutcomes.add(attemptOutcomeKey);
199
+ if (isTerminalCustomKeyOnboardingOutcome(outcome)) {
200
+ if (pendingCustomKeyOnboardingAttempt === pending) {
201
+ pendingCustomKeyOnboardingAttempt = null;
202
+ }
203
+ }
204
+ return "tracked_uncorrelated";
205
+ }
206
+ export function trackCustomKeyOnboardingOutcome(outcome) {
207
+ const stored = readCustomKeyOnboardingAttempt();
208
+ if (stored.kind === "unavailable")
209
+ return "unavailable";
210
+ if (stored.kind === "pending") {
211
+ if (stored.pending.status === "resolving") {
212
+ if (stored.pending.outcomes.includes(outcome))
213
+ return "duplicate";
214
+ stored.pending.outcomes.push(outcome);
215
+ return "pending";
216
+ }
217
+ return trackUnavailableCustomKeyOnboardingOutcome(stored.pending, outcome);
218
+ }
219
+ if (stored.kind === "stale") {
220
+ // A duplicated tab can copy sessionStorage while the original attempt is live.
221
+ try {
222
+ window.sessionStorage.removeItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY);
223
+ }
224
+ catch {
225
+ return "unavailable";
226
+ }
227
+ return "stale";
228
+ }
229
+ const { attempt } = stored;
230
+ if (!attempt)
231
+ return "missing";
232
+ if (attempt.identityKey !== getAnalyticsIdentityKey()) {
233
+ try {
234
+ window.sessionStorage.removeItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY);
235
+ }
236
+ catch {
237
+ if (pendingCustomKeyOnboardingAttempt?.attempt.id === attempt.id) {
238
+ pendingCustomKeyOnboardingAttempt = null;
239
+ }
240
+ return "unavailable";
241
+ }
242
+ if (pendingCustomKeyOnboardingAttempt?.attempt.id === attempt.id) {
243
+ pendingCustomKeyOnboardingAttempt = null;
244
+ }
245
+ return "identity_mismatch";
246
+ }
247
+ const attemptOutcomeKey = `${attempt.id}:${outcome}`;
248
+ const alreadyTracked = (outcome === "credential_entry_started" && attempt.entryStarted) ||
249
+ (outcome === "credential_validated" && attempt.credentialValidated);
250
+ if (alreadyTracked ||
251
+ locallyTrackedCustomKeyOutcomes.has(attemptOutcomeKey)) {
252
+ if (isTerminalCustomKeyOnboardingOutcome(outcome)) {
253
+ try {
254
+ window.sessionStorage.removeItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY);
255
+ }
256
+ catch {
257
+ return "unavailable";
258
+ }
259
+ }
260
+ return "duplicate";
261
+ }
262
+ trackOnboardingEvent("onboarding_method_outcome", {
263
+ flow: "first_run",
264
+ step_id: "choice",
265
+ method_id: "custom_keys",
266
+ onboarding_attempt_id: attempt.id,
267
+ outcome,
268
+ });
269
+ locallyTrackedCustomKeyOutcomes.add(attemptOutcomeKey);
270
+ if (outcome === "credential_entry_started" ||
271
+ outcome === "credential_validated") {
272
+ try {
273
+ window.sessionStorage.setItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY, JSON.stringify({
274
+ ...attempt,
275
+ [outcome === "credential_entry_started"
276
+ ? "entryStarted"
277
+ : "credentialValidated"]: true,
278
+ }));
279
+ if (pendingCustomKeyOnboardingAttempt?.attempt.id === attempt.id) {
280
+ pendingCustomKeyOnboardingAttempt.attempt = {
281
+ ...pendingCustomKeyOnboardingAttempt.attempt,
282
+ ...attempt,
283
+ [outcome === "credential_entry_started"
284
+ ? "entryStarted"
285
+ : "credentialValidated"]: true,
286
+ };
287
+ }
288
+ }
289
+ catch {
290
+ if (pendingCustomKeyOnboardingAttempt?.attempt.id !== attempt.id) {
291
+ return "tracked_storage_unavailable";
292
+ }
293
+ pendingCustomKeyOnboardingAttempt.status = "memory";
294
+ }
295
+ }
296
+ else if (isTerminalCustomKeyOnboardingOutcome(outcome)) {
297
+ try {
298
+ window.sessionStorage.removeItem(CUSTOM_KEY_ATTEMPT_STORAGE_KEY);
299
+ }
300
+ catch {
301
+ if (pendingCustomKeyOnboardingAttempt?.attempt.id !== attempt.id) {
302
+ return "tracked_storage_unavailable";
303
+ }
304
+ }
305
+ if (pendingCustomKeyOnboardingAttempt?.attempt.id === attempt.id) {
306
+ pendingCustomKeyOnboardingAttempt = null;
307
+ }
308
+ }
309
+ return "tracked";
310
+ }
311
+ function trackCustomKeyOnboardingOutcomeForAttempt(attemptId, outcome) {
312
+ const stored = readCustomKeyOnboardingAttempt();
313
+ if ((stored.kind !== "available" && stored.kind !== "pending") ||
314
+ (stored.kind === "available" && stored.attempt?.id !== attemptId) ||
315
+ (stored.kind === "pending" && stored.pending.attempt.id !== attemptId) ||
316
+ (stored.kind === "available" &&
317
+ stored.attempt?.identityKey !== getAnalyticsIdentityKey()) ||
318
+ (stored.kind === "pending" &&
319
+ stored.pending.attempt.identityKey !== undefined &&
320
+ stored.pending.attempt.identityKey !== getAnalyticsIdentityKey())) {
321
+ return "missing";
322
+ }
323
+ return trackCustomKeyOnboardingOutcome(outcome);
324
+ }
325
+ function beginCustomKeyOnboardingSave(saveOutcome, abandonmentOutcome) {
326
+ const stored = readCustomKeyOnboardingAttempt();
327
+ if (stored.kind === "available" && stored.attempt) {
328
+ if (stored.attempt.identityKey !== getAnalyticsIdentityKey())
329
+ return null;
330
+ return beginCustomKeyOnboardingSaveForAttempt(stored.attempt.id, saveOutcome, abandonmentOutcome);
331
+ }
332
+ if (stored.kind === "pending" &&
333
+ stored.pending.attempt.documentId === getOnboardingDocumentId() &&
334
+ (stored.pending.attempt.identityKey === undefined ||
335
+ stored.pending.attempt.identityKey === getAnalyticsIdentityKey())) {
336
+ return beginCustomKeyOnboardingSaveForAttempt(stored.pending.attempt.id, saveOutcome, abandonmentOutcome);
337
+ }
338
+ return null;
339
+ }
340
+ function beginCustomKeyOnboardingSaveForAttempt(attemptId, saveOutcome, abandonmentOutcome) {
341
+ const pending = pendingCustomKeyCredentialSaves.get(attemptId) ?? {
342
+ count: 0,
343
+ abandonmentRequested: false,
344
+ saved: false,
345
+ saveOutcome,
346
+ abandonmentOutcome,
347
+ };
348
+ pending.count += 1;
349
+ pendingCustomKeyCredentialSaves.set(attemptId, pending);
350
+ let finished = false;
351
+ return {
352
+ finish(saved) {
353
+ if (finished)
354
+ return;
355
+ finished = true;
356
+ const current = pendingCustomKeyCredentialSaves.get(attemptId);
357
+ if (!current)
358
+ return;
359
+ try {
360
+ if (saved) {
361
+ const result = trackCustomKeyOnboardingOutcomeForAttempt(attemptId, current.saveOutcome);
362
+ if (result === "tracked" ||
363
+ result === "tracked_uncorrelated" ||
364
+ result === "tracked_storage_unavailable" ||
365
+ result === "duplicate") {
366
+ current.saved = true;
367
+ }
368
+ }
369
+ }
370
+ finally {
371
+ current.count -= 1;
372
+ if (current.count === 0) {
373
+ pendingCustomKeyCredentialSaves.delete(attemptId);
374
+ if (current.abandonmentRequested && !current.saved) {
375
+ trackCustomKeyOnboardingOutcomeForAttempt(attemptId, current.abandonmentOutcome);
376
+ }
377
+ }
378
+ }
379
+ },
380
+ };
381
+ }
382
+ export async function withCustomKeyOnboardingCredentialSave(save) {
383
+ return withCustomKeyOnboardingSave(save, "credential_saved", "credential_abandoned");
384
+ }
385
+ export async function withCustomKeyOnboardingLocalEndpointSave(save) {
386
+ return withCustomKeyOnboardingSave(save, "local_endpoint_saved", "local_endpoint_abandoned");
387
+ }
388
+ async function withCustomKeyOnboardingSave(save, saveOutcome, abandonmentOutcome) {
389
+ const credentialSave = beginCustomKeyOnboardingSave(saveOutcome, abandonmentOutcome);
390
+ let saved = false;
391
+ try {
392
+ const result = await save();
393
+ saved = true;
394
+ return result;
395
+ }
396
+ finally {
397
+ credentialSave?.finish(saved);
398
+ }
399
+ }
400
+ function handleCustomKeyOnboardingAbandonment() {
401
+ const stored = readCustomKeyOnboardingAttempt();
402
+ const attempt = stored.kind === "available"
403
+ ? stored.attempt
404
+ : stored.kind === "pending"
405
+ ? stored.pending.attempt
406
+ : null;
407
+ if (attempt &&
408
+ (attempt.identityKey === undefined ||
409
+ attempt.identityKey === getAnalyticsIdentityKey())) {
410
+ const attemptId = attempt.id;
411
+ const completedOutcome = [
412
+ "credential_saved",
413
+ "credential_skipped",
414
+ "credential_abandoned",
415
+ "local_endpoint_saved",
416
+ "local_endpoint_skipped",
417
+ "local_endpoint_abandoned",
418
+ ].find((outcome) => locallyTrackedCustomKeyOutcomes.has(`${attemptId}:${outcome}`));
419
+ if (completedOutcome) {
420
+ trackCustomKeyOnboardingOutcome(completedOutcome);
421
+ return;
422
+ }
423
+ const pending = pendingCustomKeyCredentialSaves.get(attemptId);
424
+ if (pending && pending.count > 0) {
425
+ pending.abandonmentRequested = true;
426
+ return;
427
+ }
428
+ trackCustomKeyOnboardingOutcome(attempt.setupKind === "local_endpoint"
429
+ ? "local_endpoint_abandoned"
430
+ : "credential_abandoned");
431
+ return;
432
+ }
433
+ trackCustomKeyOnboardingOutcome("credential_abandoned");
434
+ }
435
+ export function requestCustomKeyOnboardingAbandonment() {
436
+ handleCustomKeyOnboardingAbandonment();
437
+ }
438
+ export function useCustomKeyOnboardingAttemptLifecycle() {
439
+ const mountedRef = useRef(false);
440
+ useEffect(() => {
441
+ mountedRef.current = true;
442
+ if (readCustomKeyOnboardingAttempt().kind === "stale") {
443
+ trackCustomKeyOnboardingOutcome("credential_abandoned");
444
+ }
445
+ const handlePageHide = (event) => {
446
+ if (!event.persisted) {
447
+ handleCustomKeyOnboardingAbandonment();
448
+ }
449
+ };
450
+ window.addEventListener("pagehide", handlePageHide);
451
+ return () => {
452
+ window.removeEventListener("pagehide", handlePageHide);
453
+ mountedRef.current = false;
454
+ queueMicrotask(() => {
455
+ if (!mountedRef.current) {
456
+ handleCustomKeyOnboardingAbandonment();
457
+ }
458
+ });
459
+ };
460
+ }, []);
461
+ }
9
462
  const sharedSummaryReads = new Map();
10
463
  /**
11
464
  * The setup button, the checklist panel, and the first-run surface each mount
@@ -58,11 +511,17 @@ function readOnboardingSummary(url, fresh) {
58
511
  export function __resetOnboardingSummaryReadsForTests() {
59
512
  sharedSummaryReads.clear();
60
513
  }
514
+ export function __resetOnboardingEventDedupeForTests() {
515
+ lastOnboardingStepViews.clear();
516
+ seenOnboardingEvents.clear();
517
+ }
61
518
  export function trackOnboardingEvent(name, properties) {
62
519
  if (typeof window === "undefined")
63
520
  return;
64
521
  const identityKey = getAnalyticsIdentityKey() ?? "anonymous";
522
+ const sessionId = getAnalyticsSessionId() ?? "unknown-session";
65
523
  const key = [
524
+ sessionId,
66
525
  identityKey,
67
526
  name,
68
527
  properties.flow,
@@ -70,9 +529,20 @@ export function trackOnboardingEvent(name, properties) {
70
529
  properties.extension_id,
71
530
  properties.integration_id,
72
531
  properties.role,
532
+ properties.step_view_id,
533
+ properties.onboarding_attempt_id,
73
534
  ]
74
535
  .map((value) => String(value ?? ""))
75
536
  .join(":");
537
+ const stepViewScope = JSON.stringify([
538
+ sessionId,
539
+ identityKey,
540
+ properties.flow,
541
+ ]);
542
+ const stepIdentity = JSON.stringify([
543
+ properties.step_id,
544
+ properties.extension_id,
545
+ ]);
76
546
  const isRepeatableInteraction = name.startsWith("integration_") ||
77
547
  name === "onboarding_role_save_started" ||
78
548
  name === "onboarding_method_clicked" ||
@@ -81,10 +551,27 @@ export function trackOnboardingEvent(name, properties) {
81
551
  name === "onboarding_dismissed" ||
82
552
  name === "onboarding_reopened" ||
83
553
  name === "onboarding_abandoned";
84
- if (!isRepeatableInteraction && seenOnboardingEvents.has(key))
85
- return;
86
- if (!isRepeatableInteraction)
554
+ if (name === "onboarding_step_viewed") {
555
+ if (typeof properties.step_view_id === "string" &&
556
+ properties.step_view_id) {
557
+ if (seenOnboardingEvents.has(key))
558
+ return;
559
+ seenOnboardingEvents.add(key);
560
+ }
561
+ else {
562
+ if (lastOnboardingStepViews.get(stepViewScope) === stepIdentity)
563
+ return;
564
+ lastOnboardingStepViews.set(stepViewScope, stepIdentity);
565
+ }
566
+ }
567
+ else if (name === "onboarding_reopened") {
568
+ lastOnboardingStepViews.delete(stepViewScope);
569
+ }
570
+ else if (!isRepeatableInteraction) {
571
+ if (seenOnboardingEvents.has(key))
572
+ return;
87
573
  seenOnboardingEvents.add(key);
574
+ }
88
575
  trackEvent(name, properties);
89
576
  }
90
577
  export function useOnboarding(options = {}) {
@@ -0,0 +1,8 @@
1
+ export declare const SESSION_REPLAY_MASK_ATTRIBUTE = "data-an-mask";
2
+ export declare const SESSION_REPLAY_MASK_PROPS: {
3
+ readonly "data-an-mask": "";
4
+ };
5
+ export declare const SESSION_REPLAY_BLOCK_ATTRIBUTE = "data-an-block";
6
+ export declare const SESSION_REPLAY_BLOCK_PROPS: {
7
+ readonly "data-an-block": "";
8
+ };
@@ -0,0 +1,8 @@
1
+ export const SESSION_REPLAY_MASK_ATTRIBUTE = "data-an-mask";
2
+ export const SESSION_REPLAY_MASK_PROPS = {
3
+ [SESSION_REPLAY_MASK_ATTRIBUTE]: "",
4
+ };
5
+ export const SESSION_REPLAY_BLOCK_ATTRIBUTE = "data-an-block";
6
+ export const SESSION_REPLAY_BLOCK_PROPS = {
7
+ [SESSION_REPLAY_BLOCK_ATTRIBUTE]: "",
8
+ };
@@ -39,6 +39,7 @@ export interface SessionReplayOptions {
39
39
  blockSelector?: string;
40
40
  ignoreSelector?: string;
41
41
  maskTextClass?: string | RegExp;
42
+ /** App selectors extend the framework's `data-an-mask` privacy marker. */
42
43
  maskTextSelector?: string;
43
44
  maskAllInputs?: boolean;
44
45
  recordCanvas?: boolean;
@@ -3,6 +3,7 @@ import { resolveLaneEndpoint } from "../shared/environment-lanes.js";
3
3
  import { loadOptionalPeer, OptionalPeerDependencyError, } from "../shared/optional-peer.js";
4
4
  import { isSyntheticTrafficValue } from "../shared/test-traffic.js";
5
5
  import { getOrCreateAnalyticsAnonymousId, getOrCreateAnalyticsSessionId, } from "./analytics-session.js";
6
+ import { SESSION_REPLAY_BLOCK_ATTRIBUTE, SESSION_REPLAY_MASK_ATTRIBUTE, } from "./session-replay-privacy.js";
6
7
  import { decideReplayQuotaResponse, parseRetryAfterSeconds, } from "./session-replay-quota.js";
7
8
  import { scrubUrl } from "./url-scrub.js";
8
9
  function isSyntheticBrowserTraffic() {
@@ -24,7 +25,7 @@ const SESSION_REPLAY_IFRAME_BLOCK_SELECTOR = `iframe[${SESSION_REPLAY_IFRAME_ATT
24
25
  const DEFAULT_BLOCK_SELECTOR = [
25
26
  SESSION_REPLAY_IFRAME_BLOCK_SELECTOR,
26
27
  "[data-sensitive]",
27
- "[data-an-block]",
28
+ `[${SESSION_REPLAY_BLOCK_ATTRIBUTE}]`,
28
29
  "[data-an-private]",
29
30
  "[data-private]",
30
31
  ".an-block",
@@ -39,9 +40,18 @@ const DEFAULT_BLOCK_SELECTOR = [
39
40
  "[name*='card' i]",
40
41
  "[name*='ssn' i]",
41
42
  ].join(", ");
43
+ /**
44
+ * App block selectors extend these framework markers instead of replacing them.
45
+ * Append them whole: a custom selector can mention one, as in
46
+ * `[data-an-block] .secret`, without matching the bare marker.
47
+ */
48
+ const REQUIRED_BLOCK_SELECTORS = [
49
+ SESSION_REPLAY_IFRAME_BLOCK_SELECTOR,
50
+ `[${SESSION_REPLAY_BLOCK_ATTRIBUTE}]`,
51
+ ];
42
52
  const DEFAULT_IGNORE_SELECTOR = ".an-ignore, [data-an-ignore]";
43
53
  const DEFAULT_MASK_TEXT_CLASS = "an-mask";
44
- const DEFAULT_MASK_TEXT_SELECTOR = "[data-an-mask]";
54
+ const DEFAULT_MASK_TEXT_SELECTOR = `[${SESSION_REPLAY_MASK_ATTRIBUTE}]`;
45
55
  const DEFAULT_MASK_INPUT_OPTIONS = {
46
56
  color: true,
47
57
  date: true,
@@ -586,10 +596,14 @@ function normalizeOptions(options) {
586
596
  checkoutEveryNth: options.checkoutEveryNth,
587
597
  checkoutEveryNms: options.checkoutEveryNms,
588
598
  inlineStylesheet: options.inlineStylesheet ?? true,
589
- blockSelector: mergeReplayBlockSelector(options.blockSelector || DEFAULT_BLOCK_SELECTOR),
599
+ blockSelector: options.blockSelector
600
+ ? [options.blockSelector, ...REQUIRED_BLOCK_SELECTORS].join(", ")
601
+ : DEFAULT_BLOCK_SELECTOR,
590
602
  ignoreSelector: options.ignoreSelector || DEFAULT_IGNORE_SELECTOR,
591
603
  maskTextClass: options.maskTextClass || DEFAULT_MASK_TEXT_CLASS,
592
- maskTextSelector: options.maskTextSelector || DEFAULT_MASK_TEXT_SELECTOR,
604
+ maskTextSelector: options.maskTextSelector
605
+ ? `${DEFAULT_MASK_TEXT_SELECTOR}, ${options.maskTextSelector}`
606
+ : DEFAULT_MASK_TEXT_SELECTOR,
593
607
  maskAllInputs: options.maskAllInputs ?? true,
594
608
  recordCanvas: options.recordCanvas ?? false,
595
609
  recordCrossOriginIframes: options.recordCrossOriginIframes ?? window.parent === window,
@@ -605,11 +619,6 @@ function normalizeOptions(options) {
605
619
  shouldStart: options.shouldStart,
606
620
  };
607
621
  }
608
- function mergeReplayBlockSelector(blockSelector) {
609
- return blockSelector.includes(SESSION_REPLAY_IFRAME_BLOCK_SELECTOR)
610
- ? blockSelector
611
- : `${blockSelector}, ${SESSION_REPLAY_IFRAME_BLOCK_SELECTOR}`;
612
- }
613
622
  function normalizeCaptureToggle(value, defaultMaxEvents) {
614
623
  if (value === false)
615
624
  return null;