@agent-native/core 0.98.2 → 0.98.4
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 +23 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +2 -2
- package/corpus/core/src/client/analytics.ts +12 -0
- package/corpus/core/src/client/index.ts +2 -0
- package/corpus/core/src/client/session-replay.ts +612 -26
- package/corpus/core/src/client/settings/DemoModeSection.tsx +6 -6
- package/corpus/core/src/client/settings/SettingsPanel.tsx +31 -211
- package/corpus/core/src/client/settings/agent-settings-search.ts +219 -0
- package/corpus/core/src/client/settings/index.ts +4 -0
- package/corpus/core/src/demo/actions/toggle-demo-mode.ts +2 -2
- package/corpus/core/src/demo/config.ts +6 -6
- package/corpus/core/src/demo/fetch-interceptor.ts +25 -2
- package/corpus/core/src/demo/redact.ts +57 -66
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +65 -6
- package/corpus/templates/analytics/AGENTS.md +6 -6
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +429 -244
- package/corpus/templates/analytics/app/components/layout/command-palette-search.ts +93 -0
- package/corpus/templates/analytics/app/global.css +4 -62
- package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +2 -1
- package/corpus/templates/analytics/app/lib/demo-chart-trend.ts +46 -33
- package/corpus/templates/analytics/app/pages/Settings.tsx +8 -45
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +413 -105
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +32 -16
- package/corpus/templates/analytics/app/pages/settings/settings-search.ts +126 -0
- package/corpus/templates/analytics/changelog/2026-07-12-command-menu-loading-placeholders-now-stay-below-available-r.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-command-menu-search-now-prioritizes-the-best-match.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-demo-charts-preserve-source-movement.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-fixed-session-replays-that-appeared-ultra-wide-hid-recorded-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-replay-storage-settings-stay-readable-at-narrow-widths.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-session-replays-load-faster-in-demo-mode-while-visitor-email.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-session-replays-preserve-recorded-styling-and-keep-malformed.md +6 -0
- package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +11 -2
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +14 -2
- package/corpus/templates/calendar/changelog/2026-07-12-calendar-responses-can-now-be-saved-with-command-enter-or-ct.md +6 -0
- package/dist/agent/production-agent.js +2 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +11 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +12 -3
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +488 -23
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts +3 -3
- package/dist/client/settings/DemoModeSection.js +4 -4
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +41 -191
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/agent-settings-search.d.ts +16 -0
- package/dist/client/settings/agent-settings-search.d.ts.map +1 -0
- package/dist/client/settings/agent-settings-search.js +178 -0
- package/dist/client/settings/agent-settings-search.js.map +1 -0
- package/dist/client/settings/index.d.ts +1 -0
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js +1 -0
- package/dist/client/settings/index.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/demo/actions/toggle-demo-mode.js +2 -2
- 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 +1 -0
- package/dist/demo/fetch-interceptor.d.ts.map +1 -1
- package/dist/demo/fetch-interceptor.js +19 -2
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/demo/redact.d.ts +14 -5
- package/dist/demo/redact.d.ts.map +1 -1
- package/dist/demo/redact.js +30 -56
- package/dist/demo/redact.js.map +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SettingsSearchEntry } from "./SettingsTabsPage.js";
|
|
2
|
+
export type SettingsSectionId = "account" | "llm" | "app-models" | "limits" | "voice" | "demo-mode" | "automations" | "secrets" | "hosting" | "database" | "uploads" | "auth" | "email" | "browser" | "background" | "integrations" | "usage" | "a2a";
|
|
3
|
+
export declare const SETTINGS_SECTION_IDS: Set<SettingsSectionId>;
|
|
4
|
+
export declare const ALL_SETTINGS_SECTIONS: readonly SettingsSectionId[];
|
|
5
|
+
export declare const AGENT_SETTINGS_SECTIONS: readonly SettingsSectionId[];
|
|
6
|
+
export declare const CONNECTION_SETTINGS_SECTIONS: readonly SettingsSectionId[];
|
|
7
|
+
export declare const WORKSPACE_SETTINGS_SECTIONS: readonly SettingsSectionId[];
|
|
8
|
+
export declare function buildSectionSearchEntries(sections: readonly SettingsSectionId[]): SettingsSearchEntry[];
|
|
9
|
+
export interface AgentSettingsSearchTab {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
keywords: string;
|
|
13
|
+
searchEntries?: SettingsSearchEntry[];
|
|
14
|
+
}
|
|
15
|
+
export declare function getAgentSettingsSearchTabs(): AgentSettingsSearchTab[];
|
|
16
|
+
//# sourceMappingURL=agent-settings-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-settings-search.d.ts","sourceRoot":"","sources":["../../../src/client/settings/agent-settings-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,KAAK,GACL,YAAY,GACZ,QAAQ,GACR,OAAO,GACP,WAAW,GACX,aAAa,GACb,SAAS,GACT,SAAS,GACT,UAAU,GACV,SAAS,GACT,MAAM,GACN,OAAO,GACP,SAAS,GACT,YAAY,GACZ,cAAc,GACd,OAAO,GACP,KAAK,CAAC;AAEV,eAAO,MAAM,oBAAoB,wBAmB/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,EAAE,SAAS,iBAAiB,EAmB7D,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,SAAS,iBAAiB,EAQ/D,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,SAAS,iBAAiB,EAMpE,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,SAAS,iBAAiB,EAOnE,CAAC;AAiFF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,GACrC,mBAAmB,EAAE,CAWvB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACvC;AAED,wBAAgB,0BAA0B,IAAI,sBAAsB,EAAE,CA0BrE"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export const SETTINGS_SECTION_IDS = new Set([
|
|
2
|
+
"account",
|
|
3
|
+
"llm",
|
|
4
|
+
"app-models",
|
|
5
|
+
"limits",
|
|
6
|
+
"voice",
|
|
7
|
+
"demo-mode",
|
|
8
|
+
"automations",
|
|
9
|
+
"secrets",
|
|
10
|
+
"hosting",
|
|
11
|
+
"database",
|
|
12
|
+
"uploads",
|
|
13
|
+
"auth",
|
|
14
|
+
"email",
|
|
15
|
+
"browser",
|
|
16
|
+
"background",
|
|
17
|
+
"integrations",
|
|
18
|
+
"usage",
|
|
19
|
+
"a2a",
|
|
20
|
+
]);
|
|
21
|
+
export const ALL_SETTINGS_SECTIONS = [
|
|
22
|
+
"account",
|
|
23
|
+
"llm",
|
|
24
|
+
"app-models",
|
|
25
|
+
"limits",
|
|
26
|
+
"voice",
|
|
27
|
+
"demo-mode",
|
|
28
|
+
"automations",
|
|
29
|
+
"secrets",
|
|
30
|
+
"hosting",
|
|
31
|
+
"database",
|
|
32
|
+
"uploads",
|
|
33
|
+
"auth",
|
|
34
|
+
"email",
|
|
35
|
+
"browser",
|
|
36
|
+
"background",
|
|
37
|
+
"integrations",
|
|
38
|
+
"usage",
|
|
39
|
+
"a2a",
|
|
40
|
+
];
|
|
41
|
+
export const AGENT_SETTINGS_SECTIONS = [
|
|
42
|
+
"llm",
|
|
43
|
+
"app-models",
|
|
44
|
+
"limits",
|
|
45
|
+
"voice",
|
|
46
|
+
"automations",
|
|
47
|
+
"background",
|
|
48
|
+
"a2a",
|
|
49
|
+
];
|
|
50
|
+
export const CONNECTION_SETTINGS_SECTIONS = [
|
|
51
|
+
"secrets",
|
|
52
|
+
"integrations",
|
|
53
|
+
"email",
|
|
54
|
+
"browser",
|
|
55
|
+
"usage",
|
|
56
|
+
];
|
|
57
|
+
export const WORKSPACE_SETTINGS_SECTIONS = [
|
|
58
|
+
"account",
|
|
59
|
+
"demo-mode",
|
|
60
|
+
"hosting",
|
|
61
|
+
"database",
|
|
62
|
+
"uploads",
|
|
63
|
+
"auth",
|
|
64
|
+
];
|
|
65
|
+
const SETTINGS_SECTION_SEARCH_META = {
|
|
66
|
+
account: {
|
|
67
|
+
label: "Account",
|
|
68
|
+
keywords: "profile photo avatar identity signed in email name",
|
|
69
|
+
},
|
|
70
|
+
llm: {
|
|
71
|
+
label: "LLM",
|
|
72
|
+
keywords: "model claude gpt openai anthropic gemini api key provider ai engine llm",
|
|
73
|
+
},
|
|
74
|
+
"app-models": {
|
|
75
|
+
label: "App Default Model",
|
|
76
|
+
keywords: "default model provider app template composer",
|
|
77
|
+
},
|
|
78
|
+
limits: {
|
|
79
|
+
label: "Agent Limits",
|
|
80
|
+
keywords: "max iterations budget loop timeout runtime",
|
|
81
|
+
},
|
|
82
|
+
voice: {
|
|
83
|
+
label: "Voice Transcription",
|
|
84
|
+
keywords: "microphone dictation speech to text whisper",
|
|
85
|
+
},
|
|
86
|
+
"demo-mode": {
|
|
87
|
+
label: "Demo mode",
|
|
88
|
+
keywords: "fake data anonymize redact screenshot privacy mask",
|
|
89
|
+
},
|
|
90
|
+
automations: {
|
|
91
|
+
label: "Automations",
|
|
92
|
+
keywords: "triggers scheduled events cron jobs",
|
|
93
|
+
},
|
|
94
|
+
secrets: {
|
|
95
|
+
label: "API Keys & Connections",
|
|
96
|
+
keywords: "secrets credentials tokens api keys environment variables",
|
|
97
|
+
},
|
|
98
|
+
hosting: {
|
|
99
|
+
label: "Hosting",
|
|
100
|
+
keywords: "deploy netlify vercel cloudflare builder nitro",
|
|
101
|
+
},
|
|
102
|
+
database: {
|
|
103
|
+
label: "Database",
|
|
104
|
+
keywords: "postgres sqlite neon supabase turso storage sql pglite",
|
|
105
|
+
},
|
|
106
|
+
uploads: {
|
|
107
|
+
label: "File uploads",
|
|
108
|
+
keywords: "files storage s3 avatars attachments bucket blob",
|
|
109
|
+
},
|
|
110
|
+
auth: {
|
|
111
|
+
label: "Authentication",
|
|
112
|
+
keywords: "login signup oauth google github better auth access sso",
|
|
113
|
+
},
|
|
114
|
+
email: {
|
|
115
|
+
label: "Email",
|
|
116
|
+
keywords: "resend sendgrid smtp transactional notifications reports",
|
|
117
|
+
},
|
|
118
|
+
browser: {
|
|
119
|
+
label: "Browser Automation",
|
|
120
|
+
keywords: "web scraping playwright chrome headless",
|
|
121
|
+
},
|
|
122
|
+
background: {
|
|
123
|
+
label: "Background Agent",
|
|
124
|
+
keywords: "code changes branches builder production async",
|
|
125
|
+
},
|
|
126
|
+
integrations: {
|
|
127
|
+
label: "Integrations",
|
|
128
|
+
keywords: "slack telegram whatsapp discord messaging connect",
|
|
129
|
+
},
|
|
130
|
+
usage: {
|
|
131
|
+
label: "Usage",
|
|
132
|
+
keywords: "tokens cost spend billing consumption",
|
|
133
|
+
},
|
|
134
|
+
a2a: {
|
|
135
|
+
label: "Connected Agents (A2A)",
|
|
136
|
+
keywords: "remote agents protocol a2a connected",
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
export function buildSectionSearchEntries(sections) {
|
|
140
|
+
return sections.map((section) => {
|
|
141
|
+
const meta = SETTINGS_SECTION_SEARCH_META[section];
|
|
142
|
+
return {
|
|
143
|
+
id: `section:${section}`,
|
|
144
|
+
label: meta.label,
|
|
145
|
+
keywords: meta.keywords,
|
|
146
|
+
description: meta.description,
|
|
147
|
+
hash: section,
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export function getAgentSettingsSearchTabs() {
|
|
152
|
+
return [
|
|
153
|
+
{
|
|
154
|
+
id: "agent",
|
|
155
|
+
label: "Agent",
|
|
156
|
+
keywords: "agent model llm limits voice automations",
|
|
157
|
+
searchEntries: buildSectionSearchEntries(AGENT_SETTINGS_SECTIONS),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: "connections",
|
|
161
|
+
label: "Connections",
|
|
162
|
+
keywords: "connections secrets integrations email browser usage",
|
|
163
|
+
searchEntries: buildSectionSearchEntries(CONNECTION_SETTINGS_SECTIONS),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "organization",
|
|
167
|
+
label: "Organization",
|
|
168
|
+
keywords: "organization org team members invites collaborators",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: "workspace",
|
|
172
|
+
label: "Workspace",
|
|
173
|
+
keywords: "workspace account hosting database uploads auth",
|
|
174
|
+
searchEntries: buildSectionSearchEntries(WORKSPACE_SETTINGS_SECTIONS),
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=agent-settings-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-settings-search.js","sourceRoot":"","sources":["../../../src/client/settings/agent-settings-search.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SettingsSearchEntry } from \"./SettingsTabsPage.js\";\n\nexport type SettingsSectionId =\n | \"account\"\n | \"llm\"\n | \"app-models\"\n | \"limits\"\n | \"voice\"\n | \"demo-mode\"\n | \"automations\"\n | \"secrets\"\n | \"hosting\"\n | \"database\"\n | \"uploads\"\n | \"auth\"\n | \"email\"\n | \"browser\"\n | \"background\"\n | \"integrations\"\n | \"usage\"\n | \"a2a\";\n\nexport const SETTINGS_SECTION_IDS = new Set<SettingsSectionId>([\n \"account\",\n \"llm\",\n \"app-models\",\n \"limits\",\n \"voice\",\n \"demo-mode\",\n \"automations\",\n \"secrets\",\n \"hosting\",\n \"database\",\n \"uploads\",\n \"auth\",\n \"email\",\n \"browser\",\n \"background\",\n \"integrations\",\n \"usage\",\n \"a2a\",\n]);\n\nexport const ALL_SETTINGS_SECTIONS: readonly SettingsSectionId[] = [\n \"account\",\n \"llm\",\n \"app-models\",\n \"limits\",\n \"voice\",\n \"demo-mode\",\n \"automations\",\n \"secrets\",\n \"hosting\",\n \"database\",\n \"uploads\",\n \"auth\",\n \"email\",\n \"browser\",\n \"background\",\n \"integrations\",\n \"usage\",\n \"a2a\",\n];\n\nexport const AGENT_SETTINGS_SECTIONS: readonly SettingsSectionId[] = [\n \"llm\",\n \"app-models\",\n \"limits\",\n \"voice\",\n \"automations\",\n \"background\",\n \"a2a\",\n];\n\nexport const CONNECTION_SETTINGS_SECTIONS: readonly SettingsSectionId[] = [\n \"secrets\",\n \"integrations\",\n \"email\",\n \"browser\",\n \"usage\",\n];\n\nexport const WORKSPACE_SETTINGS_SECTIONS: readonly SettingsSectionId[] = [\n \"account\",\n \"demo-mode\",\n \"hosting\",\n \"database\",\n \"uploads\",\n \"auth\",\n];\n\nconst SETTINGS_SECTION_SEARCH_META: Record<\n SettingsSectionId,\n { label: string; keywords: string; description?: string }\n> = {\n account: {\n label: \"Account\",\n keywords: \"profile photo avatar identity signed in email name\",\n },\n llm: {\n label: \"LLM\",\n keywords:\n \"model claude gpt openai anthropic gemini api key provider ai engine llm\",\n },\n \"app-models\": {\n label: \"App Default Model\",\n keywords: \"default model provider app template composer\",\n },\n limits: {\n label: \"Agent Limits\",\n keywords: \"max iterations budget loop timeout runtime\",\n },\n voice: {\n label: \"Voice Transcription\",\n keywords: \"microphone dictation speech to text whisper\",\n },\n \"demo-mode\": {\n label: \"Demo mode\",\n keywords: \"fake data anonymize redact screenshot privacy mask\",\n },\n automations: {\n label: \"Automations\",\n keywords: \"triggers scheduled events cron jobs\",\n },\n secrets: {\n label: \"API Keys & Connections\",\n keywords: \"secrets credentials tokens api keys environment variables\",\n },\n hosting: {\n label: \"Hosting\",\n keywords: \"deploy netlify vercel cloudflare builder nitro\",\n },\n database: {\n label: \"Database\",\n keywords: \"postgres sqlite neon supabase turso storage sql pglite\",\n },\n uploads: {\n label: \"File uploads\",\n keywords: \"files storage s3 avatars attachments bucket blob\",\n },\n auth: {\n label: \"Authentication\",\n keywords: \"login signup oauth google github better auth access sso\",\n },\n email: {\n label: \"Email\",\n keywords: \"resend sendgrid smtp transactional notifications reports\",\n },\n browser: {\n label: \"Browser Automation\",\n keywords: \"web scraping playwright chrome headless\",\n },\n background: {\n label: \"Background Agent\",\n keywords: \"code changes branches builder production async\",\n },\n integrations: {\n label: \"Integrations\",\n keywords: \"slack telegram whatsapp discord messaging connect\",\n },\n usage: {\n label: \"Usage\",\n keywords: \"tokens cost spend billing consumption\",\n },\n a2a: {\n label: \"Connected Agents (A2A)\",\n keywords: \"remote agents protocol a2a connected\",\n },\n};\n\nexport function buildSectionSearchEntries(\n sections: readonly SettingsSectionId[],\n): SettingsSearchEntry[] {\n return sections.map((section) => {\n const meta = SETTINGS_SECTION_SEARCH_META[section];\n return {\n id: `section:${section}`,\n label: meta.label,\n keywords: meta.keywords,\n description: meta.description,\n hash: section,\n };\n });\n}\n\nexport interface AgentSettingsSearchTab {\n id: string;\n label: string;\n keywords: string;\n searchEntries?: SettingsSearchEntry[];\n}\n\nexport function getAgentSettingsSearchTabs(): AgentSettingsSearchTab[] {\n return [\n {\n id: \"agent\",\n label: \"Agent\",\n keywords: \"agent model llm limits voice automations\",\n searchEntries: buildSectionSearchEntries(AGENT_SETTINGS_SECTIONS),\n },\n {\n id: \"connections\",\n label: \"Connections\",\n keywords: \"connections secrets integrations email browser usage\",\n searchEntries: buildSectionSearchEntries(CONNECTION_SETTINGS_SECTIONS),\n },\n {\n id: \"organization\",\n label: \"Organization\",\n keywords: \"organization org team members invites collaborators\",\n },\n {\n id: \"workspace\",\n label: \"Workspace\",\n keywords: \"workspace account hosting database uploads auth\",\n searchEntries: buildSectionSearchEntries(WORKSPACE_SETTINGS_SECTIONS),\n },\n ];\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { SettingsPanel, useAgentSettingsTabs, type SettingsPanelProps, } from "./SettingsPanel.js";
|
|
2
|
+
export { getAgentSettingsSearchTabs, type AgentSettingsSearchTab, } from "./agent-settings-search.js";
|
|
2
3
|
export { SettingsTabsPage, type SettingsSearchEntry, type SettingsTabItem, type SettingsTabsPageProps, } from "./SettingsTabsPage.js";
|
|
3
4
|
export { openBuilderConnectPopup, useBuilderConnectFlow, useBuilderStatus, withBuilderConnectTrackingParams, type BuilderConnectFlow, type BuilderConnectFlowOptions, type BuilderConnectStartOptions, type BuilderStatus, type OpenBuilderConnectPopupOptions, } from "./useBuilderStatus.js";
|
|
4
5
|
export { SecretsSection, type SecretsSectionProps } from "./SecretsSection.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,gCAAgC,EAChC,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EAClB,KAAK,8BAA8B,GACpC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,0BAA0B,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,gCAAgC,EAChC,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EAClB,KAAK,8BAA8B,GACpC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { SettingsPanel, useAgentSettingsTabs, } from "./SettingsPanel.js";
|
|
2
|
+
export { getAgentSettingsSearchTabs, } from "./agent-settings-search.js";
|
|
2
3
|
export { SettingsTabsPage, } from "./SettingsTabsPage.js";
|
|
3
4
|
export { openBuilderConnectPopup, useBuilderConnectFlow, useBuilderStatus, withBuilderConnectTrackingParams, } from "./useBuilderStatus.js";
|
|
4
5
|
export { SecretsSection } from "./SecretsSection.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,GAIjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,gCAAgC,GAMjC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAA4B,MAAM,qBAAqB,CAAC","sourcesContent":["export {\n SettingsPanel,\n useAgentSettingsTabs,\n type SettingsPanelProps,\n} from \"./SettingsPanel.js\";\nexport {\n SettingsTabsPage,\n type SettingsSearchEntry,\n type SettingsTabItem,\n type SettingsTabsPageProps,\n} from \"./SettingsTabsPage.js\";\nexport {\n openBuilderConnectPopup,\n useBuilderConnectFlow,\n useBuilderStatus,\n withBuilderConnectTrackingParams,\n type BuilderConnectFlow,\n type BuilderConnectFlowOptions,\n type BuilderConnectStartOptions,\n type BuilderStatus,\n type OpenBuilderConnectPopupOptions,\n} from \"./useBuilderStatus.js\";\nexport { SecretsSection, type SecretsSectionProps } from \"./SecretsSection.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,0BAA0B,GAE3B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,gBAAgB,GAIjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,gCAAgC,GAMjC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAA4B,MAAM,qBAAqB,CAAC","sourcesContent":["export {\n SettingsPanel,\n useAgentSettingsTabs,\n type SettingsPanelProps,\n} from \"./SettingsPanel.js\";\nexport {\n getAgentSettingsSearchTabs,\n type AgentSettingsSearchTab,\n} from \"./agent-settings-search.js\";\nexport {\n SettingsTabsPage,\n type SettingsSearchEntry,\n type SettingsTabItem,\n type SettingsTabsPageProps,\n} from \"./SettingsTabsPage.js\";\nexport {\n openBuilderConnectPopup,\n useBuilderConnectFlow,\n useBuilderStatus,\n withBuilderConnectTrackingParams,\n type BuilderConnectFlow,\n type BuilderConnectFlowOptions,\n type BuilderConnectStartOptions,\n type BuilderStatus,\n type OpenBuilderConnectPopupOptions,\n} from \"./useBuilderStatus.js\";\nexport { SecretsSection, type SecretsSectionProps } from \"./SecretsSection.js\";\n"]}
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -42,8 +42,8 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
42
42
|
*/
|
|
43
43
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
44
44
|
ok?: undefined;
|
|
45
|
-
text?: undefined;
|
|
46
45
|
error: string;
|
|
46
|
+
text?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
error?: undefined;
|
|
49
49
|
ok: boolean;
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { defineAction } from "../../action.js";
|
|
3
3
|
import { writeAppState } from "../../application-state/script-helpers.js";
|
|
4
4
|
export default defineAction({
|
|
5
|
-
description: "Turn demo mode on or off. When demo mode is on, the
|
|
5
|
+
description: "Turn demo mode on or off. When demo mode is on, the UI replaces email addresses with realistic fake data and supported dashboards reshape chart values for presentations. Agent-visible results replace emails and numbers while keeping names, free text, labels, IDs, dates, and structure intact. Use when the user asks to 'hide my data', 'turn on demo mode', 'anonymize this for a screen share / recording', or similar. This is the same toggle as the Demo mode switch in settings.",
|
|
6
6
|
schema: z.object({
|
|
7
7
|
enabled: z
|
|
8
8
|
.boolean()
|
|
@@ -13,7 +13,7 @@ export default defineAction({
|
|
|
13
13
|
return {
|
|
14
14
|
enabled,
|
|
15
15
|
message: enabled
|
|
16
|
-
? "Demo mode is ON —
|
|
16
|
+
? "Demo mode is ON — UI emails are anonymized and supported dashboard charts are reshaped for presentation. Agent-visible emails and numbers are also anonymized. Names, free text, labels, IDs, and structure are preserved."
|
|
17
17
|
: "Demo mode is OFF — real data is shown again.",
|
|
18
18
|
};
|
|
19
19
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toggle-demo-mode.js","sourceRoot":"","sources":["../../../src/demo/actions/toggle-demo-mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAE1E,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT
|
|
1
|
+
{"version":3,"file":"toggle-demo-mode.js","sourceRoot":"","sources":["../../../src/demo/actions/toggle-demo-mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAE1E,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,+dAA+d;IACje,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,OAAO,EAAE,CAAC;aACP,OAAO,EAAE;aACT,QAAQ,CAAC,kDAAkD,CAAC;KAChE,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACzB,MAAM,aAAa,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9C,OAAO;YACL,OAAO;YACP,OAAO,EAAE,OAAO;gBACd,CAAC,CAAC,4NAA4N;gBAC9N,CAAC,CAAC,8CAA8C;SACnD,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\n\nimport { defineAction } from \"../../action.js\";\nimport { writeAppState } from \"../../application-state/script-helpers.js\";\n\nexport default defineAction({\n description:\n \"Turn demo mode on or off. When demo mode is on, the UI replaces email addresses with realistic fake data and supported dashboards reshape chart values for presentations. Agent-visible results replace emails and numbers while keeping names, free text, labels, IDs, dates, and structure intact. Use when the user asks to 'hide my data', 'turn on demo mode', 'anonymize this for a screen share / recording', or similar. This is the same toggle as the Demo mode switch in settings.\",\n schema: z.object({\n enabled: z\n .boolean()\n .describe(\"true to turn demo mode on, false to turn it off.\"),\n }),\n run: async ({ enabled }) => {\n await writeAppState(\"demo-mode\", { enabled });\n return {\n enabled,\n message: enabled\n ? \"Demo mode is ON — UI emails are anonymized and supported dashboard charts are reshaped for presentation. Agent-visible emails and numbers are also anonymized. Names, free text, labels, IDs, and structure are preserved.\"\n : \"Demo mode is OFF — real data is shown again.\",\n };\n },\n});\n"]}
|
package/dist/demo/config.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Demo mode gate.
|
|
3
3
|
*
|
|
4
|
-
* Demo mode replaces
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* keeps working. The redaction WALK (see ./redact.ts) is expensive on
|
|
8
|
-
* payloads, so callers MUST gate it behind this function and only walk
|
|
9
|
-
* returns true.
|
|
4
|
+
* Demo mode replaces email addresses and numbers in every action result with
|
|
5
|
+
* deterministic fake data — for both the UI and what the agent sees — while
|
|
6
|
+
* preserving names, free text, labels, IDs, dates, URLs, and structure so the
|
|
7
|
+
* app keeps working. The redaction WALK (see ./redact.ts) is expensive on
|
|
8
|
+
* large payloads, so callers MUST gate it behind this function and only walk
|
|
9
|
+
* 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
|
package/dist/demo/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/demo/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAEtE,MAAM,MAAM,GAAG,KAAK,CAAC;AACrB,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0C,CAAC;AAEhE,oEAAoE;AACpE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC;QACH,IAAI,UAAU,GAAG,GAAG,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,EAAE,mBAAmB,EAAE,GAC3B,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;YAC/C,UAAU,GAAG,mBAAmB,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;QACrE,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAClC,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,CAAC,EAAE,GAAG,MAAM;YAAE,OAAO,GAAG,CAAC,KAAK,CAAC;QACnD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;QACxC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACnD,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;QACpE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["/**\n * Demo mode gate.\n *\n * Demo mode replaces
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/demo/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAEtE,MAAM,MAAM,GAAG,KAAK,CAAC;AACrB,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0C,CAAC;AAEhE,oEAAoE;AACpE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC;QACH,IAAI,UAAU,GAAG,GAAG,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,EAAE,mBAAmB,EAAE,GAC3B,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;YAC/C,UAAU,GAAG,mBAAmB,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;QACrE,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAClC,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,CAAC,EAAE,GAAG,MAAM;YAAE,OAAO,GAAG,CAAC,KAAK,CAAC;QACnD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;QACxC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACnD,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;QACpE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["/**\n * Demo mode gate.\n *\n * Demo mode replaces email addresses and numbers in every action result with\n * deterministic fake data — for both the UI and what the agent sees — while\n * preserving names, free text, labels, IDs, dates, URLs, and structure so the\n * app keeps working. The redaction WALK (see ./redact.ts) is expensive on\n * large payloads, so callers MUST gate it behind this function and only walk\n * when it returns true.\n *\n * This gate itself is intentionally cheap:\n * - An env-forced deployment (a hosted demo site) short-circuits with zero\n * I/O — `DEMO_MODE=true`.\n * - The per-user runtime toggle lives in `application_state` under the\n * `demo-mode` key (`{ enabled: boolean }`), written by the settings UI and\n * the `toggle-demo-mode` agent action. It's read behind a short in-process\n * TTL cache keyed by user, so a tight agent tool-call loop doesn't hit the\n * DB on every result.\n */\nimport { readAppState } from \"../application-state/script-helpers.js\";\n\nconst TTL_MS = 3_000;\nconst cache = new Map<string, { value: boolean; at: number }>();\n\n/** Deployment-wide force (hosted demo site). Zero cost — no I/O. */\nexport function isDemoModeForced(): boolean {\n return process.env.DEMO_MODE === \"true\";\n}\n\n/**\n * Whether demo-mode redaction should run for the current request/user.\n * Cheap by design — safe to call before every action result; the expensive\n * walk only happens when this is true.\n */\nexport async function isDemoModeEnabled(): Promise<boolean> {\n if (isDemoModeForced()) return true;\n try {\n let sessionKey = \"_\";\n try {\n const { getRequestUserEmail } =\n await import(\"../server/request-context.js\");\n sessionKey = getRequestUserEmail() ?? process.env.AGENT_USER_EMAIL ?? \"_\";\n } catch {\n // request-context unavailable (CLI / non-server) — use default key\n }\n const now = Date.now();\n const hit = cache.get(sessionKey);\n if (hit && now - hit.at < TTL_MS) return hit.value;\n const state = await readAppState(\"demo-mode\");\n const enabled = state?.enabled === true;\n cache.set(sessionKey, { value: enabled, at: now });\n return enabled;\n } catch {\n // No request context / DB unavailable — fail closed (no redaction).\n return false;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-interceptor.d.ts","sourceRoot":"","sources":["../../src/demo/fetch-interceptor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fetch-interceptor.d.ts","sourceRoot":"","sources":["../../src/demo/fetch-interceptor.ts"],"names":[],"mappings":"AAyDA,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMpE;AA6DD,+EAA+E;AAC/E,wBAAgB,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ/D;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAqErD"}
|
|
@@ -44,6 +44,17 @@ const SKIP_SUBSTRINGS = [
|
|
|
44
44
|
// exhaust its retries ("agent connection kept failing").
|
|
45
45
|
"/_agent-native/runs",
|
|
46
46
|
];
|
|
47
|
+
// Raw rrweb payloads are playback data, not ordinary app UI records. Walking
|
|
48
|
+
// and cloning their huge DOM/event trees on the main thread stalls playback
|
|
49
|
+
// and can corrupt replay fidelity. Small list/summary/manifest responses stay
|
|
50
|
+
// eligible so rendered visitor identities are still anonymized.
|
|
51
|
+
const RAW_REPLAY_PAYLOAD_RE = /\/api\/session-replay\/recordings\/[^/?#]+\/(?:chunks(?:\/[^/?#]+)?|events)(?:[/?#]|$)/;
|
|
52
|
+
const RAW_AGENT_REPLAY_EVENTS_RE = /\/api\/session-replay\/agent-events\.json(?:[?#]|$)/;
|
|
53
|
+
export function shouldSkipDemoResponseRedaction(url) {
|
|
54
|
+
return (SKIP_SUBSTRINGS.some((substring) => url.includes(substring)) ||
|
|
55
|
+
RAW_REPLAY_PAYLOAD_RE.test(url) ||
|
|
56
|
+
RAW_AGENT_REPLAY_EVENTS_RE.test(url));
|
|
57
|
+
}
|
|
47
58
|
let installed = false;
|
|
48
59
|
let demoEnabled = false;
|
|
49
60
|
let originalFetch = null;
|
|
@@ -138,7 +149,7 @@ export function ensureDemoModeFetchInterceptor() {
|
|
|
138
149
|
return res;
|
|
139
150
|
try {
|
|
140
151
|
const url = urlOf(input);
|
|
141
|
-
if (
|
|
152
|
+
if (shouldSkipDemoResponseRedaction(url))
|
|
142
153
|
return res;
|
|
143
154
|
// Only buffered, finite JSON. SSE / streaming / chunked-forever bodies
|
|
144
155
|
// never reach `redactDemoData`: streaming content-types are excluded,
|
|
@@ -156,7 +167,13 @@ export function ensureDemoModeFetchInterceptor() {
|
|
|
156
167
|
if (res.bodyUsed)
|
|
157
168
|
return res;
|
|
158
169
|
const data = await withTimeout(res.clone().json(), 3_000);
|
|
159
|
-
|
|
170
|
+
// Frontend reads only need identity privacy. Dashboard charts apply
|
|
171
|
+
// their purpose-built demo trend transform at render time, so mutating
|
|
172
|
+
// every numeric field in every JSON response is unnecessary work.
|
|
173
|
+
const redacted = redactDemoData(data, {
|
|
174
|
+
redactNumbers: false,
|
|
175
|
+
redactProtectedEmails: true,
|
|
176
|
+
});
|
|
160
177
|
const headers = new Headers(res.headers);
|
|
161
178
|
// Body is re-serialized — these would be wrong now.
|
|
162
179
|
headers.delete("content-length");
|
|
@@ -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\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 (SKIP_SUBSTRINGS.some((s) => url.includes(s))) 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 const redacted = redactDemoData(data);\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":["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. Walking\n// and cloning their huge DOM/event trees on the main thread stalls playback\n// and can corrupt replay fidelity. Small list/summary/manifest responses stay\n// eligible so rendered visitor identities are still anonymized.\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"]}
|
package/dist/demo/redact.d.ts
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure, dependency-free, deterministic demo-mode redactor.
|
|
3
3
|
*
|
|
4
|
-
* Replaces sensitive values (
|
|
4
|
+
* Replaces sensitive values (emails and free numbers) with stable fake
|
|
5
5
|
* substitutes so a demo looks coherent — the same input always maps to the
|
|
6
6
|
* same fake. Crucially, it NEVER rewrites identifiers, structural tokens, or
|
|
7
|
-
* timestamps
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* timestamps. Names and other free text are deliberately left alone because
|
|
8
|
+
* guessing whether arbitrary text is a person's name is too inaccurate. The
|
|
9
|
+
* string redactor uses a protect-first strategy (mask IDs with opaque
|
|
10
|
+
* placeholders before any transform runs, restore them byte-identical
|
|
11
|
+
* afterwards), and the structure-aware walker additionally protects leaf
|
|
12
|
+
* values by key name.
|
|
11
13
|
*/
|
|
12
14
|
export interface RedactOptions {
|
|
13
15
|
salt?: string;
|
|
16
|
+
/** Redact numeric values. Defaults to true for backward compatibility. */
|
|
17
|
+
redactNumbers?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Redact emails stored in otherwise protected structural fields such as
|
|
20
|
+
* `userId` and `userKey`. Intended for display-only frontend responses.
|
|
21
|
+
*/
|
|
22
|
+
redactProtectedEmails?: boolean;
|
|
14
23
|
}
|
|
15
24
|
export declare function redactDemoString(text: string, opts?: RedactOptions): string;
|
|
16
25
|
export declare function redactDemoData<T>(value: T, opts?: RedactOptions): T;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/demo/redact.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/demo/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAuhBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAM3E;AA8HD,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,CAAC,CAUnE;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAGrD"}
|