@agent-native/core 0.63.2 → 0.63.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +23 -19
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/diagram.d.ts.map +1 -1
- package/dist/client/blocks/library/diagram.js +10 -11
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe.js +2 -1
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +50 -5
- package/dist/server/onboarding-html.js.map +1 -1
- 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 +71 -41
- package/docs/content/template-assets.md +76 -3
- package/docs/content/template-brain.md +89 -1
- package/docs/content/template-calendar.md +86 -58
- package/docs/content/template-chat.md +25 -9
- package/docs/content/template-clips.md +124 -16
- package/docs/content/template-content.md +146 -47
- package/docs/content/template-design.md +62 -2
- package/docs/content/template-dispatch.md +56 -9
- package/docs/content/template-forms.md +69 -13
- package/docs/content/template-mail.md +73 -26
- package/docs/content/template-plan.md +80 -1
- package/docs/content/template-slides.md +95 -74
- package/docs/content/template-videos.md +73 -52
- 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
package/docs/content/security.md
CHANGED
|
@@ -9,6 +9,13 @@ Agent-native apps are designed to be secure by default. The framework provides a
|
|
|
9
9
|
|
|
10
10
|
## What you get for free, and what you own {#what-you-own}
|
|
11
11
|
|
|
12
|
+
```an-diagram title="Defense in layers" summary="The framework owns most of the threat surface; you own two things — tagging tables for scoping and validating external input."
|
|
13
|
+
{
|
|
14
|
+
"html": "<div class=\"sec-layers\"><div class=\"diagram-card free\"><span class=\"diagram-pill ok\">Framework owns</span><small class=\"diagram-muted\">SQL isolation · parameterized queries · XSS escaping · auth guard · CSRF cookies · secret encryption</small></div><div class=\"diagram-card you\"><span class=\"diagram-pill warn\">You own</span><small class=\"diagram-muted\">A. tag tables with ownableColumns() & route through access guards<br>B. give every action a Zod schema & send user URLs through the SSRF guard</small></div></div>",
|
|
15
|
+
"css": ".sec-layers{display:flex;flex-direction:column;gap:12px}.sec-layers .diagram-card{display:flex;flex-direction:column;gap:6px;padding:14px 16px}"
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
12
19
|
When you build on the standard patterns, the framework already handles most of the threat surface for you:
|
|
13
20
|
|
|
14
21
|
- **Data isolation** — agent SQL is rewritten so it can only see the current user's (and active org's) rows. See [Data Scoping](#data-scoping).
|
|
@@ -76,6 +83,13 @@ await db.insert(notes).values({ title, ownerEmail: email });
|
|
|
76
83
|
await exec(`INSERT INTO notes (title) VALUES ('${title}')`);
|
|
77
84
|
```
|
|
78
85
|
|
|
86
|
+
```an-callout
|
|
87
|
+
{
|
|
88
|
+
"tone": "risk",
|
|
89
|
+
"body": "Never build SQL by string concatenation or template literals. Pass user input as `args` to `exec` / `db-query`, or use Drizzle — both always parameterize. The `pnpm guards` checks catch unscoped and concatenated queries at CI time."
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
79
93
|
## XSS Prevention {#xss}
|
|
80
94
|
|
|
81
95
|
React auto-escapes all JSX expressions. Additional guidelines:
|
|
@@ -109,6 +123,13 @@ Scoping flows from the authenticated session down to the SQL the agent runs:
|
|
|
109
123
|
session.orgId → AGENT_ORG_ID → SQL row scoping
|
|
110
124
|
```
|
|
111
125
|
|
|
126
|
+
```an-diagram title="The scoping pipeline" summary="Agent SQL never touches base tables directly — it reads through a temporary view scoped to the current identity, so a bare table name can only return owned rows."
|
|
127
|
+
{
|
|
128
|
+
"html": "<div class=\"scope-pipe\"><div class=\"diagram-node\">Signed-in session<br><small class=\"diagram-muted\">email · orgId</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-node\">Request context<br><small class=\"diagram-muted\">AGENT_ORG_ID</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">Temporary VIEW<br><small class=\"diagram-muted\">WHERE owner_email = ? AND org_id = ?</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-node ok\">Agent SQL<br><small class=\"diagram-muted\">bare table names only</small></div></div>",
|
|
129
|
+
"css": ".scope-pipe{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.scope-pipe .diagram-node{display:flex;flex-direction:column;gap:2px;padding:10px 14px}.scope-pipe .diagram-arrow{font-size:22px;line-height:1}"
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
112
133
|
The signed-in session carries `email` and (when an org is active) `orgId`. The framework establishes request context from that session, exposes the active org to agent SQL as `AGENT_ORG_ID`, and rewrites every query so it can only see rows the current identity owns. The same path applies whether the query comes from the UI, an action, or the agent — the agent cannot read data for an org the user isn't a member of.
|
|
113
134
|
|
|
114
135
|
### Per-User Scoping (`owner_email`)
|
|
@@ -168,6 +189,23 @@ export const projects = table("projects", {
|
|
|
168
189
|
});
|
|
169
190
|
```
|
|
170
191
|
|
|
192
|
+
```an-schema title="What ownableColumns() adds" summary="The three columns that make a table tenant-aware and shareable."
|
|
193
|
+
{
|
|
194
|
+
"entities": [
|
|
195
|
+
{
|
|
196
|
+
"id": "ownable",
|
|
197
|
+
"name": "ownable resource",
|
|
198
|
+
"note": "Any table that spreads ...ownableColumns()",
|
|
199
|
+
"fields": [
|
|
200
|
+
{ "name": "owner_email", "type": "text", "nullable": false, "note": "Creator. Auto-filled by write actions; auto-injected on INSERT." },
|
|
201
|
+
{ "name": "org_id", "type": "text", "nullable": true, "note": "Owner's active org at creation. Drives org-visibility checks." },
|
|
202
|
+
{ "name": "visibility", "type": "enum", "nullable": false, "note": "private | org | public — coarse default, defaults to private." }
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
171
209
|
### Access guards in actions {#access-guards}
|
|
172
210
|
|
|
173
211
|
Raw agent SQL is scoped by the temporary views above. Action code that queries with Drizzle directly should go through the framework's access helpers so reads and writes stay scoped to the current identity:
|
package/docs/content/server.md
CHANGED
|
@@ -7,6 +7,13 @@ description: "Nitro server routes, plugins, framework-mounted routes, request co
|
|
|
7
7
|
|
|
8
8
|
Agent-native apps use [Nitro](https://nitro.build) for server routes and plugins. Most product behavior should live in [Actions](/docs/actions); custom routes are for protocol surfaces that actions do not fit: uploads, streaming, public pages, webhooks, OAuth callbacks, and provider-specific APIs.
|
|
9
9
|
|
|
10
|
+
```an-diagram title="What runs on the server" summary="Actions are the default. Custom file routes and framework-mounted routes share the same Nitro app and the same SQL database."
|
|
11
|
+
{
|
|
12
|
+
"html": "<div class=\"diagram-server\"><div class=\"diagram-col entry\"><div class=\"diagram-node\">Browser / UI</div><div class=\"diagram-node\">Agent loop</div><div class=\"diagram-node\">External clients<br><small class=\"diagram-muted\">HTTP · MCP · A2A</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel\" data-rough><strong>Nitro server</strong><div class=\"diagram-row\"><span class=\"diagram-pill accent\">Actions</span><small class=\"diagram-muted\">default surface</small></div><div class=\"diagram-row\"><span class=\"diagram-pill\">/_agent-native/*</span><small class=\"diagram-muted\">framework routes</small></div><div class=\"diagram-row\"><span class=\"diagram-pill\">/api/*</span><small class=\"diagram-muted\">custom file routes</small></div><div class=\"diagram-row\"><span class=\"diagram-pill\">plugins</span><small class=\"diagram-muted\">startup: migrations, jobs</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\" data-rough>SQL database<br><small class=\"diagram-muted\">Drizzle · the coordination point</small></div></div>",
|
|
13
|
+
"css": ".diagram-server{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.diagram-server .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-server .diagram-panel{display:flex;flex-direction:column;gap:8px;padding:14px 16px}.diagram-server .diagram-row{display:flex;align-items:center;gap:8px}.diagram-server .diagram-arrow{font-size:22px;line-height:1}"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
10
17
|
## File-Based Routes {#file-based-routes}
|
|
11
18
|
|
|
12
19
|
Routes live in `server/routes/` and Nitro maps filenames to methods and paths:
|
|
@@ -96,30 +103,17 @@ in an action so the UI and agent share the same capability.
|
|
|
96
103
|
|
|
97
104
|
Actions mounted by the framework automatically run with request context. Custom routes do not. If a custom route reads or writes ownable resources, load the session and wrap the work:
|
|
98
105
|
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
import { accessFilter } from "@agent-native/core/sharing";
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return runWithRequestContext(
|
|
113
|
-
{ userEmail: session.email, orgId: session.orgId },
|
|
114
|
-
async () => {
|
|
115
|
-
const db = getDb();
|
|
116
|
-
return db
|
|
117
|
-
.select()
|
|
118
|
-
.from(schema.projects)
|
|
119
|
-
.where(accessFilter(schema.projects, schema.projectShares));
|
|
120
|
-
},
|
|
121
|
-
);
|
|
122
|
-
});
|
|
106
|
+
```an-annotated-code title="Scoping a custom route to the request user"
|
|
107
|
+
{
|
|
108
|
+
"filename": "server/routes/api/projects.get.ts",
|
|
109
|
+
"language": "ts",
|
|
110
|
+
"code": "import { defineEventHandler, createError } from \"h3\";\nimport { getSession, runWithRequestContext } from \"@agent-native/core/server\";\nimport { getDb } from \"../../db/index.js\";\nimport { accessFilter } from \"@agent-native/core/sharing\";\nimport * as schema from \"../../db/schema\";\n\nexport default defineEventHandler(async (event) => {\n const session = await getSession(event);\n if (!session?.email) {\n throw createError({ statusCode: 401, statusMessage: \"Unauthorized\" });\n }\n\n return runWithRequestContext(\n { userEmail: session.email, orgId: session.orgId },\n async () => {\n const db = getDb();\n return db\n .select()\n .from(schema.projects)\n .where(accessFilter(schema.projects, schema.projectShares));\n },\n );\n});",
|
|
111
|
+
"annotations": [
|
|
112
|
+
{ "lines": "7-10", "label": "Custom routes have no auto-context", "note": "Unlike actions, a file route must load the session itself and fail closed when there is no authenticated user." },
|
|
113
|
+
{ "lines": "12-13", "label": "Establish request context", "note": "`runWithRequestContext` makes the user/org available to scoping helpers for the duration of the work." },
|
|
114
|
+
{ "lines": "18-19", "label": "Scope ownable reads", "note": "`accessFilter` constrains the query to rows the caller may see. Never run an unscoped `db.select().from(ownableTable)` here." }
|
|
115
|
+
]
|
|
116
|
+
}
|
|
123
117
|
```
|
|
124
118
|
|
|
125
119
|
`getDb` is created per app via `createGetDb(schema)` in `server/db/index.ts`, so custom routes import it from the template (`../../db/index.js`), not from `@agent-native/core/db`; see [Database — Where the DB Client Lives](/docs/database#db-client). Do not run unscoped `db.select().from(ownableTable)` in custom routes.
|
|
@@ -177,6 +171,26 @@ Agent-native does not rely on filesystem watchers or sticky in-memory state. Whe
|
|
|
177
171
|
|
|
178
172
|
This works across serverless and multi-instance deployments because the database is the coordination point. If you write custom mutations outside actions, use framework helpers or emit the appropriate sync invalidation so open UIs refresh.
|
|
179
173
|
|
|
174
|
+
```an-diagram title="SQL-backed sync loop" summary="No watchers, no sticky state. A write bumps a version in SQL; every client polls the version and refetches."
|
|
175
|
+
{
|
|
176
|
+
"html": "<div class=\"diagram-sync\"><div class=\"diagram-box\" data-rough>Action / helper<br><small class=\"diagram-muted\">mutates data</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel\" data-rough><strong>SQL database</strong><small class=\"diagram-muted\">sync version increments</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">←</div><div class=\"diagram-col\"><div class=\"diagram-node\">useDbSync()<br><small class=\"diagram-muted\">polls /_agent-native/poll</small></div><div class=\"diagram-pill ok\">invalidate caches → UI refreshes</div></div></div>",
|
|
177
|
+
"css": ".diagram-sync{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.diagram-sync .diagram-col{display:flex;flex-direction:column;gap:8px;align-items:flex-start}.diagram-sync .diagram-arrow{font-size:22px;line-height:1}"
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```an-api title="The poll endpoint" method="GET" path="/_agent-native/poll"
|
|
182
|
+
{
|
|
183
|
+
"method": "GET",
|
|
184
|
+
"path": "/_agent-native/poll",
|
|
185
|
+
"summary": "Return the current per-source database sync versions so the client can detect changes.",
|
|
186
|
+
"description": "`useDbSync()` calls this on an interval (and falls back to it when SSE is unavailable). When a returned version is higher than the client's last-seen value, the matching React Query caches are invalidated and refetch.",
|
|
187
|
+
"auth": "Session cookie (request-scoped identity)",
|
|
188
|
+
"responses": [
|
|
189
|
+
{ "status": "200", "description": "Current sync versions keyed by source." }
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
180
194
|
## Webhooks {#webhooks}
|
|
181
195
|
|
|
182
196
|
Inbound webhooks should verify, persist, and return quickly. Long-running agent work should use the integration queue pattern:
|
|
@@ -187,7 +201,15 @@ Inbound webhooks should verify, persist, and return quickly. Long-running agent
|
|
|
187
201
|
4. Return 200 immediately.
|
|
188
202
|
5. Let the fresh processor execution run the agent loop and post the result.
|
|
189
203
|
|
|
190
|
-
|
|
204
|
+
```an-diagram title="Integration queue pattern" summary="The webhook handler returns in milliseconds; a separate signed execution runs the slow agent work."
|
|
205
|
+
{
|
|
206
|
+
"html": "<div class=\"diagram-webhook\"><div class=\"diagram-box\" data-rough>Inbound webhook<br><small class=\"diagram-muted\">Slack · Stripe · email</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel\" data-rough><strong>Handler</strong><div class=\"diagram-step\"><span class=\"diagram-pill\">1</span><small class=\"diagram-muted\">verify signature</small></div><div class=\"diagram-step\"><span class=\"diagram-pill\">2</span><small class=\"diagram-muted\">insert work into SQL</small></div><div class=\"diagram-step\"><span class=\"diagram-pill\">3</span><small class=\"diagram-muted\">self-fire processor</small></div><div class=\"diagram-step\"><span class=\"diagram-pill ok\">4</span><small class=\"diagram-muted\">return 200 now</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\" data-rough>Signed processor<br><small class=\"diagram-muted\">runs agent loop, posts result</small></div></div>",
|
|
207
|
+
"css": ".diagram-webhook{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.diagram-webhook .diagram-panel{display:flex;flex-direction:column;gap:6px;padding:14px 16px}.diagram-webhook .diagram-step{display:flex;align-items:center;gap:8px}.diagram-webhook .diagram-arrow{font-size:22px;line-height:1}"
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
> [!WARNING]
|
|
212
|
+
> Do not rely on unawaited promises after returning a response — serverless hosts freeze the execution. See [Messaging](/docs/messaging) for the canonical integration queue.
|
|
191
213
|
|
|
192
214
|
## Advanced: Escape Hatches {#advanced-escape-hatches}
|
|
193
215
|
|
package/docs/content/sharing.md
CHANGED
|
@@ -27,6 +27,13 @@ Coarse visibility lives on the resource itself; fine-grained grants live in a co
|
|
|
27
27
|
|
|
28
28
|
`public` is a deliberately quiet level: a public resource is reachable by direct link, but it does **not** show up in other users' sidebars, lists, or search. That keeps "public for sharing the URL" separate from "public for cross-user discovery." Galleries and template catalogs that genuinely want cross-user discovery opt in explicitly.
|
|
29
29
|
|
|
30
|
+
```an-diagram title="Visibility, widening outward" summary="Coarse visibility on the resource sets the floor; explicit share grants in the companion table add named people on top."
|
|
31
|
+
{
|
|
32
|
+
"html": "<div class=\"share-tiers\"><div class=\"diagram-card\"><span class=\"diagram-pill\">private</span><small class=\"diagram-muted\">owner + explicit grants only · <strong>default</strong></small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">↓</div><div class=\"diagram-card\"><span class=\"diagram-pill accent\">org</span><small class=\"diagram-muted\">+ anyone in the same org (read-only)</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">↓</div><div class=\"diagram-card\"><span class=\"diagram-pill warn\">public</span><small class=\"diagram-muted\">+ anyone with the link (read-only) · hidden from others' lists/search</small></div></div>",
|
|
33
|
+
"css": ".share-tiers{display:flex;flex-direction:column;align-items:stretch;gap:8px}.share-tiers .diagram-card{display:flex;flex-direction:column;gap:4px;padding:12px 16px}.share-tiers .diagram-arrow{text-align:center;font-size:20px;line-height:1}"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
30
37
|
## Roles on a share grant {#roles}
|
|
31
38
|
|
|
32
39
|
When you share with a specific user or org, you pick a role:
|
|
@@ -120,6 +127,42 @@ export const decks = table("decks", {
|
|
|
120
127
|
export const deckShares = createSharesTable("deck_shares");
|
|
121
128
|
```
|
|
122
129
|
|
|
130
|
+
```an-schema title="Resource + companion shares table" summary="Coarse visibility lives on the resource; each fine-grained grant is a row in the shares table."
|
|
131
|
+
{
|
|
132
|
+
"entities": [
|
|
133
|
+
{
|
|
134
|
+
"id": "deck",
|
|
135
|
+
"name": "decks",
|
|
136
|
+
"note": "...ownableColumns()",
|
|
137
|
+
"fields": [
|
|
138
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
139
|
+
{ "name": "title", "type": "text", "nullable": false },
|
|
140
|
+
{ "name": "owner_email", "type": "text", "nullable": false, "note": "The single source of truth for ownership." },
|
|
141
|
+
{ "name": "org_id", "type": "text", "nullable": true },
|
|
142
|
+
{ "name": "visibility", "type": "enum", "nullable": false, "note": "private | org | public" }
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"id": "deckShare",
|
|
147
|
+
"name": "deck_shares",
|
|
148
|
+
"note": "createSharesTable() — one row per grant",
|
|
149
|
+
"fields": [
|
|
150
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
151
|
+
{ "name": "resource_id", "type": "text", "fk": "decks.id", "nullable": false },
|
|
152
|
+
{ "name": "principal_type", "type": "enum", "note": "user | org" },
|
|
153
|
+
{ "name": "principal_id", "type": "text", "note": "email (user) or org id (org)" },
|
|
154
|
+
{ "name": "role", "type": "enum", "note": "viewer | editor | admin" },
|
|
155
|
+
{ "name": "created_by", "type": "text" },
|
|
156
|
+
{ "name": "created_at", "type": "text" }
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"relations": [
|
|
161
|
+
{ "from": "deckShare", "to": "deck", "kind": "n-n", "label": "grants access to" }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
123
166
|
One registration call in `server/db/index.ts`:
|
|
124
167
|
|
|
125
168
|
```ts
|
|
@@ -155,6 +198,13 @@ registerShareableResource({
|
|
|
155
198
|
|
|
156
199
|
`allowPublic: false` prevents any caller — agent or UI — from setting the resource's visibility to `public`. `requireOrgMemberForUserShares: true` rejects individual user grants to email addresses outside the resource owner's org. Extensions set both: an extension's HTML runs inside an iframe that calls actions and DB as the _viewer_, so public access would be arbitrary code with the viewer's credentials.
|
|
157
200
|
|
|
201
|
+
```an-callout
|
|
202
|
+
{
|
|
203
|
+
"tone": "risk",
|
|
204
|
+
"body": "For resources that execute code or carry elevated trust (like extensions), set `allowPublic: false` and `requireOrgMemberForUserShares: true`. Otherwise a public share becomes arbitrary code running with the *viewer's* credentials."
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
158
208
|
`getResourcePath` gives notification emails a direct fallback link when a share is created by the agent or another non-UI caller. The full pattern (including create-action ownership stamping and the migration recipe for existing tables) lives in the `sharing` agent skill — the agent reads it on demand when building a sharing-aware feature.
|
|
159
209
|
|
|
160
210
|
## Security guarantees {#security}
|
|
@@ -13,6 +13,13 @@ Skills live at `.agents/skills/<name>/SKILL.md` and contain detailed guidance fo
|
|
|
13
13
|
|
|
14
14
|
Every skill's frontmatter `name` and `description` are always injected into the system prompt's skills block so the agent knows what skills exist. The full skill body is loaded on demand when the agent decides a skill is relevant to the task (it is also surfaced via `docs-search`). This is why keeping descriptions short and trigger-specific matters: the description is the only thing the agent reads before deciding whether to load the rest.
|
|
15
15
|
|
|
16
|
+
```an-diagram title="Progressive disclosure" summary="Only the name + description of every skill is always in context. The full body loads on demand when the task matches."
|
|
17
|
+
{
|
|
18
|
+
"html": "<div class=\"sk-flow\"><div class=\"diagram-card\"><span class=\"diagram-pill accent\">Always in the system prompt</span><div class=\"sk-list\"><span class=\"diagram-pill\">storing-data — <small class=\"diagram-muted\">add data models…</small></span><span class=\"diagram-pill\">real-time-sync — <small class=\"diagram-muted\">wire polling…</small></span><span class=\"diagram-pill\">create-skill — <small class=\"diagram-muted\">add a skill…</small></span></div><small class=\"diagram-muted\">just name + description (cheap)</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\"><small class=\"diagram-muted\">task matches a description</small><span class=\"diagram-pill accent\">load on demand</span></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">Full <code>SKILL.md</code> body<br><small class=\"diagram-muted\">rules, code, do/don't</small></div></div>",
|
|
19
|
+
"css": ".sk-flow{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.sk-flow .diagram-card{display:flex;flex-direction:column;gap:8px;padding:14px 16px;min-width:240px}.sk-flow .sk-list{display:flex;flex-direction:column;gap:6px}.sk-flow .center{display:flex;flex-direction:column;align-items:center;gap:6px}.sk-flow .diagram-arrow{font-size:22px}"
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
16
23
|
## Framework skills {#framework-skills}
|
|
17
24
|
|
|
18
25
|
These are the skills bundled with the **default template**. The exact set available in any given app depends on the template you scaffolded from — check that template's `.agents/skills/` directory for what it actually ships.
|
|
@@ -72,48 +79,18 @@ Don't create a skill when:
|
|
|
72
79
|
|
|
73
80
|
Each skill is a Markdown file with YAML frontmatter:
|
|
74
81
|
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
How to import projects from the legacy CSV export. Use when the user uploads
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Always validate the CSV header row before writing any rows. Reject unknown
|
|
88
|
-
columns rather than silently dropping them.
|
|
89
|
-
|
|
90
|
-
## Why
|
|
91
|
-
|
|
92
|
-
The legacy export has three known formats. Silently skipping columns causes data
|
|
93
|
-
loss that is hard to notice until the migration is audited.
|
|
94
|
-
|
|
95
|
-
## How
|
|
96
|
-
|
|
97
|
-
1. Call `get-import-schema` to fetch the expected columns for the target project type.
|
|
98
|
-
2. Parse the first CSV row and diff against the schema.
|
|
99
|
-
3. If any required columns are missing, return an error listing them — do not proceed.
|
|
100
|
-
4. Stream remaining rows through `create-project-item` in batches of 50.
|
|
101
|
-
5. Return a summary: rows processed, rows skipped, and any errors.
|
|
102
|
-
|
|
103
|
-
## Do
|
|
104
|
-
|
|
105
|
-
- Run `view-screen` before importing so you know the user's current project context.
|
|
106
|
-
- Use the `sharing` skill after import if the project should be shared with collaborators.
|
|
107
|
-
|
|
108
|
-
## Don't
|
|
109
|
-
|
|
110
|
-
- Don't hold all rows in memory — stream them.
|
|
111
|
-
- Don't create duplicate projects; check for an existing project with the same name first.
|
|
112
|
-
|
|
113
|
-
## Related Skills
|
|
114
|
-
|
|
115
|
-
- **storing-data** — SQL schema and write patterns for new project rows
|
|
116
|
-
- **sharing** — Exposing a project to other users after import
|
|
82
|
+
```an-annotated-code title="Anatomy of a SKILL.md"
|
|
83
|
+
{
|
|
84
|
+
"filename": ".agents/skills/project-imports/SKILL.md",
|
|
85
|
+
"language": "markdown",
|
|
86
|
+
"code": "---\nname: project-imports\ndescription: >-\n How to import projects from the legacy CSV export. Use when the user uploads\n a project CSV or asks to migrate projects from the old system.\n---\n\n# Project Imports\n\n## Rule\n\nAlways validate the CSV header row before writing any rows. Reject unknown\ncolumns rather than silently dropping them.\n\n## How\n\n1. Call `get-import-schema` to fetch the expected columns.\n2. Parse the first CSV row and diff against the schema.\n3. If any required columns are missing, return an error — do not proceed.\n4. Stream remaining rows through `create-project-item` in batches of 50.\n\n## Don't\n\n- Don't hold all rows in memory — stream them.\n- Don't create duplicate projects; check for an existing name first.\n\n## Related Skills\n\n- **storing-data** — SQL schema and write patterns for new rows\n- **sharing** — exposing a project to other users after import",
|
|
87
|
+
"annotations": [
|
|
88
|
+
{ "lines": "2", "label": "Discovery key", "note": "The `name` matches the folder; it is how the skill is invoked as `/project-imports`." },
|
|
89
|
+
{ "lines": "3-5", "label": "The trigger", "note": "This `description` is the **only** text always in context. Make it state precisely *when* the skill applies." },
|
|
90
|
+
{ "lines": "9-14", "label": "Rules first", "note": "Lead with the hard rule and the why; the agent reads the body only once the task matches." },
|
|
91
|
+
{ "lines": "27-30", "label": "Cross-link", "note": "Point at related skills so the agent can chain them instead of re-deriving guidance." }
|
|
92
|
+
]
|
|
93
|
+
}
|
|
117
94
|
```
|
|
118
95
|
|
|
119
96
|
The frontmatter `name` and `description` are used by the agent's tool system for skill discovery. The description should state when the skill triggers — be specific about the situations.
|
|
@@ -152,6 +129,14 @@ The agent-native runtime reads skills from `.agents/skills/`. Claude Code reads
|
|
|
152
129
|
|
|
153
130
|
This replaces the old hack of relying on Claude Code only reading `.claude/skills` — `scope: dev` makes the dev-vs-runtime split a first-class, explicit choice.
|
|
154
131
|
|
|
132
|
+
```an-diagram title="Which agent loads which skill" summary="scopedecides whether the in-app runtime agent sees a skill.dev skills are visible only to your coding agent."
|
|
133
|
+
{
|
|
134
|
+
"html": "<div class=\"sc-grid\"><div class=\"diagram-card\"><span class=\"diagram-pill\">.agents/skills/</span><div class=\"sc-row\"><span class=\"diagram-pill ok\">scope: both</span><small class=\"diagram-muted\">default</small></div><div class=\"sc-row\"><span class=\"diagram-pill ok\">scope: runtime</span></div><div class=\"sc-row\"><span class=\"diagram-pill warn\">scope: dev</span></div></div><div class=\"sc-targets\"><div class=\"diagram-box\">Runtime agent<br><small class=\"diagram-muted\">reads <code>both</code> + <code>runtime</code></small></div><div class=\"diagram-box\">Coding agent<br><small class=\"diagram-muted\">Claude Code reads <code>.claude/skills/</code> + <code>dev</code></small></div></div></div>",
|
|
135
|
+
"css": ".sc-grid{display:flex;gap:24px;flex-wrap:wrap;align-items:flex-start}.sc-grid .diagram-card{display:flex;flex-direction:column;gap:8px;padding:14px 16px}.sc-grid .sc-row{display:flex;align-items:center;gap:8px}.sc-grid .sc-targets{display:flex;flex-direction:column;gap:10px}"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
|
|
155
140
|
> **See also:** [Writing Agent Instructions](/docs/writing-agent-instructions) for how to word skill descriptions, apply progressive disclosure, and keep `AGENTS.md` lean.
|
|
156
141
|
|
|
157
142
|
## Skills vs AGENTS.md {#skills-vs-agents-md}
|
|
@@ -7,18 +7,22 @@ description: "Ask analytics questions in plain English, get charts and dashboard
|
|
|
7
7
|
|
|
8
8
|
Ask analytics questions in plain English, get charts and dashboards back. The agent connects to BigQuery, GA4, Amplitude, the built-in first-party event collector, HubSpot, Jira, and a dozen other sources, writes the query for you, validates it, and renders the answer as a chart, table, or saved dashboard panel.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-->
|
|
17
|
-
|
|
18
|
-

|
|
10
|
+
```an-wireframe
|
|
11
|
+
{
|
|
12
|
+
"surface": "desktop",
|
|
13
|
+
"html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:500px;box-sizing:border-box'><h1 style='margin:0'>Agent-Native Templates</h1><p class='wf-muted' style='margin:0'>Adoption and engagement across the last 12 weeks.</p><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:12px'><div class='wf-card'><small class='wf-muted'>Weekly active users</small><br/><strong>24,318</strong><br/><span class='wf-pill accent'>+12.4%</span></div><div class='wf-card'><small class='wf-muted'>New signups</small><br/><strong>1,842</strong><br/><span class='wf-pill accent'>+8.7%</span></div><div class='wf-card'><small class='wf-muted'>Revenue MRR</small><br/><strong>$48,210</strong><br/><span class='wf-pill accent'>+21.3%</span></div></div><div style='display:grid;grid-template-columns:1fr 1fr;gap:12px;flex:1'><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Weekly active users</strong><div style='flex:1;display:flex;align-items:end;gap:8px'><div style='height:38%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:44%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:58%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:74%;flex:1;background:var(--wf-accent-soft)'></div></div></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Revenue over time</strong><div style='flex:1;display:flex;align-items:end;gap:8px'><div style='height:32%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:48%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:63%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:80%;flex:1;background:var(--wf-accent-soft)'></div></div></div></div><div class='wf-card'><strong>Signups by source</strong><br/><small class='wf-muted'>Lower chart begins below the main charts.</small></div></div>"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
19
16
|
|
|
20
17
|
It's an open-source replacement for Amplitude, Mixpanel, and Looker — for teams that want to own the code, the queries, and the data.
|
|
21
18
|
|
|
19
|
+
```an-diagram title="Question to chart" summary="The agent consults the data dictionary, writes SQL, validates it against the warehouse, then renders a chart or saves a panel."
|
|
20
|
+
{
|
|
21
|
+
"html": "<div class=\"diagram-flow\"><div class=\"diagram-node\">Plain-English<br>question</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\">reads data dictionary</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\" data-rough>Writes SQL</div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-col\"><div class=\"diagram-pill ok\">Dry-run validate</div><small class=\"diagram-muted\">BigQuery / source</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">Chart, table, or<br>saved panel</div></div>",
|
|
22
|
+
"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}"
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
22
26
|
## What you can do with it
|
|
23
27
|
|
|
24
28
|
- **Ask data questions in plain English.** "What percent of signups last month converted to paid?" or "Show me weekly active users for the past 6 months." The agent picks the right source, writes the SQL, and renders the chart.
|
|
@@ -89,42 +93,15 @@ pnpm dev
|
|
|
89
93
|
|
|
90
94
|
The CLI prints the local dev URL. Sign in with Google, then open the **Data Sources** page to connect BigQuery, GA4, first-party tracking, HubSpot, Jira, and the rest.
|
|
91
95
|
|
|
92
|
-
### Key features
|
|
93
|
-
|
|
94
|
-
**Natural-language chart generation.** Ask the agent in plain English. It picks the right data source, writes the SQL, validates it against the warehouse, and renders the chart inline in chat or as a saved panel. Chart types: `line`, `area`, `bar`, `metric`, `table`, `pie`.
|
|
95
|
-
|
|
96
|
-
**Reusable SQL dashboards.** Dashboards are a named config with an array of panels. Each panel has an `id`, `title`, `sql`, `source` (`bigquery` / `ga4` / `amplitude` / `first-party`), `chartType`, and `width` (1 or 2 columns). See the full shape in `templates/analytics/app/pages/adhoc/sql-dashboard/types.ts`.
|
|
97
|
-
|
|
98
|
-
Dashboards support:
|
|
99
|
-
|
|
100
|
-
- **Parametric SQL** — declare `variables` and `filters` at the dashboard level; panels reference them with `{{var}}` interpolation.
|
|
101
|
-
- **Saved views** — per-dashboard filter presets stored in the `dashboard_views` table.
|
|
102
|
-
- **Resizable panels** — 1- or 2-column width per panel; the grid fills the rest.
|
|
103
|
-
- **Sharing** — private by default, share with users or orgs (`viewer` / `editor` / `admin`).
|
|
96
|
+
### Key features
|
|
104
97
|
|
|
105
|
-
**
|
|
98
|
+
**Ask questions, get charts.** The agent picks a data source, writes and validates SQL, then renders a chart, table, metric, or saved panel.
|
|
106
99
|
|
|
107
|
-
**
|
|
100
|
+
**Dashboards and investigations.** Reusable dashboards keep SQL panels, filters, saved views, and sharing; ad-hoc analyses save longer findings with re-run instructions.
|
|
108
101
|
|
|
109
|
-
**
|
|
102
|
+
**Living data dictionary.** Metric definitions, owners, source tables, and known caveats give the agent the real warehouse vocabulary before it writes queries.
|
|
110
103
|
|
|
111
|
-
**
|
|
112
|
-
|
|
113
|
-
**Multiple data connectors.** Built-in actions for common sources:
|
|
114
|
-
|
|
115
|
-
| Category | Actions |
|
|
116
|
-
| ------------- | ------------------------------------------------------------------------ |
|
|
117
|
-
| Warehouse | `bigquery`, `bigquery-table-info`, `ga4-report` |
|
|
118
|
-
| Product | `mixpanel-events`, `amplitude-events`, `posthog-events` |
|
|
119
|
-
| CRM & Revenue | `hubspot-deals`, `hubspot-metrics`, `hubspot-pipelines`, `apollo-search` |
|
|
120
|
-
| Engineering | `github-prs`, `jira-search`, `jira-analytics` |
|
|
121
|
-
| Support | `pylon-issues`, `gong-calls` |
|
|
122
|
-
| Community | `commonroom-members`, `twitter-tweets` |
|
|
123
|
-
| Content & SEO | `seo-top-keywords`, `seo-page-keywords`, `seo-blog-pages` |
|
|
124
|
-
|
|
125
|
-
Full list lives in `templates/analytics/actions/`. New sources are added by dropping a new action file — the agent picks them up automatically.
|
|
126
|
-
|
|
127
|
-
**Organizations and sharing.** Multi-org deployments are wired up by default via `@agent-native/core/org`. Dashboards and analyses are scoped to the active org. The `/team` route manages members and invitations. See `templates/analytics/app/routes/team.tsx`. Sharing uses the framework's `share-resource` primitive. Coarse visibility is `private` / `org` / `public`; fine-grained grants are per-principal with `viewer` / `editor` / `admin` roles.
|
|
104
|
+
**Broad connector surface.** BigQuery, GA4, product analytics, CRM, support, community, GitHub/Jira, SEO, and first-party `/track` events all come through actions the agent can call.
|
|
128
105
|
|
|
129
106
|
### Working with the agent
|
|
130
107
|
|
|
@@ -180,6 +157,59 @@ Note: the BigQuery OAuth credential for Google sign-in is a **separate** credent
|
|
|
180
157
|
|
|
181
158
|
Core tables (see `templates/analytics/server/db/schema.ts`):
|
|
182
159
|
|
|
160
|
+
```an-schema title="Analytics data model" summary="Dashboards and analyses are the resources; views, shares, and a query cache hang off them. Org tables come from @agent-native/core/org."
|
|
161
|
+
{
|
|
162
|
+
"entities": [
|
|
163
|
+
{
|
|
164
|
+
"id": "dashboards",
|
|
165
|
+
"name": "dashboards",
|
|
166
|
+
"note": "Explorer and SQL dashboards",
|
|
167
|
+
"fields": [
|
|
168
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
169
|
+
{ "name": "kind", "type": "text", "note": "\"explorer\" or \"sql\"" },
|
|
170
|
+
{ "name": "config", "type": "text", "note": "JSON matching SqlDashboardConfig" }
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"id": "dashboard_views",
|
|
175
|
+
"name": "dashboard_views",
|
|
176
|
+
"note": "Saved filter presets per dashboard",
|
|
177
|
+
"fields": [
|
|
178
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
179
|
+
{ "name": "dashboard_id", "type": "text", "fk": "dashboards.id" }
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "analyses",
|
|
184
|
+
"name": "analyses",
|
|
185
|
+
"note": "Re-runnable ad-hoc investigations",
|
|
186
|
+
"fields": [
|
|
187
|
+
{ "name": "id", "type": "text", "pk": true },
|
|
188
|
+
{ "name": "question", "type": "text" },
|
|
189
|
+
{ "name": "instructions", "type": "text", "note": "Re-run steps" },
|
|
190
|
+
{ "name": "dataSources", "type": "text", "note": "Sources touched" },
|
|
191
|
+
{ "name": "resultMarkdown", "type": "text" },
|
|
192
|
+
{ "name": "resultData", "type": "text", "nullable": true }
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"id": "bigquery_cache",
|
|
197
|
+
"name": "bigquery_cache",
|
|
198
|
+
"note": "Result cache keyed by SQL hash",
|
|
199
|
+
"fields": [
|
|
200
|
+
{ "name": "sql_hash", "type": "text", "pk": true },
|
|
201
|
+
{ "name": "bytes_processed", "type": "integer" }
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
"relations": [
|
|
206
|
+
{ "from": "dashboards", "to": "dashboard_views", "kind": "1-n", "label": "saved views" }
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Plus per-resource share tables (`dashboard_shares`, `analysis_shares`) and the org tables (`organizations`, `org_members`, `org_invitations`) provided by `@agent-native/core/org`. The data dictionary lives in the framework's `settings` table under scoped keys.
|
|
212
|
+
|
|
183
213
|
- **`dashboards`** — both Explorer and SQL dashboards. `kind` is `"explorer"` or `"sql"`; `config` is a JSON blob matching `SqlDashboardConfig`.
|
|
184
214
|
- **`dashboard_shares`** — per-resource share grants (principal, role).
|
|
185
215
|
- **`dashboard_views`** — saved filter presets per dashboard.
|
|
@@ -7,9 +7,21 @@ description: "An agent-native digital asset manager and cross-agent generation s
|
|
|
7
7
|
|
|
8
8
|
Assets is an agent-native workspace for creating and managing brand-consistent media. It organizes uploads and generated results into libraries and folders, lets teams collect examples for blog heroes, diagrams, landing pages, product shots, videos, and logos, then routes generation through the agent chat so every asset can be reviewed and refined.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
```an-wireframe
|
|
11
|
+
{
|
|
12
|
+
"surface": "desktop",
|
|
13
|
+
"html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:520px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Launch brand</h1><span class='wf-pill accent'>Blog heroes</span><span class='wf-pill'>Product shots</span><span class='wf-pill'>Logos</span><div style='flex:1'></div><button>Upload</button><button class='primary'>Generate</button></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Create brand media</strong><div class='wf-box'>Three homepage hero options using the approved logo and product references.</div><div style='display:flex;gap:8px;flex-wrap:wrap'><span class='wf-pill accent'>4 references</span><span class='wf-pill'>16:9</span><span class='wf-pill'>Web export</span></div></div><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:12px;flex:1'><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill accent'>Hero A</span></div><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill'>Reference set</span></div><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill'>Logo safe</span></div></div><div class='wf-card' style='display:grid;grid-template-columns:repeat(4,1fr);gap:8px'><div class='wf-box'>Use</div><div class='wf-box'>Refine</div><div class='wf-box'>Compare</div><div class='wf-box'>Export</div></div></div>"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
When you open the app, the selected library, prompt, references, and generated candidates stay in one workspace. The agent can browse, search, generate, refine, and export every asset through the same actions the UI uses.
|
|
11
18
|
|
|
12
|
-
|
|
19
|
+
```an-diagram title="Generate, review, reuse" summary="References and prompts feed a generate-and-choose session; chosen assets land in a library and flow out to other apps via the picker or A2A."
|
|
20
|
+
{
|
|
21
|
+
"html": "<div class=\"diagram-assets\"><div class=\"diagram-col\"><div class=\"diagram-node\">References<br><small class=\"diagram-muted\">logos, product shots, style</small></div><div class=\"diagram-node\">Prompt<br><small class=\"diagram-muted\">chat or Generate controls</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\" data-rough><span class=\"diagram-pill accent\">Generation session</span><small class=\"diagram-muted\">image & video candidates · audit log</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\" data-rough><span class=\"diagram-pill ok\">Library</span><small class=\"diagram-muted\">chosen, brand-consistent assets</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-col\"><div class=\"diagram-node\">Picker<br><small class=\"diagram-muted\">iframe / MCP App</small></div><div class=\"diagram-node\">A2A<br><small class=\"diagram-muted\">Slides · Design · Content</small></div></div></div>",
|
|
22
|
+
"css": ".diagram-assets{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-assets .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-assets .diagram-box{display:flex;flex-direction:column;gap:4px}.diagram-assets .diagram-arrow{font-size:20px;line-height:1}"
|
|
23
|
+
}
|
|
24
|
+
```
|
|
13
25
|
|
|
14
26
|
## When to pick it
|
|
15
27
|
|
|
@@ -34,7 +46,7 @@ Live demo: [assets.agent-native.com](https://assets.agent-native.com).
|
|
|
34
46
|
|
|
35
47
|
## Useful prompts
|
|
36
48
|
|
|
37
|
-
- "Generate three blog hero options using the Acme product
|
|
49
|
+
- "Generate three blog hero options using the Acme product references."
|
|
38
50
|
- "Create a square social image in the launch-campaign style."
|
|
39
51
|
- "Find all approved assets for the onboarding redesign."
|
|
40
52
|
- "Turn this uploaded diagram into a cleaner product explainer image."
|
|
@@ -118,6 +130,67 @@ Note: the SQL table names keep the legacy `image_*` prefix from when the app was
|
|
|
118
130
|
| `image_assets` | The asset record — media type, role, status, title/description/alt text, prompt, model, dimensions, MIME type, object/thumbnail keys, and lineage |
|
|
119
131
|
| `image_generation_runs` | The generation audit log — prompt, compiled prompt, model, references, status, errors, and the `source` (`chat` / `ui` / `a2a`) that triggered it |
|
|
120
132
|
|
|
133
|
+
```an-schema title="Assets data model" summary="Libraries are the ownable container; collections, folders, and presets organize them. Sessions drive generate-and-choose; assets and runs hold output and the audit log. Table names keep the legacy image_* prefix but cover all media."
|
|
134
|
+
{
|
|
135
|
+
"entities": [
|
|
136
|
+
{ "id": "library", "name": "image_libraries", "note": "Top-level ownable container", "fields": [
|
|
137
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
138
|
+
{ "name": "custom_instructions", "type": "text", "nullable": true },
|
|
139
|
+
{ "name": "style_brief", "type": "text", "nullable": true },
|
|
140
|
+
{ "name": "logo_asset_id", "type": "id", "fk": "image_assets.id", "nullable": true },
|
|
141
|
+
{ "name": "archived", "type": "boolean" }
|
|
142
|
+
] },
|
|
143
|
+
{ "id": "library_shares", "name": "image_library_shares", "note": "Framework shares table", "fields": [
|
|
144
|
+
{ "name": "library_id", "type": "id", "fk": "image_libraries.id" },
|
|
145
|
+
{ "name": "role", "type": "text", "note": "viewer / editor / admin" }
|
|
146
|
+
] },
|
|
147
|
+
{ "id": "collections", "name": "image_collections", "note": "Style/category groupings", "fields": [
|
|
148
|
+
{ "name": "library_id", "type": "id", "fk": "image_libraries.id" },
|
|
149
|
+
{ "name": "style_brief", "type": "text", "nullable": true },
|
|
150
|
+
{ "name": "prompt_template", "type": "text", "nullable": true }
|
|
151
|
+
] },
|
|
152
|
+
{ "id": "folders", "name": "asset_folders", "note": "Nestable folders", "fields": [
|
|
153
|
+
{ "name": "library_id", "type": "id", "fk": "image_libraries.id" },
|
|
154
|
+
{ "name": "parent_id", "type": "id", "fk": "asset_folders.id", "nullable": true }
|
|
155
|
+
] },
|
|
156
|
+
{ "id": "presets", "name": "image_generation_presets", "note": "Saved generation recipes", "fields": [
|
|
157
|
+
{ "name": "media_type", "type": "text" },
|
|
158
|
+
{ "name": "prompt_template", "type": "text" },
|
|
159
|
+
{ "name": "model", "type": "text" }
|
|
160
|
+
] },
|
|
161
|
+
{ "id": "sessions", "name": "image_generation_sessions", "note": "Iterative generate-and-choose", "fields": [
|
|
162
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
163
|
+
{ "name": "status", "type": "text" },
|
|
164
|
+
{ "name": "active_asset_id", "type": "id", "fk": "image_assets.id", "nullable": true }
|
|
165
|
+
] },
|
|
166
|
+
{ "id": "session_items", "name": "image_generation_session_items", "note": "Candidate assets in a session", "fields": [
|
|
167
|
+
{ "name": "session_id", "type": "id", "fk": "image_generation_sessions.id" },
|
|
168
|
+
{ "name": "asset_id", "type": "id", "fk": "image_assets.id" },
|
|
169
|
+
{ "name": "role", "type": "text" }
|
|
170
|
+
] },
|
|
171
|
+
{ "id": "assets", "name": "image_assets", "note": "The asset record", "fields": [
|
|
172
|
+
{ "name": "id", "type": "id", "pk": true },
|
|
173
|
+
{ "name": "media_type", "type": "text", "note": "image / video" },
|
|
174
|
+
{ "name": "status", "type": "text" },
|
|
175
|
+
{ "name": "prompt", "type": "text", "nullable": true },
|
|
176
|
+
{ "name": "object_key", "type": "text", "nullable": true }
|
|
177
|
+
] },
|
|
178
|
+
{ "id": "runs", "name": "image_generation_runs", "note": "Generation audit log", "fields": [
|
|
179
|
+
{ "name": "model", "type": "text" },
|
|
180
|
+
{ "name": "status", "type": "text" },
|
|
181
|
+
{ "name": "source", "type": "text", "note": "chat / ui / a2a" }
|
|
182
|
+
] }
|
|
183
|
+
],
|
|
184
|
+
"relations": [
|
|
185
|
+
{ "from": "library", "to": "collections", "kind": "1-n" },
|
|
186
|
+
{ "from": "library", "to": "folders", "kind": "1-n" },
|
|
187
|
+
{ "from": "library", "to": "assets", "kind": "1-n" },
|
|
188
|
+
{ "from": "sessions", "to": "session_items", "kind": "1-n" },
|
|
189
|
+
{ "from": "library", "to": "library_shares", "kind": "1-n" }
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
121
194
|
### Customizing it
|
|
122
195
|
|
|
123
196
|
Assets is a complete, cloneable template. Some practical extension ideas:
|