@agent-native/core 0.39.0 → 0.39.2

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 (61) hide show
  1. package/dist/cli/index.js +1 -1
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/cli/skills.d.ts +5 -5
  4. package/dist/cli/skills.d.ts.map +1 -1
  5. package/dist/cli/skills.js +458 -615
  6. package/dist/cli/skills.js.map +1 -1
  7. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  8. package/dist/client/MultiTabAssistantChat.js +2 -5
  9. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  10. package/dist/client/NewWorkspaceAppFlow.js +1 -1
  11. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  12. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  13. package/dist/client/settings/SettingsPanel.js +11 -19
  14. package/dist/client/settings/SettingsPanel.js.map +1 -1
  15. package/dist/client/use-chat-models.d.ts.map +1 -1
  16. package/dist/client/use-chat-models.js +2 -5
  17. package/dist/client/use-chat-models.js.map +1 -1
  18. package/dist/deploy/build.d.ts.map +1 -1
  19. package/dist/deploy/build.js +2 -1
  20. package/dist/deploy/build.js.map +1 -1
  21. package/dist/deploy/route-discovery.d.ts +29 -0
  22. package/dist/deploy/route-discovery.d.ts.map +1 -1
  23. package/dist/deploy/route-discovery.js +158 -11
  24. package/dist/deploy/route-discovery.js.map +1 -1
  25. package/dist/server/auth.d.ts +2 -0
  26. package/dist/server/auth.d.ts.map +1 -1
  27. package/dist/server/auth.js +9 -0
  28. package/dist/server/auth.js.map +1 -1
  29. package/dist/templates/default/.agents/skills/actions/SKILL.md +96 -11
  30. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +126 -26
  31. package/dist/templates/default/.agents/skills/capture-learnings/SKILL.md +56 -30
  32. package/dist/templates/default/.agents/skills/create-skill/SKILL.md +28 -0
  33. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +75 -5
  34. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +17 -0
  35. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +99 -124
  36. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +43 -10
  37. package/dist/templates/default/.agents/skills/security/SKILL.md +162 -144
  38. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +5 -3
  39. package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +15 -0
  40. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +116 -83
  41. package/dist/templates/default/DEVELOPING.md +10 -13
  42. package/dist/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +9 -0
  43. package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +27 -0
  44. package/docs/content/template-plan.md +5 -3
  45. package/docs/content/visual-plans.md +5 -2
  46. package/package.json +1 -1
  47. package/src/templates/default/.agents/skills/actions/SKILL.md +96 -11
  48. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +126 -26
  49. package/src/templates/default/.agents/skills/capture-learnings/SKILL.md +56 -30
  50. package/src/templates/default/.agents/skills/create-skill/SKILL.md +28 -0
  51. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +75 -5
  52. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +17 -0
  53. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +99 -124
  54. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +43 -10
  55. package/src/templates/default/.agents/skills/security/SKILL.md +162 -144
  56. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +5 -3
  57. package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +15 -0
  58. package/src/templates/default/.agents/skills/storing-data/SKILL.md +116 -83
  59. package/src/templates/default/DEVELOPING.md +10 -13
  60. package/src/templates/workspace-core/.agents/skills/client-methods/references/legacy-client-fetch-audit-2026-06-03.md +9 -0
  61. package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +27 -0
@@ -1,128 +1,161 @@
1
1
  ---
2
2
  name: storing-data
3
3
  description: >-
4
- How and where to store application data. Use when adding new data models,
5
- deciding between settings vs Drizzle tables, reading/writing app config,
6
- or working with application state.
4
+ How to store application data in agent-native apps. All data lives in SQL.
5
+ Use when adding data models, deciding where to store data, or reading/writing
6
+ application data.
7
+ metadata:
8
+ internal: true
7
9
  ---
8
10
 
9
- # Storing Data
11
+ # Storing Data — SQL is the Source of Truth
10
12
 
11
- ## Where Data Goes
13
+ ## Rule
12
14
 
13
- All data lives in one SQLite database (`data/app.db`). In production, set `DATABASE_URL` to point to Turso, Neon, Supabase, or D1 same code, no changes needed.
15
+ All application data lives in **SQL** (SQLite locally, persistent database in production). The agent and UI share the same database. Do not store durable app data in the filesystem.
14
16
 
15
- There are three storage layers, each for a different kind of data:
17
+ ## How It Works
16
18
 
17
- ### 1. Settings app configuration
19
+ Agent-native apps use Drizzle ORM over the configured SQL backend. Local development works out of the box with a SQLite file at `data/app.db`; production and shared preview deploys need a persistent `DATABASE_URL` because container/serverless filesystems can reset. The code should behave the same across backends, but the local SQLite file is not durable once deployed.
18
20
 
19
- Key-value store for persistent non-secret config that the user or agent can change. Theme, preferences, integration settings, availability schedules.
21
+ For app code, use Drizzle's schema/query DSL by default. Raw SQL is an escape hatch for additive migrations, health checks, or one-off maintenance, not the normal way to build features.
20
22
 
21
- ```ts
22
- import { getSetting, putSetting } from "@agent-native/core/settings";
23
+ ### Core SQL Stores (auto-created, available in all templates)
23
24
 
24
- // Read (returns null if not set)
25
- const prefs = await getSetting("user-preferences");
25
+ | Store | Purpose | Access |
26
+ | ------------------- | ---------------------------------------------------- | ------------------------------------------ |
27
+ | `application_state` | Ephemeral UI state (compose windows, navigation) | `readAppState()` / `writeAppState()` |
28
+ | `settings` | Persistent KV config (preferences, app settings) | `getSetting()` / `putSetting()` |
29
+ | `oauth_tokens` | OAuth credentials | `@agent-native/core/oauth-tokens` |
30
+ | `sessions` | Auth sessions | `@agent-native/core/server` |
26
31
 
27
- // Write (creates or replaces)
28
- await putSetting("user-preferences", { theme: "dark", density: "comfortable" });
29
- ```
32
+ ### Domain Data (per-template)
33
+
34
+ Define schema with the framework Drizzle helpers in `server/db/schema.ts`. Get a database instance with `const db = getDb()` from `server/db/index.ts`. All queries are async.
30
35
 
