diff --git a/.gitignore b/.gitignore
index 28fa614d..db8d3176 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,6 @@ web/waitlist.json
# Palate website-builder skill telemetry, written at the repo root during agent sessions
build-manifest.json
+
+# Native probe builds are staged locally, not committed with the website.
+web/public/downloads/agent-relay-probe/
diff --git a/docs/design/teams-dashboard-concept.md b/docs/design/teams-dashboard-concept.md
new file mode 100644
index 00000000..f6d6ebb2
--- /dev/null
+++ b/docs/design/teams-dashboard-concept.md
@@ -0,0 +1,63 @@
+# Teams dashboard concept
+
+Asset: `web/public/teams/teams-dashboard-concept.png` (1585 × 992).
+
+Created using the built-in imagegen tool. This is a product concept with example
+sessions, not a screenshot of a connected account. The image links to its full-size
+version from the Teams landing page.
+
+## Design brief
+
+A flat navy desktop interface with one session table grouped by the human running
+the agents. Columns: Session, Tool, Status, Pull request, Updated. Statuses are
+Working, Idle, and Finished; only finished sessions have PR links. The important
+columns sit before Updated so they remain visible in the landing page's right crop.
+
+| Teammate | Session | Tool | Status | PR |
+| --- | --- | --- | --- | --- |
+| Will Washburn | Add Google sign-in | Claude Code | Working | |
+| Will Washburn | Improve onboarding copy | Codex | Idle | |
+| Will Washburn | Fix duplicate uploads | Claude Code | Finished | #482 |
+| Maya Chen | Build sessions search | Codex | Working | |
+| Maya Chen | Review retry handling | Gemini CLI | Idle | |
+| Maya Chen | Add team invitations | Claude Code | Finished | #487 |
+| Alex Rivera | Audit session sharing | Claude Code | Working | |
+| Alex Rivera | Update setup guide | OpenCode | Finished | #491 |
+
+## Surface cleanup prompt
+
+Edit this Agent Relay Teams dashboard screenshot. Preserve the exact layout,
+dimensions, all text, all eight rows, all three human groups, status dots, and all
+PR numbers. Change ONLY the surface rendering. The current image has strange
+black/blue/green mottled patches and waves in the background and behind rows.
+Remove EVERY such artifact. Make the ENTIRE screenshot fully opaque, perfectly
+flat, clean digital UI. Every background surface must be a solid uniform navy
+color: page #0b1c2a, top bar #102536, group headers #132b3c, session rows #0e2333,
+subtle first-row highlight #18394f. Thin solid separators. Absolutely NO
+transparency, no black cut-out holes, no gradients, no grain, no glow, no textures,
+no ripples, no smoke, no shadows. This is a pixel-sharp product screenshot of a
+normal flat application, not an artistic treatment. Keep text and icons crisp
+with clean antialiasing and excellent contrast. Small status dots remain blue for
+Working, amber for Idle, green for Finished. Preserve all exact existing content.
+
+## Branding correction prompt
+
+Built-in imagegen edit using the existing dashboard as the target and
+`web/public/brand-kit/agent-relay-logo-horizontal-transparent.png` as the
+authoritative branding reference.
+
+Use case: compositing.
+Input image 1: edit target, the existing Agent Relay sessions dashboard concept.
+Input image 2: authoritative logo insert from the repository's brand kit, agent-relay-logo-horizontal-transparent.png.
+Replace ONLY the incorrect top-left text logo in image 1 with the EXACT real horizontal logo from image 2: the two interlocking rounded blue diamond/chevron shapes (bright sky-blue left shape, darker blue right shape) and the white lowercase agentrelay wordmark with its specific original letterforms. Composite the provided branding, do not invent or redesign the symbol or typeset a substitute. The input logo has large transparent margins: ignore those margins and use only the visible logo artwork. Fit the complete real lockup at about x=28..180, y=22..47 within the existing top bar, centered vertically; preserve the logo's original aspect ratio. Retain the existing divider around x=211 and the AgentWorkforce workspace selector.
+Preserve all other pixels and layout as closely as possible: original 1585x992 canvas, navy backgrounds, all navigation, all text, table rows, three human groups, tool icons, statuses, PR numbers, and separators. No added ornamentation. No new elements. Final result must be a crisp screenshot with the actual supplied Agent Relay brand lockup in the top left.
+
+## Final logo spacing prompt
+
+Built-in imagegen edit to increase the separation between the mark and wordmark
+at the landing page's displayed image size.
+
+Use case: precise-object-edit.
+Edit only the spacing in the Agent Relay logo at the TOP LEFT of this existing dashboard screenshot.
+The blue symbol currently ends around x=78 and the white wordmark begins around x=82. Increase that 4px gap to a clear 16px gap by shifting ONLY the entire white "agentrelay" wordmark about 12 pixels to the right, so it starts around x=94 and ends around x=227. Keep the blue symbol in its current location and at its current size, about x=34..78. Keep the divider at x=244. Keep the exact existing official blue symbol shape and exact existing white wordmark letterforms, height, weight, and baseline. Do not redesign or typeset new branding. The symbol and wordmark should feel separate with comfortable breathing room even when this screenshot is displayed at half size.
+Everything else is invariant: same 1585x992 canvas, same header positions, workspace selector, navigation, backgrounds, all table rows, text, statuses, tool icons, and PR numbers. Make no other visual changes. Preserve the rest of the screenshot.
diff --git a/docs/teams-cloud-signup.md b/docs/teams-cloud-signup.md
new file mode 100644
index 00000000..6550e284
--- /dev/null
+++ b/docs/teams-cloud-signup.md
@@ -0,0 +1,34 @@
+# Teams entry into Cloud
+
+The marketing `/teams` signup buttons link directly to Cloud's Google auth
+endpoint, `/cloud/api/auth/google/start?next=%2Fteams%2Fconnect`. They use a
+full browser navigation so Cloud can set OAuth cookies and redirect to Google.
+The post-auth `/teams/connect` destination preserves Teams signup attribution
+and then redirects to `/cloud/dashboard`.
+
+Cloud handles Google auth and sends the user to `/cloud/dashboard`. Accounts
+created through this Teams flow are marked directly on Cloud's `users` table
+with `signup_source = teams` and `dashboard_version = 2`. They see the new Hello
+world dashboard. Existing accounts keep their current dashboard, including
+when signing in through the Teams link. The assignment persists across logins.
+The migration adds both columns without changing existing user data; existing
+accounts default to version `1` with an unknown (NULL) signup source.
+
+## Local development
+
+In the Cloud companion checkout, run `npm run dev:local` (see its
+`docs/local-teams.md`). Here, run `npm run dev:teams`. Open
+`http://127.0.0.1:3100/teams`. The development-only `/cloud/*` proxy forwards to
+Cloud at `127.0.0.1:3101`, preserving one origin for API requests and auth cookies.
+Keep `NEXT_PUBLIC_CLOUD_URL` unset.
+
+Google needs a development OAuth client with the exact callback
+`http://127.0.0.1:3100/cloud/api/auth/callback/google`. Without it, visit
+`http://127.0.0.1:3100/cloud/api/auth/dev-login?source=teams` to create a separate
+local Teams signup. The test login without `source=teams` retains the original
+local account for testing the existing dashboard.
+
+The Cloud companion owns cohort persistence, dashboard routing, and all future
+setup functionality. V2 intentionally contains only Hello world for now. Its
+migration must land before the runtime change under the existing reviewed PR
+and human release gates.
diff --git a/package-lock.json b/package-lock.json
index 39558180..ab0e86f0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16684,7 +16684,8 @@
"@types/react-dom": "^19.2.3",
"typescript": "^5.9.3",
"vitest": "^4.1.0",
- "wrangler": "^4.91.0"
+ "wrangler": "^4.91.0",
+ "esbuild": "0.25.4"
},
"engines": {
"node": ">=20.9.0"
diff --git a/package.json b/package.json
index e9d7d78b..f1119451 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"dev": "npm --workspace web run dev",
"build": "npm --workspace web run build",
"test": "npm --workspaces run test",
- "preview": "npm --workspace web run preview"
+ "preview": "npm --workspace web run preview",
+ "dev:teams": "CLOUD_DEV_ORIGIN=http://127.0.0.1:3101 npm --workspace web run dev -- --hostname 127.0.0.1 --port 3100"
}
}
diff --git a/scripts/stage-probe.mjs b/scripts/stage-probe.mjs
new file mode 100644
index 00000000..24d255c7
--- /dev/null
+++ b/scripts/stage-probe.mjs
@@ -0,0 +1,18 @@
+#!/usr/bin/env node
+import { copyFile, mkdir, readFile, writeFile, chmod } from 'node:fs/promises';
+import { createHash } from 'node:crypto';
+import { resolve, dirname, isAbsolute } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { parseArgs } from 'node:util';
+const { values } = parseArgs({ options: { binary: { type: 'string' }, platform: { type: 'string' } } });
+if (!values.binary || !isAbsolute(values.binary) || !/^(darwin|linux)-(arm64|x64)$/.test(values.platform ?? '')) {
+ throw new Error('Use --binary /absolute/native-binary --platform darwin-arm64 (or supported linux/macOS variant).');
+}
+const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
+const destination = resolve(root, 'web/public/downloads/agent-relay-probe', values.platform);
+const bytes = await readFile(values.binary);
+await mkdir(destination, { recursive: true });
+await copyFile(values.binary, resolve(destination, 'agent-relay-probe'));
+await chmod(resolve(destination, 'agent-relay-probe'), 0o755);
+await writeFile(resolve(destination, 'agent-relay-probe.sha256'), createHash('sha256').update(bytes).digest('hex') + ' agent-relay-probe\n');
+console.log('Staged native probe and SHA-256 for local website testing.');
diff --git a/web/app/landing.module.css b/web/app/landing.module.css
index eeadb395..d11cd01a 100644
--- a/web/app/landing.module.css
+++ b/web/app/landing.module.css
@@ -2220,6 +2220,16 @@
border-bottom: 1px solid rgba(116, 184, 226, 0.14);
}
+.heroTermAvatar {
+ flex: none;
+ width: 24px;
+ height: 24px;
+ margin-left: auto;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 1px solid rgba(159, 212, 245, 0.4);
+}
+
.heroTermTraffic {
display: inline-flex;
flex: none;
diff --git a/web/app/sitemap.ts b/web/app/sitemap.ts
index e2c7bad8..2273d838 100644
--- a/web/app/sitemap.ts
+++ b/web/app/sitemap.ts
@@ -13,6 +13,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date();
const staticRoutes: MetadataRoute.Sitemap = [
+ { url: absoluteUrl('/teams'), lastModified: now, changeFrequency: 'weekly', priority: 0.9 },
{
url: absoluteUrl('/'),
lastModified: now,
diff --git a/web/app/teams/page.tsx b/web/app/teams/page.tsx
new file mode 100644
index 00000000..fd62fe41
--- /dev/null
+++ b/web/app/teams/page.tsx
@@ -0,0 +1,190 @@
+import { ContextCapabilities } from '../../components/home/ContextCapabilities';
+import type { Metadata } from 'next';
+import Image from 'next/image';
+import dashboardConcept from '../../public/teams/teams-dashboard-concept.png';
+import sessionConcept from '../../public/teams/teams-session-overview-concept.png';
+import { HeroTerminalMarquee } from '../../components/home/HeroTerminalMarquee';
+import { DeploymentCall } from '../../components/home/DeploymentCall';
+import { MessagingFeature } from '../../components/home/MessagingFeature';
+import { DurableWorkflowFeature } from '../../components/home/DeliveryFeature';
+import { InvestorStrip } from '../../components/InvestorStrip';
+import { GitHubIcon, WaveDivider } from '../../components/home/icons';
+import { SiteNav } from '../../components/SiteNav';
+import { SiteFooter } from '../../components/SiteFooter';
+import { TEAMS_OG_ALT, TEAMS_OG_IMAGE_PATH, ogImage } from '../../lib/og-meta';
+import { absoluteUrl } from '../../lib/site';
+import { teamsCloudUrl } from '../../lib/teams-cloud';
+import home from '../landing.module.css';
+import flows from '../flows/flows.module.css';
+import { IntegrationMarquee } from '../flows/IntegrationMarquee';
+import s from './teams-landing.module.css';
+
+export const metadata: Metadata = {
+ title: 'Agent Relay Teams | Your team’s coding sessions',
+ description: 'Connect your computer to Agent Relay and bring your team’s coding sessions into one shared history.',
+ alternates: { canonical: absoluteUrl('/teams') },
+ openGraph: {
+ title: 'Agent Relay Teams | Your team’s coding sessions',
+ description: 'See what every agent is doing. Follow the work. Share what works.',
+ url: absoluteUrl('/teams'),
+ type: 'website',
+ images: [ogImage(TEAMS_OG_IMAGE_PATH, TEAMS_OG_ALT)],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: 'Agent Relay Teams | Your team’s coding sessions',
+ description: 'See what every agent is doing. Follow the work. Share what works.',
+ images: [absoluteUrl(TEAMS_OG_IMAGE_PATH)],
+ },
+};
+
+export default function TeamsPage() {
+ const signupHref = teamsCloudUrl('/api/auth/google/start?next=%2Fteams%2Fconnect');
+ const signupLabel = 'Sign up for free';
+ const setupLink = (
+
+ {signupLabel}
+
+ );
+
+ return (
+
Open any agent session to see what it’s working toward, the constraints it’s following, and how long it’s been running. Read the conversation, or replay it from the start.
Docs
@@ -165,6 +166,7 @@ export function SiteNav({
)}
{!hideLinks && (
<>
+ setMenuOpen(false)}>Teams
setMenuOpen(false)}>
Blog
diff --git a/web/components/home/ContextCapabilities.tsx b/web/components/home/ContextCapabilities.tsx
index 6eb4c8ad..6c3fe350 100644
--- a/web/components/home/ContextCapabilities.tsx
+++ b/web/components/home/ContextCapabilities.tsx
@@ -75,12 +75,12 @@ function SharedFileMountPreview() {
* Rendered inside {@link DeliveryFeature} so it stays grouped with the delivery
* story, matching the original DOM nesting.
*/
-export function ContextCapabilities() {
+export function ContextCapabilities({ title = 'Everything agents need to collaborate' }: { title?: string }) {
return (
-
Everything agents need to collaborate
+
{title}
Agents are only as good as the context you give them. Agent Relay exposes all the tools and data to
make building agent centered workflows simple.
diff --git a/web/components/home/DeliveryFeature.tsx b/web/components/home/DeliveryFeature.tsx
index 70a3aa71..ec3fa612 100644
--- a/web/components/home/DeliveryFeature.tsx
+++ b/web/components/home/DeliveryFeature.tsx
@@ -109,19 +109,20 @@ export function DurableWorkflowFeature({
showCapabilities = true,
title = 'Build durable workflows',
previewAccent = 'blue',
-}: { showCapabilities?: boolean; title?: string; previewAccent?: 'blue' | 'orange' }) {
+ items = [
+ 'Persist every transition so long-running work survives restarts and deploys.',
+ 'Retry individual steps without repeating successful work or duplicating side effects.',
+ 'Trace every input, output, model action, artifact, and intervention in one run history.',
+ 'Trigger workflows from schedules and events, then execute them close to your systems.',
+ ],
+}: { items?: string[]; showCapabilities?: boolean; title?: string; previewAccent?: 'blue' | 'orange' }) {
return (
);
}
diff --git a/web/components/home/DeploymentCall.tsx b/web/components/home/DeploymentCall.tsx
index f0d751b0..68e1812e 100644
--- a/web/components/home/DeploymentCall.tsx
+++ b/web/components/home/DeploymentCall.tsx
@@ -5,46 +5,47 @@ import { FadeIn } from '../FadeIn';
import s from '../../app/enterprise/enterprise.module.css';
import layout from './deployment-call.module.css';
-export function DeploymentCall() {
+type Contact = { name: string; role: string; image: string; href: string };
+
+export function DeploymentCall({
+ title = 'Become a design partner',
+ example = 'flow',
+ description = "We're actively looking for design partners who are tired of agents letting them down. You'll have direct access to our team and great discounts on all paid plans.",
+ topics = [
+ 'Walk us through a flaky workflow your humans or agents handle',
+ "We'll help you write your flows and deploy them",
+ 'Review hosted, private cloud, or self-managed options',
+ 'Leave with a clear path from discovery to production',
+ ],
+ contact = { name: 'Khaliq Gant', role: 'Founder and CTO', image: '/authors/khaliq.jpeg', href: '/khaliq' },
+}: { title?: string; example?: 'flow' | 'agent'; contact?: Contact; description?: string; topics?: readonly string[] } = {}) {
return (
-
Become a design partner
-
- We're actively looking for design partners who are tired of agents letting them down. You'll have direct access to our team and great discounts on all paid plans.
-
+
{title}
+
{description}
-
-
- Walk us through a flaky workflow your humans or agents handle
-
-
-
- We'll help you write your flows and deploy them
-
-
-
- Review hosted, private cloud, or self-managed options
-
-
-
- Leave with a clear path from discovery to production
-
+ {topics.map((topic) => (
+
+
+ {topic}
+
+ ))}
-
+
- Khaliq Gant
- Founder and CTO
+ {contact.name}
+ {contact.role}