@agent-native/core 0.99.1 → 0.99.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +19 -0
- package/corpus/core/docs/content/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/ar-SA/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/de-DE/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/es-ES/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/fr-FR/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/hi-IN/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/ja-JP/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/ko-KR/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/pt-BR/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/zh-CN/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/zh-TW/a2a-protocol.mdx +42 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +12 -38
- package/corpus/core/src/audit/config.ts +0 -1
- package/corpus/core/src/chat-threads/store.ts +57 -41
- package/corpus/core/src/client/AssistantChat.tsx +1 -2
- package/corpus/core/src/client/chat/message-components.tsx +15 -3
- package/corpus/core/src/client/chat/tool-call-display.tsx +18 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +5 -9
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +0 -133
- package/corpus/core/src/client/index.ts +7 -0
- package/corpus/core/src/client/settings/DemoModeSection.tsx +9 -65
- package/corpus/core/src/client/settings/SettingsPanel.tsx +1 -1
- package/corpus/core/src/client/use-db-sync.ts +3 -15
- package/corpus/core/src/client/use-demo-mode-status.ts +16 -30
- package/corpus/core/src/demo/browser-state.ts +47 -0
- package/corpus/core/src/demo/fetch-interceptor.ts +12 -59
- package/corpus/core/src/deploy/workspace-deploy.ts +2 -2
- package/corpus/core/src/server/action-discovery.ts +0 -1
- package/corpus/core/src/server/core-routes-plugin.ts +0 -30
- package/corpus/core/src/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +67 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +48 -7
- package/corpus/templates/analytics/AGENTS.md +4 -4
- package/corpus/templates/analytics/actions/compose-dashboard.ts +12 -1
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +15 -4
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +2 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +14 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -27
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +21 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +14 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +31 -12
- package/corpus/templates/analytics/changelog/2026-07-13-dashboard-charts-now-honor-the-selected-time-range-by-defaul.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-keep-real-authorized-identities-in-analytics-incident-lookup.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-the-analytics-agents-tab-is-available-again-from-the-main-na.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +4 -0
- package/corpus/templates/analytics/server/lib/dashboard-time-scope.ts +170 -0
- package/corpus/templates/analytics/server/lib/error-capture.ts +6 -62
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +20 -5
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +1 -1
- package/corpus/templates/analytics/server/lib/session-replay.ts +5 -111
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/assets/app/routes/library.tsx +3 -4
- package/corpus/templates/assets/changelog/2026-07-13-internal-library-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +43 -40
- package/corpus/templates/calendar/app/pages/ManageBookingPage.tsx +6 -8
- package/corpus/templates/calendar/changelog/2026-07-13-booking-links-and-previews-open-in-new-tabs.md +6 -0
- package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +4 -7
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +19 -9
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +5 -3
- package/corpus/templates/clips/changelog/2026-07-13-recording-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +6 -8
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +5 -3
- package/corpus/templates/content/app/components/editor/database/FormView.tsx +6 -9
- package/corpus/templates/content/changelog/2026-07-13-document-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +33 -16
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +18 -1
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +4 -4
- package/corpus/templates/design/app/pages/DesignSystems.tsx +8 -10
- package/corpus/templates/design/app/pages/Index.tsx +6 -1
- package/corpus/templates/design/app/pages/Present.tsx +12 -6
- package/corpus/templates/design/changelog/2026-07-13-design-navigation-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-13-design-session-replays-include-preview-iframes.md +6 -0
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +11 -11
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +6 -8
- package/corpus/templates/forms/changelog/2026-07-13-form-navigation-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/mail/app/pages/DraftQueuePage.tsx +3 -8
- package/corpus/templates/mail/changelog/2026-07-13-settings-links-open-in-new-tabs.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -37
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/audit/config.d.ts.map +1 -1
- package/dist/audit/config.js +0 -1
- package/dist/audit/config.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +47 -33
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +9 -3
- 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 +10 -2
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +2 -6
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +2 -59
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts +1 -13
- package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
- package/dist/client/settings/DemoModeSection.js +5 -52
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +3 -7
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-demo-mode-status.d.ts +3 -4
- package/dist/client/use-demo-mode-status.d.ts.map +1 -1
- package/dist/client/use-demo-mode-status.js +8 -23
- package/dist/client/use-demo-mode-status.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/demo/browser-state.d.ts +10 -0
- package/dist/demo/browser-state.d.ts.map +1 -0
- package/dist/demo/browser-state.js +49 -0
- package/dist/demo/browser-state.js.map +1 -0
- package/dist/demo/fetch-interceptor.d.ts +2 -7
- package/dist/demo/fetch-interceptor.d.ts.map +1 -1
- package/dist/demo/fetch-interceptor.js +12 -56
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/deploy/workspace-deploy.js +2 -2
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +0 -1
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +0 -27
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +67 -0
- package/docs/content/a2a-protocol.mdx +42 -0
- package/docs/content/locales/ar-SA/a2a-protocol.mdx +42 -0
- package/docs/content/locales/de-DE/a2a-protocol.mdx +42 -0
- package/docs/content/locales/es-ES/a2a-protocol.mdx +42 -0
- package/docs/content/locales/fr-FR/a2a-protocol.mdx +42 -0
- package/docs/content/locales/hi-IN/a2a-protocol.mdx +42 -0
- package/docs/content/locales/ja-JP/a2a-protocol.mdx +42 -0
- package/docs/content/locales/ko-KR/a2a-protocol.mdx +42 -0
- package/docs/content/locales/pt-BR/a2a-protocol.mdx +42 -0
- package/docs/content/locales/zh-CN/a2a-protocol.mdx +42 -0
- package/docs/content/locales/zh-TW/a2a-protocol.mdx +42 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +67 -0
- package/corpus/core/src/demo/actions/toggle-demo-mode.ts +0 -23
- package/corpus/core/src/demo/config.ts +0 -57
- package/dist/demo/actions/toggle-demo-mode.d.ts +0 -8
- package/dist/demo/actions/toggle-demo-mode.d.ts.map +0 -1
- package/dist/demo/actions/toggle-demo-mode.js +0 -21
- package/dist/demo/actions/toggle-demo-mode.js.map +0 -1
- package/dist/demo/config.d.ts +0 -9
- package/dist/demo/config.d.ts.map +0 -1
- package/dist/demo/config.js +0 -58
- package/dist/demo/config.js.map +0 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { agentNativePath } from "../client/api-path.js";
|
|
2
1
|
/**
|
|
3
2
|
* Client-side demo-mode redaction.
|
|
4
3
|
*
|
|
@@ -14,8 +13,8 @@ import { agentNativePath } from "../client/api-path.js";
|
|
|
14
13
|
* can only ever post-process JSON the app already parses for display, so it
|
|
15
14
|
* physically cannot break auth, SSE streams, SSR HTML, or binary downloads.
|
|
16
15
|
*
|
|
17
|
-
* The agent is
|
|
18
|
-
*
|
|
16
|
+
* The agent transport is intentionally skipped because it carries the real
|
|
17
|
+
* backend results; this interceptor is only a browser presentation transform.
|
|
19
18
|
*
|
|
20
19
|
* Scope intentionally narrow:
|
|
21
20
|
* - Only same-document `GET` requests are redacted. Mutation responses
|
|
@@ -23,19 +22,17 @@ import { agentNativePath } from "../client/api-path.js";
|
|
|
23
22
|
* typed isn't echoed back as fake data mid-demo.
|
|
24
23
|
* - Only `application/json` 2xx bodies. Streams (`text/event-stream`),
|
|
25
24
|
* HTML, and binary are skipped by content-type.
|
|
26
|
-
* - Framework infra endpoints (poll
|
|
27
|
-
* itself) are skipped — no PII and avoids self-recursion.
|
|
25
|
+
* - Framework infra endpoints (poll and events) are skipped.
|
|
28
26
|
* - Any error during interception falls back to the original response.
|
|
29
27
|
*/
|
|
28
|
+
import { getBrowserDemoModeEnabled } from "./browser-state.js";
|
|
30
29
|
import { redactDemoData } from "./redact.js";
|
|
31
|
-
const STATUS_PATH = agentNativePath("/_agent-native/demo/status");
|
|
32
30
|
const SKIP_SUBSTRINGS = [
|
|
33
|
-
"/_agent-native/demo/status",
|
|
34
31
|
"/_agent-native/poll",
|
|
35
32
|
"/_agent-native/events",
|
|
36
|
-
// Never touch agent transport. The agent
|
|
37
|
-
//
|
|
38
|
-
//
|
|
33
|
+
// Never touch agent transport. The agent receives real tool results; faking
|
|
34
|
+
// its own transcript adds no demo value and must stay clear of the
|
|
35
|
+
// tool_use/tool_result protocol. Covers
|
|
39
36
|
// "/_agent-native/agent" (stream) and "/_agent-native/agent-chat"
|
|
40
37
|
// (thread history) and any sub-paths.
|
|
41
38
|
"/_agent-native/agent",
|
|
@@ -65,15 +62,6 @@ export function shouldSkipDemoResponseRedaction(url) {
|
|
|
65
62
|
RAW_AGENT_REPLAY_EVENTS_RE.test(url));
|
|
66
63
|
}
|
|
67
64
|
let installed = false;
|
|
68
|
-
let demoEnabled = false;
|
|
69
|
-
let originalFetch = null;
|
|
70
|
-
let refreshPromise = null;
|
|
71
|
-
// Set once the first demo-status check completes. We DO NOT block requests on
|
|
72
|
-
// it — if status isn't known yet a response is simply passed through
|
|
73
|
-
// un-redacted (a brief first-paint window) rather than delaying transport.
|
|
74
|
-
// Injecting latency into early GETs previously risked the agent's streaming
|
|
75
|
-
// reconnect logic; transport safety wins over redacting the first paint.
|
|
76
|
-
let firstStatusDone = false;
|
|
77
65
|
/** Reject after `ms` so a misclassified streaming body can never hang. */
|
|
78
66
|
function withTimeout(p, ms) {
|
|
79
67
|
return Promise.race([
|
|
@@ -101,40 +89,10 @@ function methodOf(input, init) {
|
|
|
101
89
|
"GET";
|
|
102
90
|
return m.toUpperCase();
|
|
103
91
|
}
|
|
104
|
-
async function refreshDemoFlag() {
|
|
105
|
-
const f = originalFetch ?? fetch;
|
|
106
|
-
try {
|
|
107
|
-
const res = await f(STATUS_PATH, { credentials: "same-origin" });
|
|
108
|
-
if (!res.ok)
|
|
109
|
-
return;
|
|
110
|
-
const json = (await res.json());
|
|
111
|
-
demoEnabled = json?.enabled === true || json?.forced === true;
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
// Status endpoint unreachable — leave the last known value.
|
|
115
|
-
}
|
|
116
|
-
finally {
|
|
117
|
-
firstStatusDone = true;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
/** Refresh demo mode after the shared DB-sync stream reports a real change. */
|
|
121
|
-
export function refreshDemoModeFetchInterceptor() {
|
|
122
|
-
if (!installed)
|
|
123
|
-
return Promise.resolve();
|
|
124
|
-
if (!refreshPromise) {
|
|
125
|
-
refreshPromise = refreshDemoFlag().finally(() => {
|
|
126
|
-
refreshPromise = null;
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
return refreshPromise;
|
|
130
|
-
}
|
|
131
92
|
/**
|
|
132
|
-
* Install the demo-mode fetch interceptor
|
|
133
|
-
* changes are driven by the shared DB-sync transport instead of a separate
|
|
134
|
-
* fixed polling loop.
|
|
93
|
+
* Install the browser-local demo-mode fetch interceptor.
|
|
135
94
|
* Idempotent and browser-only — safe to call from any hook that runs in
|
|
136
|
-
* every template root (we call it from `useDbSync`).
|
|
137
|
-
* mode is actually on.
|
|
95
|
+
* every template root (we call it from `useDbSync`).
|
|
138
96
|
*/
|
|
139
97
|
export function ensureDemoModeFetchInterceptor() {
|
|
140
98
|
if (typeof window === "undefined")
|
|
@@ -142,15 +100,14 @@ export function ensureDemoModeFetchInterceptor() {
|
|
|
142
100
|
if (installed)
|
|
143
101
|
return;
|
|
144
102
|
installed = true;
|
|
145
|
-
|
|
146
|
-
const base = originalFetch;
|
|
103
|
+
const base = window.fetch.bind(window);
|
|
147
104
|
window.fetch = async function patchedFetch(input, init) {
|
|
148
105
|
const res = await base(input, init);
|
|
149
|
-
// Fast path: anything that isn't a demo
|
|
106
|
+
// Fast path: anything that isn't a browser-local-demo, plain GET returns the
|
|
150
107
|
// ORIGINAL response with zero body work and zero extra awaits — when
|
|
151
108
|
// demo mode is off this wrapper is byte-for-byte native fetch, so it
|
|
152
109
|
// cannot influence agent/run/stream transport.
|
|
153
|
-
if (!
|
|
110
|
+
if (!getBrowserDemoModeEnabled())
|
|
154
111
|
return res;
|
|
155
112
|
if (methodOf(input, init) !== "GET")
|
|
156
113
|
return res;
|
|
@@ -199,6 +156,5 @@ export function ensureDemoModeFetchInterceptor() {
|
|
|
199
156
|
return res;
|
|
200
157
|
}
|
|
201
158
|
};
|
|
202
|
-
void refreshDemoModeFetchInterceptor();
|
|
203
159
|
}
|
|
204
160
|
//# sourceMappingURL=fetch-interceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-interceptor.js","sourceRoot":"","sources":["../../src/demo/fetch-interceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../client/api-path.js\";\n/**\n * Client-side demo-mode redaction.\n *\n * Why client-side and not (only) at the server action boundary: templates\n * serve a lot of their UI data through their OWN custom Nitro `/api/*`\n * handlers (e.g. mail's `/api/emails`, `/api/threads/:id/messages`,\n * `/api/contacts`, `/api/apollo/person`), which never pass through the\n * framework action runtime. On this stack (Nitro v3 / h3 v2) there is no\n * single server hook that can safely rewrite every JSON body (the `response`\n * hook hands you an immutable Web `Response` and returns `void`). Patching the\n * browser's `fetch` is the one place that is BOTH universal (every template's\n * reads — actions and custom routes alike — go through it) AND low-risk: it\n * can only ever post-process JSON the app already parses for display, so it\n * physically cannot break auth, SSE streams, SSR HTML, or binary downloads.\n *\n * The agent is handled separately and in-process (its action tool results are\n * redacted in `production-agent.ts`), so it doesn't depend on this at all.\n *\n * Scope intentionally narrow:\n * - Only same-document `GET` requests are redacted. Mutation responses\n * (POST/PUT/PATCH/DELETE) pass through untouched so a draft you just\n * typed isn't echoed back as fake data mid-demo.\n * - Only `application/json` 2xx bodies. Streams (`text/event-stream`),\n * HTML, and binary are skipped by content-type.\n * - Framework infra endpoints (poll, events, the demo-status endpoint\n * itself) are skipped — no PII and avoids self-recursion.\n * - Any error during interception falls back to the original response.\n */\nimport { redactDemoData } from \"./redact.js\";\n\nconst STATUS_PATH = agentNativePath(\"/_agent-native/demo/status\");\nconst SKIP_SUBSTRINGS = [\n \"/_agent-native/demo/status\",\n \"/_agent-native/poll\",\n \"/_agent-native/events\",\n // Never touch agent transport. The agent already gets in-process\n // redaction of its tool results; faking its own transcript adds no demo\n // value and must stay clear of the tool_use/tool_result protocol. Covers\n // \"/_agent-native/agent\" (stream) and \"/_agent-native/agent-chat\"\n // (thread history) and any sub-paths.\n \"/_agent-native/agent\",\n // Run-manager state read by the reconnect/recovery loop. Faking numeric\n // run state here would make recovery think it's not progressing and\n // exhaust its retries (\"agent connection kept failing\").\n \"/_agent-native/runs\",\n];\n\n// Raw rrweb payloads are playback data, not ordinary app UI records. Skipping\n// these is not just a perf optimization that avoids walking/cloning huge\n// DOM/event trees on the main thread: demo number\n// redaction previously ran over this exact raw replay JSON and faked any\n// integer >= 1000 it found — Meta/ViewportResize widths, pointer x/y\n// coordinates, and numeric values inside `_cssText` and SVG attributes. That\n// was the root cause of the 2026-07 \"ultra-wide replay\" bugs (stages\n// rendered thousands of pixels wide, frozen/teleporting cursors, giant\n// icons) even though every stored recording was always geometrically sane —\n// the corruption happened at *view* time, not at capture/storage time. Small\n// list/summary/manifest responses stay eligible so rendered visitor identities\n// are still anonymized. NEVER remove the raw payload skips or broaden them to\n// metadata endpoints that the UI renders directly.\nconst RAW_REPLAY_PAYLOAD_RE =\n /\\/api\\/session-replay\\/recordings\\/[^/?#]+\\/(?:chunks(?:\\/[^/?#]+)?|events)(?:[/?#]|$)/;\nconst RAW_AGENT_REPLAY_EVENTS_RE =\n /\\/api\\/session-replay\\/agent-events\\.json(?:[?#]|$)/;\n\nexport function shouldSkipDemoResponseRedaction(url: string): boolean {\n return (\n SKIP_SUBSTRINGS.some((substring) => url.includes(substring)) ||\n RAW_REPLAY_PAYLOAD_RE.test(url) ||\n RAW_AGENT_REPLAY_EVENTS_RE.test(url)\n );\n}\n\nlet installed = false;\nlet demoEnabled = false;\nlet originalFetch: typeof fetch | null = null;\nlet refreshPromise: Promise<void> | null = null;\n\n// Set once the first demo-status check completes. We DO NOT block requests on\n// it — if status isn't known yet a response is simply passed through\n// un-redacted (a brief first-paint window) rather than delaying transport.\n// Injecting latency into early GETs previously risked the agent's streaming\n// reconnect logic; transport safety wins over redacting the first paint.\nlet firstStatusDone = false;\n\n/** Reject after `ms` so a misclassified streaming body can never hang. */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n return Promise.race([\n p,\n new Promise<T>((_, reject) =>\n setTimeout(() => reject(new Error(\"redact-timeout\")), ms),\n ),\n ]);\n}\n\nfunction urlOf(input: RequestInfo | URL): string {\n try {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return (input as Request).url ?? \"\";\n } catch {\n return \"\";\n }\n}\n\nfunction methodOf(input: RequestInfo | URL, init?: RequestInit): string {\n const m =\n init?.method ??\n (typeof input !== \"string\" && !(input instanceof URL)\n ? (input as Request).method\n : undefined) ??\n \"GET\";\n return m.toUpperCase();\n}\n\nasync function refreshDemoFlag(): Promise<void> {\n const f = originalFetch ?? fetch;\n try {\n const res = await f(STATUS_PATH, { credentials: \"same-origin\" });\n if (!res.ok) return;\n const json = (await res.json()) as {\n enabled?: boolean;\n forced?: boolean;\n } | null;\n demoEnabled = json?.enabled === true || json?.forced === true;\n } catch {\n // Status endpoint unreachable — leave the last known value.\n } finally {\n firstStatusDone = true;\n }\n}\n\n/** Refresh demo mode after the shared DB-sync stream reports a real change. */\nexport function refreshDemoModeFetchInterceptor(): Promise<void> {\n if (!installed) return Promise.resolve();\n if (!refreshPromise) {\n refreshPromise = refreshDemoFlag().finally(() => {\n refreshPromise = null;\n });\n }\n return refreshPromise;\n}\n\n/**\n * Install the demo-mode fetch interceptor and read demo status once. Later\n * changes are driven by the shared DB-sync transport instead of a separate\n * fixed polling loop.\n * Idempotent and browser-only — safe to call from any hook that runs in\n * every template root (we call it from `useDbSync`). A no-op until demo\n * mode is actually on.\n */\nexport function ensureDemoModeFetchInterceptor(): void {\n if (typeof window === \"undefined\") return;\n if (installed) return;\n installed = true;\n\n originalFetch = window.fetch.bind(window);\n const base = originalFetch;\n\n window.fetch = async function patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const res = await base(input, init);\n\n // Fast path: anything that isn't a demo-enabled, plain GET returns the\n // ORIGINAL response with zero body work and zero extra awaits — when\n // demo mode is off this wrapper is byte-for-byte native fetch, so it\n // cannot influence agent/run/stream transport.\n if (!demoEnabled || !firstStatusDone) return res;\n if (methodOf(input, init) !== \"GET\") return res;\n if (!res.ok) return res;\n\n try {\n const url = urlOf(input);\n if (shouldSkipDemoResponseRedaction(url)) return res;\n\n // Only buffered, finite JSON. SSE / streaming / chunked-forever bodies\n // never reach `redactDemoData`: streaming content-types are excluded,\n // and the JSON read is hard-timeout-bounded so a misclassified stream\n // degrades to \"return the original response\" instead of hanging the\n // request (which is what tripped the reconnect/recovery loop).\n const contentType = res.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"application/json\")) return res;\n if (\n contentType.includes(\"event-stream\") ||\n contentType.includes(\"ndjson\") ||\n contentType.includes(\"stream\")\n ) {\n return res;\n }\n if (res.bodyUsed) return res;\n\n const data = await withTimeout(res.clone().json(), 3_000);\n // Frontend reads only need identity privacy. Dashboard charts apply\n // their purpose-built demo trend transform at render time, so mutating\n // every numeric field in every JSON response is unnecessary work.\n const redacted = redactDemoData(data, {\n redactNumbers: false,\n redactProtectedEmails: true,\n });\n\n const headers = new Headers(res.headers);\n // Body is re-serialized — these would be wrong now.\n headers.delete(\"content-length\");\n headers.delete(\"content-encoding\");\n\n return new Response(JSON.stringify(redacted), {\n status: res.status,\n statusText: res.statusText,\n headers,\n });\n } catch {\n // Never let redaction break a request — fall back to the real\n // response (its body stream is untouched; we only ever read a clone).\n return res;\n }\n };\n\n void refreshDemoModeFetchInterceptor();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fetch-interceptor.js","sourceRoot":"","sources":["../../src/demo/fetch-interceptor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side demo-mode redaction.\n *\n * Why client-side and not (only) at the server action boundary: templates\n * serve a lot of their UI data through their OWN custom Nitro `/api/*`\n * handlers (e.g. mail's `/api/emails`, `/api/threads/:id/messages`,\n * `/api/contacts`, `/api/apollo/person`), which never pass through the\n * framework action runtime. On this stack (Nitro v3 / h3 v2) there is no\n * single server hook that can safely rewrite every JSON body (the `response`\n * hook hands you an immutable Web `Response` and returns `void`). Patching the\n * browser's `fetch` is the one place that is BOTH universal (every template's\n * reads — actions and custom routes alike — go through it) AND low-risk: it\n * can only ever post-process JSON the app already parses for display, so it\n * physically cannot break auth, SSE streams, SSR HTML, or binary downloads.\n *\n * The agent transport is intentionally skipped because it carries the real\n * backend results; this interceptor is only a browser presentation transform.\n *\n * Scope intentionally narrow:\n * - Only same-document `GET` requests are redacted. Mutation responses\n * (POST/PUT/PATCH/DELETE) pass through untouched so a draft you just\n * typed isn't echoed back as fake data mid-demo.\n * - Only `application/json` 2xx bodies. Streams (`text/event-stream`),\n * HTML, and binary are skipped by content-type.\n * - Framework infra endpoints (poll and events) are skipped.\n * - Any error during interception falls back to the original response.\n */\nimport { getBrowserDemoModeEnabled } from \"./browser-state.js\";\nimport { redactDemoData } from \"./redact.js\";\n\nconst SKIP_SUBSTRINGS = [\n \"/_agent-native/poll\",\n \"/_agent-native/events\",\n // Never touch agent transport. The agent receives real tool results; faking\n // its own transcript adds no demo value and must stay clear of the\n // tool_use/tool_result protocol. Covers\n // \"/_agent-native/agent\" (stream) and \"/_agent-native/agent-chat\"\n // (thread history) and any sub-paths.\n \"/_agent-native/agent\",\n // Run-manager state read by the reconnect/recovery loop. Faking numeric\n // run state here would make recovery think it's not progressing and\n // exhaust its retries (\"agent connection kept failing\").\n \"/_agent-native/runs\",\n];\n\n// Raw rrweb payloads are playback data, not ordinary app UI records. Skipping\n// these is not just a perf optimization that avoids walking/cloning huge\n// DOM/event trees on the main thread: demo number\n// redaction previously ran over this exact raw replay JSON and faked any\n// integer >= 1000 it found — Meta/ViewportResize widths, pointer x/y\n// coordinates, and numeric values inside `_cssText` and SVG attributes. That\n// was the root cause of the 2026-07 \"ultra-wide replay\" bugs (stages\n// rendered thousands of pixels wide, frozen/teleporting cursors, giant\n// icons) even though every stored recording was always geometrically sane —\n// the corruption happened at *view* time, not at capture/storage time. Small\n// list/summary/manifest responses stay eligible so rendered visitor identities\n// are still anonymized. NEVER remove the raw payload skips or broaden them to\n// metadata endpoints that the UI renders directly.\nconst RAW_REPLAY_PAYLOAD_RE =\n /\\/api\\/session-replay\\/recordings\\/[^/?#]+\\/(?:chunks(?:\\/[^/?#]+)?|events)(?:[/?#]|$)/;\nconst RAW_AGENT_REPLAY_EVENTS_RE =\n /\\/api\\/session-replay\\/agent-events\\.json(?:[?#]|$)/;\n\nexport function shouldSkipDemoResponseRedaction(url: string): boolean {\n return (\n SKIP_SUBSTRINGS.some((substring) => url.includes(substring)) ||\n RAW_REPLAY_PAYLOAD_RE.test(url) ||\n RAW_AGENT_REPLAY_EVENTS_RE.test(url)\n );\n}\n\nlet installed = false;\n\n/** Reject after `ms` so a misclassified streaming body can never hang. */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n return Promise.race([\n p,\n new Promise<T>((_, reject) =>\n setTimeout(() => reject(new Error(\"redact-timeout\")), ms),\n ),\n ]);\n}\n\nfunction urlOf(input: RequestInfo | URL): string {\n try {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return (input as Request).url ?? \"\";\n } catch {\n return \"\";\n }\n}\n\nfunction methodOf(input: RequestInfo | URL, init?: RequestInit): string {\n const m =\n init?.method ??\n (typeof input !== \"string\" && !(input instanceof URL)\n ? (input as Request).method\n : undefined) ??\n \"GET\";\n return m.toUpperCase();\n}\n\n/**\n * Install the browser-local demo-mode fetch interceptor.\n * Idempotent and browser-only — safe to call from any hook that runs in\n * every template root (we call it from `useDbSync`).\n */\nexport function ensureDemoModeFetchInterceptor(): void {\n if (typeof window === \"undefined\") return;\n if (installed) return;\n installed = true;\n\n const base = window.fetch.bind(window);\n\n window.fetch = async function patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const res = await base(input, init);\n\n // Fast path: anything that isn't a browser-local-demo, plain GET returns the\n // ORIGINAL response with zero body work and zero extra awaits — when\n // demo mode is off this wrapper is byte-for-byte native fetch, so it\n // cannot influence agent/run/stream transport.\n if (!getBrowserDemoModeEnabled()) return res;\n if (methodOf(input, init) !== \"GET\") return res;\n if (!res.ok) return res;\n\n try {\n const url = urlOf(input);\n if (shouldSkipDemoResponseRedaction(url)) return res;\n\n // Only buffered, finite JSON. SSE / streaming / chunked-forever bodies\n // never reach `redactDemoData`: streaming content-types are excluded,\n // and the JSON read is hard-timeout-bounded so a misclassified stream\n // degrades to \"return the original response\" instead of hanging the\n // request (which is what tripped the reconnect/recovery loop).\n const contentType = res.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"application/json\")) return res;\n if (\n contentType.includes(\"event-stream\") ||\n contentType.includes(\"ndjson\") ||\n contentType.includes(\"stream\")\n ) {\n return res;\n }\n if (res.bodyUsed) return res;\n\n const data = await withTimeout(res.clone().json(), 3_000);\n // Frontend reads only need identity privacy. Dashboard charts apply\n // their purpose-built demo trend transform at render time, so mutating\n // every numeric field in every JSON response is unnecessary work.\n const redacted = redactDemoData(data, {\n redactNumbers: false,\n redactProtectedEmails: true,\n });\n\n const headers = new Headers(res.headers);\n // Body is re-serialized — these would be wrong now.\n headers.delete(\"content-length\");\n headers.delete(\"content-encoding\");\n\n return new Response(JSON.stringify(redacted), {\n status: res.status,\n statusText: res.statusText,\n headers,\n });\n } catch {\n // Never let redaction break a request — fall back to the real\n // response (its body stream is untouched; we only ever read a clone).\n return res;\n }\n };\n}\n"]}
|
|
@@ -263,7 +263,7 @@ function writeCloudflareRoutingManifest(distDir, apps) {
|
|
|
263
263
|
.map((a) => `import ${moduleIdent(a)} from "./${a}/_worker.js";`)
|
|
264
264
|
.join("\n");
|
|
265
265
|
const dispatch = apps
|
|
266
|
-
.map((a) => ` if (pathname === "/${a}" || pathname.startsWith("/${a}/")) return ${moduleIdent(a)}.fetch(requestForMountedApp(request, "/${a}"), env, ctx);`)
|
|
266
|
+
.map((a) => ` if (pathname === "/${a}" || pathname === "/${a}.data" || pathname.startsWith("/${a}/")) return ${moduleIdent(a)}.fetch(requestForMountedApp(request, "/${a}"), env, ctx);`)
|
|
267
267
|
.join("\n");
|
|
268
268
|
const dispatchRootFrameworkRoutes = apps.includes("dispatch")
|
|
269
269
|
? ` if (pathname === "/_agent-native" || pathname.startsWith("/_agent-native/") || pathname === "/.well-known" || pathname.startsWith("/.well-known/")) return ${moduleIdent("dispatch")}.fetch(request, env, ctx);
|
|
@@ -642,7 +642,7 @@ function patchNetlifyFunctionEntry(functionDir, app, workspaceApps, staticDir) {
|
|
|
642
642
|
const workspaceAppRouteAccess = workspaceAppRouteAccessForApp(workspaceApps, app);
|
|
643
643
|
const pathConfig = app === "dispatch"
|
|
644
644
|
? ["/_agent-native/*", "/.well-known/*", `${basePath}/*`]
|
|
645
|
-
: [basePath, `${basePath}/*`];
|
|
645
|
+
: [basePath, `${basePath}.data`, `${basePath}/*`];
|
|
646
646
|
const normalizeBasePathHelper = app === "dispatch"
|
|
647
647
|
? ""
|
|
648
648
|
: `
|