@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.
Files changed (98) hide show
  1. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  2. package/dist/client/blocks/library/AnnotatedCodeBlock.js +23 -19
  3. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  4. package/dist/client/blocks/library/diagram.d.ts.map +1 -1
  5. package/dist/client/blocks/library/diagram.js +10 -11
  6. package/dist/client/blocks/library/diagram.js.map +1 -1
  7. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  8. package/dist/client/blocks/library/wireframe.js +2 -1
  9. package/dist/client/blocks/library/wireframe.js.map +1 -1
  10. package/dist/server/auth.d.ts.map +1 -1
  11. package/dist/server/auth.js +5 -1
  12. package/dist/server/auth.js.map +1 -1
  13. package/dist/server/onboarding-html.d.ts.map +1 -1
  14. package/dist/server/onboarding-html.js +50 -5
  15. package/dist/server/onboarding-html.js.map +1 -1
  16. package/dist/styles/blocks.css +25 -3
  17. package/docs/content/a2a-protocol.md +48 -10
  18. package/docs/content/actions.md +35 -16
  19. package/docs/content/agent-mentions.md +25 -32
  20. package/docs/content/agent-surfaces.md +31 -0
  21. package/docs/content/agent-teams.md +17 -14
  22. package/docs/content/agent-web-surfaces.md +24 -15
  23. package/docs/content/authentication.md +21 -0
  24. package/docs/content/automations.md +37 -21
  25. package/docs/content/blueprint-installer.md +7 -0
  26. package/docs/content/cli-adapters.md +7 -0
  27. package/docs/content/client.md +14 -0
  28. package/docs/content/cloneable-saas.md +14 -0
  29. package/docs/content/code-agents-ui.md +27 -0
  30. package/docs/content/components.md +21 -0
  31. package/docs/content/context-awareness.md +33 -48
  32. package/docs/content/creating-templates.md +43 -52
  33. package/docs/content/cross-app-sso.md +41 -0
  34. package/docs/content/database.md +41 -21
  35. package/docs/content/deployment.md +23 -6
  36. package/docs/content/dispatch.md +27 -0
  37. package/docs/content/drop-in-agent.md +26 -27
  38. package/docs/content/durable-resume.md +13 -1
  39. package/docs/content/embedding-sdk.md +14 -0
  40. package/docs/content/evals.md +14 -0
  41. package/docs/content/extensions.md +19 -0
  42. package/docs/content/external-agents.md +38 -1
  43. package/docs/content/faq.md +14 -0
  44. package/docs/content/file-uploads.md +20 -0
  45. package/docs/content/frames.md +14 -0
  46. package/docs/content/getting-started.md +34 -16
  47. package/docs/content/harness-agents.md +14 -0
  48. package/docs/content/human-approval.md +15 -30
  49. package/docs/content/key-concepts.md +37 -0
  50. package/docs/content/local-file-mode.md +26 -19
  51. package/docs/content/mcp-apps.md +36 -1
  52. package/docs/content/mcp-clients.md +49 -0
  53. package/docs/content/mcp-protocol.md +36 -0
  54. package/docs/content/messaging.md +34 -8
  55. package/docs/content/migration-workbench.md +7 -0
  56. package/docs/content/multi-app-workspace.md +29 -16
  57. package/docs/content/multi-tenancy.md +14 -0
  58. package/docs/content/native-chat-ui.md +20 -3
  59. package/docs/content/notifications.md +30 -0
  60. package/docs/content/observability.md +20 -1
  61. package/docs/content/observational-memory.md +14 -0
  62. package/docs/content/onboarding.md +32 -41
  63. package/docs/content/plan-plugin.md +14 -0
  64. package/docs/content/pr-visual-recap.md +14 -0
  65. package/docs/content/processors.md +7 -0
  66. package/docs/content/progress.md +23 -0
  67. package/docs/content/pure-agent-apps.md +7 -0
  68. package/docs/content/real-time-collaboration.md +19 -18
  69. package/docs/content/recurring-jobs.md +22 -19
  70. package/docs/content/routing.md +8 -0
  71. package/docs/content/sandbox-adapters.md +19 -0
  72. package/docs/content/security.md +38 -0
  73. package/docs/content/server.md +47 -25
  74. package/docs/content/sharing.md +50 -0
  75. package/docs/content/skills-guide.md +27 -42
  76. package/docs/content/template-analytics.md +71 -41
  77. package/docs/content/template-assets.md +76 -3
  78. package/docs/content/template-brain.md +89 -1
  79. package/docs/content/template-calendar.md +86 -58
  80. package/docs/content/template-chat.md +25 -9
  81. package/docs/content/template-clips.md +124 -16
  82. package/docs/content/template-content.md +146 -47
  83. package/docs/content/template-design.md +62 -2
  84. package/docs/content/template-dispatch.md +56 -9
  85. package/docs/content/template-forms.md +69 -13
  86. package/docs/content/template-mail.md +73 -26
  87. package/docs/content/template-plan.md +80 -1
  88. package/docs/content/template-slides.md +95 -74
  89. package/docs/content/template-videos.md +73 -52
  90. package/docs/content/tracking.md +21 -0
  91. package/docs/content/using-your-agent.md +14 -0
  92. package/docs/content/voice-input.md +31 -10
  93. package/docs/content/what-is-agent-native.md +25 -13
  94. package/docs/content/workspace-connections.md +49 -0
  95. package/docs/content/workspace-management.md +24 -0
  96. package/docs/content/workspace.md +50 -19
  97. package/docs/content/writing-agent-instructions.md +7 -0
  98. package/package.json +1 -1
