@agent-native/core 0.63.3 → 0.63.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +33 -97
  2. package/dist/cli/index.js +7 -7
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  5. package/dist/client/blocks/library/AnnotatedCodeBlock.js +23 -19
  6. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  7. package/dist/client/blocks/library/diagram.d.ts.map +1 -1
  8. package/dist/client/blocks/library/diagram.js +10 -11
  9. package/dist/client/blocks/library/diagram.js.map +1 -1
  10. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  11. package/dist/client/blocks/library/wireframe.js +2 -1
  12. package/dist/client/blocks/library/wireframe.js.map +1 -1
  13. package/dist/server/auth.d.ts.map +1 -1
  14. package/dist/server/auth.js +5 -1
  15. package/dist/server/auth.js.map +1 -1
  16. package/dist/server/onboarding-html.d.ts.map +1 -1
  17. package/dist/server/onboarding-html.js +50 -5
  18. package/dist/server/onboarding-html.js.map +1 -1
  19. package/dist/tracking/providers.d.ts.map +1 -1
  20. package/dist/tracking/providers.js +4 -1
  21. package/dist/tracking/providers.js.map +1 -1
  22. package/docs/content/creating-templates.md +1 -1
  23. package/docs/content/template-analytics.md +11 -41
  24. package/docs/content/template-assets.md +8 -3
  25. package/docs/content/template-brain.md +6 -1
  26. package/docs/content/template-calendar.md +13 -59
  27. package/docs/content/template-chat.md +6 -9
  28. package/docs/content/template-clips.md +11 -16
  29. package/docs/content/template-content.md +14 -48
  30. package/docs/content/template-design.md +7 -2
  31. package/docs/content/template-dispatch.md +6 -9
  32. package/docs/content/template-forms.md +10 -13
  33. package/docs/content/template-mail.md +12 -27
  34. package/docs/content/template-plan.md +6 -1
  35. package/docs/content/template-slides.md +14 -75
  36. package/docs/content/template-videos.md +11 -52
  37. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/tracking/providers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in tracking providers that auto-register from env vars.\n *\n * No SDK dependencies — uses raw HTTP to keep core lightweight.\n * Set the env var and tracking starts automatically.\n *\n * POSTHOG_API_KEY + POSTHOG_HOST → PostHog\n * MIXPANEL_TOKEN → Mixpanel\n * AMPLITUDE_API_KEY → Amplitude\n * AGENT_NATIVE_ANALYTICS_PUBLIC_KEY → Agent Native Analytics\n *\n * Call `registerBuiltinProviders()` at server startup (done\n * automatically by the core-routes plugin).\n */\n\nimport { registerTrackingProvider } from \"./registry.js\";\nimport type { TrackingProvider, TrackingEvent } from \"./types.js\";\n\nconst POSTHOG_DEFAULT_HOST = \"https://us.i.posthog.com\";\nconst AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT =\n \"https://analytics.agent-native.com/track\";\nconst BATCH_INTERVAL_MS = 10_000;\nconst MAX_BATCH_SIZE = 50;\n\n// ─── Batched sender ────────────────────────────────────────────────────────\n\ninterface QueuedEvent {\n url: string;\n body: string;\n headers?: Record<string, string>;\n}\n\n// Use globalThis so multiple ESM graph instances (Vite dev + Nitro symlinks)\n// share one queue, matching the same pattern as the tracking registry.\nconst QUEUE_KEY = Symbol.for(\"@agent-native/core/tracking.queue\");\nconst TIMER_KEY = Symbol.for(\"@agent-native/core/tracking.timer\");\n\ninterface GlobalWithQueue {\n [QUEUE_KEY]?: QueuedEvent[];\n [TIMER_KEY]?: ReturnType<typeof setTimeout> | null;\n}\n\nfunction getQueue(): QueuedEvent[] {\n const g = globalThis as unknown as GlobalWithQueue;\n if (!g[QUEUE_KEY]) g[QUEUE_KEY] = [];\n return g[QUEUE_KEY]!;\n}\n\nfunction getTimer(): ReturnType<typeof setTimeout> | null {\n const g = globalThis as unknown as GlobalWithQueue;\n return g[TIMER_KEY] ?? null;\n}\n\nfunction setTimer(t: ReturnType<typeof setTimeout> | null): void {\n (globalThis as unknown as GlobalWithQueue)[TIMER_KEY] = t;\n}\n\nfunction enqueue(\n url: string,\n body: string,\n headers?: Record<string, string>,\n): void {\n const queue = getQueue();\n queue.push({ url, body, headers });\n if (queue.length >= MAX_BATCH_SIZE) {\n drainQueue();\n } else if (!getTimer()) {\n setTimer(setTimeout(drainQueue, BATCH_INTERVAL_MS));\n }\n}\n\nfunction drainQueue(): void {\n const t = getTimer();\n if (t) {\n clearTimeout(t);\n setTimer(null);\n }\n const queue = getQueue();\n const batch = queue.splice(0, queue.length);\n for (const item of batch) {\n fetch(item.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...item.headers },\n body: item.body,\n }).catch(() => {});\n }\n}\n\nfunction isLocalhostUrl(value: string | undefined): boolean {\n if (!value || !value.trim()) return false;\n const raw = value.trim();\n const withProtocol = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw)\n ? raw\n : `https://${raw}`;\n try {\n const { hostname } = new URL(withProtocol);\n const h = hostname.toLowerCase();\n return (\n h === \"localhost\" ||\n h === \"127.0.0.1\" ||\n h === \"::1\" ||\n h === \"[::1]\" ||\n h.endsWith(\".localhost\") ||\n h.endsWith(\".local\")\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldSkipAgentNativeAnalyticsForLocalhost(): boolean {\n if (process.env.AGENT_NATIVE_ANALYTICS_ALLOW_LOCALHOST === \"true\") {\n return false;\n }\n if (process.env.NODE_ENV === \"development\") return true;\n return [\n process.env.APP_URL,\n process.env.BETTER_AUTH_URL,\n process.env.URL,\n process.env.DEPLOY_URL,\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n process.env.VERCEL_URL,\n ].some(isLocalhostUrl);\n}\n\n// ─── PostHog ───────────────────────────────────────────────────────────────\n\nfunction createPostHogProvider(apiKey: string, host: string): TrackingProvider {\n return {\n name: \"posthog\",\n track(event: TrackingEvent) {\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: event.name,\n distinct_id: event.userId || \"anonymous\",\n properties: {\n ...event.properties,\n timestamp: event.timestamp,\n },\n }),\n );\n },\n identify(userId, traits) {\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: \"$identify\",\n distinct_id: userId,\n properties: { $set: traits },\n }),\n );\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Mixpanel ──────────────────────────────────────────────────────────────\n\nfunction createMixpanelProvider(token: string): TrackingProvider {\n return {\n name: \"mixpanel\",\n track(event: TrackingEvent) {\n const data = {\n event: event.name,\n properties: {\n token,\n distinct_id: event.userId || \"anonymous\",\n time: event.timestamp\n ? new Date(event.timestamp).getTime() / 1000\n : undefined,\n ...event.properties,\n },\n };\n enqueue(\"https://api.mixpanel.com/track\", JSON.stringify([data]));\n },\n identify(userId, traits) {\n const data = {\n $token: token,\n $distinct_id: userId,\n $set: traits,\n };\n enqueue(\"https://api.mixpanel.com/engage\", JSON.stringify([data]));\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Amplitude ─────────────────────────────────────────────────────────────\n\nfunction createAmplitudeProvider(apiKey: string): TrackingProvider {\n return {\n name: \"amplitude\",\n track(event: TrackingEvent) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: event.name,\n user_id: event.userId || \"anonymous\",\n event_properties: event.properties,\n time: event.timestamp\n ? new Date(event.timestamp).getTime()\n : undefined,\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n identify(userId, traits) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: \"$identify\",\n user_id: userId,\n user_properties: { $set: traits },\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Webhook (custom HTTP endpoint) ───────────────────────────────────────\n\nfunction createWebhookProvider(\n url: string,\n authHeader?: string,\n): TrackingProvider {\n const extra = authHeader ? { Authorization: authHeader } : undefined;\n return {\n name: \"webhook\",\n track(event: TrackingEvent) {\n enqueue(\n url,\n JSON.stringify({\n event: event.name,\n properties: event.properties,\n userId: event.userId,\n timestamp: event.timestamp,\n }),\n extra,\n );\n },\n identify(userId, traits) {\n enqueue(\n url,\n JSON.stringify({\n event: \"$identify\",\n userId,\n traits,\n timestamp: new Date().toISOString(),\n }),\n extra,\n );\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Agent Native Analytics ───────────────────────────────────────────────\n\nfunction createAgentNativeAnalyticsProvider(\n publicKey: string,\n endpoint: string,\n): TrackingProvider {\n return {\n name: \"agent-native-analytics\",\n track(event: TrackingEvent) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: event.name,\n properties: event.properties ?? {},\n userId: event.userId,\n timestamp: event.timestamp,\n }),\n );\n },\n identify(userId, traits) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: \"$identify\",\n userId,\n properties: traits ?? {},\n timestamp: new Date().toISOString(),\n }),\n );\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Auto-registration ────────────────────────────────────────────────────\n\nlet _registered = false;\n\nexport function registerBuiltinProviders(): void {\n if (_registered) return;\n _registered = true;\n\n const posthogKey = process.env.POSTHOG_API_KEY;\n if (posthogKey) {\n const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(\n /\\/+$/,\n \"\",\n );\n registerTrackingProvider(createPostHogProvider(posthogKey, host));\n }\n\n const mixpanelToken = process.env.MIXPANEL_TOKEN;\n if (mixpanelToken) {\n registerTrackingProvider(createMixpanelProvider(mixpanelToken));\n }\n\n const amplitudeKey = process.env.AMPLITUDE_API_KEY;\n if (amplitudeKey) {\n registerTrackingProvider(createAmplitudeProvider(amplitudeKey));\n }\n\n const agentNativeAnalyticsKey = process.env.AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;\n if (\n agentNativeAnalyticsKey &&\n !shouldSkipAgentNativeAnalyticsForLocalhost()\n ) {\n registerTrackingProvider(\n createAgentNativeAnalyticsProvider(\n agentNativeAnalyticsKey,\n (\n process.env.AGENT_NATIVE_ANALYTICS_ENDPOINT ||\n AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT\n ).replace(/\\/+$/, \"\"),\n ),\n );\n }\n\n const webhookUrl = process.env.TRACKING_WEBHOOK_URL;\n if (webhookUrl) {\n registerTrackingProvider(\n createWebhookProvider(webhookUrl, process.env.TRACKING_WEBHOOK_AUTH),\n );\n }\n}\n"]}
