@agent-native/core 0.9.1 → 0.10.0
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/README.md +4 -4
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +5 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +6 -3
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +8 -17
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +28 -11
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +12 -3
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +12 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +96 -0
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +16 -10
- package/dist/cli/create.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +6 -20
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +113 -28
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +21 -7
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-sidebar-state.d.ts +3 -0
- package/dist/client/agent-sidebar-state.d.ts.map +1 -0
- package/dist/client/agent-sidebar-state.js +24 -0
- package/dist/client/agent-sidebar-state.js.map +1 -0
- package/dist/client/analytics.d.ts +25 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +40 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/components/ui/tooltip.d.ts +2 -1
- package/dist/client/components/ui/tooltip.d.ts.map +1 -1
- package/dist/client/components/ui/tooltip.js +9 -2
- package/dist/client/components/ui/tooltip.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.js +41 -8
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +30 -0
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +26 -1
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/dev-overlay/DevOverlay.d.ts.map +1 -1
- package/dist/client/dev-overlay/DevOverlay.js +4 -4
- package/dist/client/dev-overlay/DevOverlay.js.map +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +6 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +14 -4
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionEditor.js +6 -6
- package/dist/client/extensions/ExtensionEditor.js.map +1 -1
- package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionSlot.js +2 -2
- package/dist/client/extensions/ExtensionSlot.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +39 -19
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +52 -52
- package/dist/client/extensions/ExtensionsSidebarSection.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/notifications/NotificationsBell.d.ts.map +1 -1
- package/dist/client/notifications/NotificationsBell.js +42 -6
- package/dist/client/notifications/NotificationsBell.js.map +1 -1
- package/dist/client/org/InvitationBanner.d.ts.map +1 -1
- package/dist/client/org/InvitationBanner.js +5 -5
- package/dist/client/org/InvitationBanner.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +3 -2
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +41 -7
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +2 -0
- package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
- package/dist/client/resources/use-mcp-servers.js +59 -3
- package/dist/client/resources/use-mcp-servers.js.map +1 -1
- package/dist/client/settings/SecretsSection.d.ts.map +1 -1
- package/dist/client/settings/SecretsSection.js +9 -0
- package/dist/client/settings/SecretsSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +12 -10
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +13 -30
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +27 -1
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +4 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +5 -1
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +14 -7
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +100 -19
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +5 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/route-discovery.d.ts.map +1 -1
- package/dist/deploy/route-discovery.js +1 -0
- package/dist/deploy/route-discovery.js.map +1 -1
- package/dist/deploy/workspace-core.d.ts +1 -1
- package/dist/deploy/workspace-core.d.ts.map +1 -1
- package/dist/deploy/workspace-core.js +1 -0
- package/dist/deploy/workspace-core.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +17 -3
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/routes.js +1 -1
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/schema.d.ts +14 -14
- package/dist/extensions/schema.d.ts.map +1 -1
- package/dist/extensions/schema.js +4 -4
- package/dist/extensions/schema.js.map +1 -1
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +23 -0
- package/dist/extensions/store.js.map +1 -1
- package/dist/extensions/theme.d.ts +8 -1
- package/dist/extensions/theme.d.ts.map +1 -1
- package/dist/extensions/theme.js +43 -34
- package/dist/extensions/theme.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +28 -1
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +77 -102
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +33 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +169 -68
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/credential-provider.d.ts +2 -2
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +31 -12
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +31 -0
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/google-realtime-session.d.ts.map +1 -1
- package/dist/server/google-realtime-session.js +19 -6
- package/dist/server/google-realtime-session.js.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +45 -6
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/request-context.d.ts +17 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +40 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/sentry-plugin.d.ts +11 -0
- package/dist/server/sentry-plugin.d.ts.map +1 -0
- package/dist/server/sentry-plugin.js +116 -0
- package/dist/server/sentry-plugin.js.map +1 -0
- package/dist/server/sentry.d.ts +92 -0
- package/dist/server/sentry.d.ts.map +1 -0
- package/dist/server/sentry.js +287 -0
- package/dist/server/sentry.js.map +1 -0
- package/dist/server/transcribe-voice.d.ts +2 -4
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +4 -16
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +19 -35
- package/dist/server/voice-providers-status.js.map +1 -1
- package/dist/styles/agent-native.css +15 -0
- package/docs/content/cloneable-saas.md +7 -9
- package/docs/content/deployment.md +6 -2
- package/docs/content/dispatch.md +1 -1
- package/docs/content/extensions.md +177 -142
- package/docs/content/faq.md +2 -2
- package/docs/content/getting-started.md +13 -11
- package/docs/content/multi-app-workspace.md +2 -2
- package/docs/content/observability.md +47 -0
- package/docs/content/pure-agent-apps.md +1 -1
- package/docs/content/template-clips.md +3 -3
- package/docs/content/template-design.md +3 -3
- package/docs/content/template-dispatch.md +1 -1
- package/docs/content/template-forms.md +1 -1
- package/docs/content/template-mail.md +1 -1
- package/docs/content/what-is-agent-native.md +4 -4
- package/docs/content/workspace.md +1 -1
- package/package.json +1 -1
|
@@ -1,230 +1,265 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Extensions"
|
|
3
|
-
description: "
|
|
3
|
+
description: "Mini-apps your users build inside your template — a custom KPI tile in Analytics, a meeting-prep checklist in Calendar, a contact CRM widget in Mail. No deploys, no code edits, no schema changes."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Extensions
|
|
7
7
|
|
|
8
|
-
Extensions are
|
|
8
|
+
Extensions are **mini-apps your users build inside your template**.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
If you've used QuickBooks Online, you've seen the model: QBO ships a core accounting product, and users layer on small custom widgets — a custom report, a payroll calculator, a tax-rule checker — that live inside the same app and use the same data. Extensions are the agent-native version of that idea, except your users don't write any code. They describe what they want, and the agent builds it.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
The framing matters: an extension isn't a generic "do whatever you want" sandbox. It's a **mini-app that extends a specific template** — Mail, Analytics, Calendar, Clips, Design — and uses that template's actions and data. A Mail extension reads emails. An Analytics extension reads a dashboard's metrics. A Calendar extension acts on the open event. They feel like part of the host product because they _are_ part of the host product.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Three things make extensions work:
|
|
15
15
|
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
16
|
+
- **No code, no deploy.** The agent writes them and they're live in seconds. Stored in the database, not the repo.
|
|
17
|
+
- **Full access to the template's data.** Extensions can call the same actions the agent calls — `list-emails` in Mail, `list-decks` in Slides, `list-recordings` in Clips — so they have everything the host app has.
|
|
18
|
+
- **Built-in storage.** Each extension has its own per-user / per-org key-value store, so it can save state without you adding a new SQL table.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
## A quick gallery {#gallery}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
Real extensions people would actually build, grouped by the template they live in. Each one is one focused thing — not a Swiss-army knife.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
### Mail
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
A user is reading an email from `priya@acme.com`. What kind of widget would help right there?
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
| **Requires a build** | Yes | No |
|
|
32
|
-
| **Requires a deploy** | Yes | No |
|
|
33
|
-
| **Scope** | Part of the app for all users | Private by default, shareable |
|
|
34
|
-
| **Best for** | Core app features | Personal dashboards, utilities, quick integrations |
|
|
28
|
+
- **Contact notes** — a sticky-note pad pinned to whoever the user is emailing. Loads notes for that contact, lets the user jot more.
|
|
29
|
+
- **Recent threads with this person** — a small list of the last five threads with the open contact, separate from the inbox view.
|
|
30
|
+
- **CRM enrichment** — pulls the contact's company size, last meeting date, or open deals from your CRM.
|
|
31
|
+
- **Meeting scheduler shortcut** — turns "find a time next week" into a one-click "send these slots" widget.
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
Sketch — Contact notes (saves a note tied to whoever you're emailing):
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
```html
|
|
36
|
+
<div
|
|
37
|
+
class="p-4"
|
|
38
|
+
x-data="{
|
|
39
|
+
contactEmail: window.slotContext?.contactEmail,
|
|
40
|
+
note: '',
|
|
41
|
+
async init() {
|
|
42
|
+
if (!this.contactEmail) return;
|
|
43
|
+
const saved = await extensionData.get('notes', this.contactEmail);
|
|
44
|
+
if (saved) this.note = JSON.parse(saved.data).text;
|
|
45
|
+
},
|
|
46
|
+
async save() {
|
|
47
|
+
await extensionData.set('notes', this.contactEmail, { text: this.note });
|
|
48
|
+
}
|
|
49
|
+
}"
|
|
50
|
+
>
|
|
51
|
+
<p class="text-xs text-muted-foreground mb-2" x-text="contactEmail"></p>
|
|
52
|
+
<textarea
|
|
53
|
+
x-model="note"
|
|
54
|
+
@blur="save()"
|
|
55
|
+
class="w-full rounded-md border bg-background p-2 text-sm"
|
|
56
|
+
rows="4"
|
|
57
|
+
placeholder="Notes about this contact..."
|
|
58
|
+
></textarea>
|
|
59
|
+
</div>
|
|
60
|
+
```
|
|
39
61
|
|
|
40
|
-
|
|
62
|
+
### Analytics
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
A user is staring at a dashboard. What's the missing tile?
|
|
43
65
|
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
- You want to ship it inside a single chat turn, no deploy.
|
|
66
|
+
- **Custom KPI box** — a single big number for a metric that isn't a built-in panel. "Trials started this week," "MRR delta vs last month."
|
|
67
|
+
- **Goal tracker** — pulls a metric the user picks and shows progress against a target the user typed in.
|
|
68
|
+
- **Top customers leaderboard** — joins a metric with a customer table, ranks the top 10.
|
|
48
69
|
|
|
49
|
-
|
|
70
|
+
Sketch — Custom KPI box (calls one of the analytics template's `appAction` queries):
|
|
71
|
+
|
|
72
|
+
```html
|
|
73
|
+
<div
|
|
74
|
+
class="p-4"
|
|
75
|
+
x-data="{
|
|
76
|
+
value: null,
|
|
77
|
+
async init() {
|
|
78
|
+
const result = await appAction('query-agent-native-analytics', {
|
|
79
|
+
metric: 'trials_started',
|
|
80
|
+
range: '7d'
|
|
81
|
+
});
|
|
82
|
+
this.value = result?.total ?? 0;
|
|
83
|
+
}
|
|
84
|
+
}"
|
|
85
|
+
>
|
|
86
|
+
<p class="text-xs uppercase tracking-wider text-muted-foreground">
|
|
87
|
+
Trials this week
|
|
88
|
+
</p>
|
|
89
|
+
<p class="text-3xl font-bold mt-1" x-text="value ?? '—'"></p>
|
|
90
|
+
</div>
|
|
91
|
+
```
|
|
50
92
|
|
|
51
|
-
|
|
52
|
-
- It needs new SQL tables, migrations, or shared schema changes.
|
|
53
|
-
- The UI is complex enough to warrant React components, routes, and proper testing.
|
|
54
|
-
- It's part of the product surface — something you'd advertise on a landing page.
|
|
93
|
+
### Calendar
|
|
55
94
|
|
|
56
|
-
|
|
95
|
+
The user has an event open. What would help in that moment?
|
|
57
96
|
|
|
58
|
-
|
|
97
|
+
- **Meeting prep checklist** — auto-loads agenda items, attendees, and prior thread summaries for the open event.
|
|
98
|
+
- **Travel time** — "you have 35 minutes until your next meeting at the Mission location."
|
|
99
|
+
- **Timezone helper** — shows the meeting time in every attendee's local time at a glance.
|
|
59
100
|
|
|
60
|
-
###
|
|
101
|
+
### Clips
|
|
61
102
|
|
|
62
|
-
|
|
103
|
+
A user is reviewing a screen recording. What enhances that view?
|
|
63
104
|
|
|
64
|
-
|
|
105
|
+
- **Action item extractor** — reads the clip transcript (the agent fetches it via `appAction`), lists the to-dos.
|
|
106
|
+
- **Auto-share** — one-click "post this clip's link to my #recordings Slack channel."
|
|
107
|
+
- **Highlight reel** — pulls the chapters the agent generated and turns them into a quick navigation menu.
|
|
65
108
|
|
|
66
|
-
|
|
109
|
+
### Design
|
|
67
110
|
|
|
68
|
-
|
|
111
|
+
A user has a draft Alpine/Tailwind page open. What would smooth the prototyping loop?
|
|
69
112
|
|
|
70
|
-
|
|
113
|
+
- **Brand color swatch** — palette pulled from the user's brand config, click to copy a color into the editor.
|
|
114
|
+
- **Asset picker** — lists images the user has uploaded, drops the URL on click.
|
|
115
|
+
- **Spacing inspector** — shows the gap/padding/margin tokens the active page uses, so the user can stay consistent.
|
|
71
116
|
|
|
72
|
-
|
|
117
|
+
Pattern across all of these: extensions are about **the moment** the user is in inside the host template. The agent already knows which contact, which dashboard, which event, which clip — the extension uses that context.
|
|
73
118
|
|
|
74
|
-
|
|
119
|
+
## How a user builds one {#building}
|
|
75
120
|
|
|
76
|
-
|
|
77
|
-
- **Call your app's actions** — anything your agent can do, an extension can trigger.
|
|
78
|
-
- **Query your app's database** — read and write data directly.
|
|
79
|
-
- **Store their own data** — each extension has built-in persistent storage, no setup required. Save notes, preferences, cached results — whatever the extension needs.
|
|
80
|
-
- **Call any endpoint in your app** — hit custom API routes, webhooks, or internal services.
|
|
121
|
+
The simple path:
|
|
81
122
|
|
|
82
|
-
|
|
123
|
+
1. **Click "New Extension"** in the sidebar (or just ask in chat).
|
|
124
|
+
2. **Describe what you want in one sentence.** "A sticky-note pad for the contact I'm emailing." "A KPI box for trials started this week."
|
|
125
|
+
3. **The agent writes it and it appears in your Extensions list, ready to use.**
|
|
83
126
|
|
|
84
|
-
|
|
127
|
+
No file to edit, no deploy. The agent picks the right helpers (`appAction`, `extensionData`, `extensionFetch`) and writes the Alpine.js HTML.
|
|
85
128
|
|
|
86
|
-
|
|
129
|
+
If the extension needs an API key — a CRM token, a weather API — the agent tells you what to add and where to add it. Keys are stored encrypted and locked to specific domains.
|
|
87
130
|
|
|
88
|
-
|
|
131
|
+
If you want to change something later, just say so: "Add a search box to my contact notes." The agent edits the HTML in place — no regeneration of the whole thing.
|
|
89
132
|
|
|
90
|
-
|
|
133
|
+
## What an extension can do {#capabilities}
|
|
91
134
|
|
|
92
|
-
|
|
135
|
+
Inside the iframe sandbox, every extension has these helpers on `window`:
|
|
93
136
|
|
|
94
|
-
|
|
137
|
+
| Helper | Purpose | Example |
|
|
138
|
+
| ------------------------------------------------ | ----------------------------------------------------- | ------------------------------------------------- |
|
|
139
|
+
| `appAction(name, params)` | Call any of the host template's actions | `appAction('list-emails', { view: 'inbox' })` |
|
|
140
|
+
| `appFetch(path, options)` | Call any app endpoint | `appFetch('/api/settings')` |
|
|
141
|
+
| `dbQuery(sql, args)` | Read from SQL (auto-scoped to the user) | `dbQuery('SELECT id, name FROM tools')` |
|
|
142
|
+
| `dbExec(sql, args)` | Write to SQL | `dbExec('INSERT INTO ...')` |
|
|
143
|
+
| `extensionFetch(url, options)` | Hit external APIs through a secure proxy with secrets | `extensionFetch('https://api.github.com/user')` |
|
|
144
|
+
| `extensionData.set(collection, id, data, opts?)` | Persist data per-extension (user / org scoping) | `extensionData.set('notes', id, { text: '...' })` |
|
|
145
|
+
| `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
|
|
146
|
+
| `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
|
|
147
|
+
| `extensionData.remove(collection, id, opts?)` | Delete a persisted item | `extensionData.remove('notes', 'note-1')` |
|
|
95
148
|
|
|
96
|
-
|
|
149
|
+
Two rules of thumb:
|
|
97
150
|
|
|
98
|
-
- `
|
|
99
|
-
- `'org'`
|
|
100
|
-
- `'all'` — list/get only; returns both user-scoped and org-scoped items.
|
|
151
|
+
- **Prefer `appAction` over `dbQuery`.** Actions are the template's official surface — they handle access control, scoping, and validation for you. Reach for raw SQL only when no action fits.
|
|
152
|
+
- **Prefer `extensionData` over making new tables.** Each extension gets its own isolated key-value store. No schema, no migration. Set `{ scope: 'org' }` to share with the user's org, `'user'` (default) for private.
|
|
101
153
|
|
|
102
154
|
```html
|
|
103
155
|
<script>
|
|
104
156
|
// Private to me
|
|
105
|
-
await extensionData.set('notes', 'note-1', { title: 'My
|
|
157
|
+
await extensionData.set('notes', 'note-1', { title: 'My note' });
|
|
106
158
|
|
|
107
159
|
// Shared with my org
|
|
108
|
-
await extensionData.set('notes', 'team-note', { title: 'Team
|
|
160
|
+
await extensionData.set('notes', 'team-note', { title: 'Team note' }, { scope: 'org' });
|
|
109
161
|
|
|
110
|
-
// List
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
// List both mine and the org's
|
|
114
|
-
const everything = await extensionData.list('notes', { scope: 'all' });
|
|
162
|
+
// List everything visible to me (mine + org)
|
|
163
|
+
const all = await extensionData.list('notes', { scope: 'all' });
|
|
115
164
|
</script>
|
|
116
165
|
```
|
|
117
166
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
## API keys and secrets {#secrets}
|
|
121
|
-
|
|
122
|
-
When an extension needs an API key (for GitHub, OpenAI, a weather service, etc.), the agent will tell you what's needed and where to get it. You add the key through the Settings UI in the agent sidebar.
|
|
123
|
-
|
|
124
|
-
Keys are encrypted and stored securely. Each key is restricted to specific domains — a GitHub token can only be sent to `api.github.com`, never anywhere else.
|
|
125
|
-
|
|
126
|
-
### Secrets in extensions {#secrets-in-extensions}
|
|
127
|
-
|
|
128
|
-
Extensions reference secrets in `extensionFetch()` calls (legacy alias: `toolFetch()`) using the `${keys.NAME}` template pattern. The proxy substitutes the encrypted value server-side; the actual key never reaches the browser.
|
|
167
|
+
External APIs go through `extensionFetch`, which proxies the call server-side and substitutes secrets via the `${keys.NAME}` template:
|
|
129
168
|
|
|
130
169
|
```html
|
|
131
170
|
<script>
|
|
132
171
|
const res = await extensionFetch('https://api.github.com/user', {
|
|
133
|
-
headers: {
|
|
134
|
-
Authorization: 'Bearer ${keys.GITHUB_TOKEN}',
|
|
135
|
-
},
|
|
172
|
+
headers: { Authorization: 'Bearer ${keys.GITHUB_TOKEN}' },
|
|
136
173
|
});
|
|
137
174
|
</script>
|
|
138
175
|
```
|
|
139
176
|
|
|
140
|
-
|
|
177
|
+
The actual key never reaches the browser. Each key is locked to an allowlist of domains, so a leaked extension can't exfiltrate it elsewhere.
|
|
141
178
|
|
|
142
|
-
##
|
|
179
|
+
## Slots — putting an extension inside the host UI {#slots}
|
|
143
180
|
|
|
144
|
-
|
|
181
|
+
The gallery above describes _what_ an extension does. Slots describe _where_ it appears.
|
|
145
182
|
|
|
146
|
-
|
|
183
|
+
By default, an extension lives on its own page in the Extensions list — open it like a small app. That's fine for dashboards, calculators, and standalone widgets.
|
|
147
184
|
|
|
148
|
-
|
|
149
|
-
- **Per-user sharing** — grant access to specific people as viewers, editors, or admins.
|
|
185
|
+
But the most QBO-shaped use case is different: the user wants their widget pinned _inside_ the template's UI — under the contact info in Mail's sidebar, in the corner of an Analytics dashboard, on the right side of a Calendar event. That's what **slots** are for.
|
|
150
186
|
|
|
151
|
-
|
|
187
|
+
A slot is a named widget area a template ships:
|
|
152
188
|
|
|
153
|
-
|
|
189
|
+
| Template | Example slot | Where it shows up |
|
|
190
|
+
| ------------- | ------------------------------ | -------------------------------------------- |
|
|
191
|
+
| **Mail** | `mail.contact-sidebar.bottom` | Below the contact info on every email thread |
|
|
192
|
+
| **Analytics** | `analytics.dashboard.tiles` | Alongside the dashboard's built-in panels |
|
|
193
|
+
| **Calendar** | `calendar.event-detail.bottom` | Below the open event |
|
|
194
|
+
| **Clips** | `clips.right-panel.tabs` | A new tab in the clip review panel |
|
|
154
195
|
|
|
155
|
-
|
|
196
|
+
When an extension is **installed into a slot**, the host pushes the relevant context — the contact's email, the dashboard id, the event id — into the iframe. The extension reads `window.slotContext` to know what the user is looking at.
|
|
197
|
+
|
|
198
|
+
### A concrete example
|
|
156
199
|
|
|
157
|
-
|
|
200
|
+
Imagine the contact-notes extension from the gallery. On its own, it's a standalone widget. To make it appear inside the Mail contact sidebar:
|
|
158
201
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
- **Private network protection** — extensions can't reach internal/private network addresses.
|
|
163
|
-
- **Authentication required** — only logged-in users can use extensions.
|
|
202
|
+
1. Build the extension once. Use `window.slotContext.contactEmail` so it knows which contact the user is on.
|
|
203
|
+
2. Tell it the slot it can fill: `add-extension-slot-target { extensionId, slotId: "mail.contact-sidebar.bottom" }`.
|
|
204
|
+
3. Install it: `install-extension { extensionId, slotId: "mail.contact-sidebar.bottom" }`.
|
|
164
205
|
|
|
165
|
-
|
|
206
|
+
The next time you open an email thread, your sticky-note pad is right under the contact info — populated with notes for the person you're emailing. Switch to a different thread, the notes for _that_ contact load. Same extension, different context, no rewrites.
|
|
166
207
|
|
|
167
|
-
|
|
208
|
+
In practice you don't run those three commands by hand. Just say "pin this widget to my contact sidebar" and the agent handles target + install for you.
|
|
168
209
|
|
|
169
|
-
|
|
170
|
-
| ------------------------------------------------ | -------------------------- | ------------------------------------------------- |
|
|
171
|
-
| `extensionData.set(collection, id, data, opts?)` | Persist data per-extension | `extensionData.set('notes', id, { text: '...' })` |
|
|
172
|
-
| `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
|
|
173
|
-
| `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
|
|
174
|
-
| `extensionData.remove(collection, id, opts?)` | Delete persisted item | `extensionData.remove('notes', 'note-1')` |
|
|
175
|
-
| `appAction(name, params)` | Call any app action | `appAction('list-emails', { view: 'inbox' })` |
|
|
176
|
-
| `dbQuery(sql, args)` | Read from SQL | `dbQuery('SELECT * FROM tools')` |
|
|
177
|
-
| `dbExec(sql, args)` | Write to SQL | `dbExec('INSERT INTO ...')` |
|
|
178
|
-
| `appFetch(path, options)` | Call any app endpoint | `appFetch('/api/settings')` |
|
|
179
|
-
| `extensionFetch(url, options)` | External API via proxy | `extensionFetch('https://api.github.com/...')` |
|
|
210
|
+
> **Slots are an _added_ capability, not a prerequisite.** Plenty of useful extensions never get installed into a slot — they live happily on their own page. Reach for slots when the widget needs to be _next to_ what the user is looking at in the host template.
|
|
180
211
|
|
|
181
|
-
|
|
212
|
+
For deeper detail on slots — how to declare them in your template, how the context contract works, how installs are scoped — see the `extension-points` skill.
|
|
182
213
|
|
|
183
|
-
|
|
214
|
+
## Sharing {#sharing}
|
|
215
|
+
|
|
216
|
+
Extensions are private to the user who created them by default. To share:
|
|
217
|
+
|
|
218
|
+
- **Org-visible** — everyone in the org can see and use it.
|
|
219
|
+
- **Per-user grants** — invite specific people as viewer / editor / admin.
|
|
184
220
|
|
|
185
|
-
|
|
221
|
+
Shared extensions have their own URLs and plug into the same share dialog as documents, decks, and dashboards. Slot installs are always personal — sharing an extension means others _can_ install it; it doesn't auto-pin it to their UI.
|
|
186
222
|
|
|
187
|
-
|
|
223
|
+
## Extensions vs. editing the app code {#vs-app-code}
|
|
188
224
|
|
|
189
|
-
|
|
190
|
-
| ------ | -------------------------------------- | ------------------------------------------------- |
|
|
191
|
-
| GET | `/_agent-native/extensions` | List extensions (filtered by ownership + sharing) |
|
|
192
|
-
| POST | `/_agent-native/extensions` | Create an extension |
|
|
193
|
-
| GET | `/_agent-native/extensions/:id` | Get a single extension |
|
|
194
|
-
| PUT | `/_agent-native/extensions/:id` | Update (supports `patches` for diffing) |
|
|
195
|
-
| DELETE | `/_agent-native/extensions/:id` | Delete an extension |
|
|
196
|
-
| GET | `/_agent-native/extensions/:id/render` | Render the iframe HTML |
|
|
197
|
-
| POST | `/_agent-native/extensions/proxy` | Authenticated proxy with secret injection |
|
|
225
|
+
The framework lets the agent edit the app's source code directly — components, routes, styles. So when should you reach for an extension instead?
|
|
198
226
|
|
|
199
|
-
|
|
227
|
+
| | Extension | App code edit |
|
|
228
|
+
| --------------------- | ------------------------------------------------- | ------------------------------------ |
|
|
229
|
+
| **Created by** | Agent (or user) at runtime | Agent editing source files |
|
|
230
|
+
| **Stored in** | The database | The git repository |
|
|
231
|
+
| **Requires a build** | No | Yes |
|
|
232
|
+
| **Requires a deploy** | No | Yes |
|
|
233
|
+
| **Scope** | One user (or shared with org) | The entire product, every user |
|
|
234
|
+
| **Best for** | Personal widgets, custom KPIs, per-team utilities | Core features that ship to all users |
|
|
200
235
|
|
|
201
|
-
|
|
236
|
+
Rule of thumb: **if it's for one user or one team, it's an extension.** If every user of the template should get it, ship it as a real feature.
|
|
237
|
+
|
|
238
|
+
## Security {#security}
|
|
202
239
|
|
|
203
|
-
|
|
204
|
-
| ------------------ | ------------------------------------------------------------------------- |
|
|
205
|
-
| `create-extension` | Create a new extension (name, description, Alpine.js HTML content) |
|
|
206
|
-
| `update-extension` | Update an extension — use `patches` array for find/replace diffs |
|
|
207
|
-
| `navigate` | Navigate to `--view=extensions` or `--view=extensions --extensionId=<id>` |
|
|
240
|
+
Extensions run in a sandboxed iframe:
|
|
208
241
|
|
|
209
|
-
|
|
242
|
+
- **Isolated** from the parent app's cookies, session, and DOM.
|
|
243
|
+
- **Server-side secret injection** via the `${keys.NAME}` template — the actual key value never reaches the browser.
|
|
244
|
+
- **Domain-locked secrets** — each key is bound to a URL allowlist; the proxy refuses requests to other hosts.
|
|
245
|
+
- **Private-network protection** — extensions can't reach internal addresses.
|
|
246
|
+
- **Auth required** — extensions only run for logged-in users, and `dbQuery` / `dbExec` calls are auto-scoped.
|
|
210
247
|
|
|
211
|
-
##
|
|
248
|
+
## A few things to know about naming {#naming-back-compat}
|
|
212
249
|
|
|
213
|
-
|
|
250
|
+
If you're poking around the SQL or the source, you'll see a mix of "extension" and "tool" names. Quick decoder:
|
|
214
251
|
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
- **Database explorer** — browse and query your app's data
|
|
220
|
-
- **Shipping cost calculator** — compute rates based on weight and destination
|
|
221
|
-
- **Meeting notes summarizer** — paste notes, get action items
|
|
222
|
-
- **Social media scheduler** — draft and schedule posts across platforms
|
|
252
|
+
- The user-facing primitive used to be called "Tools." It's now **Extensions**.
|
|
253
|
+
- The physical SQL tables (`tools`, `tool_data`, `tool_shares`, `tool_slots`, `tool_slot_installs`) keep their original names — renaming a table is a destructive migration, and the framework doesn't ship destructive migrations.
|
|
254
|
+
- The Drizzle / TypeScript exports use the new names: `extensions`, `extensionData`, `extensionShares`, `extensionSlots`, `extensionSlotInstalls`.
|
|
255
|
+
- Inside an extension's iframe, the canonical helpers are `extensionFetch` and `extensionData`. The legacy names `toolFetch` and `toolData` still resolve, so older extension HTML keeps working.
|
|
223
256
|
|
|
224
|
-
|
|
257
|
+
You also won't see this in normal use, but the agent has a third related concept called "LLM tools" — the function-call surface area on a model turn (defined via `defineAction`, MCP, etc.). Those are the function-calling primitive, not the user-facing widgets. When this page says "extension," it means the user-facing widget; when other docs say "tool" alongside `defineAction`, that's the LLM concept.
|
|
225
258
|
|
|
226
259
|
## What's next
|
|
227
260
|
|
|
228
|
-
- [**
|
|
229
|
-
- [**
|
|
230
|
-
- [**
|
|
261
|
+
- [**Templates**](/docs/cloneable-saas) — the host apps extensions extend
|
|
262
|
+
- [**Actions**](/docs/actions) — the operations an extension calls via `appAction`
|
|
263
|
+
- [**Sharing & Privacy**](/docs/sharing) — how extension visibility, org sharing, and per-user grants work
|
|
264
|
+
- [**Onboarding & API Keys**](/docs/onboarding) — how secrets surface in the settings UI
|
|
265
|
+
- [**Security**](/docs/security) — the framework's data scoping and access model
|
package/docs/content/faq.md
CHANGED
|
@@ -17,7 +17,7 @@ Agent-native is a framework for building apps where the AI agent and the UI are
|
|
|
17
17
|
|
|
18
18
|
Anyone who wants to ship an AI-powered product without spending a year building the boring parts. That includes:
|
|
19
19
|
|
|
20
|
-
- **Founders and PMs** who want to fork a working SaaS and make it their own — see [
|
|
20
|
+
- **Founders and PMs** who want to fork a working SaaS and make it their own — see [Templates](/docs/cloneable-saas).
|
|
21
21
|
- **Operators** who want a [pure-agent app](/docs/pure-agent-apps) — an agent that does work in the background with a minimal management UI.
|
|
22
22
|
- **Developers** building agent-driven products from scratch and want auth, multi-tenancy, real-time sync, and an architecture that won't break when AI is the primary user.
|
|
23
23
|
|
|
@@ -71,7 +71,7 @@ The framework ships with production-ready templates you can use as daily drivers
|
|
|
71
71
|
- **[Forms](/templates/forms)** — form builder (like Typeform)
|
|
72
72
|
- **[Dispatch](/templates/dispatch)** — workspace control plane: shared secrets, integrations, jobs
|
|
73
73
|
|
|
74
|
-
Each template is a complete app with UI, agent actions, database schema, and AI instructions. See [
|
|
74
|
+
Each template is a complete app with UI, agent actions, database schema, and AI instructions. See [Templates](/docs/cloneable-saas) for the full picture, or all [Templates](/templates).
|
|
75
75
|
|
|
76
76
|
### Can I customize templates? {#can-i-customize-templates}
|
|
77
77
|
|
|
@@ -12,7 +12,7 @@ By the end of this page, you'll have a working app — Mail, Calendar, Forms, or
|
|
|
12
12
|
There are two ways to use agent-native, depending on how hands-on you want to be:
|
|
13
13
|
|
|
14
14
|
- **You want to use a hosted version.** Try a template right now at [agent-native.com/templates](/templates). Each template is a live, hosted app — you sign in, start using it, and the agent is already there. No install, no setup. You can stop reading this page and head straight to the [template gallery](/templates).
|
|
15
|
-
- **You want to run locally or customize it.** You'll clone a template, run it on your machine, and shape it however you want — branding, features, integrations. The rest of this page is for you. You'll need [Node.js](https://nodejs.org) and [pnpm](https://pnpm.io) installed.
|
|
15
|
+
- **You want to run locally or customize it.** You'll clone a template, run it on your machine, and shape it however you want — branding, features, integrations. The rest of this page is for you. You'll need [Node.js 24 LTS](https://nodejs.org) and [pnpm](https://pnpm.io) installed.
|
|
16
16
|
|
|
17
17
|
Not sure which path? If you've never written code, the hosted version is for you. If you have a developer or AI coding tool ready, the local path gives you total control.
|
|
18
18
|
|
|
@@ -26,9 +26,9 @@ cd my-platform
|
|
|
26
26
|
pnpm install && pnpm dev
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The `create` command shows a multi-select picker — pick one template or several (Mail + Calendar + Forms, for example) and they all scaffold into one workspace sharing auth, brand, and agent config.
|
|
29
|
+
The `create` command defaults to a workspace monorepo. It shows a multi-select picker — pick one template or several (Mail + Calendar + Forms, for example) and they all scaffold into one workspace sharing auth, brand, and agent config. If you want one app directory instead, pass `--standalone`.
|
|
30
30
|
|
|
31
|
-
Open the URL the dev server prints
|
|
31
|
+
Open the URL the dev server prints. Workspace apps use app-specific ports, often `http://localhost:8080` or another 808x port; standalone apps usually use `http://localhost:3000`.
|
|
32
32
|
|
|
33
33
|
## What just happened? {#what-just-happened}
|
|
34
34
|
|
|
@@ -40,24 +40,26 @@ You now have a real, full-featured app running on your machine. Open it in the b
|
|
|
40
40
|
|
|
41
41
|
That parity between agent and UI is the whole point — see [What Is Agent-Native?](/docs/what-is-agent-native) for the bigger picture.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## Try one concrete next step {#first-next-step}
|
|
44
44
|
|
|
45
45
|
From here, use any AI coding tool (Claude Code, Cursor, Windsurf, Builder.io) to customize the app. The agent instructions in `AGENTS.md` are already set up so any tool understands the codebase.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Good first moves:
|
|
48
48
|
|
|
49
|
-
- **
|
|
49
|
+
- **Ask the built-in agent what it sees** — open the agent panel and type "what app am I looking at, and what can you do here?" This verifies the app, UI state, and agent loop are all talking to each other.
|
|
50
|
+
- **Make a tiny customization** — ask your coding tool to rename the app, change the first screen copy, or add one field to a form. It will read `AGENTS.md` for the framework conventions.
|
|
51
|
+
- **Add another app to the same workspace** — use `npx @agent-native/core add-app` from inside the workspace folder. The command starts at `npx`.
|
|
50
52
|
- **Single app instead of a monorepo?** Pass `--standalone` when creating: `npx @agent-native/core create my-app --standalone --template mail`.
|
|
51
|
-
- **Build a brand-new template from scratch** — see [Creating Templates](/docs/creating-templates) for the full Vite, Tailwind, and TypeScript setup.
|
|
52
|
-
- **Understand the architecture** — see [Key Concepts](/docs/key-concepts) for how SQL, actions, polling sync, and context awareness fit together.
|
|
53
53
|
|
|
54
|
-
##
|
|
54
|
+
## Next docs to read {#next-docs}
|
|
55
55
|
|
|
56
|
-
Once your app is running, the most
|
|
56
|
+
Once your app is running, the most useful follow-ups are:
|
|
57
57
|
|
|
58
58
|
- **Connect Slack or email** so you can message your agent from anywhere — see [Messaging](/docs/messaging).
|
|
59
59
|
- **Set up Dispatch as your central inbox** to triage messages and orchestrate across multiple apps — see [Dispatch](/docs/dispatch).
|
|
60
60
|
- **Customize via Workspace** — edit instructions, skills, memory, and connect MCP servers per user — see [Workspace](/docs/workspace).
|
|
61
|
+
- **Troubleshoot common setup questions** — see the [FAQ](/docs/faq).
|
|
62
|
+
- **Understand the architecture** — see [Key Concepts](/docs/key-concepts) for how SQL, actions, polling sync, and context awareness fit together.
|
|
61
63
|
|
|
62
64
|
## Templates {#templates}
|
|
63
65
|
|
|
@@ -77,7 +79,7 @@ Each template is a complete app with UI, agent actions, database schema, and AI
|
|
|
77
79
|
| [Dispatch](/docs/template-dispatch) | Workspace control plane — secrets, routing, jobs |
|
|
78
80
|
| [Starter](/docs/template-starter) | Minimal scaffold — build from scratch |
|
|
79
81
|
|
|
80
|
-
Browse the [template gallery](/templates) for live demos, or see [
|
|
82
|
+
Browse the [template gallery](/templates) for live demos, or see [Templates](/docs/cloneable-saas) for the full list and the clone → customize → deploy flow.
|
|
81
83
|
|
|
82
84
|
## Project structure {#project-structure}
|
|
83
85
|
|
|
@@ -86,13 +86,13 @@ Every app already knows how to log in, share the same database, and load the wor
|
|
|
86
86
|
From anywhere inside the workspace:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
agent-native add-app
|
|
89
|
+
npx @agent-native/core add-app
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
The CLI shows the template picker again with apps you've already installed filtered out. Pick one or more and they get scaffolded under `apps/`. Non-interactive variant:
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
|
-
agent-native add-app crm --template content
|
|
95
|
+
npx @agent-native/core add-app crm --template content
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
Any first-party template works as a workspace app — the CLI runs a small **workspacify** transform on the template that adds the shared package as a dep and resolves `workspace:*` references. No parallel "workspace-app" scaffold to maintain.
|
|
@@ -182,3 +182,50 @@ await putSetting("observability-config", {
|
|
|
182
182
|
```
|
|
183
183
|
|
|
184
184
|
The framework emits `gen_ai.*` semantic convention spans compatible with the OpenTelemetry GenAI spec.
|
|
185
|
+
|
|
186
|
+
## Error Reporting (Sentry)
|
|
187
|
+
|
|
188
|
+
Server-side errors that escape Nitro route handlers are reported to Sentry when a DSN is configured. Without it the SDK silently no-ops, so it's safe to leave the env vars unset in dev. Three independent Sentry projects cover the framework:
|
|
189
|
+
|
|
190
|
+
| Surface | SDK | Env var | Notes |
|
|
191
|
+
| ------------------ | ----------------- | ------------------------ | --------------------------------------------------------------------- |
|
|
192
|
+
| Browser / SPA | `@sentry/browser` | `VITE_SENTRY_CLIENT_DSN` | Captures unhandled errors and route-change breadcrumbs in the client. |
|
|
193
|
+
| Nitro server | `@sentry/node` | `SENTRY_SERVER_DSN` | Captures 5xx responses and Nitro lifecycle errors. Per-request user. |
|
|
194
|
+
| `agent-native` CLI | `@sentry/node` | _hardcoded_ | Crash reports from the published CLI binary; not user-configurable. |
|
|
195
|
+
|
|
196
|
+
### Server-side configuration
|
|
197
|
+
|
|
198
|
+
Set `SENTRY_SERVER_DSN` in the deploy environment (Netlify dashboard, Cloudflare secrets, etc.). The framework auto-mounts a Nitro plugin that:
|
|
199
|
+
|
|
200
|
+
1. Calls `Sentry.init` once at startup (idempotent — safe to call from multiple plugins).
|
|
201
|
+
2. Resolves the user via `getSession(event)` on every API/framework request and attaches `id` / `email` / `username` plus an `orgId` tag to Sentry's per-request isolation scope. Static-asset paths are skipped to avoid extra DB hits.
|
|
202
|
+
3. Captures every framework-route 5xx with searchable `route`, `method`, and `userAgent` tags.
|
|
203
|
+
|
|
204
|
+
Optional knobs:
|
|
205
|
+
|
|
206
|
+
- `SENTRY_SERVER_TRACES_SAMPLE_RATE` (float `0`–`1`) — opt in to performance tracing. Defaults to `0` (errors only). Invalid values clamp to `0`.
|
|
207
|
+
- `AGENT_NATIVE_RELEASE` — overrides the `release` tag. Defaults to `agent-native-server@<core-version>`.
|
|
208
|
+
|
|
209
|
+
### Templates
|
|
210
|
+
|
|
211
|
+
Every template inherits this automatically — there's nothing to import. Templates that want custom behavior (extra tags, different DSN per template, hard-disable Sentry) can override by exporting their own plugin from `server/plugins/sentry.ts`:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
// server/plugins/sentry.ts
|
|
215
|
+
import { createSentryPlugin } from "@agent-native/core/server";
|
|
216
|
+
export default createSentryPlugin();
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The CLI's hardcoded DSN is intentional — the published binary needs to phone home crashes regardless of which environment runs it. The server module never hardcodes a DSN because it runs inside customer environments where operators decide whether errors should reach Sentry at all.
|
|
220
|
+
|
|
221
|
+
### Privacy & PII
|
|
222
|
+
|
|
223
|
+
Both server and CLI initialize with `sendDefaultPii: false` and a `beforeSend` hook that strips:
|
|
224
|
+
|
|
225
|
+
- `request.headers.authorization`, `cookie`, `set-cookie`, `proxy-authorization`
|
|
226
|
+
- `request.cookies`
|
|
227
|
+
- `user.ip_address` (auto-collected without consent)
|
|
228
|
+
- `contexts.runtime_env` (process env snapshot)
|
|
229
|
+
- Any event whose top-level exception type is `ValidationError` (treated as expected user-input rejection, not a bug).
|
|
230
|
+
|
|
231
|
+
Identity fields explicitly set via `setUser({ id, email, username })` are preserved.
|
|
@@ -33,7 +33,7 @@ Pick the pure-agent pattern when:
|
|
|
33
33
|
- **The domain is one-shot.** Research bot, summary generator, report writer. There's no persistent object that needs a list view.
|
|
34
34
|
- **You're prototyping.** Ship the agent now; add a richer UI later if it turns out users actually want one.
|
|
35
35
|
|
|
36
|
-
If your product is built around persistent objects users browse, pivot, and share — emails, events, documents, charts — pick a [
|
|
36
|
+
If your product is built around persistent objects users browse, pivot, and share — emails, events, documents, charts — pick a [template](/docs/cloneable-saas) instead. Those have full UIs _plus_ the agent.
|
|
37
37
|
|
|
38
38
|
## What ships in the box {#minimum-ui}
|
|
39
39
|
|