@agent-native/core 0.7.51 → 0.7.53

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 (90) hide show
  1. package/dist/a2a/artifact-response.d.ts.map +1 -1
  2. package/dist/a2a/artifact-response.js +109 -5
  3. package/dist/a2a/artifact-response.js.map +1 -1
  4. package/dist/a2a/server.d.ts.map +1 -1
  5. package/dist/a2a/server.js +11 -0
  6. package/dist/a2a/server.js.map +1 -1
  7. package/dist/cli/templates-meta.d.ts.map +1 -1
  8. package/dist/cli/templates-meta.js +1 -0
  9. package/dist/cli/templates-meta.js.map +1 -1
  10. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  11. package/dist/client/settings/VoiceTranscriptionSection.js +54 -13
  12. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  13. package/dist/deploy/workspace-deploy.js +32 -3
  14. package/dist/deploy/workspace-deploy.js.map +1 -1
  15. package/dist/integrations/plugin.d.ts.map +1 -1
  16. package/dist/integrations/plugin.js +2 -1
  17. package/dist/integrations/plugin.js.map +1 -1
  18. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  19. package/dist/integrations/webhook-handler.js +10 -0
  20. package/dist/integrations/webhook-handler.js.map +1 -1
  21. package/dist/onboarding/plugin.d.ts.map +1 -1
  22. package/dist/onboarding/plugin.js +2 -1
  23. package/dist/onboarding/plugin.js.map +1 -1
  24. package/dist/org/plugin.d.ts.map +1 -1
  25. package/dist/org/plugin.js +2 -1
  26. package/dist/org/plugin.js.map +1 -1
  27. package/dist/scripts/call-agent.js +2 -2
  28. package/dist/scripts/call-agent.js.map +1 -1
  29. package/dist/server/action-routes.d.ts.map +1 -1
  30. package/dist/server/action-routes.js +5 -11
  31. package/dist/server/action-routes.js.map +1 -1
  32. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  33. package/dist/server/agent-chat-plugin.js +2 -1
  34. package/dist/server/agent-chat-plugin.js.map +1 -1
  35. package/dist/server/auth-plugin.d.ts.map +1 -1
  36. package/dist/server/auth-plugin.js +2 -1
  37. package/dist/server/auth-plugin.js.map +1 -1
  38. package/dist/server/auth.d.ts.map +1 -1
  39. package/dist/server/auth.js +7 -12
  40. package/dist/server/auth.js.map +1 -1
  41. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  42. package/dist/server/core-routes-plugin.js +9 -29
  43. package/dist/server/core-routes-plugin.js.map +1 -1
  44. package/dist/server/cors-origins.d.ts +10 -0
  45. package/dist/server/cors-origins.d.ts.map +1 -0
  46. package/dist/server/cors-origins.js +34 -0
  47. package/dist/server/cors-origins.js.map +1 -0
  48. package/dist/server/create-server.d.ts.map +1 -1
  49. package/dist/server/create-server.js +10 -29
  50. package/dist/server/create-server.js.map +1 -1
  51. package/dist/server/framework-request-handler.d.ts +11 -0
  52. package/dist/server/framework-request-handler.d.ts.map +1 -1
  53. package/dist/server/framework-request-handler.js +24 -1
  54. package/dist/server/framework-request-handler.js.map +1 -1
  55. package/dist/server/resources-plugin.d.ts.map +1 -1
  56. package/dist/server/resources-plugin.js +2 -1
  57. package/dist/server/resources-plugin.js.map +1 -1
  58. package/dist/terminal/terminal-plugin.d.ts.map +1 -1
  59. package/dist/terminal/terminal-plugin.js +2 -1
  60. package/dist/terminal/terminal-plugin.js.map +1 -1
  61. package/docs/content/a2a-protocol.md +93 -14
  62. package/docs/content/agent-mentions.md +2 -2
  63. package/docs/content/agent-teams.md +2 -2
  64. package/docs/content/client.md +1 -1
  65. package/docs/content/cloneable-saas.md +13 -13
  66. package/docs/content/creating-templates.md +253 -211
  67. package/docs/content/dispatch.md +94 -0
  68. package/docs/content/faq.md +2 -2
  69. package/docs/content/frames.md +1 -1
  70. package/docs/content/getting-started.md +9 -1
  71. package/docs/content/key-concepts.md +17 -1
  72. package/docs/content/messaging.md +56 -19
  73. package/docs/content/multi-app-workspace.md +10 -2
  74. package/docs/content/notifications.md +1 -1
  75. package/docs/content/observability.md +184 -0
  76. package/docs/content/onboarding.md +7 -2
  77. package/docs/content/server.md +117 -133
  78. package/docs/content/skills-guide.md +3 -3
  79. package/docs/content/template-analytics.md +8 -6
  80. package/docs/content/template-design.md +25 -27
  81. package/docs/content/template-dispatch.md +3 -1
  82. package/docs/content/template-forms.md +26 -21
  83. package/docs/content/template-mail.md +4 -0
  84. package/docs/content/template-video.md +4 -4
  85. package/docs/content/tools.md +95 -1
  86. package/docs/content/tracking.md +1 -1
  87. package/docs/content/what-is-agent-native.md +4 -2
  88. package/docs/content/workspace-management.md +5 -5
  89. package/docs/content/workspace.md +39 -30
  90. package/package.json +1 -1