31
- From scripts:
32
36
  ```ts
33
- import { readSetting, writeSetting } from "@agent-native/core/settings";
34
- const prefs = await readSetting("user-preferences");
35
- ```
37
+ import { eq } from "drizzle-orm";
38
+ import { table, text, integer, now } from "@agent-native/core/db/schema";
39
+
40
+ export const tasks = table("tasks", {
41
+ id: text("id").primaryKey(),
42
+ title: text("title").notNull(),
43
+ completed: integer("completed", { mode: "boolean" })
44
+ .notNull()
45
+ .default(false),
46
+ createdAt: text("created_at").notNull().default(now()),
47
+ });
36
48
 
37
- SSE: writes automatically notify the UI via `{ source: "settings", type: "change", key }`.
49
+ const rows = await db.select().from(tasks).where(eq(tasks.id, taskId));
50
+ ```
38
51
 
39
- ### 2. Application State ephemeral UI state
52
+ Never import `sqliteTable` / `pgTable` or column helpers from `drizzle-orm/sqlite-core` or `drizzle-orm/pg-core` in app templates. Use `@agent-native/core/db/schema` so the same schema can run against SQLite, Postgres, libSQL/Turso, D1, and other supported backends.
40
53
 
41
- For state the agent and UI share in real-time: what the user is looking at, compose drafts, navigation commands. Scoped by session — cleared between sessions.
54
+ | Template | Tables |
55
+ | ------------ | --------------------------------------------- |
56
+ | **Mail** | emails, labels (+ Gmail API when connected) |
57
+ | **Calendar** | events, bookings |
58
+ | **Forms** | forms, responses |
59
+ | **Content** | documents |
60
+ | **Slides** | decks (JSON stored in SQL) |
61
+ | **Videos** | compositions in registry + localStorage |
42
62
 
43
- ```ts
44
- import { readAppState, writeAppState, deleteAppState, listAppState } from "@agent-native/core/application-state";
63
+ ### Agent Access
45
64
 
46
- // Write state (UI updates instantly via SSE)
47
- await writeAppState("navigate", { view: "inbox", threadId: "t-123" });
65
+ The agent uses app-specific actions to read/write the database. Core DB scripts are for inspection and maintenance, not for implementing normal product behavior:
48
66
 
49
- // Read state
50
- const nav = await readAppState("navigation");
67
+ - `pnpm action db-schema` — Show all tables, columns, types
68
+ - `pnpm action db-query --sql "SELECT * FROM forms"` — Run SELECT queries
69
+ - `pnpm action db-exec --sql "UPDATE ..."` — Last-resort ad-hoc maintenance for short columns, multi-column writes, or computed updates when no domain action exists. For several related writes, prefer `--statements '[{"sql":"...","args":[...]}]'` so they run sequentially in one transaction. Schema changes are blocked; use reviewed additive migrations/startup code instead.
70
+ - `pnpm action db-patch --table <t> --column <c> --where "<clause>" --find "<old>" --replace "<new>"` — **Surgical search/replace on a large text column.** Sends the diff instead of re-transmitting the whole value, so it's dramatically more token-efficient than `db-exec UPDATE` when editing multi-kilobyte documents, slide HTML, dashboard/form JSON, etc. Targets exactly one row per call — narrow `--where` by primary key. Supports `--edits '[{find,replace},...]'` for batch edits and `--all` to replace every occurrence.
71
+ - App-specific actions for domain operations — **always prefer these over raw SQL when one exists.** They encode business rules, power the client action hooks, and for editor-backed tables (documents, slides) also push live Yjs updates to open collaborative editors. `db-patch` is the generic fallback for tables without a dedicated edit action.
51
72
 
52
- // List by prefix (e.g., all compose drafts)
53
- const drafts = await listAppState("compose-");
73
+ **For one-off maintenance, how to choose between `db-exec UPDATE` and `db-patch`:**
54
74
 
55
- // Delete (one-shot commands: UI reads, then agent or UI deletes)
56
- await deleteAppState("navigate");
57
- ```
75
+ | Scenario | Use |
76
+ | -------------------------------------------------------------- | ------------ |
77
+ | `SET status = 'published'` on one row | `db-exec` |
78
+ | `SET calories = calories + 50` | `db-exec` |
79
+ | Updating several columns at once | `db-exec` |
80
+ | Inserting/updating several rows as one logical operation | `db-exec --statements` |
81
+ | Fixing a typo in a 50KB markdown document's `content` column | `db-patch` |
82
+ | Changing a single key in a dashboard's JSON blob | `db-patch` |
83
+ | Tweaking one paragraph of slide HTML stored in `decks.data` | `db-patch` |
84
+ | Any edit where you'd otherwise re-send thousands of characters | `db-patch` |
58
85
 
59
- SSE: writes automatically notify the UI via `{ source: "app-state", type: "change", key }`.
86
+ All of these honor the per-user / per-org data scoping you can't read or write rows outside the current user's data, regardless of which tool you choose.
60
87
 
61
- ### 3. Drizzle Tables — structured domain data
88
+ ### Frontend Access
62
89
 
63
- For data with schemas, relationships, and queries: forms, bookings, emails, compositions. Define tables in `server/db/schema.ts` using Drizzle ORM.
90
+ The frontend calls actions using React Query hooks from the client API. The framework owns the HTTP transport behind these hooks, so components should not call action routes with raw `fetch`.
64
91
 
65
92
  ```ts
66
- import { table, text, integer } from "@agent-native/core/db/schema";
93
+ import { useActionQuery, useActionMutation } from "@agent-native/core/client";
67
94
 
68
- export const bookings = table("bookings", {
69
- id: text("id").primaryKey(),
70
- name: text("name").notNull(),
71
- email: text("email").notNull(),
72
- startTime: integer("start_time").notNull(),
73
- endTime: integer("end_time").notNull(),
74
- });
95
+ // Read data
96
+ const { data } = useActionQuery("list-meals", { date: "2025-01-01" });
97
+
98
+ // Write data
99
+ const { mutate } = useActionMutation("log-meal");
75
100
  ```
76
101
 
77
- Query via `getDb()` singleton from `server/db/index.ts`.
102
+ Actions are the **preferred way** for the frontend to access data. You rarely need custom `/api/` routes — only for file uploads, streaming, webhooks, or OAuth callbacks.
78
103
 
79
- ### 4. OAuth Tokens — credentials
104
+ ### Production / Cloud Deployment
80
105
 
81
- For OAuth tokens acquired at runtime (Google, etc.). Never store these in settings use the dedicated store.
106
+ Local SQLite works out of the box for development. To deploy to production or any environment where data must survive restarts:
82
107
 
83
- ```ts
84
- import { saveOAuthTokens, getOAuthTokens, listOAuthAccounts } from "@agent-native/core/oauth-tokens";
108
+ 1. Set `DATABASE_URL` to a persistent SQL database.
109
+ 2. Set `DATABASE_AUTH_TOKEN` only when the provider requires a separate token, such as Turso/libSQL.
110
+ 3. No code changes should be needed when the schema and queries stay portable.
85
111
 
