@agent-native/core 0.98.4 → 0.98.5
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 +2 -2
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/docs/content/durable-background-runs.mdx +37 -0
- package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/de-DE/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/es-ES/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +4 -2
- package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
- package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +2 -0
- package/corpus/core/docs/content/tracking.mdx +2 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +16 -16
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +25 -8
- package/corpus/core/src/agent/engine/anthropic-engine.ts +17 -3
- package/corpus/core/src/agent/engine/output-tokens.ts +6 -6
- package/corpus/core/src/agent/production-agent.ts +200 -27
- package/corpus/core/src/agent/run-manager.ts +81 -1
- package/corpus/core/src/agent/run-store.ts +301 -41
- package/corpus/core/src/client/AgentPanel.tsx +6 -2
- package/corpus/core/src/client/AssistantChat.tsx +364 -69
- package/corpus/core/src/client/agent-chat-adapter.ts +169 -1
- package/corpus/core/src/client/chat/message-components.tsx +3 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +20 -36
- package/corpus/core/src/client/conversation/use-near-bottom-autoscroll.ts +7 -0
- package/corpus/core/src/client/extensions/AgentNativeExtensionFrame.tsx +2 -0
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +2 -0
- package/corpus/core/src/client/extensions/ExtensionEditor.tsx +9 -2
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +2 -0
- package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +2 -0
- package/corpus/core/src/client/extensions/portable-extension.ts +2 -0
- package/corpus/core/src/client/session-replay.ts +361 -29
- package/corpus/core/src/client/sse-event-processor.ts +43 -1
- package/corpus/core/src/demo/actions/toggle-demo-mode.ts +1 -1
- package/corpus/core/src/demo/config.ts +6 -6
- package/corpus/core/src/demo/fetch-interceptor.ts +13 -4
- package/corpus/core/src/demo/redact.ts +13 -149
- package/corpus/core/src/extensions/html-shell.ts +3 -0
- package/corpus/core/src/extensions/session-replay-iframe.ts +131 -0
- package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +1 -20
- package/corpus/core/src/session-replay-iframe-protocol.ts +58 -0
- package/corpus/core/src/shared/reasoning-effort.ts +35 -0
- package/corpus/core/src/styles/agent-conversation.css +26 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +30 -21
- package/corpus/templates/analytics/AGENTS.md +12 -0
- package/corpus/templates/analytics/actions/get-error-issue.ts +1 -0
- package/corpus/templates/analytics/actions/get-session-replay-events.ts +1 -0
- package/corpus/templates/analytics/actions/get-session-replay-summary.ts +1 -0
- package/corpus/templates/analytics/actions/list-error-issues.ts +1 -0
- package/corpus/templates/analytics/actions/list-session-recordings.ts +1 -0
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -1
- package/corpus/templates/analytics/app/global.css +2 -2
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +41 -4
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +52 -203
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +34 -12
- package/corpus/templates/analytics/changelog/2026-07-12-analytics-chat-retries-before-showing-a-generic-no-data-mess.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-analytics-sessions-search-no-longer-loses-fast-keystrokes-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-chart-selection-respects-chat-state.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-anonymizes-error-reporting-emails.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-session-replays-show-iframe-content.md +6 -0
- package/corpus/templates/analytics/netlify.toml +0 -3
- package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +14 -0
- package/corpus/templates/analytics/server/lib/error-capture.ts +59 -2
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +24 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +2 -9
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +39 -6
- package/corpus/templates/assets/app/global.css +1 -1
- package/corpus/templates/brain/app/global.css +2 -2
- package/corpus/templates/brain/changelog/2026-07-12-fix-long-response-progress.md +6 -0
- package/corpus/templates/forms/app/components/CloudUpgrade.tsx +1 -1
- package/corpus/templates/forms/app/components/ThemeToggle.tsx +22 -6
- package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +5 -5
- package/corpus/templates/forms/app/components/builder/FieldRenderer.tsx +3 -3
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +16 -16
- package/corpus/templates/forms/app/global.css +110 -5
- package/corpus/templates/forms/app/pages/AskPage.tsx +3 -3
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +134 -52
- package/corpus/templates/forms/app/pages/FormFillPage.tsx +44 -11
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +42 -24
- package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +28 -15
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +38 -15
- package/corpus/templates/forms/app/routes/_app.settings.tsx +1 -1
- package/corpus/templates/forms/changelog/2026-07-12-form-builder-surfaces-are-calmer-fields-stay-stable-on-hover.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-12-smoother-more-tactile-controls-throughout-animated-icon-and-.md +6 -0
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +21 -13
- package/corpus/templates/mail/app/components/email/email-iframe-document.ts +18 -0
- package/dist/agent/durable-background.d.ts +10 -10
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +16 -16
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +19 -8
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +11 -3
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/output-tokens.js +6 -6
- package/dist/agent/engine/output-tokens.js.map +1 -1
- package/dist/agent/production-agent.d.ts +52 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +154 -26
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +71 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +9 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +243 -48
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +6 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +21 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +298 -67
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +146 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +2 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +3 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +13 -37
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts +1 -0
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.js +6 -0
- package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.d.ts.map +1 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.js +2 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +2 -1
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionEditor.js +5 -2
- package/dist/client/extensions/ExtensionEditor.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +2 -1
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -1
- package/dist/client/extensions/InlineExtensionFrame.js +2 -1
- package/dist/client/extensions/InlineExtensionFrame.js.map +1 -1
- package/dist/client/extensions/portable-extension.d.ts.map +1 -1
- package/dist/client/extensions/portable-extension.js +2 -0
- package/dist/client/extensions/portable-extension.js.map +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +308 -29
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +5 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +37 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/demo/actions/toggle-demo-mode.js +1 -1
- package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
- package/dist/demo/config.js +6 -6
- package/dist/demo/config.js.map +1 -1
- package/dist/demo/fetch-interceptor.d.ts.map +1 -1
- package/dist/demo/fetch-interceptor.js +13 -4
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/demo/redact.d.ts +7 -7
- package/dist/demo/redact.d.ts.map +1 -1
- package/dist/demo/redact.js +8 -145
- package/dist/demo/redact.js.map +1 -1
- package/dist/extensions/html-shell.d.ts.map +1 -1
- package/dist/extensions/html-shell.js +2 -0
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/extensions/session-replay-iframe.d.ts +10 -0
- package/dist/extensions/session-replay-iframe.d.ts.map +1 -0
- package/dist/extensions/session-replay-iframe.js +121 -0
- package/dist/extensions/session-replay-iframe.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +1 -19
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/session-replay-iframe-protocol.d.ts +36 -0
- package/dist/session-replay-iframe-protocol.d.ts.map +1 -0
- package/dist/session-replay-iframe-protocol.js +24 -0
- package/dist/session-replay-iframe-protocol.js.map +1 -0
- package/dist/shared/reasoning-effort.d.ts +11 -0
- package/dist/shared/reasoning-effort.d.ts.map +1 -1
- package/dist/shared/reasoning-effort.js +36 -0
- package/dist/shared/reasoning-effort.js.map +1 -1
- package/dist/styles/agent-conversation.css +26 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
- package/docs/content/durable-background-runs.mdx +37 -0
- package/docs/content/locales/ar-SA/tracking.mdx +2 -0
- package/docs/content/locales/de-DE/tracking.mdx +2 -0
- package/docs/content/locales/es-ES/tracking.mdx +2 -0
- package/docs/content/locales/fr-FR/tracking.mdx +4 -2
- package/docs/content/locales/hi-IN/tracking.mdx +2 -0
- package/docs/content/locales/ja-JP/tracking.mdx +2 -0
- package/docs/content/locales/ko-KR/tracking.mdx +2 -0
- package/docs/content/locales/pt-BR/tracking.mdx +2 -0
- package/docs/content/locales/zh-CN/tracking.mdx +2 -0
- package/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
- package/docs/content/locales/zh-TW/tracking.mdx +2 -0
- package/docs/content/tracking.mdx +2 -0
- package/package.json +2 -2
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
- package/corpus/core/src/observability/hosted-model-experiment.ts +0 -118
- package/dist/observability/hosted-model-experiment.d.ts +0 -39
- package/dist/observability/hosted-model-experiment.d.ts.map +0 -1
- package/dist/observability/hosted-model-experiment.js +0 -90
- package/dist/observability/hosted-model-experiment.js.map +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Demo mode gate.
|
|
3
3
|
*
|
|
4
|
-
* Demo mode replaces email
|
|
5
|
-
*
|
|
6
|
-
* preserving names, free text, labels, IDs, dates, URLs,
|
|
7
|
-
* app keeps working. The redaction WALK (see ./redact.ts)
|
|
8
|
-
* large payloads, so callers MUST gate it behind this function
|
|
9
|
-
* when it returns true.
|
|
4
|
+
* Demo mode replaces every email address with `anonymous@builder.io` and
|
|
5
|
+
* anonymizes numbers in every action result — for both the UI and what the
|
|
6
|
+
* agent sees — while preserving names, free text, labels, IDs, dates, URLs,
|
|
7
|
+
* and structure so the app keeps working. The redaction WALK (see ./redact.ts)
|
|
8
|
+
* is expensive on large payloads, so callers MUST gate it behind this function
|
|
9
|
+
* and only walk when it returns true.
|
|
10
10
|
*
|
|
11
11
|
* This gate itself is intentionally cheap:
|
|
12
12
|
* - An env-forced deployment (a hosted demo site) short-circuits with zero
|
|
@@ -46,10 +46,19 @@ const SKIP_SUBSTRINGS = [
|
|
|
46
46
|
"/_agent-native/runs",
|
|
47
47
|
];
|
|
48
48
|
|
|
49
|
-
// Raw rrweb payloads are playback data, not ordinary app UI records.
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
49
|
+
// Raw rrweb payloads are playback data, not ordinary app UI records. Skipping
|
|
50
|
+
// these is not just a perf optimization that avoids walking/cloning huge
|
|
51
|
+
// DOM/event trees on the main thread: demo number
|
|
52
|
+
// redaction previously ran over this exact raw replay JSON and faked any
|
|
53
|
+
// integer >= 1000 it found — Meta/ViewportResize widths, pointer x/y
|
|
54
|
+
// coordinates, and numeric values inside `_cssText` and SVG attributes. That
|
|
55
|
+
// was the root cause of the 2026-07 "ultra-wide replay" bugs (stages
|
|
56
|
+
// rendered thousands of pixels wide, frozen/teleporting cursors, giant
|
|
57
|
+
// icons) even though every stored recording was always geometrically sane —
|
|
58
|
+
// the corruption happened at *view* time, not at capture/storage time. Small
|
|
59
|
+
// list/summary/manifest responses stay eligible so rendered visitor identities
|
|
60
|
+
// are still anonymized. NEVER remove the raw payload skips or broaden them to
|
|
61
|
+
// metadata endpoints that the UI renders directly.
|
|
53
62
|
const RAW_REPLAY_PAYLOAD_RE =
|
|
54
63
|
/\/api\/session-replay\/recordings\/[^/?#]+\/(?:chunks(?:\/[^/?#]+)?|events)(?:[/?#]|$)/;
|
|
55
64
|
const RAW_AGENT_REPLAY_EVENTS_RE =
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure, dependency-free, deterministic demo-mode redactor.
|
|
3
3
|
*
|
|
4
|
-
* Replaces
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* placeholders before any transform runs, restore them byte-identical
|
|
4
|
+
* Replaces every email with one canonical anonymous address and free numbers
|
|
5
|
+
* with stable fake substitutes. Crucially, it NEVER rewrites identifiers,
|
|
6
|
+
* structural tokens, or timestamps. Names and other free text are deliberately
|
|
7
|
+
* left alone because guessing whether arbitrary text is a person's name is too
|
|
8
|
+
* inaccurate. The string redactor uses a protect-first strategy (mask IDs with
|
|
9
|
+
* opaque placeholders before any transform runs, restore them byte-identical
|
|
11
10
|
* afterwards), and the structure-aware walker additionally protects leaf
|
|
12
11
|
* values by key name.
|
|
13
12
|
*/
|
|
@@ -58,10 +57,6 @@ function seededRng(value: string, salt: string): () => number {
|
|
|
58
57
|
return mulberry32(seedFn());
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
function pick<T>(rng: () => number, pool: readonly T[]): T {
|
|
62
|
-
return pool[Math.floor(rng() * pool.length) % pool.length];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
60
|
/* ------------------------------------------------------------------ *
|
|
66
61
|
* Stable mapping cache (bounded, TTL, leak-free)
|
|
67
62
|
*
|
|
@@ -157,150 +152,19 @@ function memoFake(
|
|
|
157
152
|
return value;
|
|
158
153
|
}
|
|
159
154
|
|
|
160
|
-
/* ------------------------------------------------------------------ *
|
|
161
|
-
* Curated name pools
|
|
162
|
-
* ------------------------------------------------------------------ */
|
|
163
|
-
|
|
164
|
-
const FIRST_NAMES: readonly string[] = [
|
|
165
|
-
"Jane",
|
|
166
|
-
"John",
|
|
167
|
-
"Alex",
|
|
168
|
-
"Maria",
|
|
169
|
-
"Sam",
|
|
170
|
-
"Olivia",
|
|
171
|
-
"Liam",
|
|
172
|
-
"Emma",
|
|
173
|
-
"Noah",
|
|
174
|
-
"Ava",
|
|
175
|
-
"Lucas",
|
|
176
|
-
"Mia",
|
|
177
|
-
"Ethan",
|
|
178
|
-
"Sofia",
|
|
179
|
-
"Mason",
|
|
180
|
-
"Isla",
|
|
181
|
-
"Leo",
|
|
182
|
-
"Aria",
|
|
183
|
-
"Henry",
|
|
184
|
-
"Zoe",
|
|
185
|
-
"Owen",
|
|
186
|
-
"Nora",
|
|
187
|
-
"Caleb",
|
|
188
|
-
"Lily",
|
|
189
|
-
"Ryan",
|
|
190
|
-
"Ruby",
|
|
191
|
-
"Adam",
|
|
192
|
-
"Chloe",
|
|
193
|
-
"Eli",
|
|
194
|
-
"Hazel",
|
|
195
|
-
"Max",
|
|
196
|
-
"Iris",
|
|
197
|
-
"Ben",
|
|
198
|
-
"Clara",
|
|
199
|
-
"Theo",
|
|
200
|
-
"Maya",
|
|
201
|
-
"Felix",
|
|
202
|
-
"Elena",
|
|
203
|
-
"Jonah",
|
|
204
|
-
"Greta",
|
|
205
|
-
];
|
|
206
|
-
|
|
207
|
-
const LAST_NAMES: readonly string[] = [
|
|
208
|
-
"Doe",
|
|
209
|
-
"Smith",
|
|
210
|
-
"Johnson",
|
|
211
|
-
"Lee",
|
|
212
|
-
"Brown",
|
|
213
|
-
"Garcia",
|
|
214
|
-
"Miller",
|
|
215
|
-
"Davis",
|
|
216
|
-
"Wilson",
|
|
217
|
-
"Moore",
|
|
218
|
-
"Taylor",
|
|
219
|
-
"Anderson",
|
|
220
|
-
"Thomas",
|
|
221
|
-
"Jackson",
|
|
222
|
-
"White",
|
|
223
|
-
"Harris",
|
|
224
|
-
"Martin",
|
|
225
|
-
"Clark",
|
|
226
|
-
"Lewis",
|
|
227
|
-
"Walker",
|
|
228
|
-
"Hall",
|
|
229
|
-
"Young",
|
|
230
|
-
"King",
|
|
231
|
-
"Wright",
|
|
232
|
-
"Hill",
|
|
233
|
-
"Green",
|
|
234
|
-
"Adams",
|
|
235
|
-
"Baker",
|
|
236
|
-
"Nelson",
|
|
237
|
-
"Carter",
|
|
238
|
-
"Mitchell",
|
|
239
|
-
"Perez",
|
|
240
|
-
"Roberts",
|
|
241
|
-
"Turner",
|
|
242
|
-
"Phillips",
|
|
243
|
-
"Campbell",
|
|
244
|
-
"Parker",
|
|
245
|
-
"Evans",
|
|
246
|
-
"Edwards",
|
|
247
|
-
"Collins",
|
|
248
|
-
];
|
|
249
|
-
|
|
250
155
|
/* ------------------------------------------------------------------ *
|
|
251
156
|
* Fake value generators (deterministic in value + salt)
|
|
252
157
|
* ------------------------------------------------------------------ */
|
|
253
158
|
|
|
254
|
-
|
|
255
|
-
// local part is fake, so no real mailbox is implied) plus the canonical
|
|
256
|
-
// reserved fictional-company domains (Contoso/Fabrikam/Northwind), which read
|
|
257
|
-
// as real businesses but are intentional placeholders. No example.com.
|
|
258
|
-
const FAKE_EMAIL_DOMAINS: readonly string[] = [
|
|
259
|
-
"gmail.com",
|
|
260
|
-
"outlook.com",
|
|
261
|
-
"icloud.com",
|
|
262
|
-
"yahoo.com",
|
|
263
|
-
"proton.me",
|
|
264
|
-
"hey.com",
|
|
265
|
-
"fastmail.com",
|
|
266
|
-
"contoso.com",
|
|
267
|
-
"fabrikam.com",
|
|
268
|
-
"northwind.com",
|
|
269
|
-
"acme.io",
|
|
270
|
-
"globex.net",
|
|
271
|
-
];
|
|
159
|
+
export const DEMO_ANONYMOUS_EMAIL = "anonymous@builder.io";
|
|
272
160
|
|
|
273
161
|
function fakeEmail(original: string, salt: string): string {
|
|
274
|
-
return memoFake(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const shape = Math.floor(rng() * 6);
|
|
281
|
-
let local: string;
|
|
282
|
-
switch (shape) {
|
|
283
|
-
case 0:
|
|
284
|
-
local = `${first}.${last}`;
|
|
285
|
-
break;
|
|
286
|
-
case 1:
|
|
287
|
-
local = `${first}${last}`;
|
|
288
|
-
break;
|
|
289
|
-
case 2:
|
|
290
|
-
local = `${first[0]}${last}`;
|
|
291
|
-
break;
|
|
292
|
-
case 3:
|
|
293
|
-
local = `${first}.${last}${1 + Math.floor(rng() * 89)}`;
|
|
294
|
-
break;
|
|
295
|
-
case 4:
|
|
296
|
-
local = `${first}_${last}`;
|
|
297
|
-
break;
|
|
298
|
-
default:
|
|
299
|
-
local = `${last}.${first}`;
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
return `${local}@${domain}`;
|
|
303
|
-
});
|
|
162
|
+
return memoFake(
|
|
163
|
+
"email",
|
|
164
|
+
original.toLowerCase(),
|
|
165
|
+
salt,
|
|
166
|
+
() => DEMO_ANONYMOUS_EMAIL,
|
|
167
|
+
);
|
|
304
168
|
}
|
|
305
169
|
|
|
306
170
|
/**
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { buildSessionReplayIframeBootstrap } from "./session-replay-iframe.js";
|
|
2
|
+
|
|
1
3
|
const EXTENSION_IFRAME_CSP_BASE =
|
|
2
4
|
"default-src 'none'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self'; img-src 'self' data: blob:; media-src 'self' data: blob:; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none';";
|
|
3
5
|
|
|
@@ -744,6 +746,7 @@ export function buildExtensionHtml(
|
|
|
744
746
|
}
|
|
745
747
|
});
|
|
746
748
|
</script>
|
|
749
|
+
${buildSessionReplayIframeBootstrap()}
|
|
747
750
|
</head>
|
|
748
751
|
<body${extensionId ? ` data-extension-id="${extensionIdAttr}" data-tool-id="${extensionIdAttr}"` : ""} class="text-foreground">
|
|
749
752
|
${content}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isTrustedSessionReplayIframeParentOrigin,
|
|
3
|
+
SESSION_REPLAY_IFRAME_PROBE,
|
|
4
|
+
SESSION_REPLAY_IFRAME_START,
|
|
5
|
+
SESSION_REPLAY_IFRAME_STOP,
|
|
6
|
+
} from "../session-replay-iframe-protocol.js";
|
|
7
|
+
|
|
8
|
+
export const RRWEB_RECORD_IFRAME_CDN_URL =
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/@rrweb/record@2.1.0/umd/record.min.js";
|
|
10
|
+
export const RRWEB_RECORD_IFRAME_SRI =
|
|
11
|
+
"sha384-MrD66HBNSykaP2N95+6hQCFlF5oH2tvL3TD/zyvHNkP/sAFWZx98DX9MEDy8MdVT";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Installs the cooperative side of rrweb's cross-origin iframe protocol.
|
|
15
|
+
* Nothing is fetched or recorded until a trusted first-party parent asks the
|
|
16
|
+
* frame to start. rrweb then forwards its events directly to that parent.
|
|
17
|
+
*/
|
|
18
|
+
export function buildSessionReplayIframeBootstrap(): string {
|
|
19
|
+
const probeType = JSON.stringify(SESSION_REPLAY_IFRAME_PROBE);
|
|
20
|
+
const startType = JSON.stringify(SESSION_REPLAY_IFRAME_START);
|
|
21
|
+
const stopType = JSON.stringify(SESSION_REPLAY_IFRAME_STOP);
|
|
22
|
+
const scriptUrl = JSON.stringify(RRWEB_RECORD_IFRAME_CDN_URL);
|
|
23
|
+
const integrity = JSON.stringify(RRWEB_RECORD_IFRAME_SRI);
|
|
24
|
+
|
|
25
|
+
return `<script>
|
|
26
|
+
(function() {
|
|
27
|
+
var recorderStop = null;
|
|
28
|
+
var recorderLoad = null;
|
|
29
|
+
var startGeneration = 0;
|
|
30
|
+
var isTrustedParentOrigin = ${isTrustedSessionReplayIframeParentOrigin.toString()};
|
|
31
|
+
|
|
32
|
+
function stopRecorder() {
|
|
33
|
+
startGeneration += 1;
|
|
34
|
+
if (typeof recorderStop === 'function') {
|
|
35
|
+
try { recorderStop(); } catch (_) {}
|
|
36
|
+
}
|
|
37
|
+
recorderStop = null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getRecord() {
|
|
41
|
+
return window.rrwebRecord && typeof window.rrwebRecord.record === 'function'
|
|
42
|
+
? window.rrwebRecord.record
|
|
43
|
+
: null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function loadRecorder() {
|
|
47
|
+
if (recorderLoad) return recorderLoad;
|
|
48
|
+
recorderLoad = new Promise(function(resolve, reject) {
|
|
49
|
+
var script = document.createElement('script');
|
|
50
|
+
script.src = ${scriptUrl};
|
|
51
|
+
script.integrity = ${integrity};
|
|
52
|
+
script.crossOrigin = 'anonymous';
|
|
53
|
+
script.referrerPolicy = 'no-referrer';
|
|
54
|
+
script.async = true;
|
|
55
|
+
script.onload = function() {
|
|
56
|
+
var record = getRecord();
|
|
57
|
+
if (record) resolve(record);
|
|
58
|
+
else reject(new Error('rrweb recorder did not initialize'));
|
|
59
|
+
};
|
|
60
|
+
script.onerror = function() {
|
|
61
|
+
recorderLoad = null;
|
|
62
|
+
reject(new Error('rrweb recorder failed to load'));
|
|
63
|
+
};
|
|
64
|
+
document.head.appendChild(script);
|
|
65
|
+
});
|
|
66
|
+
return recorderLoad;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function validOptions(options) {
|
|
70
|
+
return !!options &&
|
|
71
|
+
typeof options === 'object' &&
|
|
72
|
+
typeof options.blockSelector === 'string' &&
|
|
73
|
+
typeof options.ignoreSelector === 'string' &&
|
|
74
|
+
typeof options.maskTextSelector === 'string' &&
|
|
75
|
+
typeof options.maskAllInputs === 'boolean' &&
|
|
76
|
+
typeof options.recordCanvas === 'boolean' &&
|
|
77
|
+
typeof options.collectFonts === 'boolean' &&
|
|
78
|
+
typeof options.inlineImages === 'boolean' &&
|
|
79
|
+
!!options.sampling && typeof options.sampling === 'object';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function startRecorder(options) {
|
|
83
|
+
if (!validOptions(options)) return;
|
|
84
|
+
stopRecorder();
|
|
85
|
+
var generation = startGeneration;
|
|
86
|
+
loadRecorder().then(function(record) {
|
|
87
|
+
if (generation !== startGeneration) return;
|
|
88
|
+
recorderStop = record({
|
|
89
|
+
blockSelector: options.blockSelector,
|
|
90
|
+
ignoreSelector: options.ignoreSelector,
|
|
91
|
+
maskTextClass: options.maskTextClass,
|
|
92
|
+
maskTextSelector: options.maskTextSelector,
|
|
93
|
+
maskAllInputs: options.maskAllInputs,
|
|
94
|
+
maskInputOptions: options.maskInputOptions,
|
|
95
|
+
recordCanvas: options.recordCanvas,
|
|
96
|
+
collectFonts: options.collectFonts,
|
|
97
|
+
inlineImages: options.inlineImages,
|
|
98
|
+
sampling: options.sampling,
|
|
99
|
+
recordCrossOriginIframes: true
|
|
100
|
+
}) || null;
|
|
101
|
+
}).catch(function() {});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
window.addEventListener('message', function(event) {
|
|
105
|
+
if (event.source !== window.parent || !isTrustedParentOrigin(event.origin, window.location.href)) return;
|
|
106
|
+
var message = event.data;
|
|
107
|
+
if (!message || typeof message !== 'object') return;
|
|
108
|
+
if (message.type === ${startType}) startRecorder(message.options);
|
|
109
|
+
else if (message.type === ${stopType}) stopRecorder();
|
|
110
|
+
});
|
|
111
|
+
window.addEventListener('pagehide', stopRecorder);
|
|
112
|
+
|
|
113
|
+
if (window.parent !== window) {
|
|
114
|
+
window.parent.postMessage({ type: ${probeType} }, '*');
|
|
115
|
+
}
|
|
116
|
+
})();
|
|
117
|
+
</script>`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function injectSessionReplayIframeBootstrap(html: string): string {
|
|
121
|
+
const bootstrap = buildSessionReplayIframeBootstrap();
|
|
122
|
+
const headClose = html.search(/<\/head\s*>/i);
|
|
123
|
+
if (headClose >= 0) {
|
|
124
|
+
return `${html.slice(0, headClose)}${bootstrap}${html.slice(headClose)}`;
|
|
125
|
+
}
|
|
126
|
+
const bodyOpen = html.search(/<body(?:\s[^>]*)?>/i);
|
|
127
|
+
if (bodyOpen >= 0) {
|
|
128
|
+
return `${html.slice(0, bodyOpen)}${bootstrap}${html.slice(bodyOpen)}`;
|
|
129
|
+
}
|
|
130
|
+
return `${bootstrap}${html}`;
|
|
131
|
+
}
|
|
@@ -15,8 +15,6 @@ import {
|
|
|
15
15
|
normalizeModelForEngine,
|
|
16
16
|
} from "../../agent/engine/index.js";
|
|
17
17
|
import type { ActionTool } from "../../agent/types.js";
|
|
18
|
-
import { resolveHostedDefaultModelExperiment } from "../../observability/hosted-model-experiment.js";
|
|
19
|
-
import { getRequestUserEmail } from "../../server/request-context.js";
|
|
20
18
|
import { getSetting } from "../../settings/index.js";
|
|
21
19
|
|
|
22
20
|
export const tool: ActionTool = {
|
|
@@ -84,24 +82,13 @@ export async function run(args: Record<string, string> = {}): Promise<string> {
|
|
|
84
82
|
? current?.model
|
|
85
83
|
: undefined;
|
|
86
84
|
const currentEngineName = currentEntry?.name ?? "anthropic";
|
|
87
|
-
|
|
85
|
+
const currentModel =
|
|
88
86
|
currentEntry && !envUnavailable
|
|
89
87
|
? normalizeModelForEngine(
|
|
90
88
|
currentEntry,
|
|
91
89
|
currentModelCandidate ?? currentEntry.defaultModel,
|
|
92
90
|
)
|
|
93
91
|
: (currentModelCandidate ?? DEFAULT_MODEL);
|
|
94
|
-
const hostedExperiment =
|
|
95
|
-
currentEntry && !currentModelCandidate
|
|
96
|
-
? resolveHostedDefaultModelExperiment({
|
|
97
|
-
userId: getRequestUserEmail(),
|
|
98
|
-
engineName: currentEntry.name,
|
|
99
|
-
isDefaultModelSelection: true,
|
|
100
|
-
supportedModels: currentEntry.supportedModels,
|
|
101
|
-
})
|
|
102
|
-
: null;
|
|
103
|
-
if (hostedExperiment) currentModel = hostedExperiment.model;
|
|
104
|
-
|
|
105
92
|
const result = {
|
|
106
93
|
engines: engines.map((e) => ({
|
|
107
94
|
name: e.name,
|
|
@@ -119,12 +106,6 @@ export async function run(args: Record<string, string> = {}): Promise<string> {
|
|
|
119
106
|
: {
|
|
120
107
|
engine: currentEngineName,
|
|
121
108
|
model: currentModel,
|
|
122
|
-
...(hostedExperiment
|
|
123
|
-
? {
|
|
124
|
-
modelSelectionSource: "experiment",
|
|
125
|
-
experimentAssignment: hostedExperiment.assignment,
|
|
126
|
-
}
|
|
127
|
-
: {}),
|
|
128
109
|
},
|
|
129
110
|
};
|
|
130
111
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const SESSION_REPLAY_IFRAME_ATTRIBUTE =
|
|
2
|
+
"data-agent-native-session-replay";
|
|
3
|
+
export const SESSION_REPLAY_IFRAME_PROBE = "agent-native-session-replay:probe";
|
|
4
|
+
export const SESSION_REPLAY_IFRAME_START = "agent-native-session-replay:start";
|
|
5
|
+
export const SESSION_REPLAY_IFRAME_STOP = "agent-native-session-replay:stop";
|
|
6
|
+
|
|
7
|
+
export interface SessionReplayIframePrivacyOptions {
|
|
8
|
+
blockSelector: string;
|
|
9
|
+
ignoreSelector: string;
|
|
10
|
+
maskTextClass?: string | RegExp;
|
|
11
|
+
maskTextSelector: string;
|
|
12
|
+
maskAllInputs: boolean;
|
|
13
|
+
maskInputOptions?: Record<string, boolean>;
|
|
14
|
+
recordCanvas: boolean;
|
|
15
|
+
collectFonts: boolean;
|
|
16
|
+
inlineImages: boolean;
|
|
17
|
+
sampling: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SessionReplayIframeProbeMessage {
|
|
21
|
+
type: typeof SESSION_REPLAY_IFRAME_PROBE;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SessionReplayIframeStartMessage {
|
|
25
|
+
type: typeof SESSION_REPLAY_IFRAME_START;
|
|
26
|
+
options: SessionReplayIframePrivacyOptions;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SessionReplayIframeStopMessage {
|
|
30
|
+
type: typeof SESSION_REPLAY_IFRAME_STOP;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type SessionReplayIframeMessage =
|
|
34
|
+
| SessionReplayIframeProbeMessage
|
|
35
|
+
| SessionReplayIframeStartMessage
|
|
36
|
+
| SessionReplayIframeStopMessage;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `srcdoc` HTML came from the immediate parent, so that parent already owns the
|
|
40
|
+
* document being rendered. Server-rendered extension URLs are stricter: only a
|
|
41
|
+
* parent on the render URL's origin may activate recording. That supports
|
|
42
|
+
* custom app domains without allowing an external embed host to make an opaque
|
|
43
|
+
* extension frame disclose its DOM through rrweb's postMessage transport.
|
|
44
|
+
*/
|
|
45
|
+
export function isTrustedSessionReplayIframeParentOrigin(
|
|
46
|
+
parentOrigin: string,
|
|
47
|
+
frameHref: string,
|
|
48
|
+
): boolean {
|
|
49
|
+
if (frameHref === "about:srcdoc") {
|
|
50
|
+
return parentOrigin !== "null" && parentOrigin !== "";
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const frameOrigin = new URL(frameHref).origin;
|
|
54
|
+
return frameOrigin !== "null" && parentOrigin === frameOrigin;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -170,6 +170,41 @@ function isClaudeReasoningModel(model: string) {
|
|
|
170
170
|
return opusMatch ? parseInt(opusMatch[1], 10) >= 6 : false;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Anthropic's adaptive-thinking API is only available on the newer Claude
|
|
175
|
+
* model families. Claude Haiku 4.5 is reasoning-capable, but it still
|
|
176
|
+
* requires the legacy manual `budget_tokens` configuration.
|
|
177
|
+
*/
|
|
178
|
+
export function supportsClaudeAdaptiveThinking(model: string | undefined) {
|
|
179
|
+
if (!model) return false;
|
|
180
|
+
const id = model.toLowerCase().replace(/^anthropic\//, "");
|
|
181
|
+
if (id.includes("fable-5") || id.includes("mythos-5")) return true;
|
|
182
|
+
if (id.includes("sonnet-5") || id.includes("sonnet-4-6")) return true;
|
|
183
|
+
const opusMatch = id.match(/opus-4[-.](\d+)/);
|
|
184
|
+
return opusMatch ? parseInt(opusMatch[1], 10) >= 6 : false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Map the shared reasoning ladder to Anthropic's manual thinking budgets for
|
|
189
|
+
* models that do not support adaptive thinking (currently Claude Haiku 4.5).
|
|
190
|
+
*/
|
|
191
|
+
export function anthropicManualThinkingBudget(effort: ReasoningEffort) {
|
|
192
|
+
switch (effort) {
|
|
193
|
+
case "low":
|
|
194
|
+
return 1_024;
|
|
195
|
+
case "medium":
|
|
196
|
+
return 4_096;
|
|
197
|
+
case "high":
|
|
198
|
+
return 8_000;
|
|
199
|
+
case "xhigh":
|
|
200
|
+
return 16_000;
|
|
201
|
+
case "max":
|
|
202
|
+
return 32_000;
|
|
203
|
+
default:
|
|
204
|
+
return 4_096;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
173
208
|
function supportsClaudeXHigh(model: string) {
|
|
174
209
|
const id = model.toLowerCase().replace(/^anthropic\//, "");
|
|
175
210
|
// Models that support the xhigh effort tier (built-in extended thinking via
|
|
@@ -493,6 +493,32 @@
|
|
|
493
493
|
-webkit-mask-image: linear-gradient(to bottom, transparent, black 1.5rem);
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
+
.agent-chat-collapse {
|
|
497
|
+
display: grid;
|
|
498
|
+
grid-template-rows: 1fr;
|
|
499
|
+
opacity: 1;
|
|
500
|
+
transition:
|
|
501
|
+
grid-template-rows 200ms var(--ease-collapse),
|
|
502
|
+
opacity 150ms ease-out;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.agent-chat-collapse[data-state="closed"] {
|
|
506
|
+
grid-template-rows: 0fr;
|
|
507
|
+
opacity: 0;
|
|
508
|
+
pointer-events: none;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.agent-chat-collapse__content {
|
|
512
|
+
min-height: 0;
|
|
513
|
+
overflow: hidden;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
@media (prefers-reduced-motion: reduce) {
|
|
517
|
+
.agent-chat-collapse {
|
|
518
|
+
transition-duration: 0.01ms;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
496
522
|
/* ─── Agent-Markdown (AssistantChat react-markdown render surface) ─────────── */
|
|
497
523
|
/* These classes are applied by the SmoothMarkdownText/MarkdownText components */
|
|
498
524
|
/* in the AssistantChat/AssistantChatInner tree. The shiki dual-theme rules */
|
|
@@ -158,21 +158,6 @@ The agent loop reads active experiments via `resolveActiveExperimentConfig()` an
|
|
|
158
158
|
|
|
159
159
|
Compute results with `POST /_agent-native/observability/experiments/:id/results`.
|
|
160
160
|
|
|
161
|
-
#### First-party hosted default-model rollout
|
|
162
|
-
|
|
163
|
-
First-party apps on `agent-native.com` automatically run the July 2026
|
|
164
|
-
default-model experiment for authenticated users on the Builder engine:
|
|
165
|
-
|
|
166
|
-
- 80% `claude-sonnet-5`
|
|
167
|
-
- 20% `gpt-5-6-luna`
|
|
168
|
-
- Assignment is sticky by normalized user email and shared across templates.
|
|
169
|
-
- The rollout applies only when the user, app, and deployment have not selected
|
|
170
|
-
a model. Explicit model choices and BYO provider engines always win.
|
|
171
|
-
- Set `AGENT_NATIVE_HOSTED_MODEL_EXPERIMENT=off` for an emergency rollback, or
|
|
172
|
-
`true` to exercise the rollout on a non-first-party preview.
|
|
173
|
-
- `$ai_generation` tracking includes `experiment_id`,
|
|
174
|
-
`experiment_variant`, and `model_selection_source` for central analysis.
|
|
175
|
-
|
|
176
161
|
In production, experiment management routes require the caller's email in the
|
|
177
162
|
comma-separated `AGENT_NATIVE_EXPERIMENT_ADMIN_EMAILS` allowlist. This gate is
|
|
178
163
|
separate from normal app/org admin roles because an experiment affects every
|
|
@@ -125,23 +125,29 @@ agent answers about browser recordings in the Analytics template.
|
|
|
125
125
|
historical replays and resolve external imports/fonts at capture before
|
|
126
126
|
blocking the recorded resource origins. Do not diagnose current font loss as
|
|
127
127
|
CSP without checking the deployed response headers first.
|
|
128
|
-
- Let rrweb own
|
|
129
|
-
outer wrapper
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
128
|
+
- Let rrweb own iframe sizing entirely via Meta / ViewportResize, and keep the
|
|
129
|
+
outer wrapper on the exact same raw dimensions for fit-to-stage scaling.
|
|
130
|
+
Player geometry and pointer coordinates are fully stock and untouched — do
|
|
131
|
+
not add width/aspect-ratio "recovery" heuristics or pointer-coordinate
|
|
132
|
+
projection. There is no such thing as a stored recording with corrupt
|
|
133
|
+
viewport geometry: a census of all production recordings found zero stored
|
|
134
|
+
widths >= 3,000px. The 2026-07 "ultra-wide replay" bugs (stages rendered
|
|
135
|
+
3,000–9,500px wide, frozen/teleporting cursors, giant icons) were caused
|
|
136
|
+
entirely by demo mode's fetch interceptor: its number redactor faked any
|
|
137
|
+
integer >= 1000 inside raw replay JSON at *view* time, corrupting Meta /
|
|
138
|
+
ViewportResize widths, pointer x/y coordinates, and numeric values inside
|
|
139
|
+
`_cssText` and SVG attributes before rrweb ever saw the payload (heights
|
|
140
|
+
below 1000 stayed real, which is why the symptom looked like a viewport
|
|
141
|
+
problem rather than a redaction bug — two different sessions that both
|
|
142
|
+
stored a 1,152px width read back as the same 4,491px, a deterministic
|
|
143
|
+
salted-hash fingerprint of the redactor, not two coincidentally identical
|
|
144
|
+
malformed recordings). This is fixed in
|
|
145
|
+
`packages/core/src/demo/fetch-interceptor.ts`: raw replay payload and
|
|
146
|
+
manifest URLs are skipped from demo number redaction entirely, and must
|
|
147
|
+
never be routed through it again. Do not reintroduce viewport clamping or
|
|
148
|
+
pointer-coordinate projection in the player — they can now only corrupt
|
|
149
|
+
genuine future recordings (for example, a real 3440x900 ultrawide browser
|
|
150
|
+
window, or a short vertical window under 1,000px tall).
|
|
145
151
|
- Keep rrweb's stock cursor stylesheet and its hotspot transform. During
|
|
146
152
|
playback, hide the viewer's native pointer over Analytics' transparent
|
|
147
153
|
click-to-pause overlay so it cannot masquerade as a frozen recorded cursor.
|
|
@@ -152,10 +158,13 @@ agent answers about browser recordings in the Analytics template.
|
|
|
152
158
|
hide generic framework primitives such as
|
|
153
159
|
`[data-radix-popper-content-wrapper]`: Radix dropdowns, selects, tooltips,
|
|
154
160
|
and other real recorded product UI all share that wrapper.
|
|
155
|
-
- Keep the realistic
|
|
156
|
-
`SessionDetailPage.spec.ts
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
- Keep the realistic-fidelity purity/pass-through tests in
|
|
162
|
+
`SessionDetailPage.spec.ts` — raw event identity, raw viewport dimensions,
|
|
163
|
+
and raw resize-state derivation (including the 3,189x885 tripwire against
|
|
164
|
+
reintroducing a clamp) — as regression guards against reintroducing any
|
|
165
|
+
viewport "recovery" or pointer-projection heuristic. Do not change their
|
|
166
|
+
expectations merely to bless a new sanitizer or clamp; validate the affected
|
|
167
|
+
replay in a browser first.
|
|
159
168
|
- The event timeline soft-highlights the active marker, auto-scrolls it into
|
|
160
169
|
view (pausing briefly after manual scroll), and supports search. It appears
|
|
161
170
|
beside the player from ~880px content width upward.
|