@@ -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 &middot; parameterized queries &middot; XSS escaping &middot; auth guard &middot; CSRF cookies &middot; 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() &amp; route through access guards<br>B. give every action a Zod schema &amp; 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 &middot; orgId</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</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\">&rarr;</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\">&rarr;</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:
@@ -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\">&rarr;</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\">&rarr;</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
- ```ts
100
- import { defineEventHandler, createError } from "h3";
101
- import { getSession, runWithRequestContext } from "@agent-native/core/server";
102
- import { getDb } from "../../db/index.js";
103
- import { accessFilter } from "@agent-native/core/sharing";
104
- import * as schema from "../../db/schema";
105
-
106
- export default defineEventHandler(async (event) => {
107
- const session = await getSession(event);
108
- if (!session?.email) {
109
- throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
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\">&rarr;</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\">&larr;</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 &rarr; 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
- Do not rely on unawaited promises after returning a response. See [Messaging](/docs/messaging) for the canonical integration queue.
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\">&rarr;</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\">&rarr;</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
 
@@ -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 &middot; <strong>default</strong></small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&darr;</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\">&darr;</div><div class=\"diagram-card\"><span class=\"diagram-pill warn\">public</span><small class=\"diagram-muted\">+ anyone with the link (read-only) &middot; 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 &mdash; <small class=\"diagram-muted\">add data models&hellip;</small></span><span class=\"diagram-pill\">real-time-sync &mdash; <small class=\"diagram-muted\">wire polling&hellip;</small></span><span class=\"diagram-pill\">create-skill &mdash; <small class=\"diagram-muted\">add a skill&hellip;</small></span></div><small class=\"diagram-muted\">just name + description (cheap)</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</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\">&rarr;</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
- ```markdown
76
- ---
77
- name: project-imports
78
- description: >-
79
- How to import projects from the legacy CSV export. Use when the user uploads
80
- a project CSV or asks to migrate projects from the old system.
81
- ---
82
-
83
- # Project Imports
84
-
85
- ## Rule
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
- <!-- screenshot:
11
- app: analytics
12
- view: /adhoc/<dashboard-id>
13
- shows: Adhoc dashboard with 3 KPI cards (Weekly active users 24,318 / New signups 1,842 / Revenue MRR $48,210), Weekly active users line chart and Revenue over time area chart side by side, Signups by source bar chart below
14
- account: screenshot-account (dashboard authored on this account against a seeded warehouse)
15
- capture: 1400x800 viewport, cropped 90px from bottom (final 1400x710)
16
- -->
17
-
18
- ![Analytics dashboard with KPIs and charts](/screenshots/analytics.png)
10
+ ```an-wireframe
11
+ {
12
+ "surface": "desktop",
13
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:500px;box-sizing:border-box'><h1 style='margin:0'>Agent-Native Templates</h1><p class='wf-muted' style='margin:0'>Adoption and engagement across the last 12 weeks.</p><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:12px'><div class='wf-card'><small class='wf-muted'>Weekly active users</small><br/><strong>24,318</strong><br/><span class='wf-pill accent'>+12.4%</span></div><div class='wf-card'><small class='wf-muted'>New signups</small><br/><strong>1,842</strong><br/><span class='wf-pill accent'>+8.7%</span></div><div class='wf-card'><small class='wf-muted'>Revenue MRR</small><br/><strong>$48,210</strong><br/><span class='wf-pill accent'>+21.3%</span></div></div><div style='display:grid;grid-template-columns:1fr 1fr;gap:12px;flex:1'><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Weekly active users</strong><div style='flex:1;display:flex;align-items:end;gap:8px'><div style='height:38%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:44%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:58%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:74%;flex:1;background:var(--wf-accent-soft)'></div></div></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Revenue over time</strong><div style='flex:1;display:flex;align-items:end;gap:8px'><div style='height:32%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:48%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:63%;flex:1;background:var(--wf-accent-soft)'></div><div style='height:80%;flex:1;background:var(--wf-accent-soft)'></div></div></div></div><div class='wf-card'><strong>Signups by source</strong><br/><small class='wf-muted'>Lower chart begins below the main charts.</small></div></div>"
14
+ }
15
+ ```
19
16
 
