diff --git a/.changeset/core-reusable-sidebar-and-alpha-badge.md b/.changeset/core-reusable-sidebar-and-alpha-badge.md
new file mode 100644
index 00000000000..f0c8d069a3b
--- /dev/null
+++ b/.changeset/core-reusable-sidebar-and-alpha-badge.md
@@ -0,0 +1,7 @@
+---
+"@agent-native/core": patch
+"@agent-native/dispatch": patch
+"@agent-native/toolkit": patch
+---
+
+Add reusable AppSidebar in toolkit and core, support top-left configurable alpha badges, and update app layouts to match the new sidebar design.
diff --git a/.claude/launch.json b/.claude/launch.json
index b7d8ff467bd..f150d373c74 100644
--- a/.claude/launch.json
+++ b/.claude/launch.json
@@ -684,6 +684,58 @@
"3197"
],
"port": 3197
+ },
+ {
+ "name": "dispatch-vault-verify",
+ "runtimeExecutable": "pnpm",
+ "runtimeArgs": [
+ "exec",
+ "tsx",
+ "scripts/claude-launch.ts",
+ "--name",
+ "dispatch-vault-verify",
+ "--dir",
+ "templates/dispatch",
+ "--env",
+ "DATABASE_URL=",
+ "--env",
+ "SLACK_BOT_TOKEN=",
+ "--env",
+ "SLACK_SIGNING_SECRET=",
+ "--env",
+ "TELEGRAM_BOT_TOKEN=",
+ "--env",
+ "SENDGRID_API_KEY=",
+ "--env",
+ "WORKSPACE_GATEWAY_URL=",
+ "--env",
+ "AGENT_NATIVE_ENABLE_RECURRING_JOBS=0",
+ "--env",
+ "SENTRY_DSN=",
+ "--env",
+ "SENTRY_SERVER_DSN=",
+ "--env",
+ "SENTRY_CLIENT_DSN=",
+ "--env",
+ "VITE_SENTRY_CLIENT_DSN=",
+ "--env",
+ "AGENT_NATIVE_ANALYTICS_ENDPOINT=",
+ "--env",
+ "VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT=",
+ "--env",
+ "AMPLITUDE_API_KEY=",
+ "--env",
+ "VITE_AMPLITUDE_API_KEY=",
+ "--env",
+ "VITE_AGENT_NATIVE_SESSION_REPLAY_ENABLED=0",
+ "--env",
+ "AGENT_NATIVE_BUILDER_SSO_SECRET=",
+ "--",
+ "dev",
+ "--port",
+ "3190"
+ ],
+ "port": 3190
}
]
}
diff --git a/packages/core/docs/content/template-clips-anonymous-intake.mdx b/packages/core/docs/content/template-clips-anonymous-intake.mdx
index b7b070c5d3a..b5866379892 100644
--- a/packages/core/docs/content/template-clips-anonymous-intake.mdx
+++ b/packages/core/docs/content/template-clips-anonymous-intake.mdx
@@ -12,11 +12,13 @@ The intake URL is a narrow upload capability. It is not a share link, a library
## Capability boundaries
-| Capability | Allows | Does not allow | Lifetime |
-| --- | --- | --- | --- |
-| Intake URL | Create one private recording, upload its chunks, finalize it, or abort it | List recordings, read media, open the library, or use normal agent APIs | One hour by default. Four hours maximum |
-| Agent context link | Read the completed recording through the normal scoped Clips agent surface after an authenticated host-side exchange | Be minted by the anonymous intake bearer, read any other recording, or act as the recording owner | Seven days for bug-report completions |
-| Clips session | Mint intake URLs and use the normal authenticated Clips UI | Nothing is added to the anonymous visitor's browser | Controlled by normal Clips auth |
+
+
+| Capability | Allows | Does not allow | Lifetime |
+| ------------------ | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------- |
+| Intake URL | Create one private recording, upload its chunks, finalize it, or abort it | List recordings, read media, open the library, or use normal agent APIs | One hour by default. Four hours maximum |
+| Agent context link | Read the completed recording through the normal scoped Clips agent surface after an authenticated host-side exchange | Be minted by the anonymous intake bearer, read any other recording, or act as the recording owner | Seven days for bug-report completions |
+| Clips session | Mint intake URLs and use the normal authenticated Clips UI | Nothing is added to the anonymous visitor's browser | Controlled by normal Clips auth |
The intake token is signed with the existing scoped-token mechanism. The token is never stored in SQL. The database stores only the intake id, owner and organization binding, recording id, status, and expiry.
diff --git a/packages/core/docs/content/template-clips-developers.mdx b/packages/core/docs/content/template-clips-developers.mdx
index 6825889216a..de1fae38e66 100644
--- a/packages/core/docs/content/template-clips-developers.mdx
+++ b/packages/core/docs/content/template-clips-developers.mdx
@@ -273,7 +273,7 @@ Two things live outside this schema on purpose: organization/member/invitation r
| `recording_ctas` | Call-to-action buttons (label, url, color, placement) overlaid on a recording |
| `recording_comments` | Threaded, timestamped comments with emoji-reaction map and resolved flag |
| `recording_bug_reports` | Redacted host-product bug report metadata keyed by `recording_id` |
-| `clips_intake_sessions` | One-use anonymous intake capabilities bound to an owner, organization, and recording |
+| `clips_intake_sessions` | One-use anonymous intake capabilities bound to an owner, organization, and recording |
| `recording_reactions` | Emoji reactions pinned to a video timestamp (anonymous viewers allowed) |
| `recording_viewers` / `recording_events` | View analytics: per-viewer watch time and completion, plus granular events (view-start, watch-progress, seek, pause, cta-click, reaction) |
| `recording_views` | Append-only per-view log (who viewed, when) — backs the owner-facing "Viewed by" popover, kept separate from the aggregate counters above |
diff --git a/packages/core/src/client/EnvironmentBadge.render.spec.tsx b/packages/core/src/client/EnvironmentBadge.render.spec.tsx
index e0019729757..cf468e098e7 100644
--- a/packages/core/src/client/EnvironmentBadge.render.spec.tsx
+++ b/packages/core/src/client/EnvironmentBadge.render.spec.tsx
@@ -57,7 +57,7 @@ describe("EnvironmentBadge render", () => {
vi.clearAllMocks();
});
- it("renders a non-navigating dev pill for configured local development", () => {
+ it("renders a non-navigating alpha pill for configured local development", () => {
Object.defineProperty(window, "location", {
configurable: true,
value: {
@@ -77,7 +77,7 @@ describe("EnvironmentBadge render", () => {
act(() => root.render(