@@ -0,0 +1,94 @@
1
+ ---
2
+ title: "Dispatch"
3
+ description: "The workspace control plane: secrets vault, integration hub, cross-app delegate, and central inbox for Slack, email, Telegram, WhatsApp."
4
+ ---
5
+
6
+ # Dispatch
7
+
8
+ Dispatch is the central app that sits in front of every other app in your workspace and handles secrets, integrations, messaging, and cross-app delegation. It is the **workspace control plane** — the single agent your team talks to, the single place credentials live, and the single router that decides which specialist app should handle a given request.
9
+
10
+ Without Dispatch, every app in a multi-app workspace ends up re-implementing the same plumbing: its own Slack bot, its own secret store, its own scheduled jobs, its own copy of the workspace's instructions. Rotating one API key turns into ten redeployments. Adding a new policy turns into ten copy-pastes. Dispatch centralizes all of that in one app so the others stay focused on their domain.
11
+
12
+ > Dispatch is shipped as a first-party template. This page covers the **concept** — what it is, why you'd want it, and how it fits into a workspace. For the scaffolded app itself (routes, screens, agent guide), see the [Dispatch template](/templates/dispatch).
13
+
14
+ ## When you want Dispatch {#when}
15
+
16
+ Reach for Dispatch when any of these are true:
17
+
18
+ - You're running a [multi-app workspace](/docs/multi-app-workspace) — mail, calendar, analytics, content, recruiting — and you don't want one Slack bot per app.
19
+ - You want **one inbox for "the agent"** so users DM a single bot and the right specialist app picks up the work behind the scenes.
20
+ - You have **workspace-wide secrets** (Stripe key, OpenAI key, third-party API tokens) that several apps need but you'd rather grant per-app than copy into every `.env`.
21
+ - You want a **runtime approval flow** in front of sensitive changes (saved destinations, policy edits) so non-admins can request and admins can sign off without a code deploy.
22
+ - You want **shared skills, instructions, and agent profiles** that every app in the workspace inherits — change once, reach all.
23
+
24
+ If you're running a single template standalone, you don't need Dispatch — each template can wire its own messaging integrations directly. See [Messaging](/docs/messaging) for the standalone setup.
25
+
26
+ ## What Dispatch does {#what-it-does}
27
+
28
+ Five capabilities, all sitting on top of the same workspace database the other apps use.
29
+
30
+ ### Central inbox
31
+
32
+ Slack, email, Telegram, and WhatsApp all flow into Dispatch's agent loop. Connect each platform once in **Settings → Messaging** and every channel reaches the same agent with the same memory and tools. A Slack DM and an email to `agent@yourcompany.com` end up as two surfaces on one conversation history, not two disconnected bots.
33
+
34
+ See [Messaging](/docs/messaging) for the credentials and webhook URLs for each platform.
35
+
36
+ ### Secret vault
37
+
38
+ Store credentials once in Dispatch's vault and grant them to the apps that need them. Non-admins can **request** a secret for an app; admins **approve**, which creates the secret + grant in one step. Every read, grant, sync, and rotation is captured in an audit log. `sync-vault-to-app` pushes granted secrets into the target app's env so you don't have to redeploy or re-paste anything.
39
+
40
+ This is what makes "rotate the OpenAI key" a one-click operation across ten apps instead of ten PRs.
41
+
42
+ ### Cross-app delegation
43
+
44
+ Dispatch auto-discovers the other apps in your workspace as A2A peers — no manual registration, no per-app config. When a user asks "summarize last week's signups" in Slack, Dispatch recognizes that as an analytics request and calls the analytics app over [A2A](/docs/a2a-protocol). When they ask "draft a reply to Alice", it routes to the mail app. Dispatch posts the final answer back in the originating thread.
45
+
46
+ The behavioral rule lives in the dispatch agent's instructions: domain work belongs to the domain app. Dispatch is the orchestrator, not the specialist.
47
+
48
+ ### Workspace resources
49
+
50
+ Skills, agent profiles, and instructions can be authored once in Dispatch and granted out to the rest of the workspace. `sync-workspace-resources-to-all` pushes them to every app's `.agents/` directory so every agent in every app picks them up. This is how a team-wide change ("always use British English in customer-facing replies") propagates without editing ten repos.
51
+
52
+ ### Approval flow
53
+
54
+ Dispatch can gate sensitive runtime changes behind admin review. Today this covers **saved destinations** (the Slack channels and email addresses the agent can proactively send to) and **dispatch approval policy** itself. When the policy is enabled, the change is queued and the agent surfaces an inline approval preview directly in chat — admins approve or reject without leaving the conversation. Resource-wide approval interception is planned but not yet shipped.
55
+
56
+ ## How a Slack message flows through Dispatch {#flow}
57
+
58
+ Walk through one example end-to-end. A user DMs the bot: _"summarize last week's signups."_
59
+
60
+ 1. **Slack → webhook.** Slack `POST`s to `/_agent-native/integrations/slack/webhook` on the Dispatch app. The handler verifies the signature and **inserts a row into `integration_pending_tasks`**, then fires a self-targeted `POST` to its own processor and returns `200` immediately so Slack doesn't retry.
61
+ 2. **Fresh processor execution.** The processor endpoint runs in a brand-new function execution with its own full timeout. It atomically claims the task and starts the agent loop.
62
+ 3. **Dispatch agent decides.** The agent reads the message, recognizes "signups" as an analytics intent, and invokes `call-agent` against the analytics app's [A2A endpoint](/docs/a2a-protocol). The actual SQL work runs over there.
63
+ 4. **Reply posted in thread.** The analytics agent returns a result. Dispatch formats it and posts back into the same Slack thread the user wrote in, using the linked identity if there is one (so the agent acts with the requester's permissions, not the workspace owner's).
64
+ 5. **Recovery if anything dies.** If the processor crashes mid-flight — A2A timeout, downstream agent error, function freeze — a retry job sweeps stuck tasks every 60 seconds and re-fires the processor. Up to three attempts before the task is marked `failed`. The user still gets a reply on the next sweep instead of the message disappearing into the void.
65
+
66
+ The same flow applies for email, Telegram, and WhatsApp — only the adapter changes.
67
+
68
+ ## Reliability story {#reliability}
69
+
70
+ The whole pipeline is built to survive on every serverless host (Netlify, Vercel, Cloudflare Workers) without leaning on platform-specific background-execution APIs.
71
+
72
+ - **Webhook → SQL queue → fresh-execution processor.** The agent loop never runs inside the webhook handler. The handler's only job is to verify, enqueue, and return 200. A separate fresh execution drains the queue, so a slow agent run can never tie up the inbound webhook or cause the platform to retry.
73
+ - **A2A continuation polling.** When Dispatch delegates to another app, it polls the downstream task with a bounded timeout. If the downstream agent takes too long or crashes, Dispatch records the continuation and the retry job picks it up — the user's Slack reply still arrives.
74
+ - **Auto-signed cross-app A2A.** Hosted multi-app workspaces auto-generate per-app A2A credentials at deploy time, so apps in the same workspace can call each other without you ever pasting a JWT secret. Dispatch's agent-discovery layer reads those creds from the workspace database so newly added apps appear as callable peers automatically.
75
+
76
+ This is the recently-hardened story — see PRs #439, #441, and #443 for the underlying changes. Conceptually: every step that crosses a network or a process boundary is recoverable.
77
+
78
+ ## Setup {#setup}
79
+
80
+ Three short steps:
81
+
82
+ 1. **Scaffold a workspace that includes Dispatch.** Run `pnpm dlx @agent-native/core create my-company-platform` and pick `dispatch` alongside whatever domain templates you want. Dispatch lives at `apps/dispatch` and the rest of the apps sit beside it. See [Multi-App Workspace](/docs/multi-app-workspace).
83
+ 2. **Connect messaging.** Open **Settings → Messaging** in Dispatch and click connect for Slack, Email, Telegram, or WhatsApp. The form fields match the env vars in the [Messaging](/docs/messaging) doc — refer there for what each platform needs.
84
+ 3. **Add other apps.** Run `agent-native add-app` from the workspace root for each domain app. They auto-appear as A2A peers in Dispatch's `list-workspace-apps` — no manual registration, no agent-card editing. Dispatch will start delegating to them as soon as their agent cards are reachable.
85
+
86
+ Then add credentials to the vault, grant them to the apps that need them, and (optionally) author workspace skills under **Resources** and sync them out.
87
+
88
+ ## See also {#see-also}
89
+
90
+ - [Dispatch template](/templates/dispatch) — the actual scaffolded app, with its full action catalog and agent guide
91
+ - [Messaging](/docs/messaging) — connecting Slack, email, Telegram, WhatsApp
92
+ - [A2A Protocol](/docs/a2a-protocol) — how cross-app delegation works under the hood
93
+ - [Multi-App Workspace](/docs/multi-app-workspace) — the deployment shape Dispatch is built for
94
+ - [Workspace Management](/docs/workspace-management) — git/GitHub governance that pairs with Dispatch's runtime governance
@@ -67,7 +67,7 @@ The framework ships with production-ready templates you can use as daily drivers
67
67
  - **[Video](/templates/video)** — video composition with Remotion
68
68
  - **[Analytics](/templates/analytics)** — data platform (like Amplitude/Mixpanel)
69
69
  - **[Clips](/templates/clips)** — async screen + camera recording (replaces Loom)
70
- - **[Design](/templates/design)** — agent-native design tool (like Figma/Canva)
70
+ - **[Design](/templates/design)** — agent-native HTML prototyping studio
71
71
  - **[Forms](/templates/forms)** — form builder (like Typeform)
72
72
  - **[Dispatch](/templates/dispatch)** — workspace control plane: shared secrets, integrations, jobs
73
73
 
@@ -123,7 +123,7 @@ Anywhere. The server runs on Nitro, which compiles to any deployment target: Nod
123
123
 
124
124
  ### Why polling instead of WebSockets? {#why-polling-not-websockets}
125
125
 
126
- Polling works in every deployment environment — including serverless, edge, and container platforms where persistent connections aren't available. The framework polls every 2 seconds using a lightweight version counter. When changes are detected, React Query caches are invalidated and components re-render. It's simple, reliable, and universal. SSE is also supported as an alternative.
126
+ Polling works in every deployment environment — including serverless, edge, and container platforms where persistent connections aren't available. The framework polls every 2 seconds using a lightweight version counter. When changes are detected, React Query caches are invalidated and components re-render. It's simple, reliable, and universal.
127
127
 
128
128
  ### Why can't the UI call an LLM directly? {#why-no-inline-llm-calls}
129
129
 
@@ -41,7 +41,7 @@ The framework provides type-safe APIs so you never deal with raw messaging:
41
41
 
42
42
  1. **Agent chat** — use `sendToAgentChat()` to send messages to the agent
43
43
  2. **Generation state** — use `useAgentChatGenerating()` to track when the agent is running
44
- 3. **File watching** — SSE endpoint keeps UI in sync when the agent modifies files
44
+ 3. **Polling sync** — database-backed sync keeps UI caches fresh when the agent changes state
45
45
  4. **Action system** — `pnpm action <name>` dispatches to callable actions
46
46
 
47
47
  Your app code is identical regardless of how the agent is provided.
@@ -51,6 +51,14 @@ Common next steps:
51
51
  - **Build a brand-new template from scratch** — see [Creating Templates](/docs/creating-templates) for the full Vite, Tailwind, and TypeScript setup.
52
52
  - **Understand the architecture** — see [Key Concepts](/docs/key-concepts) for how SQL, actions, polling sync, and context awareness fit together.
53
53
 
54
+ ## What's next {#next-steps}
55
+
56
+ Once your app is running, the most common next steps are:
57
+
58
+ - **Connect Slack or email** so you can message your agent from anywhere — see [Messaging](/docs/messaging).
59
+ - **Set up Dispatch as your central inbox** to triage messages and orchestrate across multiple apps — see [Dispatch](/docs/dispatch).
60
+ - **Customize via Workspace** — edit instructions, skills, memory, and connect MCP servers per user — see [Workspace](/docs/workspace).
61
+
54
62
  ## Templates {#templates}
55
63
 
56
64
  Each template is a complete app with UI, agent actions, database schema, and AI instructions ready to go:
@@ -64,7 +72,7 @@ Each template is a complete app with UI, agent actions, database schema, and AI
64
72
  | [Video](/templates/video) | video editing |
65
73
  | [Analytics](/templates/analytics) | Amplitude, Mixpanel, Looker |
66
74
  | [Clips](/templates/clips) | Replaces Loom — screen + camera recording |
67
- | [Design](/templates/design) | Figma, Canva |
75
+ | [Design](/templates/design) | HTML prototyping studios |
68
76
  | [Forms](/docs/template-forms) | Typeform |
69
77
  | [Dispatch](/docs/template-dispatch) | Workspace control plane — secrets, routing, jobs |
70
78
  | [Starter](/docs/template-starter) | Minimal scaffold — build from scratch |
@@ -46,7 +46,7 @@ Six rules govern the architecture:
46
46
  Adopting the framework is valuable mostly because of what you stop having to build. The moment your app follows the six rules, you inherit:
47
47
 
48
48
  - **One action = four surfaces.** Every action defined with `defineAction()` is simultaneously an agent tool, a typesafe frontend mutation (`useActionMutation("name")`), an HTTP endpoint at `/_agent-native/actions/:name`, and an MCP tool (when MCP is enabled). External agents can call it over [A2A](/docs/a2a-protocol) too. One implementation, four consumers.
49
- - **A full workspace per user.** Skills, memory (`learnings.md`), `AGENTS.md`, custom sub-agents, scheduled jobs, connected MCP servers — all SQL-backed, per-user, no dev-box required. See [Workspace](/docs/workspace).
49
+ - **A full workspace per user.** Skills, shared `LEARNINGS.md`, personal `memory/MEMORY.md`, `AGENTS.md`, custom sub-agents, scheduled jobs, connected MCP servers — all SQL-backed, no dev-box required. See [Workspace](/docs/workspace).
50
50
  - **Drop-in React components.** `<AgentPanel />` and `<AgentSidebar />` render chat + workspace anywhere in your app. See [Drop-in Agent](/docs/drop-in-agent).
51
51
  - **Live sync between agent and UI.** A 2-second poll invalidates React Query caches whenever the agent writes to the DB. No WebSockets, no serverless-unfriendly long-lived connections. See [Polling Sync](#polling-sync) below.
52
52
  - **Auth, orgs, RBAC.** Better Auth with orgs/members/roles is wired in for every template. See [Authentication](/docs/authentication).
@@ -262,6 +262,22 @@ Never use Node-specific APIs (`fs`, `child_process`, `path`) in server routes or
262
262
 
263
263
  Never assume a persistent server process. Serverless and edge environments are stateless — no in-memory caches, no long-lived connections. Use the SQL database for all state.
264
264
 
265
+ ## Workspace {#workspace}
266
+
267
+ Every user gets a personal **workspace** — instructions, skills, memory, custom sub-agents, scheduled jobs, and connected MCP servers — all stored in SQL rather than files. That makes Claude-Code-level customization viable inside multi-tenant SaaS without spinning up a container per user. See [Workspace](/docs/workspace).
268
+
269
+ ## Dispatch {#dispatch}
270
+
271
+ **Dispatch** is the workspace control plane: a central inbox for Slack/email/Telegram, a shared secrets vault, scheduled jobs, and an orchestrator agent that delegates domain work to specialist apps over A2A. Run it alongside your domain apps when you have more than one. See [Dispatch](/docs/dispatch).
272
+
273
+ ## Tools {#tools}
274
+
275
+ **Tools** are sandboxed mini-apps the agent can create at runtime — Alpine.js HTML rendered inside an iframe, with built-in helpers for persistent storage (`toolData`), calling app actions (`appAction`), and proxied external APIs (`toolFetch`). No source-code changes, no schema migrations. See [Tools](/docs/tools).
276
+
277
+ ## A2A {#a2a}
278
+
279
+ Agent-to-agent (**A2A**) is how apps in the same workspace discover and call each other. Each app publishes an agent card with skill metadata; other agents can invoke its actions over JSON-RPC. Same-origin deploys skip JWT; cross-origin uses a shared secret. See [A2A Protocol](/docs/a2a-protocol).
280
+
265
281
  ## Deep dives {#deep-dives}
266
282
 
267
283
  For detailed guidance on specific patterns:
@@ -7,7 +7,7 @@ description: "Talk to your agent from Slack, email, Telegram, or WhatsApp — sa
7
7
 
8
8
  Connect your agent to Slack, email, Telegram, or WhatsApp so you can chat with it from the apps you already use. It's the same agent — same memory, same tools, same threads — just reachable from more places.
9
9
 
10
- > **Using the Dispatch template?** All of this is wired up for you in **Settings → Messaging**. Click to connect each platform — you don't need to read the rest of this page unless you're customizing or building your own template. See [Dispatch](/docs/template-dispatch).
10
+ > **Using the Dispatch template?** All of this is wired up for you in **Settings → Messaging**. Click to connect each platform — you don't need to read the rest of this page unless you're customizing or building your own template. See [Dispatch](/docs/dispatch) or the [Dispatch template reference](/docs/template-dispatch).
11
11
 
12
12
  ## What you can do {#what-you-can-do}
13
13
 
@@ -31,11 +31,15 @@ Connect your agent to Slack, email, Telegram, or WhatsApp so you can chat with i
31
31
  - `chat:write` — lets the agent send messages
32
32
  - `app_mentions:read` — lets the agent see when it's @-mentioned (optional)
33
33
  - `im:history` — lets the agent read DMs sent to it
34
+ - `assistant:write` — optional; lets Slack show native "is thinking..." status in assistant threads
35
+ - `users:read.email` — optional; helps templates such as Mail verify Slack sender email for draft-queue identity
34
36
  3. Click **Install to Workspace** at the top of that page. Slack will give you a **Bot User OAuth Token** that starts with `xoxb-`. Copy it.
35
37
  4. Go to **Basic Information** in the sidebar and copy the **Signing Secret**.
36
38
  5. Open your app's settings (or your hosting provider's environment variable panel) and paste:
37
39
  - `SLACK_BOT_TOKEN` — the `xoxb-…` token
38
40
  - `SLACK_SIGNING_SECRET` — the signing secret
41
+ - `SLACK_ALLOWED_TEAM_IDS` — recommended in production; comma-separated Slack workspace/team IDs allowed to send events
42
+ - `SLACK_ALLOWED_API_APP_IDS` — recommended for multi-workspace apps; comma-separated Slack app IDs allowed to use this signing secret
39
43
  6. Back in Slack, open **Event Subscriptions**, toggle it on, and paste this Request URL:
40
44
 
41
45
  ```text
@@ -51,6 +55,7 @@ Connect your agent to Slack, email, Telegram, or WhatsApp so you can chat with i
51
55
  - **Channel mentions** — the bot only responds in channels when it's @-mentioned, to avoid noise.
52
56
  - **DMs** — every DM is treated as a private conversation with the agent.
53
57
  - **Same identity, all channels** — if a Slack user has the same email as a registered user in your app, the agent treats them as the same person.
58
+ - **Production allowlists** — set `SLACK_ALLOWED_TEAM_IDS` and, for shared Slack apps, `SLACK_ALLOWED_API_APP_IDS` so a valid signing secret cannot be reused by an unexpected workspace.
54
59
 
55
60
  ## Set up Telegram {#telegram}
56
61
 
@@ -168,7 +173,7 @@ Email is the most powerful integration — your agent gets its own address, repl
168
173
 
169
174
  ## Use Dispatch as your agent's central inbox {#dispatch}
170
175
 
171
- If you're running multiple agent-native apps (mail, calendar, analytics, etc.), the recommended pattern is to set up messaging on the **[Dispatch template](/docs/template-dispatch)** and let it route work to your domain apps over [A2A](/docs/a2a-protocol).
176
+ If you're running multiple agent-native apps (mail, calendar, analytics, etc.), the recommended pattern is to set up messaging on **[Dispatch](/docs/dispatch)** (see also the [template reference](/docs/template-dispatch)) and let it route work to your domain apps over [A2A](/docs/a2a-protocol).
172
177
 
173
178
  Why this is nice:
174
179
 
@@ -186,19 +191,37 @@ Everything below is the technical reference. If you've finished the setup steps
186
191
 
187
192
  ### How it works {#how-it-works}
188
193
 
189
- Each platform talks to your app via a standard HTTP webhook:
194
+ Inbound platform webhooks use a cross-platform SQL-queue pattern so they work on every serverless host (Netlify, Vercel, Cloudflare Workers, Fly, Render, Node) without relying on platform-specific background-execution APIs.
190
195
 
191
- 1. A user sends a message on Slack/Telegram/WhatsApp/email.
192
- 2. The platform `POST`s to `/_agent-native/integrations/<platform>/webhook`.
193
- 3. The integrations plugin verifies the signature, parses the message, and maps it to an internal conversation thread.
194
- 4. The agent runs the same pipeline as the web chat same system prompt, same actions, same tools.
195
- 5. The response is posted back to the platform in the same thread.
196
+ 1. The platform `POST`s to `/_agent-native/integrations/<platform>/webhook`. The handler verifies the signature, parses the payload into an `IncomingMessage`, and **inserts a row into `integration_pending_tasks`** with `status='pending'`.
197
+ 2. The handler fires a fire-and-forget `POST /_agent-native/integrations/process-task` and returns `200` immediately, well inside Slack's 3-second SLA.
198
+ 3. The processor endpoint runs in a **fresh function execution** with its own full timeout budget. It atomically claims the task (`pending` → `processing` via `claimPendingTask`), runs the agent loop, posts the reply through the adapter, and marks the task `completed`.
199
+ 4. A recurring retry job (`startPendingTasksRetryJob`, every 60s) sweeps tasks stuck in `pending` >90s or `processing` >5min and re-fires the processor. Capped at 3 attempts, then marked `failed`.
196
200
 
197
201
  ```text
198
- UserPlatform webhook → Verify ParseAgent runs Response posted back
202
+ Platform/webhook → verify + parseINSERT pending task ──► return 200
203
+
204
+ └─ fetch /process-task (fire-and-forget)
205
+
206
+ fresh exec ──► claim → agent loop → adapter.sendResponse → completed
207
+
208
+ (every 60s) retry job: sweep stuck tasks → re-fire /process-task (≤3 attempts)
199
209
  ```
200
210
 
201
- No polling, no long-lived connections. Inbound and outbound conversations live in the same SQL thread, so you can continue a Slack DM from the web UI or vice versa.
211
+ Inbound and outbound conversations live in the same SQL thread, so you can continue a Slack DM from the web UI or vice versa.
212
+
213
+ #### Why this pattern (and not the platform-native shortcuts) {#why-this-pattern}
214
+
215
+ Serverless functions freeze the moment the response is sent. Anything still running — including a fire-and-forget Promise, a deferred LLM call, or an in-flight tool — gets killed mid-execution. The only way to keep an agent loop alive is to start a **new** function execution for it, which is what the self-fired `/process-task` POST does.
216
+
217
+ Do NOT use any of these alternatives:
218
+
219
+ - **Netlify Background Functions** — Netlify-only, requires a `-background.ts` filename suffix, breaks on every other host.
220
+ - **Cloudflare `event.waitUntil()`** — CF Workers only, not portable.
221
+ - **Vercel `after()` / Fluid** — Vercel-only, gated behind specific runtimes.
222
+ - **Naked fire-and-forget Promises after `return`** — silently killed when the function freezes; no error in the logs, the user just never gets a reply.
223
+
224
+ The SQL-queue + self-webhook + retry-job combination is the only thing that works identically on every supported host. The retry job is the safety net — never assume the initial dispatch flushed before the function froze.
202
225
 
203
226
  ### The integrations plugin {#plugin}
204
227
 
@@ -234,12 +257,12 @@ POST /_agent-native/integrations/telegram/setup
234
257
 
235
258
  ### Environment variables {#env-vars}
236
259
 
237
- | Platform | Required | Optional |
238
- | -------- | ---------------------------------------------------------------------------- | ------------------------------ |
239
- | Slack | `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET` | |
240
- | Telegram | `TELEGRAM_BOT_TOKEN` | — |
241
- | Email | `EMAIL_AGENT_ADDRESS`, plus one of `RESEND_API_KEY` or `SENDGRID_API_KEY` | `EMAIL_INBOUND_WEBHOOK_SECRET` |
242
- | WhatsApp | `WHATSAPP_ACCESS_TOKEN`, `WHATSAPP_VERIFY_TOKEN`, `WHATSAPP_PHONE_NUMBER_ID` | — |
260
+ | Platform | Required | Optional |
261
+ | -------- | ---------------------------------------------------------------------------- | ----------------------------------------------------- |
262
+ | Slack | `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET` | `SLACK_ALLOWED_TEAM_IDS`, `SLACK_ALLOWED_API_APP_IDS` |
263
+ | Telegram | `TELEGRAM_BOT_TOKEN` | — |
264
+ | Email | `EMAIL_AGENT_ADDRESS`, plus one of `RESEND_API_KEY` or `SENDGRID_API_KEY` | `EMAIL_INBOUND_WEBHOOK_SECRET` |
265
+ | WhatsApp | `WHATSAPP_ACCESS_TOKEN`, `WHATSAPP_VERIFY_TOKEN`, `WHATSAPP_PHONE_NUMBER_ID` | — |
243
266
 
244
267
  All credentials live in env vars — never the database, never source code. Use the sidebar settings UI or your hosting provider's env panel.
245
268
 
@@ -259,7 +282,7 @@ External threads appear in the web UI alongside web-originated threads, tagged w
259
282
 
260
283
  Every incoming webhook is signature-verified before processing:
261
284
 
262
- - **Slack** — HMAC-SHA256 of the body using `SLACK_SIGNING_SECRET`, checked against the `X-Slack-Signature` header.
285
+ - **Slack** — HMAC-SHA256 of the body using `SLACK_SIGNING_SECRET`, checked against the `X-Slack-Signature` header. The first time you save a Request URL in Slack's Event Subscriptions panel, Slack POSTs a `url_verification` challenge to it; the framework's adapter detects this and replies with the `challenge` value automatically, so the URL flips green in Slack without any extra work on your end.
263
286
  - **Telegram** — secret token set when registering the webhook.
264
287
  - **WhatsApp** — Meta's verification challenge (using `WHATSAPP_VERIFY_TOKEN`) plus payload signature.
265
288
  - **Email** — Svix-style signature verification when `EMAIL_INBOUND_WEBHOOK_SECRET` is set (Resend and SendGrid both use this format). If the secret is unset, the webhook is accepted but a warning is logged.
@@ -317,8 +340,22 @@ export default createIntegrationsPlugin({
317
340
 
318
341
  Reference implementations live in `packages/core/src/integrations/adapters/` (`slack.ts`, `telegram.ts`, `whatsapp.ts`, `email.ts`) — the email adapter is the most complete example, including signature verification, threading, rate limiting, and HTML rendering.
319
342
 
343
+ ### Reliability via Dispatch + A2A continuations {#reliability}
344
+
345
+ When [Dispatch](/docs/dispatch) delegates a request to another app over [A2A](/docs/a2a-protocol#continuations), the continuation-recovery flow guarantees the user gets a Slack/email reply even if the downstream agent crashes mid-execution. The original webhook task stays in `processing` until the continuation either resolves or the retry sweep marks it stuck; either way, the platform thread gets a final reply rather than going silent.
346
+
347
+ This means a multi-app workspace fronted by Dispatch is more resilient than a single template wired to messaging directly — failures in any one downstream app degrade to a graceful error message instead of a dropped reply. See [A2A continuations](/docs/a2a-protocol#continuations) for the full delivery-guarantee story.
348
+
349
+ ### Common pitfalls {#pitfalls}
350
+
351
+ - **Don't double-read the request body.** h3 v2's body stream is consume-once: if you call `readBody(event)` after the framework has already parsed `event.node.req.body` (or vice versa), the second read hangs the request indefinitely. This shows up most often with Resend and SendGrid — both stream the inbound payload and the dangling read never resolves, the platform times out, and the webhook gets retried until it dedups. If you wrap the framework's webhook handler in your own middleware, pass the already-parsed `IncomingMessage` via the `incoming` option rather than letting the handler re-parse.
352
+ - **Don't run agent loops inside the webhook handler.** The handler must enqueue and return — the agent loop runs in the processor's fresh execution. Putting it inline guarantees serverless freeze kills the run.
353
+ - **Don't rely on dedup memory across cold starts.** The dedup key lives in the SQL `(platform, external_event_key)` unique index, not an in-process Map. If you replace the queue, keep the SQL-level dedup or duplicate Slack retries will trigger duplicate agent runs.
354
+ - **Keep the self-webhook URL reachable.** The processor URL is built from `APP_URL` / `URL` / `DEPLOY_URL` / `BETTER_AUTH_URL`, falling back to the inbound request headers. On preview deploys with rewritten hostnames, set one of these explicitly or the dispatch will hit a 404.
355
+
320
356
  ### See also {#see-also}
321
357
 
322
- - [Dispatch template](/docs/template-dispatch) — recommended central inbox for multi-app workspaces
323
- - [A2A Protocol](/docs/a2a-protocol) — how Dispatch delegates work to other agents
358
+ - [Dispatch](/docs/dispatch) — concept overview for using a central inbox across apps
359
+ - [Dispatch template reference](/docs/template-dispatch) — recommended central inbox for multi-app workspaces
360
+ - [A2A Protocol](/docs/a2a-protocol) — how Dispatch delegates work to other agents, including continuation recovery
324
361
  - [Agent Mentions](/docs/agent-mentions) — `@`-mentioning agents inside the web chat
@@ -1,9 +1,11 @@
1
1
  ---
2
- title: "Multi-App Workspace"
2
+ title: "Multi-App Workspaces"
3
3
  description: "Host many agent-native apps in one monorepo with shared auth, RBAC, instructions, skills, components, and credentials."
4
4
  ---
5
5
 
6
- # Multi-App Workspace
6
+ # Multi-App Workspaces
7
+
8
+ > For what a workspace _is_ — the customization layer, `AGENTS.md`, `LEARNINGS.md`, personal memory, skills, and custom agents — see [Workspace](/docs/workspace). This page is about the **deployment shape**: hosting many agent-native apps in one monorepo with shared auth, components, and a unified deploy.
7
9
 
8
10
  When vibe-coding an internal tool takes an afternoon, you don't stop at one. A team ends up with a CRM, a support inbox, a dashboard, a recruiting tracker, an ops console — ten small apps, each scaffolded independently. That's great until you need to change something in all of them.
9
11
 
@@ -239,3 +241,9 @@ The workspace pattern is intentionally narrow. A few things it deliberately does
239
241
  - **Encrypted credential vault.** Shared credentials live in the `settings` table as plain text today. Rotate responsibly.
240
242
  - **Publishing shared code to private npm.** The shared package is `workspace:*` only; multi-repo sharing via a private registry is doable but not scaffolded.
241
243
  - **Opinionated component library.** `packages/shared` is where _you_ put shared components. The framework doesn't force shadcn/ui or any other system into that slot.
244
+
245
+ ## See also {#see-also}
246
+
247
+ - [Workspace](/docs/workspace) — the customization layer (`AGENTS.md`, `LEARNINGS.md`, personal memory, skills, custom agents) every app in the workspace shares.
248
+ - [Workspace Governance](/docs/workspace-management) — branching, CODEOWNERS, PR review across many apps in one repo.
249
+ - [Dispatch](/docs/dispatch) — the runtime control plane that typically lives inside a multi-app workspace as the secrets vault, integration catalog, and approvals hub.
@@ -195,5 +195,5 @@ Automations can chain off this — e.g. _"if a critical notification fires, also
195
195
  ## What's next
196
196
 
197
197
  - [**Automations**](/docs/automations) — the most common caller of `notify()`
198
- - [**Secrets**](/docs/security) — the `${keys.NAME}` substitution that powers the webhook channel
198
+ - [**Security**](/docs/security) — the `${keys.NAME}` substitution that powers the webhook channel
199
199
  - [**Server plugins**](/docs/server) — where custom channels are registered at startup
@@ -0,0 +1,184 @@
1
+ ---
2
+ title: "Observability"
3
+ description: "Agent traces, evals, feedback, A/B experiments, and the admin dashboard — all built-in with zero configuration."
4
+ ---
5
+
6
+ # Agent Observability
7
+
8
+ Every agent-native app gets observability out of the box. Traces, automated evals, user feedback, and A/B experiments work with zero configuration — all data lives in the app's own SQL database.
9
+
10
+ ## What's Captured Automatically
11
+
12
+ When a user sends a message, the framework automatically records:
13
+
14
+ - **Token usage** — input, output, cache read, cache write
15
+ - **Cost** — computed from token counts and model pricing
16
+ - **Latency** — total duration and time per tool call
17
+ - **Tool calls** — which actions were invoked, success/error status, duration
18
+ - **Automated evals** — 5 quality scores computed after every run
19
+
20
+ No code changes needed. The instrumentation hooks into `production-agent.ts` transparently.
21
+
22
+ ## The Dashboard
23
+
24
+ Add the dashboard to any template with a single route:
25
+
26
+ ```tsx
27
+ // app/routes/observability.tsx
28
+ import { ObservabilityDashboard } from "@agent-native/core/client";
29
+
30
+ export default function ObservabilityPage() {
31
+ return (
32
+ <div className="min-h-screen bg-background p-6">
33
+ <ObservabilityDashboard />
34
+ </div>
35
+ );
36
+ }
37
+ ```
38
+
39
+ The dashboard has 5 tabs:
40
+
41
+ | Tab | What it shows |
42
+ | ----------------- | ------------------------------------------------------------------------------- |
43
+ | **Overview** | Key metrics — runs, cost, latency, tool success rate, satisfaction, eval score |
44
+ | **Conversations** | Trace list with drill-down to individual spans (agent_run, llm_call, tool_call) |
45
+ | **Evals** | Automated eval scores by criteria, trends over time |
46
+ | **Experiments** | A/B test list with status badges, variant results with confidence intervals |
47
+ | **Feedback** | Thumbs up/down stream, category breakdown, frustration scores |
48
+
49
+ ## User Feedback
50
+
51
+ ### Explicit Feedback
52
+
53
+ Thumbs up/down buttons render inline on every agent message in the chat UI. Thumbs down opens a category popover (Inaccurate, Not helpful, Wrong tool, Too slow). This is wired into `AssistantChat.tsx` automatically.
54
+
55
+ ### Implicit Feedback (Frustration Index)
56
+
57
+ The framework computes a Frustration Index (0-100) from conversation signals:
58
+
59
+ | Signal | Weight | What it detects |
60
+ | -------------- | ------ | ----------------------------------- |
61
+ | Rephrasing | 30% | User repeats similar messages |
62
+ | Retry patterns | 20% | "Try again", "no that's wrong" |
63
+ | Abandonment | 20% | Session ends shortly after response |
64
+ | Sentiment | 15% | Negative language patterns |
65
+ | Length trend | 15% | Declining message lengths |
66
+
67
+ Score interpretation: 0-20 = healthy, 20-40 = friction, 40-60 = dissatisfied, 60+ = broken session.
68
+
69
+ ## Automated Evals
70
+
71
+ Five deterministic scorers run after every agent run:
72
+
73
+ | Criteria | What it measures | Score range |
74
+ | ------------------- | ------------------------------------------------------ | ----------- |
75
+ | `tool_success_rate` | % of tool calls without errors | 0-1 |
76
+ | `step_efficiency` | Penalizes excessive LLM iterations for tool-using runs | 0-1 |
77
+ | `latency_score` | Normalized against 10s/tool baseline | 0-1 |
78
+ | `cost_efficiency` | Normalized against cost baseline | 0-1 |
79
+ | `error_recovery` | Did the agent recover from tool errors? | 0 or 1 |
80
+
81
+ ### LLM-as-Judge (Optional)
82
+
83
+ Enable sampled LLM-based evaluation by setting `evalSampleRate`:
84
+
85
+ ```ts
86
+ import { putSetting } from "@agent-native/core/settings";
87
+
88
+ await putSetting("observability-config", {
89
+ enabled: true,
90
+ evalSampleRate: 0.05, // 5% of runs
91
+ });
92
+ ```
93
+
94
+ Custom criteria use natural language rubrics:
95
+
96
+ ```ts
97
+ const criteria = {
98
+ name: "helpfulness",
99
+ description: "Was the response helpful and complete?",
100
+ rubric: "0.0 = unhelpful, 0.5 = partially helpful, 1.0 = fully resolved",
101
+ };
102
+ ```
103
+
104
+ ## A/B Experiments
105
+
106
+ Test different models, temperatures, or agent configurations:
107
+
108
+ ```ts
109
+ // Create via API
110
+ POST /_agent-native/observability/experiments
111
+ {
112
+ "name": "sonnet-vs-haiku",
113
+ "variants": [
114
+ { "id": "control", "weight": 50, "config": { "model": "claude-sonnet-4-6" } },
115
+ { "id": "treatment", "weight": 50, "config": { "model": "claude-haiku-4-5-20251001" } }
116
+ ],
117
+ "metrics": ["cost", "latency", "satisfaction"]
118
+ }
119
+
120
+ // Start the experiment
121
+ PUT /_agent-native/observability/experiments/:id
122
+ { "status": "running" }
123
+ ```
124
+
125
+ The agent loop automatically resolves the user's variant and applies the config override. Assignment uses consistent hashing — same user always gets the same variant.
126
+
127
+ ## Configuration
128
+
129
+ All settings are stored in the `observability-config` key:
130
+
131
+ ```ts
132
+ {
133
+ enabled: true, // Master switch
134
+ capturePrompts: false, // Store prompt content in traces
135
+ captureToolArgs: false, // Store action input arguments
136
+ captureToolResults: false, // Store action results
137
+ evalSampleRate: 0, // 0-1, fraction of runs to LLM-judge
138
+ exporters: [] // OTLP export targets
139
+ }
140
+ ```
141
+
142
+ Content is **redacted by default** — only token counts, costs, and timing are stored. Opt in to content capture when needed for debugging.
143
+
144
+ ## API Endpoints
145
+
146
+ All auto-mounted at `/_agent-native/observability/`:
147
+
148
+ | Method | Path | Purpose |
149
+ | ------ | -------------------------- | ------------------------------ |
150
+ | GET | `/` | Overview stats |
151
+ | GET | `/traces` | List trace summaries |
152
+ | GET | `/traces/:runId` | Trace detail (summary + spans) |
153
+ | GET | `/traces/:runId/evals` | Evals for a run |
154
+ | POST | `/feedback` | Submit feedback |
155
+ | GET | `/feedback` | List feedback |
156
+ | GET | `/feedback/stats` | Feedback aggregation |
157
+ | GET | `/satisfaction` | Satisfaction scores |
158
+ | GET | `/evals/stats` | Eval statistics |
159
+ | POST | `/experiments` | Create experiment |
160
+ | GET | `/experiments` | List experiments |
161
+ | PUT | `/experiments/:id` | Update experiment |
162
+ | POST | `/experiments/:id/results` | Compute results |
163
+ | GET | `/experiments/:id/results` | Get results |
164
+
165
+ All endpoints support `?since=N` (ms timestamp) and `?limit=N` query params.
166
+
167
+ ## Export to External Platforms
168
+
169
+ Send traces to Langfuse, Datadog, Grafana, or any OTel-compatible backend:
170
+
171
+ ```ts
172
+ await putSetting("observability-config", {
173
+ enabled: true,
174
+ exporters: [
175
+ {
176
+ type: "otlp",
177
+ endpoint: "https://cloud.langfuse.com/api/public/otel",
178
+ headers: { Authorization: "Bearer sk-..." },
179
+ },
180
+ ],
181
+ });
182
+ ```
183
+
184
+ The framework emits `gen_ai.*` semantic convention spans compatible with the OpenTelemetry GenAI spec.
@@ -1,3 +1,8 @@
1
+ ---
2
+ title: "Onboarding & API Keys"
3
+ description: "Setup checklist for first-run configuration — API keys, OAuth, and provider connections"
4
+ ---
5
+
1
6
  # Onboarding
2
7
 
3
8
  When you first open an app built on the agent-native framework, you'll see a
@@ -33,7 +38,7 @@ behind a picker or disclosure when a step has several equivalent providers.
33
38
  OAuth provider, or email provider, paste the value(s), click **Save**.
34
39
  Secret fields use a password input so the value isn't shown on screen. Saved
35
40
  values go into your local `.env` (or workspace settings) — see
36
- [Secrets](/docs/secrets) for where they live.
41
+ [Security](/docs/security) for where they live.
37
42
  - **Open a link** — some steps point to a sign-in page or docs. Click
38
43
  **Continue** and finish the flow in the new tab.
39
44
  - **Ask the agent** — a few steps offer a "Let the agent set it up" option.
@@ -174,6 +179,6 @@ function MySidebar() {
174
179
  ```
175
180
 
176
181
  For background on where step values are stored and how secrets are handled,
177
- see [Secrets](/docs/secrets). For end-user messaging touchpoints (invitations,
182
+ see [Security](/docs/security). For end-user messaging touchpoints (invitations,
178
183
  password resets) that depend on the **Email delivery** step, see
179
184
  [Messaging](/docs/messaging).