20
17
  It's an open-source replacement for Amplitude, Mixpanel, and Looker — for teams that want to own the code, the queries, and the data.
21
18
 
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\">&rarr;</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\">&rarr;</div><div class=\"diagram-box\" data-rough>Writes SQL</div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</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\">&rarr;</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 (technical)
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
- **Ad-hoc analyses.** Long-form investigations that cross-reference sources. An analysis saves the original question, step-by-step re-run instructions, the data sources it touched, and the full findings in Markdown. Anyone with access can re-run it against fresh data. Stored in the `analyses` table (see `templates/analytics/server/db/schema.ts`).
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
- **Living data dictionary.** Canonical catalog of metrics — metric name, definition, table, columns, SQL template, known gotchas, owner, and data lag. The agent reads it before writing any SQL, so it uses the real warehouse column names (`hs_is_closed`, not guessed `is_closed`) and knows about caveats like "excludes internal emails". Seeded by asking the agent to import definitions from an existing source (dbt descriptions, a Notion page, a team wiki).
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
- **SQL query explorer.** Direct queries against BigQuery and supported analytics backends from the **Ad-hoc** view. Useful for iterating before saving a dashboard panel.
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
- **First-party analytics collector.** Hosted apps can send product and template events to `/track` with a public write key. Query those events through `query-agent-native-analytics` or dashboard panels with `source: "first-party"`.
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
- ![Assets library for brand media and generated output](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F769092170a14474f998cbca47384f891?format=webp&width=1200)
10
+ ```an-wireframe
11
+ {
12
+ "surface": "desktop",
13
+ "html": "<div style='display:flex;flex-direction:column;gap:14px;padding:18px;min-height:520px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Launch brand</h1><span class='wf-pill accent'>Blog heroes</span><span class='wf-pill'>Product shots</span><span class='wf-pill'>Logos</span><div style='flex:1'></div><button>Upload</button><button class='primary'>Generate</button></div><div class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Create brand media</strong><div class='wf-box'>Three homepage hero options using the approved logo and product references.</div><div style='display:flex;gap:8px;flex-wrap:wrap'><span class='wf-pill accent'>4 references</span><span class='wf-pill'>16:9</span><span class='wf-pill'>Web export</span></div></div><div style='display:grid;grid-template-columns:repeat(3,1fr);gap:12px;flex:1'><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill accent'>Hero A</span></div><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill'>Reference set</span></div><div class='wf-card' style='display:flex;align-items:end;min-height:130px'><span class='wf-pill'>Logo safe</span></div></div><div class='wf-card' style='display:grid;grid-template-columns:repeat(4,1fr);gap:8px'><div class='wf-box'>Use</div><div class='wf-box'>Refine</div><div class='wf-box'>Compare</div><div class='wf-box'>Export</div></div></div>"
14
+ }
15
+ ```
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
- When you open the app, you see your libraries and folders on the left, the assets in the selected library in the middle, and a chat composer for generating new media. The agent can browse, search, generate, refine, and export every asset through the same actions the UI uses.
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\">&rarr;</div><div class=\"diagram-box\" data-rough><span class=\"diagram-pill accent\">Generation session</span><small class=\"diagram-muted\">image &amp; video candidates · audit log</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</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\">&rarr;</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 screenshots as references."
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: