@agent-native/core 0.63.2 → 0.63.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles/blocks.css +25 -3
- package/docs/content/a2a-protocol.md +48 -10
- package/docs/content/actions.md +35 -16
- package/docs/content/agent-mentions.md +25 -32
- package/docs/content/agent-surfaces.md +31 -0
- package/docs/content/agent-teams.md +17 -14
- package/docs/content/agent-web-surfaces.md +24 -15
- package/docs/content/authentication.md +21 -0
- package/docs/content/automations.md +37 -21
- package/docs/content/blueprint-installer.md +7 -0
- package/docs/content/cli-adapters.md +7 -0
- package/docs/content/client.md +14 -0
- package/docs/content/cloneable-saas.md +14 -0
- package/docs/content/code-agents-ui.md +27 -0
- package/docs/content/components.md +21 -0
- package/docs/content/context-awareness.md +33 -48
- package/docs/content/creating-templates.md +43 -52
- package/docs/content/cross-app-sso.md +41 -0
- package/docs/content/database.md +41 -21
- package/docs/content/deployment.md +23 -6
- package/docs/content/dispatch.md +27 -0
- package/docs/content/drop-in-agent.md +26 -27
- package/docs/content/durable-resume.md +13 -1
- package/docs/content/embedding-sdk.md +14 -0
- package/docs/content/evals.md +14 -0
- package/docs/content/extensions.md +19 -0
- package/docs/content/external-agents.md +38 -1
- package/docs/content/faq.md +14 -0
- package/docs/content/file-uploads.md +20 -0
- package/docs/content/frames.md +14 -0
- package/docs/content/getting-started.md +34 -16
- package/docs/content/harness-agents.md +14 -0
- package/docs/content/human-approval.md +15 -30
- package/docs/content/key-concepts.md +37 -0
- package/docs/content/local-file-mode.md +26 -19
- package/docs/content/mcp-apps.md +36 -1
- package/docs/content/mcp-clients.md +49 -0
- package/docs/content/mcp-protocol.md +36 -0
- package/docs/content/messaging.md +34 -8
- package/docs/content/migration-workbench.md +7 -0
- package/docs/content/multi-app-workspace.md +29 -16
- package/docs/content/multi-tenancy.md +14 -0
- package/docs/content/native-chat-ui.md +20 -3
- package/docs/content/notifications.md +30 -0
- package/docs/content/observability.md +20 -1
- package/docs/content/observational-memory.md +14 -0
- package/docs/content/onboarding.md +32 -41
- package/docs/content/plan-plugin.md +14 -0
- package/docs/content/pr-visual-recap.md +14 -0
- package/docs/content/processors.md +7 -0
- package/docs/content/progress.md +23 -0
- package/docs/content/pure-agent-apps.md +7 -0
- package/docs/content/real-time-collaboration.md +19 -18
- package/docs/content/recurring-jobs.md +22 -19
- package/docs/content/routing.md +8 -0
- package/docs/content/sandbox-adapters.md +19 -0
- package/docs/content/security.md +38 -0
- package/docs/content/server.md +47 -25
- package/docs/content/sharing.md +50 -0
- package/docs/content/skills-guide.md +27 -42
- package/docs/content/template-analytics.md +60 -0
- package/docs/content/template-assets.md +68 -0
- package/docs/content/template-brain.md +83 -0
- package/docs/content/template-calendar.md +74 -0
- package/docs/content/template-chat.md +19 -0
- package/docs/content/template-clips.md +113 -0
- package/docs/content/template-content.md +133 -0
- package/docs/content/template-design.md +55 -0
- package/docs/content/template-dispatch.md +50 -0
- package/docs/content/template-forms.md +59 -0
- package/docs/content/template-mail.md +62 -0
- package/docs/content/template-plan.md +74 -0
- package/docs/content/template-slides.md +82 -0
- package/docs/content/template-videos.md +62 -0
- package/docs/content/tracking.md +21 -0
- package/docs/content/using-your-agent.md +14 -0
- package/docs/content/voice-input.md +31 -10
- package/docs/content/what-is-agent-native.md +25 -13
- package/docs/content/workspace-connections.md +49 -0
- package/docs/content/workspace-management.md +24 -0
- package/docs/content/workspace.md +50 -19
- package/docs/content/writing-agent-instructions.md +7 -0
- package/package.json +1 -1
|
@@ -19,6 +19,13 @@ Forms is an agent-native form builder. Describe the form you want, refine it in
|
|
|
19
19
|
|
|
20
20
|
When you open the app, you see your forms, the current editor, and a live preview. The agent can create a form from a prompt, update field labels and options, change validation, and connect submission destinations using the same actions the UI uses.
|
|
21
21
|
|
|
22
|
+
```an-diagram title="Build, publish, collect" summary="The agent and the visual editor edit one SQL-backed form definition. The public fill page is unauthenticated, and submissions route server-side to your destinations."
|
|
23
|
+
{
|
|
24
|
+
"html": "<div class=\"diagram-flow\"><div class=\"diagram-col\"><div class=\"diagram-node\">Agent prompt<br><small class=\"diagram-muted\">\"add an NPS question\"</small></div><div class=\"diagram-node\">Visual editor<br><small class=\"diagram-muted\">labels, validation, order</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\"><span class=\"diagram-pill accent\">create-form · update-form</span><small class=\"diagram-muted\">fields JSON, settings JSON</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">forms table<br><small class=\"diagram-muted\">SQL via Drizzle</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-col\"><div class=\"diagram-box\">Public fill page<br><small class=\"diagram-muted\">unauthenticated</small></div><div class=\"diagram-box\">responses<br><small class=\"diagram-muted\">+ Slack / webhook / Sheets</small></div></div></div>",
|
|
25
|
+
"css": ".diagram-flow{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-flow .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-flow .diagram-arrow{font-size:22px;line-height:1}.diagram-flow .center{display:flex;flex-direction:column;align-items:center;gap:4px}"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
22
29
|
## What you can do with it
|
|
23
30
|
|
|
24
31
|
- **Build forms conversationally.** "Create a contact form," "add an NPS score question," "make the email field required." The agent updates the form schema and the preview updates from SQL-backed state.
|
|
@@ -90,6 +97,58 @@ All data lives in SQL via Drizzle ORM. Schema: `templates/forms/server/db/schema
|
|
|
90
97
|
|
|
91
98
|
The `fields` and `settings` JSON shapes are defined in `templates/forms/shared/types.ts` (`FormField`, `FormSettings`). Owner-private settings such as integration webhook URLs and allowed origins are stripped before any data reaches the public fill page via `toPublicFormSettings`.
|
|
92
99
|
|
|
100
|
+
```an-schema title="Forms data model" summary="Three tables. Fields and integrations are JSON columns on forms, so the agent's edits are surgical patches rather than cross-table row changes."
|
|
101
|
+
{
|
|
102
|
+
"entities": [
|
|
103
|
+
{
|
|
104
|
+
"id": "forms",
|
|
105
|
+
"name": "forms",
|
|
106
|
+
"note": "A form definition (ownable)",
|
|
107
|
+
"fields": [
|
|
108
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
109
|
+
{ "name": "title", "type": "string" },
|
|
110
|
+
{ "name": "description", "type": "string", "nullable": true },
|
|
111
|
+
{ "name": "slug", "type": "string", "note": "unique; public URL" },
|
|
112
|
+
{ "name": "fields", "type": "json", "note": "FormField[] — all field types" },
|
|
113
|
+
{ "name": "settings", "type": "json", "note": "FormSettings — integrations, etc." },
|
|
114
|
+
{ "name": "status", "type": "enum", "note": "draft | published | closed" },
|
|
115
|
+
{ "name": "deleted_at", "type": "datetime", "nullable": true, "note": "soft delete" },
|
|
116
|
+
{ "name": "owner_email", "type": "string" },
|
|
117
|
+
{ "name": "org_id", "type": "id", "nullable": true }
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "responses",
|
|
122
|
+
"name": "responses",
|
|
123
|
+
"note": "One submission per row",
|
|
124
|
+
"fields": [
|
|
125
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
126
|
+
{ "name": "form_id", "type": "id", "fk": "forms.id" },
|
|
127
|
+
{ "name": "data", "type": "json", "note": "{ fieldId: value }" },
|
|
128
|
+
{ "name": "submitted_at", "type": "datetime" },
|
|
129
|
+
{ "name": "ip", "type": "string", "nullable": true },
|
|
130
|
+
{ "name": "submitter_email", "type": "string", "nullable": true }
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "form_shares",
|
|
135
|
+
"name": "form_shares",
|
|
136
|
+
"note": "Framework shares table — principals to roles per form",
|
|
137
|
+
"fields": [
|
|
138
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
139
|
+
{ "name": "form_id", "type": "id", "fk": "forms.id" },
|
|
140
|
+
{ "name": "principal", "type": "string", "note": "user or org" },
|
|
141
|
+
{ "name": "role", "type": "enum", "note": "viewer | editor | admin" }
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
"relations": [
|
|
146
|
+
{ "from": "forms", "to": "responses", "kind": "1-n", "label": "has responses" },
|
|
147
|
+
{ "from": "forms", "to": "form_shares", "kind": "1-n", "label": "has share grants" }
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
93
152
|
### Key actions
|
|
94
153
|
|
|
95
154
|
Every operation is a TypeScript file in `templates/forms/actions/`, auto-mounted at `POST /_agent-native/actions/:name`:
|
|
@@ -19,6 +19,13 @@ An agent-powered email client. Connect your Gmail account and the agent can read
|
|
|
19
19
|
|
|
20
20
|
When you open the app, you'll see your inbox on the left, the open thread in the middle, and the agent in the sidebar on the right. The agent always knows which view you're in and which thread you have open, so you can say "archive this" or "draft a friendly decline" without explaining what "this" is.
|
|
21
21
|
|
|
22
|
+
```an-diagram title="How a mail request flows" summary="Keyboard shortcuts and agent prompts run the same actions. Email lives in Gmail; drafts, automations, and tracking live in SQL and application_state."
|
|
23
|
+
{
|
|
24
|
+
"html": "<div class=\"diagram-flow\"><div class=\"diagram-col\"><div class=\"diagram-node\">You drive<br><small class=\"diagram-muted\">J/K/E/R shortcuts</small></div><div class=\"diagram-node\">You ask the agent<br><small class=\"diagram-muted\">\"draft a friendly decline\"</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\"><span class=\"diagram-pill accent\">Actions</span><small class=\"diagram-muted\">list-emails · get-thread · manage-draft · send</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-col\"><div class=\"diagram-box\">Gmail<br><small class=\"diagram-muted\">multi-account, via OAuth</small></div><div class=\"diagram-box\">SQL + application_state<br><small class=\"diagram-muted\">drafts · automations · tracking</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">↻</div><div class=\"diagram-box\">Inbox refreshes live</div></div>",
|
|
25
|
+
"css": ".diagram-flow{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-flow .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-flow .diagram-arrow{font-size:22px;line-height:1}.diagram-flow .center{display:flex;flex-direction:column;align-items:center;gap:4px}"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
22
29
|
## What you can do with it
|
|
23
30
|
|
|
24
31
|
- **Read and triage email** with keyboard shortcuts (`J`/`K` to move, `E` to archive, `R` to reply, `C` to compose).
|
|
@@ -164,6 +171,61 @@ When a Google account is connected, email lives in Gmail — the app is a view o
|
|
|
164
171
|
|
|
165
172
|
Emails flowing through the API have the shape `{ id, threadId, from, to, cc, subject, snippet, body, date, isRead, isStarred, isArchived, isTrashed, labelIds, accountEmail, attachments }`.
|
|
166
173
|
|
|
174
|
+
```an-schema title="Mail SQL tables" summary="Email itself lives in Gmail. The SQL tables hold what Gmail doesn't: queued drafts, send-tracking events, and OAuth tokens. Settings and ephemeral state live in the settings and application_state stores."
|
|
175
|
+
{
|
|
176
|
+
"entities": [
|
|
177
|
+
{
|
|
178
|
+
"id": "queued_email_drafts",
|
|
179
|
+
"name": "queued_email_drafts",
|
|
180
|
+
"note": "Teammate/Slack-requested drafts awaiting owner review",
|
|
181
|
+
"fields": [
|
|
182
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
183
|
+
{ "name": "assignedTo", "type": "string", "note": "org member who reviews/sends" },
|
|
184
|
+
{ "name": "subject", "type": "string" },
|
|
185
|
+
{ "name": "body", "type": "markdown" },
|
|
186
|
+
{ "name": "status", "type": "enum", "note": "review at /draft-queue/<id>" }
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "email_tracking",
|
|
191
|
+
"name": "email_tracking",
|
|
192
|
+
"note": "Open-pixel events for sent messages",
|
|
193
|
+
"fields": [
|
|
194
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
195
|
+
{ "name": "messageId", "type": "string" },
|
|
196
|
+
{ "name": "openedAt", "type": "datetime" }
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"id": "email_link_tracking",
|
|
201
|
+
"name": "email_link_tracking",
|
|
202
|
+
"note": "Link-click events for sent messages",
|
|
203
|
+
"fields": [
|
|
204
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
205
|
+
{ "name": "messageId", "type": "string", "fk": "email_tracking.messageId" },
|
|
206
|
+
{ "name": "url", "type": "string" },
|
|
207
|
+
{ "name": "clickedAt", "type": "datetime" }
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"id": "oauth_tokens",
|
|
212
|
+
"name": "oauth_tokens",
|
|
213
|
+
"note": "Framework table — one row per connected Google account",
|
|
214
|
+
"fields": [
|
|
215
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
216
|
+
{ "name": "provider", "type": "string", "note": "\"google\"" },
|
|
217
|
+
{ "name": "accountEmail", "type": "string" },
|
|
218
|
+
{ "name": "accessToken", "type": "string" },
|
|
219
|
+
{ "name": "refreshToken", "type": "string" }
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
"relations": [
|
|
224
|
+
{ "from": "email_tracking", "to": "email_link_tracking", "kind": "1-n", "label": "click events" }
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
167
229
|
Routes in the UI:
|
|
168
230
|
|
|
169
231
|
- `/_index.tsx` — redirects to the default inbox view.
|
|
@@ -23,6 +23,13 @@ commit, branch, or git diff — into a high-altitude visual code review. Both op
|
|
|
23
23
|
the same review surface, so you annotate, comment, and hand feedback back to the
|
|
24
24
|
agent the same way.
|
|
25
25
|
|
|
26
|
+
```an-diagram title="Two commands, one review surface" summary="Both commands publish through the hosted Plan MCP connector into the same annotate-and-comment surface."
|
|
27
|
+
{
|
|
28
|
+
"html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">before code — architecture, UI, refactor</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">after code — PR, commit, branch, diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\">Plan MCP connector<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">Review surface<br><small class=\"diagram-muted\">diagrams · wireframes · annotated code · comments</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">↔</div><div class=\"diagram-node\">Coding agent<br><small class=\"diagram-muted\">feedback handed back</small></div></div>",
|
|
29
|
+
"css": ".diagram-plan{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-plan .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-plan .diagram-arrow{font-size:22px;line-height:1}.diagram-plan .center{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}"
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
26
33
|

|
|
27
34
|
|
|
28
35
|
There are two ways into Plans:
|
|
@@ -384,6 +391,73 @@ Schema lives in `templates/plan/server/db/schema.ts`. Core tables:
|
|
|
384
391
|
| `plan_guest_mints` | Rate-limit records for guest session issuance |
|
|
385
392
|
| `plan_assets` | Inline image assets stored as base64 (fallback when no upload provider) |
|
|
386
393
|
|
|
394
|
+
```an-schema title="Plan data model" summary="One plan row owns ordered sections plus comments, events, versions, shares, and inline assets."
|
|
395
|
+
{
|
|
396
|
+
"entities": [
|
|
397
|
+
{ "id": "plans", "name": "plans", "note": "each plan or recap", "fields": [
|
|
398
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
399
|
+
{ "name": "title", "type": "text" },
|
|
400
|
+
{ "name": "brief", "type": "text", "nullable": true },
|
|
401
|
+
{ "name": "kind", "type": "enum", "note": "plan | recap" },
|
|
402
|
+
{ "name": "status", "type": "text" },
|
|
403
|
+
{ "name": "source", "type": "text", "nullable": true },
|
|
404
|
+
{ "name": "hosted_plan_id", "type": "text", "nullable": true, "note": "hosted_plan_url paired" },
|
|
405
|
+
{ "name": "source_url", "type": "text", "nullable": true },
|
|
406
|
+
{ "name": "deleted_at", "type": "timestamp", "nullable": true, "note": "soft delete; deleted_by paired" }
|
|
407
|
+
] },
|
|
408
|
+
{ "id": "plan_sections", "name": "plan_sections", "note": "ordered sections within a plan", "fields": [
|
|
409
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
410
|
+
{ "name": "plan_id", "type": "text", "fk": "plans.id" },
|
|
411
|
+
{ "name": "type", "type": "text" },
|
|
412
|
+
{ "name": "title", "type": "text", "nullable": true },
|
|
413
|
+
{ "name": "body", "type": "text", "nullable": true },
|
|
414
|
+
{ "name": "html", "type": "text", "nullable": true },
|
|
415
|
+
{ "name": "sort_order", "type": "integer" },
|
|
416
|
+
{ "name": "created_by", "type": "text", "nullable": true }
|
|
417
|
+
] },
|
|
418
|
+
{ "id": "plan_comments", "name": "plan_comments", "note": "threaded comments", "fields": [
|
|
419
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
420
|
+
{ "name": "plan_id", "type": "text", "fk": "plans.id" },
|
|
421
|
+
{ "name": "kind", "type": "text" },
|
|
422
|
+
{ "name": "status", "type": "text" },
|
|
423
|
+
{ "name": "anchor", "type": "json", "nullable": true },
|
|
424
|
+
{ "name": "message", "type": "text" },
|
|
425
|
+
{ "name": "resolution_target", "type": "text", "nullable": true, "note": "agent | human | null" },
|
|
426
|
+
{ "name": "mentions_json", "type": "json", "nullable": true },
|
|
427
|
+
{ "name": "resolved_by", "type": "text", "nullable": true }
|
|
428
|
+
] },
|
|
429
|
+
{ "id": "plan_events", "name": "plan_events", "note": "audit log of agent/human events", "fields": [
|
|
430
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
431
|
+
{ "name": "plan_id", "type": "text", "fk": "plans.id" }
|
|
432
|
+
] },
|
|
433
|
+
{ "id": "plan_versions", "name": "plan_versions", "note": "point-in-time snapshots", "fields": [
|
|
434
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
435
|
+
{ "name": "plan_id", "type": "text", "fk": "plans.id" }
|
|
436
|
+
] },
|
|
437
|
+
{ "id": "plan_shares", "name": "plan_shares", "note": "per-principal grants", "fields": [
|
|
438
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
439
|
+
{ "name": "plan_id", "type": "text", "fk": "plans.id" },
|
|
440
|
+
{ "name": "role", "type": "enum", "note": "viewer | editor | admin" }
|
|
441
|
+
] },
|
|
442
|
+
{ "id": "plan_guest_mints", "name": "plan_guest_mints", "note": "rate-limit records for guest session issuance", "fields": [
|
|
443
|
+
{ "name": "id", "type": "text", "pk": true }
|
|
444
|
+
] },
|
|
445
|
+
{ "id": "plan_assets", "name": "plan_assets", "note": "inline image assets as base64", "fields": [
|
|
446
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
447
|
+
{ "name": "plan_id", "type": "text", "fk": "plans.id" }
|
|
448
|
+
] }
|
|
449
|
+
],
|
|
450
|
+
"relations": [
|
|
451
|
+
{ "from": "plans", "to": "plan_sections", "kind": "1-n", "label": "has sections" },
|
|
452
|
+
{ "from": "plans", "to": "plan_comments", "kind": "1-n", "label": "has comments" },
|
|
453
|
+
{ "from": "plans", "to": "plan_events", "kind": "1-n", "label": "has events" },
|
|
454
|
+
{ "from": "plans", "to": "plan_versions", "kind": "1-n", "label": "has versions" },
|
|
455
|
+
{ "from": "plans", "to": "plan_shares", "kind": "1-n", "label": "has shares" },
|
|
456
|
+
{ "from": "plans", "to": "plan_assets", "kind": "1-n", "label": "has assets" }
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
387
461
|
### Key actions
|
|
388
462
|
|
|
389
463
|
Actions in `templates/plan/actions/`:
|
|
@@ -19,6 +19,13 @@ Generate full presentation decks from a prompt, edit slides visually, and presen
|
|
|
19
19
|
|
|
20
20
|
When you open a deck, you get a slide editor in the middle, a sidebar of slides on the left, and the agent on the right.
|
|
21
21
|
|
|
22
|
+
```an-diagram title="Prompt to deck" summary="Ask for a deck and the agent streams slides in one at a time through the same actions you could call from the CLI."
|
|
23
|
+
{
|
|
24
|
+
"html": "<div class=\"diagram-flow\"><div class=\"diagram-node\">Prompt<br><small class=\"diagram-muted\">\"10-slide pitch deck\"</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\"><span class=\"diagram-pill accent\">Agent</span><small class=\"diagram-muted\">picks layouts</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-col\"><div class=\"diagram-pill\">create-deck</div><div class=\"diagram-pill\">add-slide × n</div><small class=\"diagram-muted\">parallel, streaming</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\" data-rough>decks (SQL)</div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">↻</div><div class=\"diagram-box\">Editor renders live</div></div>",
|
|
25
|
+
"css": ".diagram-flow{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-flow .diagram-col{display:flex;flex-direction:column;gap:6px;align-items:center}.diagram-flow .center{display:flex;flex-direction:column;align-items:center;gap:4px}.diagram-flow .diagram-arrow{font-size:22px;line-height:1}"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
22
29
|
## What you can do with it
|
|
23
30
|
|
|
24
31
|
- **Generate decks from a prompt.** "Generate a 10-slide pitch deck for a coffee subscription service, audience is investors."
|
|
@@ -165,6 +172,81 @@ The agent can embed a live slide preview directly in a chat reply using the fram
|
|
|
165
172
|
|
|
166
173
|
All deck data lives in SQL via Drizzle ORM. Schema: `templates/slides/server/db/schema.ts`.
|
|
167
174
|
|
|
175
|
+
```an-schema title="Slides data model" summary="A deck owns its slides as JSON in decks.data; comments, versions, shares, and design systems hang off it."
|
|
176
|
+
{
|
|
177
|
+
"entities": [
|
|
178
|
+
{
|
|
179
|
+
"id": "decks",
|
|
180
|
+
"name": "decks",
|
|
181
|
+
"note": "Slides live as JSON in data; carries ownableColumns",
|
|
182
|
+
"fields": [
|
|
183
|
+
{ "name": "id", "type": "text", "pk": true, "note": "e.g. deck-1712345-abc" },
|
|
184
|
+
{ "name": "title", "type": "text" },
|
|
185
|
+
{ "name": "data", "type": "text", "note": "JSON: { title, slides: [{ id, content, layout }] }" },
|
|
186
|
+
{ "name": "created_at", "type": "text" },
|
|
187
|
+
{ "name": "updated_at", "type": "text" }
|
|
188
|
+
]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"id": "slide_comments",
|
|
192
|
+
"name": "slide_comments",
|
|
193
|
+
"fields": [
|
|
194
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
195
|
+
{ "name": "deck_id", "type": "text", "fk": "decks.id" },
|
|
196
|
+
{ "name": "slide_id", "type": "text", "note": "Slide the comment lives on" },
|
|
197
|
+
{ "name": "thread_id", "type": "text", "note": "Threading" },
|
|
198
|
+
{ "name": "parent_id", "type": "text", "nullable": true },
|
|
199
|
+
{ "name": "content", "type": "text" },
|
|
200
|
+
{ "name": "quoted_text", "type": "text", "nullable": true },
|
|
201
|
+
{ "name": "author_email", "type": "text" },
|
|
202
|
+
{ "name": "author_name", "type": "text" },
|
|
203
|
+
{ "name": "resolved", "type": "boolean" }
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"id": "deck_versions",
|
|
208
|
+
"name": "deck_versions",
|
|
209
|
+
"note": "Point-in-time snapshots for restore",
|
|
210
|
+
"fields": [
|
|
211
|
+
{ "name": "deck_id", "type": "text", "fk": "decks.id" },
|
|
212
|
+
{ "name": "title", "type": "text" },
|
|
213
|
+
{ "name": "data", "type": "text", "note": "Full deck JSON" },
|
|
214
|
+
{ "name": "change_label", "type": "text", "nullable": true }
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "design_systems",
|
|
219
|
+
"name": "design_systems",
|
|
220
|
+
"note": "Reusable brand tokens; ownableColumns",
|
|
221
|
+
"fields": [
|
|
222
|
+
{ "name": "data", "type": "text", "note": "colors / typography / spacing" },
|
|
223
|
+
{ "name": "assets", "type": "text", "nullable": true },
|
|
224
|
+
{ "name": "custom_instructions", "type": "text", "nullable": true },
|
|
225
|
+
{ "name": "is_default", "type": "boolean" }
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"id": "deck_share_links",
|
|
230
|
+
"name": "deck_share_links",
|
|
231
|
+
"note": "Persisted public share-link snapshots",
|
|
232
|
+
"fields": [
|
|
233
|
+
{ "name": "token", "type": "text", "pk": true },
|
|
234
|
+
{ "name": "title", "type": "text" },
|
|
235
|
+
{ "name": "slides", "type": "text", "note": "JSON slides snapshot" },
|
|
236
|
+
{ "name": "aspect_ratio", "type": "text", "nullable": true },
|
|
237
|
+
{ "name": "created_at", "type": "text" }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
"relations": [
|
|
242
|
+
{ "from": "decks", "to": "slide_comments", "kind": "1-n", "label": "comments" },
|
|
243
|
+
{ "from": "decks", "to": "deck_versions", "kind": "1-n", "label": "snapshots" }
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Framework shares tables (`deck_shares`, `design_system_shares`) map principals to viewer / editor / admin roles per resource.
|
|
249
|
+
|
|
168
250
|
#### decks
|
|
169
251
|
|
|
170
252
|
| Column | Type | Notes |
|
|
@@ -19,6 +19,13 @@ A programmatic video studio for the kind of motion graphics, product demos, and
|
|
|
19
19
|
|
|
20
20
|
When you open the studio, you'll see a list of compositions on the home screen. Click into one and you get a player on top, a timeline at the bottom, and a properties panel on the right. The agent always knows which composition you have open.
|
|
21
21
|
|
|
22
|
+
```an-diagram title="Animation as data" summary="A composition is a React component; every animation reads from a track so the agent and the timeline edit the same data."
|
|
23
|
+
{
|
|
24
|
+
"html": "<div class=\"diagram-flow\"><div class=\"diagram-col\"><div class=\"diagram-node\">Timeline<br><small class=\"diagram-muted\">drag, resize, scrub</small></div><div class=\"diagram-node\">Agent<br><small class=\"diagram-muted\">\"fade in at 2s\"</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\"><span class=\"diagram-pill accent\">AnimationTrack</span><small class=\"diagram-muted\">startFrame / easing / animatedProps</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\" data-rough>React composition<br><small class=\"diagram-muted\">Remotion <Player></small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">MP4 / WebM</div></div>",
|
|
25
|
+
"css": ".diagram-flow{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-flow .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-flow .center{display:flex;flex-direction:column;align-items:center;gap:4px}.diagram-flow .diagram-arrow{font-size:22px;line-height:1}"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
22
29
|
## What you can do with it
|
|
23
30
|
|
|
24
31
|
- **Generate animations from a prompt.** "Add a title card that fades in at 2 seconds and holds until 5." The agent edits the composition.
|
|
@@ -133,6 +140,61 @@ Under the hood the agent calls actions like `navigate`, `save-composition`, and
|
|
|
133
140
|
|
|
134
141
|
Server-side schema is in `templates/videos/server/db/schema.ts`:
|
|
135
142
|
|
|
143
|
+
```an-schema title="Video data model" summary="SQL-backed compositions plus design systems and nestable folders, each with a framework shares table."
|
|
144
|
+
{
|
|
145
|
+
"entities": [
|
|
146
|
+
{
|
|
147
|
+
"id": "compositions",
|
|
148
|
+
"name": "compositions",
|
|
149
|
+
"note": "User-created compositions and overrides; ownableColumns",
|
|
150
|
+
"fields": [
|
|
151
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
152
|
+
{ "name": "title", "type": "text" },
|
|
153
|
+
{ "name": "type", "type": "text" },
|
|
154
|
+
{ "name": "data", "type": "text", "note": "Full composition JSON blob" },
|
|
155
|
+
{ "name": "created_at", "type": "text" },
|
|
156
|
+
{ "name": "updated_at", "type": "text" }
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"id": "design_systems",
|
|
161
|
+
"name": "design_systems",
|
|
162
|
+
"note": "Reusable brand tokens; ownableColumns",
|
|
163
|
+
"fields": [
|
|
164
|
+
{ "name": "data", "type": "text", "note": "colors / typography / spacing" },
|
|
165
|
+
{ "name": "assets", "type": "text", "nullable": true },
|
|
166
|
+
{ "name": "custom_instructions", "type": "text", "nullable": true },
|
|
167
|
+
{ "name": "is_default", "type": "boolean" }
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"id": "folders",
|
|
172
|
+
"name": "folders",
|
|
173
|
+
"note": "Nestable folders; ownableColumns",
|
|
174
|
+
"fields": [
|
|
175
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
176
|
+
{ "name": "name", "type": "text" }
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"id": "folder_memberships",
|
|
181
|
+
"name": "folder_memberships",
|
|
182
|
+
"note": "Many-to-many join",
|
|
183
|
+
"fields": [
|
|
184
|
+
{ "name": "folder_id", "type": "text", "fk": "folders.id" },
|
|
185
|
+
{ "name": "composition_id", "type": "text", "fk": "compositions.id" }
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
"relations": [
|
|
190
|
+
{ "from": "folders", "to": "folder_memberships", "kind": "1-n", "label": "members" },
|
|
191
|
+
{ "from": "compositions", "to": "folder_memberships", "kind": "1-n", "label": "in folders" }
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Each table also has a matching framework shares table (`composition_shares`, `design_system_shares`, `folder_shares`) produced by `createSharesTable()`.
|
|
197
|
+
|
|
136
198
|
- `compositions` — id, title, type, `data` (full composition JSON blob), ownership columns, timestamps.
|
|
137
199
|
- `composition_shares` — standard share grants produced by `createSharesTable()`.
|
|
138
200
|
- `design_systems` — reusable brand tokens (colors, typography, spacing, assets, custom instructions, `is_default` flag) with `ownableColumns`.
|
package/docs/content/tracking.md
CHANGED
|
@@ -19,6 +19,13 @@ track(
|
|
|
19
19
|
);
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
```an-diagram title="One track() call, every provider" summary="Server and client callers hit the same registry, which fans every event out to all active providers in parallel."
|
|
23
|
+
{
|
|
24
|
+
"html": "<div class=\"trk\"><div class=\"diagram-col\"><div class=\"diagram-node\">Server code<br><small class=\"diagram-muted\">actions · plugins · routes</small></div><div class=\"diagram-node\">Browser code<br><small class=\"diagram-muted\">POST /_agent-native/track</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\"><span class=\"diagram-pill accent\">Provider registry</span><small class=\"diagram-muted\">fan-out, fire-and-forget</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-col\"><div class=\"diagram-box\">PostHog</div><div class=\"diagram-box\">Mixpanel</div><div class=\"diagram-box\">Amplitude</div><div class=\"diagram-box\">Webhook</div></div></div>",
|
|
25
|
+
"css": ".trk{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.trk .diagram-col{display:flex;flex-direction:column;gap:8px}.trk .diagram-arrow{font-size:22px;line-height:1}.trk .center{display:flex;flex-direction:column;align-items:center;gap:4px}"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
22
29
|
## Built-in providers {#built-in}
|
|
23
30
|
|
|
24
31
|
Set an env var and the provider auto-registers at server startup. No code changes required.
|
|
@@ -95,6 +102,20 @@ Track calls are fire-and-forget — they return immediately and never block the
|
|
|
95
102
|
|
|
96
103
|
`track()` also works from browser/app code. Import the client twin from `@agent-native/core/client` and call it the same way — it POSTs the event to the framework route at `POST /_agent-native/track`, which forwards it to the **same** registered server-side providers (PostHog, Mixpanel, Amplitude, webhook). No analytics SDK ships to the browser and no provider keys are exposed client-side.
|
|
97
104
|
|
|
105
|
+
```an-api title="The client tracking route"
|
|
106
|
+
{
|
|
107
|
+
"method": "POST",
|
|
108
|
+
"path": "/_agent-native/track",
|
|
109
|
+
"summary": "Forward a browser event to the registered server-side providers",
|
|
110
|
+
"auth": "Session required + same-origin/CSRF marker (set automatically by the client helper). Not an open analytics relay.",
|
|
111
|
+
"params": [
|
|
112
|
+
{ "name": "name", "in": "body", "type": "string", "required": true, "description": "Event name. Capped at 200 characters." },
|
|
113
|
+
{ "name": "properties", "in": "body", "type": "object", "description": "Event properties (~16KB cap). `source: \"client\"` and the active `org_id` are added server-side." }
|
|
114
|
+
],
|
|
115
|
+
"description": "Identity is resolved **server-side** from the session — browser code never passes a `userId`. Fire-and-forget: never blocks the UI, never throws, swallows network errors. Oversized or malformed payloads are rejected."
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
98
119
|
```ts
|
|
99
120
|
import { track } from "@agent-native/core/client";
|
|
100
121
|
|
|
@@ -9,12 +9,26 @@ The defining idea behind agent-native is that the agent and the UI are **equal p
|
|
|
9
9
|
|
|
10
10
|
There's a simple through-line. The agent **sees** what you're looking at, you **direct** it toward what you want, you can **embed** it anywhere, you can go fully **UI-light** when that's the better fit, and you can **co-edit** the same documents at the same time. Each of those is a page in this section.
|
|
11
11
|
|
|
12
|
+
```an-diagram title="The day-to-day loop" summary="Five ways of working with a docked agent — each is a page in this section."
|
|
13
|
+
{
|
|
14
|
+
"html": "<div class=\"diagram-loop\"><div class=\"diagram-card\"><strong>Sees</strong><small class=\"diagram-muted\">your view & selection</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-card\"><strong>Direct</strong><small class=\"diagram-muted\">@-mentions & voice</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-card\"><strong>Embed</strong><small class=\"diagram-muted\">drop into any app</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-card\"><strong>UI-light</strong><small class=\"diagram-muted\">chat is the product</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-card accent-card\"><span class=\"diagram-pill accent\">Co-edit</span><small class=\"diagram-muted\">live, side by side</small></div></div>",
|
|
15
|
+
"css": ".diagram-loop{display:flex;align-items:stretch;gap:10px;flex-wrap:wrap}.diagram-loop .diagram-card{display:flex;flex-direction:column;gap:6px;padding:14px 16px;min-width:130px;flex:1}.diagram-loop .diagram-arrow{align-self:center;font-size:22px;line-height:1}"
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
12
19
|
## It sees what you're looking at {#it-sees}
|
|
13
20
|
|
|
14
21
|
The agent isn't blind to your screen. Open an email and it knows which thread. Select a chart and it knows which chart. Highlight a paragraph and it can act on just that range. That shared awareness is what lets you say "reply to this" or "summarize the selection" without spelling out the context every time.
|
|
15
22
|
|
|
16
23
|
This works because the current navigation and selection live in `application_state` SQL, which the agent reads as part of its context. The agent can also drive that same state back — opening a view, selecting a row — so you watch it work in the real UI rather than in a transcript.
|
|
17
24
|
|
|
25
|
+
```an-callout
|
|
26
|
+
{
|
|
27
|
+
"tone": "info",
|
|
28
|
+
"body": "**Shared awareness is two-way.** You and the agent both read and write `application_state`, so \"reply to this\" or \"summarize the selection\" just works — and when the agent navigates, the real UI moves with it."
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
18
32
|
→ [**Context Awareness**](/docs/context-awareness) — navigation state, view-screen, navigate commands, and how the agent stays in sync with your screen.
|
|
19
33
|
|
|
20
34
|
## You direct it {#you-direct-it}
|
|
@@ -19,6 +19,13 @@ The composer's voice button records audio in the browser, then picks a provider:
|
|
|
19
19
|
|
|
20
20
|
Provider choice is stored in application state under `voice-transcription-prefs` so the user can force `"auto"` (default — picks the best available provider), `"builder-gemini"`, `"builder"`, `"gemini"`, `"groq"`, `"openai"`, or `"browser"` in the sidebar settings.
|
|
21
21
|
|
|
22
|
+
```an-diagram title="Voice transcription provider fallback" summary="The composer records audio, then walks server providers in order, dropping to the browser Web Speech API only when no server provider is available."
|
|
23
|
+
{
|
|
24
|
+
"html": "<div class=\"diagram-voice\"><div class=\"diagram-node\">Mic button<br><small class=\"diagram-muted\">records webm/opus</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-card col\"><div class=\"diagram-pill accent\">1 · Builder Gemini</div><small class=\"diagram-muted\">default when Builder connected</small><div class=\"diagram-pill\">2 · BYOK cloud</div><small class=\"diagram-muted\">Gemini · Groq · OpenAI Whisper</small></div><div class=\"diagram-arrow diagram-warn\" aria-hidden=\"true\">↓</div><div class=\"diagram-box diagram-warn\" data-rough>3 · Browser Web Speech<br><small class=\"diagram-muted\">fallback on 400 · streams live</small></div></div>",
|
|
25
|
+
"css": ".diagram-voice{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-voice .col{display:flex;flex-direction:column;gap:6px;padding:14px}.diagram-voice .diagram-arrow{font-size:22px;line-height:1}"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
22
29
|
The route is **same-origin only** — cross-site POSTs are rejected so an attacker can't burn transcription credits from an external page.
|
|
23
30
|
|
|
24
31
|
## Enabling Providers {#enabling-providers}
|
|
@@ -33,19 +40,33 @@ The user sets their own key via the agent sidebar settings UI. It's stored as a
|
|
|
33
40
|
|
|
34
41
|
Set `GEMINI_API_KEY`, `GROQ_API_KEY`, or `OPENAI_API_KEY` as an environment variable or in the `settings` table. Every user's transcription hits the shared key.
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
```an-callout
|
|
44
|
+
{
|
|
45
|
+
"tone": "info",
|
|
46
|
+
"body": "**Credential resolution order:** the route checks the user's own encrypted secret first, then the shared deployment key. A power user with their own key always overrides the shared one. If neither exists, the route returns a 400 the composer recognizes and silently falls back to browser Web Speech."
|
|
47
|
+
}
|
|
48
|
+
```
|
|
39
49
|
|
|
40
50
|
## The route {#route}
|
|
41
51
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
```an-api title="Voice transcription route"
|
|
53
|
+
{
|
|
54
|
+
"method": "POST",
|
|
55
|
+
"path": "/_agent-native/transcribe-voice",
|
|
56
|
+
"summary": "Transcribe a recorded audio clip into prompt text",
|
|
57
|
+
"auth": "Active session (Better Auth cookie). Same-origin only.",
|
|
58
|
+
"description": "The composer POSTs the recorded clip here; the route resolves a provider and returns the transcribed text. You should not call this directly.",
|
|
59
|
+
"params": [
|
|
60
|
+
{ "name": "audio", "in": "body", "type": "file", "required": true, "description": "The recorded clip, webm/opus by default. Max 25 MB." },
|
|
61
|
+
{ "name": "provider", "in": "body", "type": "string", "required": false, "description": "Optional override, e.g. gemini, groq, openai, builder." }
|
|
62
|
+
],
|
|
63
|
+
"request": { "contentType": "multipart/form-data" },
|
|
64
|
+
"responses": [
|
|
65
|
+
{ "status": "200", "description": "Transcription succeeded", "example": "{ \"text\": \"reply to Sara that I'll be there by 3\" }" },
|
|
66
|
+
{ "status": "400", "description": "No server provider configured — the composer recognizes this and falls back to Web Speech", "example": "{ \"error\": \"no_provider\" }" }
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
49
70
|
|
|
50
71
|
You don't need to call this directly — the composer does. If you're building a custom input surface, first reuse the shared composer/voice client pieces from `@agent-native/core/client`. Treat this route as the low-level transport boundary for custom helpers that need to send multipart audio.
|
|
51
72
|
|
|
@@ -50,6 +50,13 @@ Three things change when you reach rung 3:
|
|
|
50
50
|
|
|
51
51
|
That's rung 3. That's agent-native.
|
|
52
52
|
|
|
53
|
+
```an-diagram title="The Ladder Principle" summary="Most teams stop at rung 1 or 2. Agent-native is rung 3 — a real app and a real agent over one shared action surface."
|
|
54
|
+
{
|
|
55
|
+
"html": "<div class=\"diagram-ladder\"><div class=\"diagram-card rung rung-3\"><span class=\"diagram-pill accent\">Rung 3 · agent-native</span><strong>Agent + UI as equal partners</strong><small class=\"diagram-muted\">One action surface. Every agent tool is also a button; every button runs the same logic the agent uses.</small></div><div class=\"diagram-card rung rung-2\"><span class=\"diagram-pill\">Rung 2</span><strong>A chat with tools</strong><small class=\"diagram-muted\">The agent can act — but it is still just a chat window. No dashboards, lists, or shortcuts.</small></div><div class=\"diagram-card rung rung-1\"><span class=\"diagram-pill warn\">Rung 1</span><strong>A single LLM call</strong><small class=\"diagram-muted\">Prompt in, string out. Impressive in a demo; breaks the moment reality gets messy.</small></div></div>",
|
|
56
|
+
"css": ".diagram-ladder{display:flex;flex-direction:column;gap:14px}.diagram-ladder .rung{display:flex;flex-direction:column;gap:6px;padding:16px 18px}.diagram-ladder .rung-2{margin-inline-end:48px}.diagram-ladder .rung-1{margin-inline-end:96px}"
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
53
60
|
See [Key Concepts — Protocols](/docs/key-concepts#protocols) for how all of this hangs off the same action definition.
|
|
54
61
|
|
|
55
62
|
## Why every agent needs a UI {#why-every-agent-needs-a-ui}
|
|
@@ -82,6 +89,13 @@ This is the defining principle.
|
|
|
82
89
|
>
|
|
83
90
|
> **From the agent** — natural language, other agents via A2A, Slack, Telegram. The agent writes to the database; the UI updates automatically.
|
|
84
91
|
|
|
92
|
+
```an-diagram title="One system, two ways in" summary="The agent and the UI write to the same actions and the same database. Whatever one does, the other sees."
|
|
93
|
+
{
|
|
94
|
+
"html": "<div class=\"diagram-parity\"><div class=\"diagram-col\"><div class=\"diagram-node\">Human<br><small class=\"diagram-muted\">clicks, forms, shortcuts</small></div><div class=\"diagram-node\">Agent<br><small class=\"diagram-muted\">natural language · A2A · Slack</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\"><span class=\"diagram-pill accent\">Actions</span><small class=\"diagram-muted\">defined once</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">SQL database</div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">↻</div><div class=\"diagram-box\">UI updates live</div></div>",
|
|
95
|
+
"css": ".diagram-parity{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-parity .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-parity .diagram-arrow{font-size:22px;line-height:1}.diagram-parity .center{display:flex;flex-direction:column;align-items:center;gap:4px}"
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
85
99
|
When the agent creates a draft email, it appears in the UI. When you click "Send," the agent knows it was sent. There's no separate "agent world" and "UI world" — it's one system. See [Key Concepts](/docs/key-concepts) for the architecture that makes this work.
|
|
86
100
|
|
|
87
101
|
## Customization usually reserved for power tools {#workspace-customization}
|
|
@@ -145,19 +159,17 @@ This is powered by [A2A](/docs/a2a-protocol) and [MCP](/docs/mcp-protocol) under
|
|
|
145
159
|
|
|
146
160
|
If you're building or extending an agent-native app, here's the central pattern: every operation in the app is an **action** — defined once, available to both the agent and the UI.
|
|
147
161
|
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
},
|
|
160
|
-
});
|
|
162
|
+
```an-annotated-code title="One action, defined once"
|
|
163
|
+
{
|
|
164
|
+
"filename": "actions/reply-to-email.ts",
|
|
165
|
+
"language": "ts",
|
|
166
|
+
"code": "import { defineAction } from \"@agent-native/core/action\";\nimport { z } from \"zod\";\n\nexport default defineAction({\n description: \"Reply to an email thread\",\n schema: z.object({ emailId: z.string(), body: z.string() }),\n run: async ({ emailId, body }) => {\n // db and schema come from your app's server/db setup\n await db.insert(schema.replies).values({ emailId, body });\n },\n});",
|
|
167
|
+
"annotations": [
|
|
168
|
+
{ "lines": "5", "label": "Tool surface", "note": "The `description` is what the agent reads to decide when to call this as a tool." },
|
|
169
|
+
{ "lines": "6", "label": "Typed contract", "note": "One zod `schema` validates input from **every** surface — agent, UI, HTTP, MCP, and A2A." },
|
|
170
|
+
{ "lines": "7-10", "label": "One implementation", "note": "The `run` body is the single source of truth. The UI button and the agent tool both execute exactly this." }
|
|
171
|
+
]
|
|
172
|
+
}
|
|
161
173
|
```
|
|
162
174
|
|
|
163
175
|
```tsx
|