1
+ {"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/tracking/providers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in tracking providers that auto-register from env vars.\n *\n * No SDK dependencies — uses raw HTTP to keep core lightweight.\n * Set the env var and tracking starts automatically.\n *\n * POSTHOG_API_KEY + POSTHOG_HOST → PostHog\n * MIXPANEL_TOKEN → Mixpanel\n * AMPLITUDE_API_KEY → Amplitude\n * AGENT_NATIVE_ANALYTICS_PUBLIC_KEY → Agent Native Analytics\n *\n * Call `registerBuiltinProviders()` at server startup (done\n * automatically by the core-routes plugin).\n */\n\nimport { registerTrackingProvider } from \"./registry.js\";\nimport type { TrackingProvider, TrackingEvent } from \"./types.js\";\n\nconst POSTHOG_DEFAULT_HOST = \"https://us.i.posthog.com\";\nconst AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT =\n \"https://analytics.agent-native.com/track\";\nconst BATCH_INTERVAL_MS = 10_000;\nconst MAX_BATCH_SIZE = 50;\n\n// ─── Batched sender ────────────────────────────────────────────────────────\n\ninterface QueuedEvent {\n url: string;\n body: string;\n headers?: Record<string, string>;\n}\n\n// Use globalThis so multiple ESM graph instances (Vite dev + Nitro symlinks)\n// share one queue, matching the same pattern as the tracking registry.\nconst QUEUE_KEY = Symbol.for(\"@agent-native/core/tracking.queue\");\nconst TIMER_KEY = Symbol.for(\"@agent-native/core/tracking.timer\");\n\ninterface GlobalWithQueue {\n [QUEUE_KEY]?: QueuedEvent[];\n [TIMER_KEY]?: ReturnType<typeof setTimeout> | null;\n}\n\nfunction getQueue(): QueuedEvent[] {\n const g = globalThis as unknown as GlobalWithQueue;\n if (!g[QUEUE_KEY]) g[QUEUE_KEY] = [];\n return g[QUEUE_KEY]!;\n}\n\nfunction getTimer(): ReturnType<typeof setTimeout> | null {\n const g = globalThis as unknown as GlobalWithQueue;\n return g[TIMER_KEY] ?? null;\n}\n\nfunction setTimer(t: ReturnType<typeof setTimeout> | null): void {\n (globalThis as unknown as GlobalWithQueue)[TIMER_KEY] = t;\n}\n\nfunction enqueue(\n url: string,\n body: string,\n headers?: Record<string, string>,\n): void {\n const queue = getQueue();\n queue.push({ url, body, headers });\n if (queue.length >= MAX_BATCH_SIZE) {\n drainQueue();\n } else if (!getTimer()) {\n const timer = setTimeout(drainQueue, BATCH_INTERVAL_MS);\n if (timer.unref) timer.unref();\n setTimer(timer);\n }\n}\n\nfunction drainQueue(): void {\n const t = getTimer();\n if (t) {\n clearTimeout(t);\n setTimer(null);\n }\n const queue = getQueue();\n const batch = queue.splice(0, queue.length);\n for (const item of batch) {\n fetch(item.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...item.headers },\n body: item.body,\n }).catch(() => {});\n }\n}\n\nfunction isLocalhostUrl(value: string | undefined): boolean {\n if (!value || !value.trim()) return false;\n const raw = value.trim();\n const withProtocol = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw)\n ? raw\n : `https://${raw}`;\n try {\n const { hostname } = new URL(withProtocol);\n const h = hostname.toLowerCase();\n return (\n h === \"localhost\" ||\n h === \"127.0.0.1\" ||\n h === \"::1\" ||\n h === \"[::1]\" ||\n h.endsWith(\".localhost\") ||\n h.endsWith(\".local\")\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldSkipAgentNativeAnalyticsForLocalhost(): boolean {\n if (process.env.AGENT_NATIVE_ANALYTICS_ALLOW_LOCALHOST === \"true\") {\n return false;\n }\n if (process.env.NODE_ENV === \"development\") return true;\n return [\n process.env.APP_URL,\n process.env.BETTER_AUTH_URL,\n process.env.URL,\n process.env.DEPLOY_URL,\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n process.env.VERCEL_URL,\n ].some(isLocalhostUrl);\n}\n\n// ─── PostHog ───────────────────────────────────────────────────────────────\n\nfunction createPostHogProvider(apiKey: string, host: string): TrackingProvider {\n return {\n name: \"posthog\",\n track(event: TrackingEvent) {\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: event.name,\n distinct_id: event.userId || \"anonymous\",\n properties: {\n ...event.properties,\n timestamp: event.timestamp,\n },\n }),\n );\n },\n identify(userId, traits) {\n enqueue(\n `${host}/capture/`,\n JSON.stringify({\n api_key: apiKey,\n event: \"$identify\",\n distinct_id: userId,\n properties: { $set: traits },\n }),\n );\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Mixpanel ──────────────────────────────────────────────────────────────\n\nfunction createMixpanelProvider(token: string): TrackingProvider {\n return {\n name: \"mixpanel\",\n track(event: TrackingEvent) {\n const data = {\n event: event.name,\n properties: {\n token,\n distinct_id: event.userId || \"anonymous\",\n time: event.timestamp\n ? new Date(event.timestamp).getTime() / 1000\n : undefined,\n ...event.properties,\n },\n };\n enqueue(\"https://api.mixpanel.com/track\", JSON.stringify([data]));\n },\n identify(userId, traits) {\n const data = {\n $token: token,\n $distinct_id: userId,\n $set: traits,\n };\n enqueue(\"https://api.mixpanel.com/engage\", JSON.stringify([data]));\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Amplitude ─────────────────────────────────────────────────────────────\n\nfunction createAmplitudeProvider(apiKey: string): TrackingProvider {\n return {\n name: \"amplitude\",\n track(event: TrackingEvent) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: event.name,\n user_id: event.userId || \"anonymous\",\n event_properties: event.properties,\n time: event.timestamp\n ? new Date(event.timestamp).getTime()\n : undefined,\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n identify(userId, traits) {\n const data = {\n api_key: apiKey,\n events: [\n {\n event_type: \"$identify\",\n user_id: userId,\n user_properties: { $set: traits },\n },\n ],\n };\n enqueue(\"https://api2.amplitude.com/2/httpapi\", JSON.stringify(data));\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Webhook (custom HTTP endpoint) ───────────────────────────────────────\n\nfunction createWebhookProvider(\n url: string,\n authHeader?: string,\n): TrackingProvider {\n const extra = authHeader ? { Authorization: authHeader } : undefined;\n return {\n name: \"webhook\",\n track(event: TrackingEvent) {\n enqueue(\n url,\n JSON.stringify({\n event: event.name,\n properties: event.properties,\n userId: event.userId,\n timestamp: event.timestamp,\n }),\n extra,\n );\n },\n identify(userId, traits) {\n enqueue(\n url,\n JSON.stringify({\n event: \"$identify\",\n userId,\n traits,\n timestamp: new Date().toISOString(),\n }),\n extra,\n );\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Agent Native Analytics ───────────────────────────────────────────────\n\nfunction createAgentNativeAnalyticsProvider(\n publicKey: string,\n endpoint: string,\n): TrackingProvider {\n return {\n name: \"agent-native-analytics\",\n track(event: TrackingEvent) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: event.name,\n properties: event.properties ?? {},\n userId: event.userId,\n timestamp: event.timestamp,\n }),\n );\n },\n identify(userId, traits) {\n enqueue(\n endpoint,\n JSON.stringify({\n publicKey,\n event: \"$identify\",\n userId,\n properties: traits ?? {},\n timestamp: new Date().toISOString(),\n }),\n );\n },\n flush: () => {\n drainQueue();\n return Promise.resolve();\n },\n };\n}\n\n// ─── Auto-registration ────────────────────────────────────────────────────\n\nlet _registered = false;\n\nexport function registerBuiltinProviders(): void {\n if (_registered) return;\n _registered = true;\n\n const posthogKey = process.env.POSTHOG_API_KEY;\n if (posthogKey) {\n const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(\n /\\/+$/,\n \"\",\n );\n registerTrackingProvider(createPostHogProvider(posthogKey, host));\n }\n\n const mixpanelToken = process.env.MIXPANEL_TOKEN;\n if (mixpanelToken) {\n registerTrackingProvider(createMixpanelProvider(mixpanelToken));\n }\n\n const amplitudeKey = process.env.AMPLITUDE_API_KEY;\n if (amplitudeKey) {\n registerTrackingProvider(createAmplitudeProvider(amplitudeKey));\n }\n\n const agentNativeAnalyticsKey = process.env.AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;\n if (\n agentNativeAnalyticsKey &&\n !shouldSkipAgentNativeAnalyticsForLocalhost()\n ) {\n registerTrackingProvider(\n createAgentNativeAnalyticsProvider(\n agentNativeAnalyticsKey,\n (\n process.env.AGENT_NATIVE_ANALYTICS_ENDPOINT ||\n AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT\n ).replace(/\\/+$/, \"\"),\n ),\n );\n }\n\n const webhookUrl = process.env.TRACKING_WEBHOOK_URL;\n if (webhookUrl) {\n registerTrackingProvider(\n createWebhookProvider(webhookUrl, process.env.TRACKING_WEBHOOK_AUTH),\n );\n }\n}\n"]}
@@ -33,7 +33,7 @@ npx @agent-native/core@latest create my-platform
33
33
 