86
- await saveOAuthTokens("google", "user@gmail.com", { access_token: "...", refresh_token: "..." });
87
- const tokens = await getOAuthTokens("google", "user@gmail.com");
88
- const accounts = await listOAuthAccounts("google");
89
- ```
112
+ Turso is one valid option, not the required option. Common choices include Neon or Supabase Postgres, Turso/libSQL, plain Postgres, durable SQLite, Cloudflare D1 bindings, and managed platform SQL environments when available.
113
+
114
+ ### Real-time Sync
115
+
116
+ Polling streams database changes to the UI. When the agent writes to the database via scripts, the UI updates automatically via `useDbSync()` which invalidates React Query caches.
117
+
118
+ ## Do
119
+
120
+ - Use Drizzle ORM for structured domain data (forms, bookings, documents)
121
+ - Use Drizzle query builder methods (`select`, `insert`, `update`, `delete`) and portable operators from `drizzle-orm` (`eq`, `and`, `or`, `inArray`, `desc`, etc.) for app reads/writes
122
+ - Use framework schema helpers from `@agent-native/core/db/schema`, not dialect-specific Drizzle imports
123
+ - Use the `settings` store for app configuration and user preferences
124
+ - Use `application-state` for ephemeral UI state that the agent and UI share
125
+ - Use `oauth-tokens` for OAuth credentials
126
+ - Use core DB scripts (`db-schema`, `db-query`, `db-exec`, `db-patch`) for ad-hoc database operations
127
+ - Use `db-exec --statements` instead of several separate `db-exec` calls for related writes; it is faster and rolls back the whole batch if one statement fails
128
+ - Reach for `db-patch` instead of `db-exec UPDATE` whenever you're making a small change to a large text/JSON column — it's much cheaper on tokens
90
129
 
91
- ### 5. Secrets / Credentials — encrypted values
130
+ ## Don't
92
131
 
93
- For API keys, service tokens, webhook secrets, and user/org/workspace
94
- credentials. Register user-facing secrets with the secrets registry and read
95
- them server-side with `readAppSecret`, or use `saveCredential` /
96
- `resolveCredential` for scoped credential lookup. Never store these values in
97
- settings, application state, source code, docs, examples, logs, or action
98
- responses.
132
+ - Don't store structured app data as JSON files
133
+ - Don't store app state in localStorage, sessionStorage, or cookies (except for UI-only preferences like sidebar width)
134
+ - Don't keep state only in memory (server variables, global stores)
135
+ - Don't use Redis or any external state store for app data
136
+ - Don't implement product features with raw SQL or `getDbExec()` when Drizzle can express the query
137
+ - Don't write SQLite-only or Postgres-only SQL in app code
138
+ - Don't interpolate user input directly into SQL queries — use Drizzle ORM's query builder
99
139
 
100
- ## Which Layer to Use
140
+ ## Security
101
141
 
102
- | Data | Layer | Why |
103
- |------|-------|-----|
104
- | User preferences, theme, config | Settings | Persistent KV, SSE notifications, simple read/write |
105
- | What the user sees on screen | Application State | Ephemeral, real-time sync, agent ↔ UI bridge |
106
- | Compose drafts, wizard steps | Application State | Temporary, deleted when done |
107
- | Domain records (forms, bookings) | Drizzle table | Needs schema, queries, relationships |
108
- | API keys, service tokens, webhook secrets | Secrets / credentials | Encrypted and scoped; never client-readable |
109
- | OAuth refresh tokens | OAuth Tokens | Secure, per-provider, per-account |
142
+ - **SQL injection** Use Drizzle ORM's query builder, never raw string interpolation for SQL queries
143
+ - **Validate before writing** — Check data shape before writing, especially for user-submitted data
110
144
 
111
- ## Environment Variables
145
+ ## Application State and Context Awareness
112
146
 
113
- Infrastructure config stays in `.env`these differ per deployment:
147
+ When storing app-state, include **navigation state** the agent needs to know what the user is looking at. The `application_state` table holds ephemeral UI state that both the agent and UI share. Key patterns:
114
148
 
115
- - `DATABASE_URL`database connection (default: `file:./data/app.db`)
116
- - `DATABASE_AUTH_TOKEN`for remote databases
117
- - `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` — OAuth app credentials
118
- - `ACCESS_TOKEN` — production auth token
149
+ - **`navigation` key** the UI writes current view and selection on every route change. The agent reads this before acting.
150
+ - **`navigate` key** the agent writes one-shot commands to navigate the UI. The UI processes and deletes them.
151
+ - **Domain-specific keys** (e.g., `compose-{id}`)bidirectional state for features like email drafts.
119
152
 
120
- Everything else (user settings, app state, domain data) goes in SQL through the
121
- appropriate store. User/org/workspace credential values go in the encrypted
122
- secrets/credential stores, not plain settings rows.
153
+ When adding a new data model or feature, also consider what navigation and selection state needs to be exposed via application-state. See the **context-awareness** skill for the full pattern.
123
154
 
124
- ## Security Rules
155
+ ## Related Skills
125
156
 
126
- - **Never store API keys or secrets in Settings or Application State** — use the secrets registry / vault or `saveCredential` / `resolveCredential` for API keys and service tokens, deploy env vars only for deploy-level secrets, and `oauth_tokens` for OAuth credentials. Settings and application state are readable by the client.
127
- - **Every Drizzle table with user data must have `owner_email`** the framework auto-scopes queries in production so users only see their own data. Run `pnpm action db-check-scoping` to verify. See the `security` skill for the full model.
128
- - **Never return secrets in action responses** — action responses may be visible in the agent chat or sent to the client. Keep credentials server-side only.
157
+ - **context-awareness** — How to expose navigation and selection state via application-state
158
+ - **real-time-sync**Set up polling so the UI updates when the database changes
159
+ - **actions** — Create actions with `defineAction` to query the database
160
+ - **client-methods** — Keep route details behind named client helpers/hooks
161
+ - **self-modifying-code** — The agent can also modify the app's source code
@@ -6,7 +6,7 @@ This guide is for development-mode agents editing this app's source code. For ap
6
6
 
7
7
  **Client-side-first rendering:** This app uses React Router v7 framework mode with `ssr: true`, but all app content renders **client-side only**. The server renders only the HTML shell (meta tags, styles, scripts) plus a loading spinner. This is enforced by the `ClientOnly` wrapper in `root.tsx` — never remove it. Browser APIs (`window`, `localStorage`, `new Date()`) are safe to use anywhere in app code because components never run on the server.
8
8
 
9
- **Do NOT fetch data server-side** in route loaders. The standard pattern is: server renders a spinner, client hydrates, React Query hooks fetch from `/api/*`.
9
+ **Do NOT fetch data server-side** in route loaders unless the page genuinely needs SEO/OG content. The standard pattern is: SSR renders the shell, client hydrates, and React reads/writes normal app data through actions with `useActionQuery` / `useActionMutation`.
10
10
 
11
11
  ## Adding a Page
12
12
 
@@ -30,7 +30,7 @@ In a workspace, this app can be mounted under `/<app-id>`. React Router already
30
30
  | `app/routes/review.tsx` | `/review` | `/<app-id>/review` |
31
31
  | `app/routes/$id.tsx` | `/:id` | `/<app-id>/:id` |
32
32
 
33
- Use `<Link to="/review">` and `navigate("/review")` inside this app. Do not prefix React Router paths with `/<app-id>` or the URL can double-prefix, e.g. `/<app-id>/<app-id>/review`. Use `appPath()` for raw `href`s/static assets, `appApiPath()` for `/api/*`, and `agentNativePath()` for `/_agent-native/*`.
33
+ Use `<Link to="/review">` and `navigate("/review")` inside this app. Do not prefix React Router paths with `/<app-id>` or the URL can double-prefix, e.g. `/<app-id>/<app-id>/review`. Use `appPath()` for raw `href`s/static assets, `agentNativePath()` for `/_agent-native/*`, and `appApiPath()` only for legitimate route-only `/api/*` endpoints.
34
34
 
35
35
  Each route file exports a default component and optional `meta()`:
36
36
 
@@ -46,17 +46,15 @@ export default function MyPageRoute() {
46
46
  }
47
47
  ```
48
48
 
49
- ## Adding an API Route
49
+ ## Adding App Data
50
50
 
51
- Create a file in `server/routes/api/`. The filename determines the URL path and HTTP method:
51
+ Normal app data starts as an action, not a custom route. Add `actions/<verb>-<resource>.ts` with `defineAction`, mark reads with `http: { method: "GET" }`, and call reads/writes from React with `useActionQuery` / `useActionMutation` from `@agent-native/core/client`. This keeps the UI and agent on one contract and lets mutating actions refresh action-backed queries automatically.
52
52
 
53
- ```
54
- server/routes/api/items/index.get.ts → GET /api/items
55
- server/routes/api/items/[id].get.ts → GET /api/items/:id
56
- server/routes/api/items/[id].patch.ts → PATCH /api/items/:id
57
- ```
53
+ ## Adding a Route-Only Endpoint
54
+
55
+ Use `server/routes/api/` only for protocols that cannot be modeled as JSON actions: multipart uploads, streaming/SSE/WebSocket, webhooks, OAuth callbacks/redirects, public SEO/OG endpoints, or binary/static asset serving. Do not add `/api/*` routes for normal CRUD, data queries, or pass-through wrappers around actions; the action endpoint already exists at `/_agent-native/actions/:name`.
58
56
 
59
- Each file exports a default `defineEventHandler`.
57
+ Each route-only endpoint still exports a default `defineEventHandler`, but keep shared app logic in actions or server libraries so agent and UI behavior do not fork.
60
58
 
61
59
  ## Server Plugins
62
60
 
@@ -84,10 +82,9 @@ export default defineNitroPlugin(async (nitroApp) => {
84
82
  | Agent chat context state helpers | Optional advanced helpers for two-way sync with staged context chips |
85
83
  | `agentChat` | Send messages to agent from scripts (server-side) |
86
84
 
87
- ## Adding a Script
85
+ ## Adding an Action
88
86
 
89
- Create `actions/my-script.ts` exporting `default async function(args: string[])`.
90
- Run with: `pnpm action my-script --arg value`
87
+ Create `actions/<verb>-<resource>.ts` with `defineAction`. Run with `pnpm action <name> --id value`; React callers should use `useActionQuery` for GET actions and `useActionMutation` for mutating actions, not a matching `/api/*` wrapper.
91
88
 
92
89
  ## Sending to Agent Chat
93
90
 
@@ -7,6 +7,11 @@ contracts; some need new actions or helper modules first.
7
7
 
8
8
  ## Highest Priority
9
9
 
10
+ - 2026-06-07 follow-up: the same high-priority route-first clusters are still
11
+ present. The biggest migrations remain Analytics, Calendar, Mail, Slides, and
12
+ Content. Do not copy these patterns into new work; when editing the relevant
13
+ area, add or reuse actions first, then call them with `useActionQuery`,
14
+ `useActionMutation`, or `callAction`.
10
15
  - `templates/analytics/app/pages/analyses/AnalysesList.tsx`,
11
16
  `templates/analytics/app/pages/analyses/AnalysisDetail.tsx`,
12
17
  `templates/analytics/app/components/layout/Sidebar.tsx`, and
@@ -43,6 +48,10 @@ contracts; some need new actions or helper modules first.
43
48
  - Content comments and versions are partially migrated. Add missing actions such
44
49
  as `resolve-comment`, `delete-comment`, `list-document-versions`, and
45
50
  `restore-document-version`.
51
+ - Plans version history is the model to copy for new history/rollback work:
52
+ `list-plan-versions`, `get-plan-version`, and `restore-plan-version` are
53
+ action-native, and the UI calls them through action hooks. Do not copy
54
+ Content's legacy document-version `/api/*` helpers into new version panels.
46
55
 
47
56
  ## Acceptable Exceptions
48
57
 
@@ -74,6 +74,33 @@ Keep one canonical instructions file: `AGENTS.md`. If a client expects
74
74
  hand-maintained files drift, and the agent ends up with contradictory rules.
75
75
  One source of truth, linked where needed.
76
76
 
77
+ ## Keep generated guidance in sync
78
+
79
+ Framework guidance is authored once in this repo and copied outward. Treat
80
+ `.agents/skills/` as the canonical source for shared skills. Generated
81
+ workspace skills in `packages/core/src/templates/workspace-core/.agents/skills/`
82
+ and first-party template copies of shared skills must stay byte-for-byte in
83
+ sync; run `pnpm sync:workspace-skills` after editing a shared skill, and
84
+ `pnpm guard:workspace-skills` before calling the guidance done.
85
+
86
+ Generated app and workspace instructions must teach the same action-first data
87
+ contract:
88
+
89
+ - Normal app data goes through `defineAction` files in `actions/`.
90
+ - React calls actions with `useActionQuery`, `useActionMutation`, or
91
+ `callAction`; route paths are a transport detail hidden behind helpers.
92
+ - Custom `/api/*` routes are only for route-shaped protocols such as uploads,
93
+ streaming, webhooks, OAuth callbacks, public SEO/OG endpoints, or binary
94
+ assets.
95
+ - Do not create pass-through routes whose main job is to call, repackage, or
96
+ re-export an action.
97
+
98
+ When documenting version history, restore, or audit trails, use actions for
99
+ full restorable snapshots (`list-<resource>-versions`,
100
+ `get-<resource>-version`, `restore-<resource>-version`). Do not copy legacy
101
+ raw-route version panels, such as document-version `/api/*` helpers, into new
102
+ features. The Plans version-history pattern is the preferred model.
103
+
77
104
  ## SKILL.md frontmatter must say what AND when
78
105
 
79
106
  The `description` is the only thing the agent sees when deciding whether to read
@@ -33,7 +33,10 @@ The command installs `/visual-plan` plus the companion commands:
33
33
  - `/ui-plan` for UI-first plans with mockups, states, and screen-level review.
34
34
  - `/prototype-plan` for clickable prototype-first plans with live comments.
35
35
  - `/visual-questions` for visual intake before a plan.
36
- - `/visualize-plan` for turning an existing text plan into a visual companion.
36
+
37
+ Use `/visual-plan` for both fresh plans and existing Codex, Claude Code,
38
+ Markdown, or pasted plans; when source plan text already exists, the agent builds
39
+ from that plan instead of starting over.
37
40
 
38
41
  By default the CLI targets Codex. Add `--client claude-code` or `--client all`
39
42
  when you want to configure another host:
@@ -62,7 +65,6 @@ After installation, ask your agent for the command that fits the work:
62
65
  - `/prototype-plan` creates a clickable prototype above the plan document, with
63
66
  static mocks, comments, and a focused browser popout.
64
67
  - `/visual-questions` opens a visual intake questionnaire before planning.
65
- - `/visualize-plan` imports a plan you already have and makes it reviewable.
66
68
 
67
69
  The agent should inspect the codebase first, then create the visual plan when a
68
70
  wrong direction would be costly. The returned Plans link opens the review UI so
@@ -101,7 +103,7 @@ it to the agent starts a revision turn against the existing plan.
101
103
  - "Create a `/ui-plan` for the new onboarding screen with mobile and desktop states."
102
104
  - "Create a `/prototype-plan` for the checkout flow so I can click through it."
103
105
  - "Use `/visual-questions` to help me choose the dashboard direction first."
104
- - "Run `/visualize-plan` on the Markdown plan below and make it easier to review."
106
+ - "Use `/visual-plan` on the Markdown plan below and make it easier to review."
105
107
 
106
108
  ## For developers
107
109
 
@@ -20,7 +20,7 @@ Install with the Agent-Native CLI. The command installs the skill instructions,
20
20
  agent-native skills add visual-plan
21
21
  ```
22
22
 
23
- Authentication is a one-time browser sign-in at setup — this is intended, and it is what lets the agent persist and share the plans it generates. This also installs the companion commands `/ui-plan`, `/visual-questions`, and `/visualize-plan` (see [Invoking the skill](#invoke)).
23
+ Authentication is a one-time browser sign-in at setup — this is intended, and it is what lets the agent persist and share the plans it generates. This also installs the companion commands `/ui-plan`, `/prototype-plan`, `/plan-design`, and `/visual-questions` (see [Invoking the skill](#invoke)).
24
24
 
25
25
  What the auth step does depends on your client:
26
26
 
@@ -40,11 +40,14 @@ Once installed, use the slash command that fits the work:
40
40
 
41
41
  - `/visual-plan` — the canonical command for any rich plan (architecture, backend, refactors, UI).
42
42
  - `/ui-plan` — UI-first work that should start with the screens.
43
+ - `/prototype-plan` — prototype-first work that should start with a clickable flow.
44
+ - `/plan-design` — full-fidelity branded UI direction before implementation.
43
45
  - `/visual-questions` — a short visual intake form before planning.
44
- - `/visualize-plan` — turn an existing Codex, Claude Code, Markdown, or pasted plan into a visual companion.
45
46
 
46
47
  The agent gates hard: it only builds a polished visual plan when a wrong direction would be costly, and skips it for trivial, unambiguous work. Each command generates a plan and opens the editor.
47
48
 
49
+ When a Codex, Claude Code, Markdown, or pasted plan already exists, use `/visual-plan`. The agent should preserve that source plan and build the richer review surface from it instead of starting over.
50
+
48
51
  When a plan has unresolved decisions that are useful to answer after the first pass, the agent can put them in an **Open Questions** form at the bottom of the same plan. You can choose single or multiple options, fill in freeform answers, and send the answers back to the agent to revise the plan.
49
52
 
50
53
  ## Editing in the browser as a guest {#guest}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.39.0",
3
+ "version": "0.39.2",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -2,20 +2,45 @@
2
2
  name: actions
3
3
  description: >-
4
4
  How to create and run agent actions. Actions are the single source of truth
5
- for app operations — the agent calls them as tools, the frontend calls them
6
- as HTTP endpoints. Use when creating a new action, adding an API integration,
7
- or wiring up frontend data fetching.
5
+ for app operations — the agent calls them as tools and frontend code calls
6
+ them through client hooks. Use when creating a new action, adding an API
7
+ integration, or wiring up frontend data fetching.
8
+ metadata:
9
+ internal: true
8
10
  ---
9
11
 
10
12
  # Agent Actions
11
13
 
12
14
  ## Rule
13
15
 
14
- Actions in `actions/` are the **single source of truth** for app operations. The agent calls them as tools, and the framework auto-exposes them as HTTP endpoints at `/_agent-native/actions/:name`. The frontend calls those endpoints using React Query hooks. No duplicate `/api/` routes needed.
16
+ Actions in `actions/` are the **single source of truth** for app operations. The agent calls them as tools, and the frontend calls them through `useActionQuery` / `useActionMutation`. The framework owns the HTTP transport behind those hooks. No duplicate `/api/` routes needed.
17
+
18
+ Before creating any custom REST/API route for app data, inspect `actions/` and the action table in `AGENTS.md`. If an action already exists, call it directly from the agent or with `useActionQuery` / `useActionMutation` from the UI. If the capability is missing, create or update a `defineAction`. Do not add `/api/*`, `server/routes/*`, or other pass-through endpoints whose main job is to call, repackage, or re-export an action.
15
19
 
16
20
  ## Why
17
21
 
18
- Actions give the agent callable tools with structured input/output, AND they give the frontend type-safe HTTP endpoints automatically. One implementation serves both the agent and the UI. They keep the agent's chat context clean, they're reusable, and they can be tested independently.
22
+ Actions give the agent callable tools with structured input/output, AND they give the frontend a typed client contract through hooks. One implementation serves both the agent and the UI. They keep the agent's chat context clean, they're reusable, and they can be tested independently.
23
+
24
+ ## Keep the Action Surface Small and Orthogonal
25
+
26
+ Every agent-exposed action is a tool in the model's context window. There is a real cost to each one: more tools means more for the model to read, disambiguate, and choose between, which degrades tool-selection quality. Treat the action list like an API you have to maintain — add the fewest, most orthogonal actions that cover the capability, not one per UI affordance.
27
+
28
+ - **Prefer one CRUD-style `update` over N per-field actions.** A single `update-<thing>` that takes a patch of optional fields beats `update-<thing>-name`, `update-<thing>-order`, `update-<thing>-color`, … The agent (and the UI) pass only the fields that change. Same for `create`/`delete` — one orthogonal action per resource, not one per code path.
29
+ - **Reach for a generic query / escape hatch before minting a new read action.** If the agent needs more or different data, do not add `get-<thing>-by-x`, `list-<thing>-filtered-by-y`, etc. For provider data, expose the shared `provider-api-catalog` / `provider-api-docs` / `provider-api-request` trio (see `templates/dispatch/actions/`) so the agent can hit any endpoint or filter without a new action each time. For app data in dev, the `db-query` tool already answers arbitrary read questions.
30
+ - **Hide UI-only or purely programmatic actions from the model with `agentTool: false`.** An action that only the frontend or an HTTP/cron caller needs should not spend a slot in the model's tool list. `agentTool: false` keeps it callable from `useActionMutation` / `callAction` / `/_agent-native/actions/<name>` while removing it from every agent tool surface (in-app assistant, MCP, A2A).
31
+ - **`agentTool: false` is NOT `toolCallable: false`.** They are different switches:
32
+ - `agentTool: false` → hidden from the **model entirely** (it is no longer a tool the agent can see or call). Still frontend/HTTP-callable.
33
+ - `toolCallable: false` → only blocks the **sandboxed extension ("tools") iframe bridge** (`appAction(...)`). The action stays fully visible to the model, the UI, the CLI, MCP, and A2A. Use it for high-blast-radius operations (account/org/auth changes), not for trimming the tool list.
34
+ - **Remove or hide stale actions.** When the UI stops using an action, delete it or set `agentTool: false` — do not leave it exposed to the model as dead tool weight. The advisory audit below helps you spot these.
35
+
36
+ ### Audit Script (Advisory)
37
+
38
+ `pnpm actions:audit [template ...]` (or `node scripts/audit-template-actions.mjs`) statically scans a template's `actions/` and prints two kinds of suggestions:
39
+
40
+ 1. **Likely UI-dead** — HTTP-exposed mutating actions whose name is never referenced under `app/` (candidates to delete or mark `agentTool: false`).
41
+ 2. **Likely redundant clusters** — groups like `update-foo-name` / `update-foo-order` that could collapse into one orthogonal `update-foo`.
42
+
43
+ It is **advisory only**: it always exits 0, never fails CI, and uses conservative heuristics, so expect some false positives (e.g. an action the agent calls but the UI doesn't). Use it as a prompt to review, not a gate.
19
44
 
20
45
  ## How to Create an Action
21
46
 
@@ -45,14 +70,59 @@ export default defineAction({
45
70
 
46
71
  The `schema` field accepts a Zod schema (or any Standard Schema-compatible library). It provides runtime validation with clear error messages (400 for HTTP, error result for agent), full TypeScript type inference for `run()` args, and auto-generated JSON Schema for the agent's tool definition. `zod` is a dependency of all templates.
47
72
 
73
+ When an action reads or writes app data, use Drizzle's query builder and portable operators from `drizzle-orm`. Do not use raw SQL, `getDbExec()`, or dialect-specific schema imports in normal actions unless there is a documented reason Drizzle cannot express the query.
74
+
75
+ When an action calls an external service, never hardcode API keys, bearer
76
+ tokens, webhook URLs, signing secrets, OAuth refresh tokens, private
77
+ Builder/internal data, or customer data. Read user/org/workspace credentials
78
+ from `readAppSecret`, `resolveCredential`, OAuth token helpers, or the provider
79
+ API credential adapter. Use `process.env` only for explicitly deploy-level
80
+ configuration, and keep examples to obvious placeholders.
81
+
48
82
  Tips:
49
83
  - Use `.describe()` for parameter descriptions
50
84
  - Use `.optional()` for optional params
51
- - Use `z.coerce.number()` / `z.coerce.boolean()` for params that arrive as strings from HTTP
85
+ - Use `z.coerce.number()` for numeric params that arrive as strings from HTTP.
86
+ For booleans, use an explicit string parser/helper instead of
87
+ `z.coerce.boolean()` because JavaScript treats any non-empty string,
88
+ including `"false"`, as truthy.
52
89
  - Use `z.enum(["draft", "published"])` for constrained values
53
90
 
54
91
  The legacy `parameters` field (plain JSON Schema object) still works as a fallback but does not provide runtime validation or type inference.
55
92
 
93
+ ## Decision Order
94
+
95
+ When you need app data or a mutation:
96
+
97
+ 1. **Use an existing action** if one already performs the operation.
98
+ 2. **Create or extend a `defineAction`** when the agent and UI both need a new operation.
99
+ 3. **Create a custom route only for route-only concerns** such as uploads, streaming, webhooks, OAuth callbacks, or a non-JSON protocol.
100
+
101
+ Do not build an umbrella REST API to make actions "easier" to call. Actions are already callable by agents, CLIs, React hooks, HTTP, MCP/A2A exposure, and external hosts through the framework.
102
+
103
+ ## Flexible Provider APIs
104
+
105
+ For provider integrations used in ad hoc analysis, querying, reporting, or
106
+ cross-source research, do not hardcode every provider endpoint as a separate
107
+ rigid action. Expose the shared provider API action trio instead:
108
+
109
+ - `provider-api-catalog`: lists provider base URLs, auth style, credential keys,
110
+ docs/spec URLs, placeholders, and examples without exposing secrets.
111
+ - `provider-api-docs`: fetches registered provider docs/spec URLs when the
112
+ exact endpoint, filter operator, payload shape, or pagination contract is
113
+ uncertain.
114
+ - `provider-api-request`: makes a constrained authenticated HTTP request to the
115
+ provider host, injects configured credentials, blocks private/internal URLs,
116
+ and redacts secrets.
117
+
118
+ Use `@agent-native/core/provider-api` as the shared substrate. A template should
119
+ only add a thin credential adapter when it has app-specific credential lookup
120
+ rules. Keep `provider-api-request` `http: false` unless you have a separate UI
121
+ permission model for arbitrary provider writes. Specific actions such as
122
+ `hubspot-deals`, `search-emails`, or `sync-source` are convenience shortcuts,
123
+ not capability limits; agents should fall back to the provider API trio when a
124
+ question requires an endpoint or filter that the shortcut does not model.
125
+
56
126
  ### The `http` Option
57
127
 
58
128
  Controls how the action is exposed as an HTTP endpoint:
@@ -99,7 +169,7 @@ run: async (args) => {
99
169
 
100
170
  ## Frontend Hooks
101
171
 
102
- The frontend calls action endpoints using React Query hooks from `@agent-native/core/client`:
172
+ The frontend calls actions using React Query hooks from `@agent-native/core/client`. Components should not hand-write `fetch("/_agent-native/actions/...")`; add or reuse a client hook/helper instead. Use `callAction` from the same package for imperative cases that do not fit a hook, such as debounced search, prefetching, or non-React event handlers.
103
173
 
104
174
  ### `useActionQuery` — for GET actions
105
175
 
@@ -135,6 +205,17 @@ function AddMealButton() {
135
205
 
136
206
  Mutations automatically invalidate all `["action"]` query keys on success, so GET queries refetch.
137
207
 
208
+ ### `callAction` — for imperative client code
209
+
210
+ ```ts
211
+ import { callAction } from "@agent-native/core/client";
212
+
213
+ const people = await callAction("search-people", { query }, { method: "GET" });
214
+ ```
215
+
216
+ Prefer hooks in React data flows. Use `callAction` when a hook would be awkward;
217
+ do not hand-write action route fetches in components.
218
+
138
219
  ## How to Run (Agent)
139
220
 
140
221
  ```bash
@@ -161,7 +242,7 @@ Most operations should be actions. You only need custom routes in `server/routes
161
242
  - **Webhooks** — external services POST to a specific URL
162
243
  - **OAuth callbacks** — redirect-based flows that need specific URL patterns
163
244
 
164
- If it's a standard CRUD operation or data query, use an action instead.
245
+ If it's a standard CRUD operation, data query, or a wrapper around an action, use the action instead.
165
246
 
166
247
  ## Legacy Pattern (bare export)
167
248
 
@@ -171,7 +252,6 @@ Older actions use a bare async function export with `parseArgs`:
171
252
  import { parseArgs, loadEnv, fail } from "@agent-native/core";
172
253
 
173
254
  export default async function myAction(args: string[]) {
174
- // Only for deploy-level runtime config. User/org credentials use secrets/OAuth.
175
255
  loadEnv();
176
256
  const parsed = parseArgs(args);
177
257
  // ...
@@ -186,11 +266,15 @@ This still works but is not auto-exposed as HTTP. Prefer `defineAction` for all
186
266
  - **Return structured data.** Return objects/arrays, not `JSON.stringify()`.
187
267
  - **Use `http: { method: "GET" }`** for read-only actions. Default is POST.
188
268
  - **Use `http: false`** for agent-only actions (`navigate`, `view-screen`).
269
+ - **Use `agentTool: false`** for UI-only / programmatic actions that should NOT be a tool in the model's context window. It stays frontend/HTTP-callable but is hidden from the agent. Distinct from `toolCallable: false`, which only blocks the sandboxed extension iframe bridge.
270
+ - **Document reusable actions.** If a new action should be called by agents outside one narrow screen, update `AGENTS.md` with when to use it, important args, and which return fields to preserve.
271
+ - **Promote workflow-heavy actions to skills.** If the action is part of a provider-backed, cross-app, MCP/A2A, or multi-step workflow, create or update a skill in `.agents/skills/` and add app-skill visibility (`internal`, `exported`, or `both`) when it should ship through a marketplace.
189
272
  - **Use `loadEnv()`** only for deploy-level configuration. User/org/workspace
190
273
  credentials belong in the encrypted secrets/credential/OAuth stores, never as
191
- hardcoded literals, shared env fallbacks, logs, or action responses.
274
+ hardcoded literals or shared env fallbacks.
192
275
  - **Use `fail()`** for user-friendly error messages (exits with message, no stack trace).
193
276
  - **Import from `@agent-native/core`** — Don't redefine `parseArgs()` or other utilities locally.
277
+ - **Do not re-export actions as REST.** The mounted `/_agent-native/actions/:name` endpoint is the REST surface; duplicating it under `/api/*` creates drift and hides the operation from agents.
194
278
 
195
279
  ## Common Patterns
196
280
 
@@ -263,5 +347,6 @@ export default defineAction({
263
347
 
264
348
  - **storing-data** — Actions read/write data in SQL
265
349
  - **delegate-to-agent** — The agent invokes actions via `pnpm action <name>`
266
- - **real-time-sync** — Database writes from actions trigger poll events to update the UI
350
+ - **real-time-sync** — Database writes from actions trigger change events to update the UI
267
351
  - **adding-a-feature** — Actions are area 2 of the four-area checklist
352
+ - **client-methods** — Client code uses named helpers/hooks instead of raw REST calls