34
34
  Chat gives you auth, durable chat threads, SQL-backed resources, tools, application state, actions, and polling sync. You add the domain model and product UI.
35
35
 
36
- If you are not building a reusable UI template yet, use the headless on-ramp in [Getting Started](/docs/getting-started#create-your-agent): define one action, run it with `pnpm agent`, and add UI later when the workflow needs a durable surface.
36
+ If you are not building a reusable UI template yet, use the headless on-ramp in [Getting Started](/docs/getting-started#1-create-your-app): define one action, run it with `pnpm agent`, and add UI later when the workflow needs a durable surface.
37
37
 
38
38
  ## Project Structure {#project-structure}
39
39
 
@@ -7,15 +7,12 @@ description: "Ask analytics questions in plain English, get charts and dashboard
7
7
 
8
8
  Ask analytics questions in plain English, get charts and dashboards back. The agent connects to BigQuery, GA4, Amplitude, the built-in first-party event collector, HubSpot, Jira, and a dozen other sources, writes the query for you, validates it, and renders the answer as a chart, table, or saved dashboard panel.
9
9
 
10
- <!-- screenshot:
11
- app: analytics
12
- view: /adhoc/<dashboard-id>
13
- shows: Adhoc dashboard with 3 KPI cards (Weekly active users 24,318 / New signups 1,842 / Revenue MRR $48,210), Weekly active users line chart and Revenue over time area chart side by side, Signups by source bar chart below
14
- account: screenshot-account (dashboard authored on this account against a seeded warehouse)
15
- capture: 1400x800 viewport, cropped 90px from bottom (final 1400x710)
16
- -->
17
-
18
- ![Analytics dashboard with KPIs and charts](/screenshots/analytics.png)
10
+ ```an-wireframe
11
+ {
12
+ "surface": "desktop",
13
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:500px;box-sizing:border-box'><h1 style='margin:0'>Agent-Native Templates</h1><p class='wf-muted' style='margin:0'>Adoption and engagement across the last 12 weeks.</p><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:12px'><div class='wf-card'><small class='wf-muted'>Weekly active users</small><br/><strong>24,318</strong><br/><span class='wf-pill accent'>+12.4%</span></div><div class='wf-card'><small class='wf-muted'>New signups</small><br/><strong>1,842</strong><br/><span class='wf-pill accent'>+8.7%</span></div><div class='wf-card'><small class='wf-muted'>Revenue MRR</small><br/><strong>$48,210</strong><br/><span class='wf-pill accent'>+21.3%</span></div></div><div style='display:grid;grid-template-columns:1fr 1fr;gap:12px;flex:1'><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Weekly active users</strong><div style='flex:1;display:flex;align-items:end;gap:8px'><div style='height:38%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:44%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:58%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:74%;flex:1;background:var(--wf-accent-soft)'></div></div></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Revenue over time</strong><div style='flex:1;display:flex;align-items:end;gap:8px'><div style='height:32%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:48%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:63%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:80%;flex:1;background:var(--wf-accent-soft)'></div></div></div></div><div class='wf-card'><strong>Signups by source</strong><br/><small class='wf-muted'>Lower chart begins below the main charts.</small></div></div>"
14
+ }
15
+ ```
19
16
 
20
17
  It's an open-source replacement for Amplitude, Mixpanel, and Looker — for teams that want to own the code, the queries, and the data.
21
18
 
@@ -96,42 +93,15 @@ pnpm dev
96
93
 
97
94
  The CLI prints the local dev URL. Sign in with Google, then open the **Data Sources** page to connect BigQuery, GA4, first-party tracking, HubSpot, Jira, and the rest.
98
95
 
99
- ### Key features (technical)
100
-
101
- **Natural-language chart generation.** Ask the agent in plain English. It picks the right data source, writes the SQL, validates it against the warehouse, and renders the chart inline in chat or as a saved panel. Chart types: `line`, `area`, `bar`, `metric`, `table`, `pie`.
102
-
103
- **Reusable SQL dashboards.** Dashboards are a named config with an array of panels. Each panel has an `id`, `title`, `sql`, `source` (`bigquery` / `ga4` / `amplitude` / `first-party`), `chartType`, and `width` (1 or 2 columns). See the full shape in `templates/analytics/app/pages/adhoc/sql-dashboard/types.ts`.
104
-
105
- Dashboards support:
106
-
107
- - **Parametric SQL** — declare `variables` and `filters` at the dashboard level; panels reference them with `{{var}}` interpolation.
108
- - **Saved views** — per-dashboard filter presets stored in the `dashboard_views` table.
109
- - **Resizable panels** — 1- or 2-column width per panel; the grid fills the rest.
110
- - **Sharing** — private by default, share with users or orgs (`viewer` / `editor` / `admin`).
111
-
112
- **Ad-hoc analyses.** Long-form investigations that cross-reference sources. An analysis saves the original question, step-by-step re-run instructions, the data sources it touched, and the full findings in Markdown. Anyone with access can re-run it against fresh data. Stored in the `analyses` table (see `templates/analytics/server/db/schema.ts`).
113
-
114
- **Living data dictionary.** Canonical catalog of metrics — metric name, definition, table, columns, SQL template, known gotchas, owner, and data lag. The agent reads it before writing any SQL, so it uses the real warehouse column names (`hs_is_closed`, not guessed `is_closed`) and knows about caveats like "excludes internal emails". Seeded by asking the agent to import definitions from an existing source (dbt descriptions, a Notion page, a team wiki).
115
-
116
- **SQL query explorer.** Direct queries against BigQuery and supported analytics backends from the **Ad-hoc** view. Useful for iterating before saving a dashboard panel.
117
-
118
- **First-party analytics collector.** Hosted apps can send product and template events to `/track` with a public write key. Query those events through `query-agent-native-analytics` or dashboard panels with `source: "first-party"`.
96
+ ### Key features
119
97
 
120
- **Multiple data connectors.** Built-in actions for common sources:
98
+ **Ask questions, get charts.** The agent picks a data source, writes and validates SQL, then renders a chart, table, metric, or saved panel.
121
99
 
122
- | Category | Actions |
123
- | ------------- | ------------------------------------------------------------------------ |
124
- | Warehouse | `bigquery`, `bigquery-table-info`, `ga4-report` |
125
- | Product | `mixpanel-events`, `amplitude-events`, `posthog-events` |
126
- | CRM & Revenue | `hubspot-deals`, `hubspot-metrics`, `hubspot-pipelines`, `apollo-search` |
127
- | Engineering | `github-prs`, `jira-search`, `jira-analytics` |
128
- | Support | `pylon-issues`, `gong-calls` |
129
- | Community | `commonroom-members`, `twitter-tweets` |
130
- | Content & SEO | `seo-top-keywords`, `seo-page-keywords`, `seo-blog-pages` |
100
+ **Dashboards and investigations.** Reusable dashboards keep SQL panels, filters, saved views, and sharing; ad-hoc analyses save longer findings with re-run instructions.
131
101
 
132
- Full list lives in `templates/analytics/actions/`. New sources are added by dropping a new action file the agent picks them up automatically.
102
+ **Living data dictionary.** Metric definitions, owners, source tables, and known caveats give the agent the real warehouse vocabulary before it writes queries.
133
103
 
134
- **Organizations and sharing.** Multi-org deployments are wired up by default via `@agent-native/core/org`. Dashboards and analyses are scoped to the active org. The `/team` route manages members and invitations. See `templates/analytics/app/routes/team.tsx`. Sharing uses the framework's `share-resource` primitive. Coarse visibility is `private` / `org` / `public`; fine-grained grants are per-principal with `viewer` / `editor` / `admin` roles.
104
+ **Broad connector surface.** BigQuery, GA4, product analytics, CRM, support, community, GitHub/Jira, SEO, and first-party `/track` events all come through actions the agent can call.
135
105
 
136
106
  ### Working with the agent
137
107
 
@@ -7,9 +7,14 @@ description: "An agent-native digital asset manager and cross-agent generation s
7
7
 
8
8
  Assets is an agent-native workspace for creating and managing brand-consistent media. It organizes uploads and generated results into libraries and folders, lets teams collect examples for blog heroes, diagrams, landing pages, product shots, videos, and logos, then routes generation through the agent chat so every asset can be reviewed and refined.
9
9
 
10
- ![Assets library for brand media and generated output](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F769092170a14474f998cbca47384f891?format=webp&width=1200)
10
+ ```an-wireframe
11
+ {
12
+ "surface": "desktop",
13
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:520px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Launch brand</h1><span class='wf-pill accent'>Blog heroes</span><span class='wf-pill'>Product shots</span><span class='wf-pill'>Logos</span><div style='flex:1'></div><button>Upload</button><button class='primary'>Generate</button></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Create brand media</strong><div class='wf-box'>Three homepage hero options using the approved logo and product references.</div><div style='display:flex;gap:8px;flex-wrap:wrap'><span class='wf-pill accent'>4 references</span><span class='wf-pill'>16:9</span><span class='wf-pill'>Web export</span></div></div><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:12px;flex:1'><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill accent'>Hero A</span></div><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill'>Reference set</span></div><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill'>Logo safe</span></div></div><div class='wf-card' style='display:grid;grid-template-columns:repeat(4,1fr);gap:8px'><div class='wf-box'>Use</div><div class='wf-box'>Refine</div><div class='wf-box'>Compare</div><div class='wf-box'>Export</div></div></div>"
14
+ }
15
+ ```
11
16
 
12
- When you open the app, you see your libraries and folders on the left, the assets in the selected library in the middle, and a chat composer for generating new media. The agent can browse, search, generate, refine, and export every asset through the same actions the UI uses.
17
+ When you open the app, the selected library, prompt, references, and generated candidates stay in one workspace. The agent can browse, search, generate, refine, and export every asset through the same actions the UI uses.
13
18
 
14
19
  ```an-diagram title="Generate, review, reuse" summary="References and prompts feed a generate-and-choose session; chosen assets land in a library and flow out to other apps via the picker or A2A."
15
20
  {
@@ -41,7 +46,7 @@ Live demo: [assets.agent-native.com](https://assets.agent-native.com).
41
46
 
42
47
  ## Useful prompts
43
48
 
44
- - "Generate three blog hero options using the Acme product screenshots as references."
49
+ - "Generate three blog hero options using the Acme product references."
45
50
  - "Create a square social image in the launch-campaign style."
46
51
  - "Find all approved assets for the onboarding redesign."
47
52
  - "Turn this uploaded diagram into a cleaner product explainer image."
@@ -26,7 +26,12 @@ surfaces for connecting data, approving proposals, and inspecting cited memory.
26
26
  }
27
27
  ```
28
28
 
29
- ![Brain company chat with cited memory sources](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F9c9fe3b5b9494e33803cd3f494cba356?format=webp&width=1200)
29
+ ```an-wireframe
30
+ {
31
+ "surface": "desktop",
32
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:520px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Ask company memory</h1><span class='wf-pill accent'>42 approved memories</span><span class='wf-pill'>3 sources</span><div style='flex:1'></div><button>Sources</button><button>Review</button></div><div class='wf-card' style='display:flex;align-items:center;gap:10px'><span data-icon='search' aria-label='Search'></span><strong style='flex:1'>Why did we choose usage pricing?</strong><button class='primary'>Ask</button></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Answer</strong><p style='margin:0'>The team chose usage pricing after pilots showed seat counts undercounted automation value.</p><div style='display:flex;gap:8px;flex-wrap:wrap'><span class='wf-pill accent'>Pricing RFC</span><span class='wf-pill'>Launch retro</span><span class='wf-pill'>Sales notes</span></div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:8px'><strong>Source timeline</strong><div class='wf-box'>May 3 · Decision captured</div><div class='wf-box'>May 8 · Customer evidence added</div><div class='wf-box'>May 12 · Legal note approved</div></div></div>"
33
+ }
34
+ ```
30
35
 
31
36
  When you open the app, **Ask** is front and center — a clean chat over reviewed
32
37
  company memory. **Sources**, **Review**, and **Knowledge** sit alongside it as
@@ -7,17 +7,14 @@ description: "An agent-powered calendar with Google Calendar sync and Calendly-s
7
7
 
8
8
  An agent-powered calendar app. Connect your Google Calendar and the agent can read your schedule, find free slots, create events, and manage Calendly-style booking links — all in plain English. It replaces the Google Calendar + Calendly combo with one app you own.
9
9
 
10
- <!-- screenshot:
11
- app: calendar
12
- view: / (week)
13
- shows: Calendar week view (May 3-9) with ~17 events scattered across the week (All-hands kickoff, Q3 launch sync, Design review, 1:1 with Marcus, Coffee w/ Hannah, Eng standup, Lunch block, Customer call - Acme, Design crit, Focus block, Roadmap planning, Skip-level w/ Priya, Friday demo, All-hands) events render in the default blue tone and the agent sidebar with calendar-aware suggestions
14
- account: screenshot-account (Google Calendar connected and a representative week populated via create-event)
15
- capture: 1400x800 viewport, cropped 90px from bottom (final 1400x710)
16
- -->
17
-
18
- ![Calendar week view with a populated week and the agent sidebar](/screenshots/calendar.png)
10
+ ```an-wireframe
11
+ {
12
+ "surface": "desktop",
13
+ "html": "<div style='display:flex;flex-direction:column;min-height:530px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1.4px solid var(--wf-line)'><button>Week</button><button>Today</button><button>‹</button><button>›</button><div style='flex:1'></div><strong>May 3-9, 2026</strong><div style='flex:1'></div><button class='primary'>New Event</button></div><div style='display:grid;grid-template-columns:56px repeat(7,minmax(0,1fr));grid-template-rows:36px repeat(5,72px);gap:7px;padding:14px;flex:1'><div></div><strong>Sun 3</strong><strong>Mon 4</strong><strong>Tue 5</strong><strong>Wed 6</strong><strong>Thu 7</strong><strong>Fri 8</strong><strong>Sat 9</strong><small class='wf-muted'>7 AM</small><div class='wf-box' style='opacity:.45'></div><div></div><div></div><div></div><div></div><div></div><div></div><small class='wf-muted'>9 AM</small><div class='wf-box'>All-hands</div><div class='wf-box'>Eng standup</div><div class='wf-box'>Eng standup</div><div class='wf-box'>Eng standup</div><div></div><div class='wf-box'>Planning</div><div></div><small class='wf-muted'>11 AM</small><div class='wf-box'>Design review</div><div></div><div class='wf-box'>Design crit</div><div class='wf-box'>Roadmap</div><div class='wf-box'>Friday demo</div><div></div><div></div><small class='wf-muted'>1 PM</small><div></div><div class='wf-box'>1:1</div><div class='wf-box'>Focus block</div><div></div><div></div><div class='wf-box'>All-hands</div><div></div><small class='wf-muted'>3 PM</small><div></div><div></div><div></div><div class='wf-box'>Skip-level</div><div></div><div></div><div></div></div></div>"
14
+ }
15
+ ```
19
16
 
20
- When you open the app, you'll see your calendar in the middle and the agent in the sidebar. The agent always knows which day, week, or event you're looking at, so you can say "schedule a 30-minute call with Alex on this day" without spelling everything out.
17
+ When you open the app, the active calendar view is the primary surface. The agent still knows which day, week, or event you're looking at, so you can say "schedule a 30-minute call with Alex on this day" without spelling everything out.
21
18
 
22
19
  ```an-diagram title="How a scheduling request flows" summary="Whether you click in the calendar or ask the agent, the same actions read live from Google Calendar and write back to the same view."
23
20
  {
@@ -87,60 +84,17 @@ Open `http://localhost:8082` (the default Calendar dev port).
87
84
 
88
85
  To connect Google Calendar in dev, open the Settings view, paste a `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` from [Google Cloud Console](https://console.cloud.google.com/), and click "Connect Google Calendar". The OAuth redirect URI is `http://localhost:8082/_agent-native/google/callback` in dev. Tokens are stored in the `oauth_tokens` SQL table and refresh automatically.
89
86
 
90
- ### Key features (technical)
91
-
92
- **Calendar views.** The main view at `/` (route `app/routes/_app._index.tsx`) renders the calendar in day, week, or month mode. Switch views from the toolbar, or ask the agent to switch for you. Events are fetched live from Google Calendar — no local sync step is required to see the latest state.
93
-
94
- **Multi-account Google Calendar sync.** Connect as many Google accounts as you like. Each connection appears in Settings, and events from every connected calendar overlay in the main view. Sync is pull-based, so no webhooks or background workers are required. The `sync-google-calendar` action refetches a date range on demand. Supporting actions: `list-events`, `search-events`, `get-event`, `create-event`, `sync-google-calendar`.
95
-
96
- **External calendars (ICS subscriptions).** Subscribe to read-only ICS or `webcal://` feeds — useful for HR time off, conference schedules, or shared team calendars. Feeds are added in Settings and stored per user. Relevant actions: `add-external-calendar`, `list-external-calendars`, `remove-external-calendar`, `update-external-calendars`.
97
-
98
- **Availability rules.** Availability is a weekly schedule of time windows per day, plus a timezone. It is stored in the settings table under the key `calendar-availability`:
99
-
100
- ```json
101
- {
102
- "timezone": "America/Los_Angeles",
103
- "schedule": {
104
- "monday": [{ "start": "09:00", "end": "17:00" }],
105
- "tuesday": [{ "start": "09:00", "end": "17:00" }],
106
- "wednesday": [
107
- { "start": "09:00", "end": "12:00" },
108
- { "start": "13:00", "end": "17:00" }
109
- ],
110
- "thursday": [{ "start": "09:00", "end": "17:00" }],
111
- "friday": [{ "start": "09:00", "end": "16:00" }],
112
- "saturday": [],
113
- "sunday": []
114
- }
115
- }
116
- ```
117
-
118
- Edit it at `/availability` (`app/routes/_app.availability.tsx`) or via the `update-availability` action. The `check-availability` action reads this schedule, subtracts your existing Google Calendar events, and returns free slots of the requested duration.
119
-
120
- **Public booking pages.** Booking links are the Calendly replacement. Create one in the UI at `/booking-links` (`app/routes/_app.booking-links._index.tsx`), configure it in the editor at `/booking-links/{id}`, and share the public URL.
121
-
122
- Every link has:
123
-
124
- - A URL slug, so the public page lives at `/book/{slug}`.
125
- - A primary duration plus an optional list of alternative durations (for example 15, 30, or 60 minutes).
126
- - Optional custom fields collected at booking time.
127
- - A conferencing option (Google Meet, Zoom, or a custom meeting link).
128
- - An `isActive` toggle to pause bookings without deleting the link.
129
-
130
- Visitors land on the public page, pick a date and time from the available slots, fill in name, email, and any custom fields, and receive a confirmation. Bookings are stored in the `bookings` table with a `cancelToken`, which powers the public cancel/reschedule page at `/booking/manage/{token}`.
87
+ ### Key features
131
88
 
132
- There is also a per-user public URL at `/meet/{username}/{slug}` for clean personal sharing.
89
+ **Live calendar views.** Day, week, and month views read directly from connected Google accounts, with optional read-only ICS feeds layered into the same schedule.
133
90
 
134
- **Sharing booking links with teammates.** Booking links are private by default only the creator can edit or delete them. To let a teammate manage a link, either change the link's visibility or grant explicit access. The framework ships these actions, auto-mounted for any `booking-link` resource:
91
+ **Availability and free-slot search.** Weekly availability rules, timezone support, and existing events all feed the same availability action the UI and agent use.
135
92
 
136
- - `share-resource` grant a user or org `viewer`, `editor`, or `admin` access.
137
- - `unshare-resource` — revoke a grant.
138
- - `list-resource-shares` — show current visibility plus all grants.
139
- - `set-resource-visibility` — change the coarse visibility (`private`, `org`, or `public`).
93
+ **Booking links.** Public `/book/{slug}` pages collect name, email, custom fields, conferencing preferences, and cancellation/reschedule tokens.
140
94
 
141
- Sharing only controls who can manage the link. The public booking URL always accepts bookings from unauthenticated visitors as long as `isActive` is true.
95
+ **Shareable management.** Booking links are private by default, but can be shared with teammates through the framework sharing actions.
142
96
 
143
- **Inline event previews in chat.** The `/event` route (`app/routes/event.tsx`) renders a compact, chromeless event card that the agent can embed in chat when you ask about a specific event. Title, time, location, attendees, and a description snippet are shown with a button to jump into the main calendar.
97
+ **Inline event previews.** The agent can embed compact event cards in chat with title, time, location, attendees, and a jump-back button.
144
98
 
145
99
  ### Working with the agent
146
100
 
@@ -9,15 +9,12 @@ Chat is the basic agent-native app starting point. It gives you a clean ChatGPT-
9
9
 
10
10
  If you want the smallest action-only runtime with no browser UI, start with [Pure-Agent Apps](/docs/pure-agent-apps). If you want a finished domain product shape, start from [Calendar](/docs/template-calendar), [Mail](/docs/template-mail), [Content](/docs/template-content), [Forms](/docs/template-forms), [Analytics](/docs/template-analytics), or another domain template.
11
11
 
12
- <!-- screenshot:
13
- app: chat
14
- view: /
15
- shows: Full-page chat app with standard left sidebar, centered empty-state composer, model controls, and no domain data
16
- account: screenshot-account (no domain data needed — chat ships with no seed schema)
17
- capture: 2434x1440 app screenshot
18
- -->
19
-
20
- ![Chat template with a centered agent composer and app navigation sidebar](/screenshots/chat.png)
12
+ ```an-wireframe
13
+ {
14
+ "surface": "desktop",
15
+ "html": "<div style='min-height:560px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;padding:56px 40px'><div style='display:flex;flex-direction:column;align-items:center;justify-content:center;gap:28px;width:min(700px,92%);min-height:430px'><div style='height:34px'></div><div style='text-align:center'><h1 style='margin:0'>How can I help?</h1><p class='wf-muted' style='margin:10px 0 0'>Chat about anything. Add actions, components, pages, jobs, or your own backend.</p></div><div class='wf-card' style='width:100%;min-height:150px;display:flex;flex-direction:column;gap:18px'><span class='wf-muted'>Message the agent...</span><div style='flex:1'></div><div style='display:flex;align-items:center;gap:10px'><span data-icon='plus' aria-label='Attach'></span><div style='flex:1'></div><span class='wf-pill'>Sonnet 4.6 · Auto</span><span class='wf-pill'>Act</span><button class='primary'>↑</button></div></div><div style='height:34px'></div></div></div>"
16
+ }
17
+ ```
21
18
 
22
19
  ## What's in it {#whats-in-it}
23
20
 
@@ -7,15 +7,12 @@ description: "Async screen recording, calendar-synced meeting notes, and push-to
7
7
 
8
8
  A capture-everything app: screen recordings, meeting notes from your calendar, and Fn-hold voice dictation. The agent transcribes, titles, summarizes, and indexes all of it — then lets you ask "find the clip where we discussed the rollout plan" and searches across every transcript you've ever made.
9
9
 
10
- <!-- screenshot:
11
- app: clips
12
- view: /library
13
- shows: Library with Acme Co. organization, folders (Onboarding videos / Customer calls / Bug repros) and spaces (Engineering / Design / Sales) in the sidebar, six recordings in a 3-column grid (Q3 OKRs review meeting, Walkthrough of new onboarding flow, Eng standup May 4, Dictation - Ideas for landing page copy, Customer call - Acme Corp pricing review, Bug repro - drag-and-drop in Safari)
14
- account: screenshot-account (recordings imported into this org via the standard upload + meetings flow)
15
- capture: 1400x800 viewport, cropped 90px from bottom (final 1400x710)
16
- -->
17
-
18
- ![Clips library with recordings, folders, and spaces](/screenshots/clips.png)
10
+ ```an-wireframe
11
+ {
12
+ "surface": "desktop",
13
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:520px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Engineering clips</h1><span class='wf-pill accent'>Library</span><span class='wf-pill'>Meetings</span><span class='wf-pill'>Dictation</span><div style='flex:1'></div><button>Import</button><button class='primary'>Record</button></div><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:12px'><div class='wf-card' style='height:120px;display:flex;flex-direction:column;justify-content:end'><strong>OKRs review</strong><small>35 min</small></div><div class='wf-card' style='height:120px;display:flex;flex-direction:column;justify-content:end'><strong>Onboarding flow</strong><small>12 min</small></div><div class='wf-card' style='height:120px;display:flex;flex-direction:column;justify-content:end'><strong>Bug repro</strong><small>4 min</small></div></div><div class='wf-card' style='display:flex;gap:10px;align-items:center'><span class='wf-pill accent'>Agent-readable</span><span>Transcript + frames ready for share links</span><div style='flex:1'></div><button>Share</button></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:8px'><strong>Transcript search</strong><div class='wf-box'>Matched chapter 03:12 · rollout risks and owner handoff</div><div class='wf-box'>Meeting summary and action items</div></div></div>"
14
+ }
15
+ ```
19
16
 
20
17
  Think along the lines of Loom + Granola + Wispr Flow rolled into one app — but the agent is a first-class editor across every surface, and the recordings, meetings, and dictations are yours, not a SaaS vendor's. Clips also makes shared recordings agent-readable: paste a normal Clips share link into an agent, and it can "hear" the transcript and "see" timestamped frames even when the underlying model cannot ingest raw video or audio.
21
18
 
@@ -141,17 +138,15 @@ Clips is a larger template with a native recorder (it ships a desktop companion
141
138
  2. **Google Calendar (optional).** To sync upcoming meetings, connect a Google Calendar account from Settings. The OAuth callback URL in dev is `http://localhost:8094/_agent-native/google/callback`. Set up a Google OAuth client in [Google Cloud Console](https://console.cloud.google.com/) with the Gmail and Google Calendar APIs enabled.
142
139
  3. **Screen-capture permissions.** On macOS, grant Screen Recording permission to the browser (or the desktop companion app) in System Settings → Privacy & Security → Screen Recording.
143
140
 
144
- ### Key features (technical)
145
-
146
- **One library, three capture types.** Screen recordings, calendar-sourced meetings, and push-to-talk dictations all live in the same searchable library. Recordings and transcripts are deliberately split into separate tables so the library grid and the transcript view each render fast.
141
+ ### Key features
147
142
 
148
- **Transcript and AI pipeline.** Each recording gets timestamped transcript segments (`{startMs, endMs, text}`), an auto-generated title, summary, and chapter markers. `cleanup-transcript` and `finalize-meeting` are server-side media-pipeline calls; most other AI features (titles, summaries, quotes) delegate to the agent chat.
143
+ **One library, three capture types.** Screen recordings, calendar meetings, and push-to-talk dictations share one searchable library.
149
144
 
150
- **Non-destructive editing.** Trim, split, filler-word removal, silence removal, and stitching accumulate in a recording's `edits_json` rather than re-encoding. The client concatenates and exports through ffmpeg.wasm, so the original media stays intact.
145
+ **Transcript and AI pipeline.** Recordings get timestamped transcript segments, generated titles, summaries, and chapter markers.
151
146
 
152
- **Agent-readable share links.** A public Clips share link advertises a compact agent context URL that points at the transcript and frame APIs, so text- and image-only models can understand a recording without ingesting raw video — see [Agent-readable clips](#agent-readable-clips) above.
147
+ **Non-destructive editing.** Trim, split, filler-word removal, silence removal, and stitching stay in `edits_json` so original media remains intact.
153
148
 
154
- **Meetings compose with recordings.** A meeting owns the recording it captures, but the `recordings` row stays the source of truth for the video and per-segment transcript instead of duplicating media.
149
+ **Agent-readable share links.** Public share links expose transcript and frame APIs so agents can understand recordings without ingesting raw video.
155
150
 
156
151
  ### Data model
157
152
 
@@ -11,17 +11,14 @@ you. Open a doc, ask "rewrite this paragraph to be more concise" or "create a
11
11
  page called Q4 Planning with sub-pages for Goals, Metrics, and Risks" - same
12
12
  result whether you do it yourself or ask.
13
13
 
14
- <!-- screenshot:
15
- app: content
16
- view: /<doc-id>
17
- shows: Workspace with sidebar tree (Q3 Roadmap favorited and expanded with Goals/Metrics/Risks, Engineering Wiki with On-call playbook + Architecture overview + Deployment guide, Personal section with Reading list and Ideas, Weekly sync May 1) and the Q3 Roadmap document open in the editor with the agent sidebar
18
- account: screenshot-account (page tree authored on this account; the doc body should NOT begin with the page title — the page chrome already renders it)
19
- capture: 1400x800 viewport, cropped 90px from bottom (final 1400x710)
20
- -->
21
-
22
- ![Content workspace with the page tree, an open document, and the agent sidebar](/screenshots/content.png)
14
+ ```an-wireframe
15
+ {
16
+ "surface": "desktop",
17
+ "html": "<div style='display:grid;grid-template-columns:210px 1fr;gap:14px;padding:16px;min-height:500px;box-sizing:border-box'><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Content</strong><span class='wf-pill accent'>Q3 Roadmap</span><span class='wf-pill'>Goals</span><span class='wf-pill'>Metrics</span><span class='wf-pill'>Risks</span><hr/><span class='wf-pill'>Engineering wiki</span><span class='wf-pill'>Reading list</span><span class='wf-pill'>Weekly sync</span></aside><main style='display:flex;flex-direction:column;gap:12px;min-width:0;padding:8px 20px'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Q3 Roadmap</h1><div style='flex:1'></div><button>Share</button><button class='primary'>Publish</button></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:12px;padding:22px'><h2 style='margin:0'>Launch goals</h2><p style='margin:0'>Ship the onboarding flow, reduce setup time, and document owner handoffs.</p><div class='wf-box'>At a glance · owner, window, status</div><div class='wf-box'>Top objectives</div><div class='wf-box'>Workstreams table</div></div></main></div>"
18
+ }
19
+ ```
23
20
 
24
- When you open the app, you'll see a sidebar tree of pages on the left, the editor in the middle, and the agent in the sidebar on the right. The agent always knows which page you're viewing and what text you have selected.
21
+ When you open the app, you'll see a page tree next to the editor. The agent always knows which page you're viewing and what text you have selected, so document edits can stay grounded in the current page.
25
22
 
26
23
  ```an-diagram title="One document, many editors" summary="You and the agent both write through the same Yjs pipeline. SQL is the canonical store; local files and Notion are optional sync surfaces."
27
24
  {
@@ -108,50 +105,19 @@ pnpm install
108
105
  pnpm dev
109
106
  ```
110
107
 
111
- Open `http://localhost:8083` and create your first page. The agent panel is on the right — try asking it to "create a page called Onboarding and add three sub-pages under it".
112
-
113
- ### Key features (technical) {#key-features}
114
-
115
- ### Hierarchical pages
116
-
117
- Documents nest infinitely via a `parent_id` column. The sidebar renders a draggable tree; children move with their parents and ordering uses an integer `position` field. See `app/components/sidebar/DocumentSidebar.tsx` and `app/components/sidebar/DocumentTreeItem.tsx`.
118
-
119
- ### Rich-text editor
120
-
121
- The editor is built on Tiptap with a custom extension set. It supports headings, lists, tables, code blocks with syntax highlighting, images, and links. Implementation lives in `app/components/editor/DocumentEditor.tsx` and `app/components/editor/VisualEditor.tsx`, with custom nodes under `app/components/editor/extensions/` (`CodeBlockNode.tsx`, `ImageNode.ts`, `DragHandle.tsx`, `NotionExtensions.tsx`).
122
-
123
- Interactive surfaces include:
124
-
125
- - `BubbleToolbar.tsx` — formatting toolbar that appears over a selection
126
- - `SlashCommandMenu.tsx` — slash-command inserter for blocks
127
- - `LinkHoverPreview.tsx` — hover previews for inline links
128
- - `TableHoverControls.tsx` — add/remove table rows and columns
129
- - `EmojiPicker.tsx` — emoji picker for page icons
130
-
131
- ### Collaborative editing
132
-
133
- Content is edited through Yjs CRDT so multiple users and the agent can type into the same document at once without clobbering each other. The agent's `edit-document` action writes through the same pipeline as a human keystroke, so changes appear live in every open editor. See [Real-time collaboration](/docs/real-time-collaboration) for the sync model.
134
-
135
- ### Search
136
-
137
- Full-text search across titles and markdown content, powered by `actions/search-documents.ts`. The sidebar exposes a search box; the agent uses the same action via `pnpm action search-documents --query "..."`.
108
+ Open `http://localhost:8083` and create your first page. Then ask the agent to "create a page called Onboarding and add three sub-pages under it".
138
109
 
139
- ### Favorites and icons
110
+ ### Key features {#key-features}
140
111
 
141
- Each document can be favorited (`is_favorite`) and given an emoji `icon`. The index route auto-opens your first favorite on load — see `app/routes/_app._index.tsx`.
112
+ **Nested pages.** Documents form a draggable tree with favorites, icons, ordering, and page-level sharing.
142
113
 
143
- ### Notion sync
114
+ **Rich MDX editor.** Tiptap powers headings, lists, tables, code blocks, images, links, slash commands, selection toolbars, and local React components.
144
115
 
145
- Documents can be linked to a Notion page and synced in either direction:
116
+ **Live collaboration.** Yjs keeps multiple editors and agent edits in sync without clobbering each other.
146
117
 
147
- - `connect-notion-status` check whether a Notion integration is connected
148
- - `link-notion-page` — link a local doc to a Notion page
149
- - `pull-notion-page` — overwrite local content from Notion
150
- - `push-notion-page` — overwrite Notion content from local
151
- - `list-notion-links` — list all linked documents
152
- - `sync-notion-comments` — bidirectionally sync comment threads
118
+ **Search and comments.** Full-text search, anchored comments, version history, and restore flows are built into the document surface.
153
119
 
154
- Sync state is tracked in the `document_sync_links` table (last synced time, conflict flag, last error). Markdown-to-Notion block conversion lives in `shared/notion-markdown.ts`. Conflict and status UI is in `app/components/editor/NotionConflictBanner.tsx` and `NotionSyncBar.tsx`. See the `notion-integration` skill for the full flow.
120
+ **Sync surfaces.** Documents can sync with Notion or local Markdown/MDX folders, with SQL acting as the collaborative cache/history layer.
155
121
 
156
122
  ### Local file sync
157
123
 
@@ -7,9 +7,14 @@ description: "An agent-native HTML prototyping studio — generate, refine, prev
7
7
 
8
8
  Design is an agent-native HTML prototyping studio. Instead of a layered drawing canvas, the agent generates complete self-contained Alpine/Tailwind HTML prototypes, renders them in an iframe, and lets you refine the result with prompts and tweak controls.
9
9
 
10
- ![Design studio showing generated HTML prototypes and tweak controls](https://cdn.builder.io/api/v1/image/assets%2F348da13fcd8b414c87de9066196f7266%2F961bedb713a94463b834c1f2f4643bcf?format=webp&width=1200)
10
+ ```an-wireframe
11
+ {
12
+ "surface": "desktop",
13
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:520px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Product launch page</h1><span class='wf-pill accent'>Desktop</span><span class='wf-pill'>Tablet</span><span class='wf-pill'>Mobile</span><div style='flex:1'></div><button>Preview</button><button class='primary'>Export code</button></div><div class='wf-card' style='flex:1;display:grid;grid-template-rows:auto 1fr auto;gap:12px'><div style='display:flex;gap:8px'><span class='wf-pill accent'>Hero</span><span class='wf-pill'>Pricing</span><span class='wf-pill'>FAQ</span></div><div class='wf-box' style='display:flex;align-items:center;justify-content:center;min-height:230px'><strong>Generated HTML prototype</strong></div><div class='wf-card' style='display:flex;align-items:center;gap:10px'><span class='wf-muted'>Make the hero denser and the CTA clearer.</span><div style='flex:1'></div><button class='primary'>Apply revision</button></div></div></div>"
14
+ }
15
+ ```
11
16
 
12
- When you open the app, you see your designs on the left, the generated prototype rendered in an iframe in the middle, and the agent plus tweak controls on the right. Everything the agent produces is real HTML you can refine, export, or hand off.
17
+ When you open the app, the generated prototype is the center of the workspace, with preview modes, prompt revisions, and export controls close at hand. Everything the agent produces is real HTML you can refine, export, or hand off.
13
18
 
14
19
  ```an-diagram title="One artifact, no translation" summary="The agent generates standalone Alpine/Tailwind HTML; the iframe, the editable source, and every export all read the same files. A linked design system feeds tokens into each pass."
15
20
  {
@@ -9,15 +9,12 @@ description: "Dispatch is the workspace control plane — central inbox, cross-a
9
9
 
10
10
  Dispatch is the **workspace control plane**. Where other templates are domain apps (Mail, Calendar, Analytics, Brain), Dispatch is the app you run _alongside_ them to coordinate everything: a central inbox, a secrets vault, scheduled jobs, Slack/Telegram integration, and an orchestrator agent that delegates domain work to the right specialist app over [A2A](/docs/a2a-protocol).
11
11
 
12
- <!-- screenshot:
13
- app: dispatch
14
- view: /overview
15
- shows: Overview with "What should we do next?" composer, prompt suggestions (Create a lightweight customer onboarding app / Ask Slides to draft a board update from our latest metrics / Schedule a Monday morning analytics digest), and the Workspace apps grid (Mail / Calendar / Slides / Analytics / Forms / Content + Create app placeholder) showing each mounted path + description
16
- account: screenshot-account (workspace seeded with the six sibling apps registered as A2A peers)
17
- capture: 1400x900 viewport, cropped 90px from bottom (final 1400x810)
18
- -->
19
-
20
- ![Dispatch overview with the orchestrator chat and workspace apps grid](/screenshots/dispatch.png)
12
+ ```an-wireframe
13
+ {
14
+ "surface": "desktop",
15
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:520px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Dispatch</h1><span class='wf-pill accent'>Overview</span><span class='wf-pill'>Inbox</span><span class='wf-pill'>Secrets</span><span class='wf-pill'>Approvals</span><div style='flex:1'></div><button>Schedules</button></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>What should we do next?</strong><div class='wf-box'>Ask Analytics for this week's signups and draft a Slack update.</div><button class='primary'>Delegate</button></div><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:10px'><div class='wf-card'><strong>Mail</strong><br/><small>/mail</small></div><div class='wf-card'><strong>Calendar</strong><br/><small>/calendar</small></div><div class='wf-card'><strong>Analytics</strong><br/><small>/analytics</small></div><div class='wf-card'><strong>Slides</strong><br/><small>/slides</small></div><div class='wf-card'><strong>Forms</strong><br/><small>/forms</small></div><div class='wf-card'><strong>Create app</strong><br/><small>+</small></div></div><div class='wf-card' style='display:grid;grid-template-columns:repeat(3,1fr);gap:8px'><div class='wf-box'>Slack DM needs reply</div><div class='wf-box'>A2A task completed</div><div class='wf-box'>Approval required</div></div></div>"
16
+ }
17
+ ```
21
18
 
22
19
  If you're running an [multi-app workspace](/docs/multi-app-workspace) with many apps, Dispatch is the glue.
23
20