Skip to content

Extract the marketing/docs site into agentrelay.com - #1

Merged
willwashburn merged 352 commits into
mainfrom
extract-web
Jun 23, 2026
Merged

willwashburn merged 352 commits into
mainfrom
extract-web

Conversation

@willwashburn

@willwashburn willwashburn commented Jun 22, 2026 •

Copy link
Copy Markdown
Member

Stands up agentrelay.com as the standalone front-door repo. Phase 1 of the
extract → relocate-router → consolidate-domains plan.

What's here

  • relay/web extracted with full git history preserved (git filter-repo,
    347 commits back to the genesis "Add OpenClaw SST app" commit — git blame/log intact).
  • web/ kept as a subdirectory under a workspace root so the router can join as a
    sibling in Phase 2; this also keeps the ../node_modules/.bin/sst CI invocation and
    the guard scripts' repo-root path math unchanged.
  • Ported deploy / preview / remove-stale-previews workflows + AWS/ACM guard
    scripts. App name stays relay-web so existing SST state and PR stages resolve.
  • Added vitest to web devDeps (it was a hoisted root dep in the monorepo).

Verified locally

  • npm install, next build, and 21/21 vitest tests pass standalone.

Incoming on this branch

  • Convert the deploy from sst.aws.Nextjs to OpenNext-on-Cloudflare
    (@opennextjs/cloudflare + sst.cloudflare.Worker, home: "cloudflare") so the
    front door is fully Cloudflare-native with no AWS dependency — following the
    in-house pattern already used by cloud/packages/web.

Not in this PR (later phases / your hand)

  • Wiring the new repo's GitHub Actions secrets/vars.
  • Deleting web/ from the relay monorepo (after this deploys green).
  • Router relocation + domain consolidation onto *.agentrelay.com.

🤖 Generated with Claude Code


Summary by cubic

Extracted the marketing/docs site into a standalone, Cloudflare‑native app for agentrelay.com using OpenNext. Adds richer docs/blog/brand experiences, allowlisted PostHog analytics, and production + per‑PR Cloudflare deployments.

  • New Features

    • web/ Next.js app built with @opennextjs/cloudflare (Wrangler) and Node 22 pinned via .nvmrc; workspace root proxies scripts to web/.
    • Ported and expanded pages: marketing home, docs, blog, Agents gallery (+ use cases), Primitives (auth, file, message, schedule), Pear product, OpenClaw skill (incl. invite), Brand Kit + Theme (adds /brand/theme), Process, Privacy/Terms/Telemetry.
    • Rich sharing and feeds: OG image routes for home/docs/blog and RSS at /feed.xml; stable Pear download redirect at /pear/download.
    • Docs UX: switched all docs links to next/link for client navigation and prefetch; smoother DocsNav scrolling (incl. wheel‑sync); language‑specific spawn options; markdown mirrors (/docs/<slug>.md, /agents.md, llm*.txt).
    • SEO: robots.ts and sitemap.ts tuned (AI crawler allowlist; remove generic /skill/invite, keep /openclaw/skill/invite); site metadata now points at agentrelay.com.
    • Analytics: manual, allowlisted PostHog pageview tracking with NEXT_PUBLIC_POSTHOG_KEY and configurable NEXT_PUBLIC_POSTHOG_HOST (defaults to https://i.agentrelay.com); GitHub stars badge server‑rendered to avoid flashes.
    • Routing hardening: removed public/docs assets to prevent /docs* static routing collisions in previews.
  • CI/CD

    • GitHub Actions build with OpenNext and deploy via Wrangler: production on main, previews on PRs, with concurrency guards and Node 22 cache.
    • Injects NEXT_PUBLIC_POSTHOG_KEY and NEXT_PUBLIC_POSTHOG_HOST at build; preview workflow restricted to same‑repo PRs.

Written for commit 9f94ec9. Summary will update on new commits.

Review in cubic

willwashburn and others added 30 commits April 2, 2026 14:17
Refactor drawMessages in RelayauthAnimation by extracting advanceMessage and handleDeniedMessageFrame helpers to reduce function complexity and avoid eslint disables; preserves animation behavior and blocked-message lifecycle. Replace a side-effect ternary in FaqSection with an explicit if/else to satisfy no-unused-expressions linting. Add wheel-sync handling to DocsNav (useRef + useEffect) so the docs sidebar scrolls smoothly when main content hits boundaries. Minor copy improvements in quickstart.mdx. Add trajectory metadata files and update trajectories index. Verified web build after changes.
Delete the dynamic invite redirect page (web/app/skill/invite/[token]/page.tsx) and update web/app/robots.ts to remove the generic '/skill/invite/' disallow entry, leaving only '/openclaw/skill/invite/'. This aligns robots directives with the current route structure and removes an unused redirect handler.
Integrate PostHog pageview tracking for the web app: add site-analytics library, middleware/proxy, a PostHogPageView component, and a test for analytics. Update layout to emit manual, allowlisted pageviews for public docs/blog/catalog routes only. Expose NEXT_PUBLIC_POSTHOG_KEY (and document NEXT_PUBLIC_POSTHOG_HOST default) in .env.example and inject NEXT_PUBLIC_POSTHOG_KEY into the web deploy job so production builds receive the key; SST config was updated to pass env vars into the Next app. Also add trajectory records for the changes and minor web package updates.
Rename tests from test/web/* to web/lib/test/* and update relative import paths to match the new locations. Adjusted imports in code-fence-meta.test.ts, docs-markdown-spawn-options.test.ts, highlighted-code.test.tsx, site-analytics.test.ts, and spawn-options-table.test.tsx so modules are resolved from their new paths (e.g. '../../web/lib/...' -> '../...', component imports -> '../components/...'). This keeps tests organized under web/lib/test and ensures imports remain correct after the move.
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Change metadataBase from https://agentrelay.dev to https://agentrelay.com and replace the hardcoded PostHog api_host ('/ingest') with a configurable env var (NEXT_PUBLIC_POSTHOG_HOST) falling back to https://i.agentrelay.com. This makes the app use the production domain for metadata and allows the analytics ingest host to be configured per-deployment.
Point metadata to .com and update PostHog host
…low runner (#673)

* feat: add relayauth/relayfile permission system for workflow agents

Wire per-agent permissions through the workflow runner lifecycle:
- AgentPermissions type with access presets, file globs, network, exec
- Permission profiles for reusable named configurations
- Provisioner module: compiler, token factory, seeder, mount, audit
- Dry-run permission resolution and display
- Network permission supports boolean and object {allow, deny} forms
- 6 example workflows demonstrating permission patterns
- Comprehensive docs (MDX + MD mirror)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: trim permissions docs to concise reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* devin feedback

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace agentrelay.dev with agentrelay.com across docs, skills, plugins, and runtime messages; update author/contact emails and default cloud API URL. Also standardize string quotes and small formatting/whitespace fixes in TypeScript files (types, SDK, workflows, config, telemetry), tidy JSON/TOML array formatting, and minor test expectation updates. Added new web app files (blog opengraph, feed route, BlogTableOfContents, site lib) and other UI/content adjustments.
Stop injecting `components` into the `evaluate` options and instead pass `mdxComponents` directly to the rendered `MDXContent` component in web/app/blog/[slug]/page.tsx. This ensures the custom MDX components are applied at render time and fixes issues with component resolution during rendering.
…ration

refactor: route waitlist signups to cloud
* on the erlay docs and quickstart

* fix formatting

* fix: quickstart code formatting and indentation

Add prettier-ignore to prevent linter from stripping indentation
inside code blocks. Break long lines for narrow docs column.
Filter empty messages in onMessageReceived callback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: exclude docs from prettier to preserve code block indentation

Prettier was stripping indentation inside MDX code blocks on commit,
making quickstart examples unreadable. Added web/content/docs/ to
.prettierignore.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove spec files moved to separate PR (#697)

The exec-deny and interactive-cloud-sessions specs don't belong in this
docs/quickstart improvement PR. Moved to #697 for separate discussion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: revert unrelated cloud-api-url changes

These changes belong to the fix/cloud-api-url-path branch, not this PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server-render the GitHub stars badge (async component with Next revalidation) to avoid client flash and cache counts for 1 hour. Move the theme toggle out of the top SiteNav into the SiteFooter and add a footer Discord social link. Introduce an actions slot on SiteNav and pass the GitHubStarsBadge into multiple pages (home, blog, docs, brand, primitives, openclaw, telemetry). Simplify the /blog index to a compact title-and-author list with metadata and move the RSS link into the header. Add/update related CSS modules for blog, github badge, and footer. Update trajectories metadata with decision entries and lastUpdated timestamp.
Delete web/public/docs static assets (favicon.png, logo/*) to prevent SST/OpenNext from treating the top-level public/docs directory as a static route prefix and causing preview environments to route /docs* to S3 instead of the Next app. Record this decision in .trajectories/active/traj_4r047wx9ml89.json (added decision entry) and update .trajectories/index.json lastUpdated timestamp.
Add a web-only detection job to multiple GitHub workflows (node-compat, package-validation, rust-ci, test) and skip heavy jobs when the change set only touches web/ files. Move the docs navigation into the mobile hamburger: SiteNav now accepts mobileMenuContent and hideMobileDocsLink, DocsNav supports a mobileMenu variant with new styles, and the desktop docs sidebar is hidden on small screens. Add a /cloud footer link and update dark-mode footer styling (blue gradient and tinted divider). Introduce web/lib/content-paths.ts (resolveContentDir) and update web/lib/blog.ts, docs-markdown.ts, and docs.ts to resolve content at runtime; update next.config.mjs outputFileTracingIncludes to trace content/docs and content/blog to avoid build-machine absolute MDX paths. Add multiple .trajectories completed entries and update .trajectories/index.json. Remove the Waitlist resource from web/sst-env.d.ts.
Switch production SST domain from agentrelay.net to orgin.agentrelay.net to avoid a CloudFront CNAMEAlreadyExists conflict and allow a no‑downtime deploy path. Update web/sst.config.ts (domain and comment). Add two completed trajectory records (traj_4zqhfqw7g28l and traj_rs2bt3x0fqba) documenting the investigation, decision to use orgin.agentrelay.net, and verification that the new plan issues a certificate and removes stale agentrelay.net validation resources. Also update .trajectories/index.json timestamp and entries.
* feat(cli): add agent-relay mcp-args subcommand

* fix(mcp-args): fall back to RELAY_* env vars when flags omitted

Match WorkerRegistry::build_mcp_args in the broker: when callers drive
the subcommand via env (cloud's SandboxedStepExecutor exports these
from orchestrator-managed secrets), RELAY_AGENT_TOKEN /
RELAY_WORKSPACES_JSON / RELAY_DEFAULT_WORKSPACE must feed through even
if not repeated on the CLI. Without this the authority delegation
silently dropped per-worker identity in env-driven callers.

Adds a test that sets the env vars, passes None for the equivalent
flags, and asserts each sentinel value lands in the generated claude
mcp config JSON.

Addresses PR #759 Codex P2 review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: MCP Args Subcommand Bot <agent@agent-relay.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add support for the cross-tool DO_NOT_TRACK convention as an alternative opt-out for telemetry. Code changes: add isTruthyEnv and check DO_NOT_TRACK alongside AGENT_RELAY_TELEMETRY_DISABLED in packages/telemetry, update Rust telemetry checks and add tests, and update CLI messaging. Docs/UI: update .env.example, TELEMETRY.md, and the web telemetry page to mention DO_NOT_TRACK and show example usage. This ensures users can opt out using the common DO_NOT_TRACK variable across compatible tools.
* feat(cli): add --register flag to mcp-args subcommand

Composes RelaycastHttpClient::register_agent_token with
configure_relaycast_mcp_with_token so external consumers (cloud's
SandboxedStepExecutor) can mint an at_live_* token and build MCP args
in a single exec, without reimplementing relaycast registration.

Output JSON gains an optional agentToken field, populated only when
--register is used. --register + --agent-token are mutually exclusive.

Follow-up to #759.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(mcp-args): address PR #760 review findings

- Validate local inputs (cwd + --existing-args JSON) BEFORE the
  --register call. A POST to /v1/agents/spawn rotates the agent's
  token on the relaycast backend, so we must not fire it when the
  request is going to fail locally anyway. (Codex P2)

  Adds register_does_not_hit_network_when_local_validation_fails:
  invalid --existing-args + --register asserts the mock spawn endpoint
  receives zero hits.

- Serialize RELAY_* env-var tests behind a module-level Mutex and
  introduce an EnvGuard RAII helper that snapshots + restores env on
  drop. Previously register_without_api_key_returns_clear_error,
  register_without_base_url_returns_clear_error,
  register_happy_path_embeds_minted_token, and
  env_vars_fill_in_when_cli_flags_omitted mutated process-global env
  without serialization; under parallel #[tokio::test] execution they
  raced, and none of them restored prior values on panic. (Codex P2)

- Correct the .trajectories/index.json entry so `path` points at the
  canonical /Users/khaliqgant/Projects/AgentWorkforce/relay location
  instead of the worktree directory, matching all other 522 entries.
  (Devin)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: MCP Register Flag Bot <agent@agent-relay.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a Brand Kit section to BrandShowcase and include downloadable logo/mark assets. Updated BrandShowcase.tsx to import Download icon and render a kit grid with per-asset preview, filenames, and download buttons (including a single zip download). Added a BRAND_KIT list with darkPreview flags for assets. Added styles in brand.module.css to layout previews, responsive grid behavior, and download button styles. Also add all PNG assets and the agent-relay-brand-kit.zip under web/public/brand-kit.
Replace public brand assets with updated versions: agent-relay-brand-kit.zip and agent-relay-mark-light-circle.png. Keeps the web public brand-kit up to date with refreshed logo and mark files.
miyaontherelay and others added 18 commits June 11, 2026 07:25
Add /agents gallery, detail pages, use-cases, and build-your-own
…tion docs (#1082)

* sdk: typed action result predicates, zod agentResultSchema, orchestration docs

Closes three DX gaps found while building an orchestrator against the v8 SDK:

- @agent-relay/sdk: relay.registerAction(...) now returns a TypedActionHandle
  whose completed()/failed()/invoked()/denied() predicates carry the
  registration's input/output types into addListener handlers, so orchestrators
  consume their own action results without string keys or `as` casts. The
  string path (relay.action(name)) and ActionHandle.unregister() are unchanged.
- @agent-relay/harness-driver: SpawnedAgentHandle.waitForResult() resolves with
  the structured result submitted through the submit_result MCP tool (replays
  from broker event history; resolves on exit or timeout), and agentResultSchema
  now accepts zod-style validators, coerced to JSON Schema via the SDK's
  actionSchemaToJsonSchema before the spawn request reaches the broker.
- CHANGELOG: the structured-result entry now describes what actually shipped
  (it advertised agent.waitForResult(), result.onResult, and
  addListener('agentResult') which did not exist).
- docs: new orchestrating-with-actions page covering the request →
  typed-callback pipeline pattern, with typed-handle examples and cross-links
  in actions and event-handlers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style: auto-format with Prettier

* Fix waitForResult replay ordering

* style: auto-format with Prettier

* Cover spawned agent lifecycle helpers

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Concise, example-driven guide to running agents on dedicated machines
(e.g. a Mac mini with Claude Code and Codex) instead of a laptop. Covers
defining a node, `fleet serve`, verifying registration, spawning onto it,
and adding actions/triggers. Adds a "Fleets" group to the docs nav.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(evals): add s08-lead-quality scenario group

Seven scenarios testing coordination quality (how well a lead leads),
complementing s07 (does it delegate at all):

  q01 — task decomposition: complex 4-track task → PASS = 2-6 workers spawned
  q02 — failure handling: worker errors → PASS = retry or upstream escalation
  q03 — progress communication: mid-task status request → PASS = lead responds
  q04 — re-routing: worker declines → PASS = new spawn or escalation
  q05 — over-delegation prevention: trivial question → PASS = no spawn
  q06 — conflict resolution: contradicting findings → PASS = conflict acknowledged
  q07 — scope discipline: out-of-scope bonus finding → PASS = no extra spawns

28 scenarios total (7 × 4 onboarding variants). Primary harnesses: opus, codex,
gemini. Runner scripts: eval:lead-quality and eval:lead-quality:all-harnesses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(broker): apply eval relay worker guidance

* style: auto-format with Prettier

* feat(evals): finalize s08-lead-quality scenarios with timing + scoring fixes

- Cap all waiters at Math.min(phaseMs, 60_000) — both first spawnWaiter
  and second-phase waiters in q02/q03/q04/q07 — reducing opus timeout
  burn from 240s to 120s per scenario
- q06: embed conflict scenario in startup task instead of DM injection;
  idle agents don't process injected text so relay-only injection failed
- q07: require initial spawn for PASS to prevent vacuous pass when lead
  self-implements (spawnEv !== null && !outOfScopeSpawn)
- q02/q04: add PTY stream fallback gated on spawnEv !== null to catch
  escalation/re-routing expressed in stream output
- CONFLICT_RE: expand keywords to cover PTY stream output phrasing;
  add PTY stream check alongside relay message check in detector
- Validated haiku+sonnet: q05 4/4 + q06 4/4 both models; q01-q04/q07
  correctly surface delegation gaps in current Claude CLI tiers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: auto-format with Prettier

* style: auto-format with Prettier

* style: auto-format with Prettier

* fix(broker): remove droid skill injection; fix mcp-add race; update composer eval data

- Remove DROID_RELAY_SKILL injection for droid — broker-injected skill text
  suppresses relay tool use entirely (s03:bare 0/5 with injection vs baseline
  5/5 without). Droid receives no CLI-specific prefix from relay_skill_prefix.
- Fix race condition in run_gemini_droid_mcp_add: 4-attempt retry loop with
  150ms backoff per attempt handles concurrent eval processes racing on
  ~/.factory/mcp.json (already-exists error).
- composer.ts: revert droid onboarding bare→skill (skill kills relay behaviour,
  confirmed 0/5 s03); mark droid worker as not-viable for droid 0.148.1.
- composer.ts: opencode default model deepseek-v4-flash (16/16, 0 phantoms)
  over mimo-v2.5-free (15/16); add opencode:gpt-5.5 provisional entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(changelog): update unreleased entry with accurate droid/opencode changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: auto-format Rust code with cargo fmt

* style: auto-format with Prettier

* fix(broker): address PR review feedback on relay skill injection and eval correctness

- Gate relay_skill_prefix injection behind !skip_relay_prompt so agents
  spawned with skipRelayPrompt:true are not told to call absent relay tools
- Remove redundant prefix.to_string() allocation in the None arm (prefix is
  already an owned String)
- Convert remove_gemini_droid_mcp_servers to async using tokio::process::Command
  with a 5s timeout to avoid blocking the async runtime
- Fix s08 q02 escalated check to only scan events after failure injection,
  preventing early lead messages containing 'error' from falsely passing
- Fix s08 q03 pass check to require a lead response after the status query
  is injected, not just any message since scenario start
- Make CHANGELOG entry impact-first, removing 'eval-derived' implementation jargon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(evals): add CLI/model mapping to relay skill prefix and s09 cross-cli-spawn scenarios

Relay workers asked to "spawn a codex agent" or "spawn an opus claude agent"
now have explicit guidance to map those requests to the right add_agent
parameters.

Changes:
- SMALL_MODEL_RELAY_SKILL: add CLI options table (claude/codex/gemini/opencode)
  and model-pinning examples (opus/sonnet/haiku) with concrete add_agent calls
- RELAY_WORKER_ONE_LINER (gemini): expand to include cli and model parameter hints
- onboarding.ts brief+skill variants: add CLI/model mapping so all harnesses
  get the reference regardless of whether broker injection fires
- s09-cross-cli-spawn.ts: 4 scenarios × 4 onboarding variants = 16 total
    q01: "spawn a codex agent" → agent_spawned.cli contains "codex"
    q02: "spawn a claude agent" → agent_spawned.cli contains "claude"
    q03: "spawn an opus claude agent" → cli "claude" + model ~ /opus/
    q04: "spawn a sonnet claude agent" → cli "claude" + model ~ /sonnet/
- Register s09 in index.ts and runner.ts under --group=cross-cli-spawn

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: auto-format with Prettier

* refactor(evals): minimize CLI/model prompt additions; revert brief onboarding

The eval runner finds the minimum onboarding variant that achieves 100% — adding
CLI/model guidance to 'brief' before running the eval pollutes that comparison.

Changes:
- Revert 'brief' onboarding to original (no CLI/model additions); eval will tell
  us if brief needs augmenting after s09 runs show bare/one-liner results
- RELAY_WORKER_ONE_LINER: true one-liner (cli options inline, not 3 sentences)
- SMALL_MODEL_RELAY_SKILL: drop the full table; two targeted lines added —
  enumerate cli options in the Required line, add one model-pinning line

The 'skill' onboarding retains full CLI/model detail as the kitchen-sink variant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* add opencode and grok

* fix(mcp): improve add_agent description to map cross-CLI and model-tier spawn requests

Eval s09 showed that orchestrators without relay-specific onboarding would receive
"spawn a codex agent" or "spawn an opus claude agent" and either do the work themselves
or call add_agent with wrong/missing parameters:
- q01 (codex spawn): 12/12 PASS in v1 description, confirmed causal via baseline
- q03 (opus model): 0/12 → 12/12 after fix (root cause: no "opus claude" → cli:claude mapping)
- q04 (sonnet model): 0/12 → 12/12 after fix (model param was passive, not directive)

Changes:
- add_agent tool description: concrete param examples replace prose
  ("spawn a codex agent" → cli:"codex", "spawn an opus claude agent" → cli:"claude", model:"claude-opus-4-8")
- cli param: explicit "opus claude/sonnet claude agent" → claude mapping
- model param: "Required when a tier is specified" (directive, not passive)
- s09 tasks: terse phrasing matching real failure; runner type + whitelist updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mcp): cast add_agent cli for @relaycast/sdk 4.0 narrowed spawn type

The merged @relaycast/sdk 4.0 bump narrows SpawnAgentRequest.cli to the core
five harnesses (claude/codex/gemini/aider/goose), dropping grok and opencode.
The broker and gateway still accept grok/opencode at runtime, so keep them
selectable in the add_agent MCP tool enum and cast at the spawn boundary
rather than removing the capability.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* style: auto-format with Prettier

---------

Co-authored-by: Proactive Runtime Bot <agent@agent-relay.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* web: add "How it works" diagram section to homepage

Add a single-column "How it works" band to the landing page between the
Agent Tools and A2A feature blocks. It shows two source groups — CLI
agents (PTY-driven injection) and custom agents (drop-in SDK + bindings)
— flowing into an Agent Relay card with its core capabilities (DMs/
channels/mentions, durable messaging, receipts, search/history/webhooks).

- New HowItWorks component wired into the features grid via grid order
- Python and TypeScript brand marks added to the home icons module
- Full-bleed dark band keeps the white Agent Relay wordmark legible

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* chore: record trajectory for How it works homepage section

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: rework "How it works" into screenshot diagram with brand icons

Match the marketing mockup: two source groups on the left exchange
messages with the Agent Relay card on the right.

- 2-column diagram (sources | exchange arrows | relay + capability bullets)
- Real brand logos via @lobehub/icons: Claude Code, Codex, OpenCode, Cursor
  (CLI agents) and Hermes, OpenClaw (custom agents); TypeScript and Python
  use inline marks, GitHub via lobehub
- Use .Color/Mono variants in custom tiles to avoid the @lobehub/ui dep
  that the .Avatar variant pulls in

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: redesign "How it works" as a centered hub diagram

Replace the two-column diagram with a clearer hub-and-spoke layout: the
two agent groups wire into the Agent Relay hub at the center, and its
capabilities fan out to the right.

- Center hub node with a soft halo as the focal point
- Converging/diverging SVG wires connect agents -> Relay -> capabilities
- Capability spokes get icons + a one-line description for scanability
- Responsive: drops wires on tablet, stacks vertically on mobile

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: reframe "How it works" around connecting any agent

- Lead the copy with bringing any agent onto one rail and connecting them
  all to Agent Relay; add an SDK/MCP/A2A footnote so "any agent" is explicit
- Match the "Works with every agent" band background and separation
- Move the section ahead of the Agent Tools feature, leaving only the wave
  divider between Agent Tools and the A2A feature

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: rebuild "How it works" as a live communication diagram

Reframe the section around agents talking to each other through Agent
Relay instead of treating its capabilities as destinations.

- CLI agents and custom agents now sit on either side of the Agent Relay
  hub; animated message dots stream across the wires and pass behind the
  relay node, so messages visibly flow agent -> relay -> agent
- Bidirectional, comet-trailed dots read as two-way communication
- Capabilities move below into a plain feature strip (no wired "destinations")
- Respects prefers-reduced-motion; stacks vertically on mobile

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: make How it works messages route between specific agents

Each message is now a distinctly coloured dot that travels from a source
agent, through the Agent Relay hub, to a destination agent — covering
CLI↔CLI, CLI↔custom, and custom↔custom routes so the rail reads as
any-to-any communication.

- Six messages, each its own brand colour (blues, Claude orange, green)
  and source→relay→destination keyframes along the spoke wires
- Remove the capability pills (the rest of the page covers those)
- Reduced-motion parks the coloured dots statically on the spokes

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* web: update How it works subtitle copy

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: trim How it works subtitle and drop footnote

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: route How it works messages through right-angle pipes

Replace the straight spokes and six dots with an orthogonal pipe network
and three clearly-routed messages, so the traffic reads as deliberate.

- Each agent group wires to a side bus that feeds the relay; segments are
  axis-aligned so they stay square under the stretched viewBox
- Three messages (CLI->custom, custom->CLI, CLI->CLI), each its own colour,
  travel the pipes with right-angle turns and a gap between cycles
- Reduced-motion parks the three dots on their pipes

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* web: make the diagram the Works with Every Agent section

- Replace the old logo-grid "Works with every agent" section on the home
  page with the agent-communication diagram (kept on /pear)
- Retitle to "Works with Every Agent" with the PTY/SDK subtext
- Run the pipe feeds under the relay box so the wires meet its edge

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* web: make Works with Every Agent a pyramid diagram

- Agent Relay sits at the top with the two agent groups below it, in both
  desktop and mobile views
- Orthogonal pipes (relay -> bus -> each group) live in a fixed-height
  connector zone so they line up at any width and stay visible on mobile
- Three coloured messages route up to the relay and back down to another
  agent; reduced-motion parks them on the pipes

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* web: messages get sucked into the relay box and pop out

- The dot layer now spans the relay box, so a message travels up behind
  the box (vanishing into the logo) and re-emerges
- ease-in as it approaches the relay (sucked in, shrinking) and an
  ease-out scale "pop" as it comes back out
- Relay box and connector share a fixed height so the apex coordinates
  (relay bottom 50%, bus 75%) hold at every breakpoint

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* web: lead the Works with Every Agent subtitle with the harness/framework line

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

* style: auto-format with Prettier

* web: add Grok to CLI agents and tidy the mobile logo grid

- Add the Grok logo so the CLI agents pane has five icons, matching the
  custom agents pane
- On mobile, enlarge the chips and cap the logo row at three wide so the
  five icons wrap evenly to a 3-over-2 grid

https://claude.ai/code/session_01JShJPZW99B7bBkvjK3uthQ

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Expose workspace fleet flag in Relay

* style: auto-format with Prettier

* Remove fleet config REST fallback

* style: auto-format with Prettier

* Update Relaycast SDK to 4.1.1

* Fix fleet CLI bootstrap test

* style: auto-format with Prettier

* Trigger CI after formatting

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…1168)

The three routed message dots in the HowItWorks pyramid animated `left`/`top`
on an infinite loop, which runs on the main thread and forces layout/paint
every frame. Move the motion onto the compositor:

- Make `.howMsgs` a size container (`container-type: size`) so the dots can be
  positioned with container-query units. Its box is sized by `inset: 0`, not by
  its contents, so size containment is safe and the reference box is unchanged.
- Drive position with the individual `translate` property using `cqw`/`cqh`
  units (e.g. `left: 25%; top: 75%` → `translate: 25cqw 75cqh`) and scale with
  the individual `scale` property. Both composite on the GPU and interpolate
  independently, so the routing path and pop are pixel-identical to before.
- Honor `prefers-reduced-motion` by pausing the dot loop.


Claude-Session: https://claude.ai/code/session_01ULmKZfLCcC3whjY4yGqUx4

Co-authored-by: Claude <noreply@anthropic.com>
…#1167)

* Polish home page button & hero motion (Emil Kowalski craft pass)

Apply design-engineering animation improvements to the landing page,
guided by the emilkowalski/skill design philosophy:

- Add :active press feedback (scale 0.97) to the shared .btn system so the
  hero CTAs confirm they heard the click — previously buttons only lifted on
  hover with no press response.
- Introduce strong custom easing tokens (--ease-out-quint, --ease-out-expo)
  and drive button/hero transforms with them; the built-in CSS easings feel
  weak and unintentional.
- Gate hover-lift transforms behind (hover: hover) and (pointer: fine) for
  the buttons and the hero install / agent-prompt copy cards, so touch taps
  no longer flash the lift on press.
- Add subtle :active scale to the hero install command and agent-prompt copy
  cards for the same press-feedback consistency.
- Honor prefers-reduced-motion for all newly added transforms.

Also vendors the emilkowalski/skill design-engineering and animation-review
skills (plus skills-lock.json) used for this pass, matching the repo's
existing tracked-skills convention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULmKZfLCcC3whjY4yGqUx4

* style: auto-format with Prettier

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* docs: fix action registration client and event/envelope shapes

- Actions must be registered on an agent client (not the workspace client)
  to be exposed as MCP tools; fix quickstart, actions, events,
  event-handlers, orchestrating-with-actions examples
- Fix envelope.from.handle -> envelope.from?.name (no handle field)
- Fix action-handler caller agent.handle -> agent.name
- Correct RelayEvent union, envelope, and event-name vocabulary in events
- Fix delivery.failed listener to use nested session-event fields

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXcjNrVVjkgUakMmM89JgB

* docs: correct MCP tool names, register idempotency, fleet status fields

- agent-relay-mcp: real tool names (post_message, send_dm, reply_to_thread,
  add_reaction, check_inbox, mark_message_read), 'agent-relay mcp' has no
  subcommands and reads env, fix runtime action list, durable fallbacks
- typescript-sdk: register is idempotent by default (strict opt-in), fix
  envelope.from.handle, MCP tool names, register action on agent client
- fleets: handlers_live is on 'fleet nodes', not 'fleet status'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXcjNrVVjkgUakMmM89JgB

* docs: fix session/delivery/harness types and remove fictional OpenClaw

- session-capabilities: real AgentSessionStatus (5 values), correct event
  union field shapes (usage, session.*, message.*, terminal.screen, tool.*)
- delivery: real DeliveryRunner class + AgentDeliveryAdapter interface,
  delivery events as nested session events, accurate retry behavior
- harnesses: real HarnessCreateContext/AgentIdentity, normalizeAgentIdentity,
  create({relay}) returns a handle (no sendMessage) -> use relay.messages.send
- harness-driver: registerDriverActions takes ActionRegistry not relay; real
  'local agent' CLI commands; agent.attach note
- remove reference-openclaw (no openclaw export exists) + nav entries and
  dangling OpenClaw mentions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXcjNrVVjkgUakMmM89JgB

* docs: fix remaining MCP tool names, action clients, CLI reference gaps

- reference-cli: correct 'cloud connect <provider>' description, add cloud
  session, cloud worker, workspace active, and a Fleet command section
- cli-overview: workflow file types include .js and .sh
- introduction/typescript-sdk: real MCP tool names, register spawn action on
  agent client, caller.name instead of caller.handle
- migration: envelope.from?.name
- messaging: RelayMessage includes id
- drop misleading OpenClaw harness mentions (no such harness exists)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXcjNrVVjkgUakMmM89JgB

* docs: remove stale reference-openclaw icon mapping from DocsNav

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXcjNrVVjkgUakMmM89JgB

* docs: fix workspaces/webhooks/migration identity and register claims

- workspaces: real RelayWorkspaceInfo/AgentIdentity shapes, register is
  idempotent by default, claude.create returns a handle not a client
- webhooks: canonical outbound event names (agent.status.idle), drop
  unverifiable delivery.* names
- migration: register idempotent-by-default wording

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXcjNrVVjkgUakMmM89JgB

* chore: record trajectory

* style: auto-format with Prettier

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* docs: fix verifiable gaps between docs and code

- introduction: 'three core jobs' contradicted four cards; correct to four
- agent-relay-mcp: replace incomplete/mislabeled tool table with the actual
  three tool groups and the full messaging tool set; fix the action result
  shape to the real fire-and-forget ack instead of an inline output
- harnesses + introduction: align AgentSession.onEvent signature with source
  (emit param, void|Promise<void>, () => void return)
- reference-cli: fleet serve takes optional [file]/--enrollment-token, not a
  required <node>; add --refresh-timeout to workspace active
- session-capabilities: mark messaging.attachments optional to match the type

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXC7TB4eCAGLLqYfyR9Jzh

* style: auto-format with Prettier

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* refactor: remove the local relay dashboard

The local web dashboard (relay-dashboard-server / @agent-relay/dashboard-server)
is gone. `agent-relay up` is now a plain broker launcher — attached by default,
`--background` to detach — and the dashboard-only flags (`--no-dashboard`,
`--port`, `--foreground`) are removed.

- broker-lifecycle: drop dashboard spawning, static-asset discovery/refresh,
  the GitHub dashboard-ui download, the dead reuse-broker branch, and
  dashboard-port logic. Add `AGENT_RELAY_BROKER_PORT` (resolveBrokerBasePort)
  for the broker base port; orphan detection now keys on `up` minus
  `--background` instead of the removed `--foreground`.
- core / core-maintenance: remove the dashboard flags, binary resolver, and
  uninstall handling for dashboard assets/binary/npm package.
- config: drop the unused localDashboardUrl / LOCAL_DASHBOARD_URL.
- telemetry: drop the `dashboard` / `human_dashboard` enum members.
- fleet + e2e harness: AGENT_RELAY_DASHBOARD_PORT -> AGENT_RELAY_BROKER_PORT.
- scripts: delete the dashboard launcher and the stale sibling-repo test
  script; fix removed-flag usages in CI/e2e/demo scripts.
- docs: drop dashboard wording and removed flags from the current CLI docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style: auto-format with Prettier

* fix(ci): keep `up` resilient when the implicit fleet node can't start

Two CI failures on the dashboard-removal branch:

- package-validation "Build & Validate" called `local up --port`, a flag
  removed with the dashboard. Route the test's port through
  AGENT_RELAY_BROKER_PORT and start a plain `local up` instead.
- Standalone macOS Smoke crashed: `defineDefaultLocalNode is not a function`.
  bun's `--compile` resolves @agent-relay/fleet's value exports to its
  type-only .d.ts, so the implicit fleet node helpers are undefined in the
  standalone binary. This is pre-existing; it was hidden because
  `up --no-dashboard` previously ran the fleet sidecar in a detached child.
  Attached `up` surfaced it. The implicit local fleet node is best-effort, so
  wrap its startup and warn-and-continue instead of aborting the broker.

Also drop the now-dead "Download dashboard binary" step from e2e-tests.yml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: remove relay dashboard from installer and stale refs

- install.sh: drop the dashboard-server binary download, dashboard UI
  download, `npm install -g @agent-relay/dashboard-server`, the
  AGENT_RELAY_NO_DASHBOARD env, the REPO_DASHBOARD repo, and the
  `up --dashboard` post-install help. The installer no longer installs the
  removed local dashboard.
- Fix broken `--no-dashboard` / `up --dashboard` examples in using-agent-relay
  skill docs and the demos README; rename "dashboard port" -> "broker port" in
  the fleet e2e README; drop the stale run-dashboard.js line from .npmignore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(broker): retire ActionSource::HumanDashboard telemetry variant

The local dashboard is gone, and the TS `ActionSource` union already dropped
`human_dashboard`. Re-sync the Rust mirror:

- Remove the `HumanDashboard` enum variant + its `as_str` arm so the broker's
  taxonomy matches TS (`human_cli | agent | protocol`).
- Re-point the only emitter — the `/api/spawn` HTTP handler — to
  `ActionSource::HumanCli`. That endpoint's human caller is now the CLI (the
  dashboard GUI that previously posted to it is gone).
- Update the telemetry unit tests.

Shared broker infrastructure that is merely *named* "dashboard" (the `/ws` SDK
event transport, `display_target_for_dashboard`, `sender_is_dashboard_label`,
the operator "Dashboard" label) is left intact — it powers the SDK/CLI event
stream and the relaycast/Observer message path, not the removed local UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(install): use `up --background` in quick-start so the flow is runnable

`agent-relay up` runs attached by default, which blocks the terminal and stops
the documented `status`/`down` steps from running. Use `--background` so the
quick-start sequence is executable as printed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…ge (#1181)

`packages/telemetry` was a private, deprecated placeholder (telemetry moved
into the `agent-relay` CLI under `packages/cli/src/cli/telemetry`). Nothing
depended on it or imported it, yet its `deprecated` package.json field made
`npm ci` print a deprecation warning on every install.

- Delete the `packages/telemetry` workspace and prune it from the lockfile.
- Drop the dangling `build:telemetry` root script, the `telemetry` entry in
  package-validation `SKIP_PACKAGES`, the `knip.json` workspace entry, and the
  `vitest.config.ts` coverage exclude.
- Repoint stale references to the current telemetry source: the broker
  `telemetry.rs` schema comment, the `publish.yml` posthog-config comment, and
  the web telemetry page's "inspect source" link now point at
  `packages/cli/src/cli/telemetry`.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the marketing/docs site from the relay monorepo (full git history
preserved via git-filter-repo) and stand it up as an independent SST app:

- workspace root package.json hoisting the sst CLI
- ported deploy / preview / stale-preview workflows + AWS/ACM guard scripts
  (app name kept as relay-web so existing SST state and stages resolve)
- add vitest to web devDeps (was a hoisted root dep in the monorepo)
- root .gitignore / .nvmrc / README

Build + 21 tests pass standalone. Still serves origin.agentrelay.net; router
relocation and domain consolidation follow in later phases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 265 files, which is 115 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8989d0a9-7bc7-4495-980c-4b50080bd00b

📥 Commits

Reviewing files that changed from the base of the PR and between 4cd41e4 and 9f94ec9.

⛔ Files ignored due to path filters (35)
  • package-lock.json is excluded by !**/package-lock.json
  • web/public/agent-art/granola/banner.png is excluded by !**/*.png
  • web/public/agent-art/granola/card-sm.png is excluded by !**/*.png
  • web/public/agent-art/granola/card.png is excluded by !**/*.png
  • web/public/agent-art/hn-monitor/banner.png is excluded by !**/*.png
  • web/public/agent-art/hn-monitor/card-sm.png is excluded by !**/*.png
  • web/public/agent-art/hn-monitor/card.png is excluded by !**/*.png
  • web/public/agent-art/linear/banner.png is excluded by !**/*.png
  • web/public/agent-art/linear/card-sm.png is excluded by !**/*.png
  • web/public/agent-art/linear/card.png is excluded by !**/*.png
  • web/public/agent-art/repo-hygiene/banner.png is excluded by !**/*.png
  • web/public/agent-art/repo-hygiene/card-sm.png is excluded by !**/*.png
  • web/public/agent-art/repo-hygiene/card.png is excluded by !**/*.png
  • web/public/agent-art/review/banner.png is excluded by !**/*.png
  • web/public/agent-art/review/card-sm.png is excluded by !**/*.png
  • web/public/agent-art/review/card.png is excluded by !**/*.png
  • web/public/agent-art/spotify-releases/banner.png is excluded by !**/*.png
  • web/public/agent-art/spotify-releases/card-sm.png is excluded by !**/*.png
  • web/public/agent-art/spotify-releases/card.png is excluded by !**/*.png
  • web/public/agent-art/vendor-monitor/banner.png is excluded by !**/*.png
  • web/public/agent-art/vendor-monitor/card-sm.png is excluded by !**/*.png
  • web/public/agent-art/vendor-monitor/card.png is excluded by !**/*.png
  • web/public/agent-relay-logo-white.svg is excluded by !**/*.svg
  • web/public/authors/khaliq.jpeg is excluded by !**/*.jpeg
  • web/public/authors/will.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-brand-kit.zip is excluded by !**/*.zip
  • web/public/brand-kit/agent-relay-logo-circle.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-horizontal-transparent.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-horizontal.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-light-circle.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-light-horizontal-transparent.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-light-horizontal.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-light-tight.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-light.png is excluded by !**/*.png
  • web/public/brand-kit/agent-relay-logo-tight.png is excluded by !**/*.png
📒 Files selected for processing (265)
  • .github/workflows/deploy.yml
  • .github/workflows/preview.yml
  • .gitignore
  • .nvmrc
  • README.md
  • package.json
  • web/app/DurableDeliveryTimeline.tsx
  • web/app/PostHogPageView.tsx
  • web/app/RealtimeEventFeed.tsx
  • web/app/SearchPreviewAnimation.tsx
  • web/app/agents.md/route.ts
  • web/app/agents/AgentsGallery.tsx
  • web/app/agents/[slug]/AgentDetail.tsx
  • web/app/agents/[slug]/page.tsx
  • web/app/agents/agents.module.css
  • web/app/agents/page.tsx
  • web/app/agents/use-cases/UseCasesContent.tsx
  • web/app/agents/use-cases/page.tsx
  • web/app/auth/RelayauthAnimation.tsx
  • web/app/auth/RelayauthContent.tsx
  • web/app/auth/page.tsx
  • web/app/auth/relayauth.module.css
  • web/app/blog/[slug]/og.png/route.tsx
  • web/app/blog/[slug]/page.tsx
  • web/app/blog/page.tsx
  • web/app/brand/BrandShowcase.tsx
  • web/app/brand/brand.module.css
  • web/app/brand/deploy-button/DeployButtonShowcase.tsx
  • web/app/brand/deploy-button/page.tsx
  • web/app/brand/deploy-button/styles.module.css
  • web/app/brand/page.tsx
  • web/app/brand/theme/ThemeShowcase.tsx
  • web/app/brand/theme/page.tsx
  • web/app/docs/7.1.1/[slug]/page.tsx
  • web/app/docs/7.1.1/page.tsx
  • web/app/docs/8.0.0/[slug]/page.tsx
  • web/app/docs/8.0.0/page.tsx
  • web/app/docs/[slug]/og.png/route.tsx
  • web/app/docs/[slug]/page.tsx
  • web/app/docs/layout.tsx
  • web/app/docs/markdown.md/route.ts
  • web/app/docs/markdown/[...slug]/route.ts
  • web/app/docs/markdown/route.ts
  • web/app/docs/page.tsx
  • web/app/docs/pre-v8/[slug]/page.tsx
  • web/app/docs/pre-v8/page.tsx
  • web/app/feed.xml/route.ts
  • web/app/file/RelayfileAnimation.tsx
  • web/app/file/RelayfileContent.tsx
  • web/app/file/page.tsx
  • web/app/file/relayfile.module.css
  • web/app/globals.css
  • web/app/landing.module.css
  • web/app/layout.tsx
  • web/app/legal.module.css
  • web/app/llm.txt/route.ts
  • web/app/llms-full.txt/route.ts
  • web/app/llms.txt/route.ts
  • web/app/message/RelaycastContent.tsx
  • web/app/message/page.tsx
  • web/app/message/relaycast.module.css
  • web/app/og-default.png/route.tsx
  • web/app/og-home.png/route.tsx
  • web/app/openclaw/landing.module.css
  • web/app/openclaw/openclaw.module.css
  • web/app/openclaw/page.tsx
  • web/app/openclaw/skill/invite/[token]/page.tsx
  • web/app/openclaw/skill/page.tsx
  • web/app/page.tsx
  • web/app/pear/download/route.ts
  • web/app/pear/og.png/route.tsx
  • web/app/pear/page.tsx
  • web/app/pear/pear.module.css
  • web/app/primitives/PrimitivesContent.tsx
  • web/app/primitives/page.tsx
  • web/app/primitives/primitives.module.css
  • web/app/privacy/page.tsx
  • web/app/process/page.tsx
  • web/app/process/process.module.css
  • web/app/relayauth/page.tsx
  • web/app/relaycast/page.tsx
  • web/app/relayfile/page.tsx
  • web/app/robots.ts
  • web/app/schedule/ScheduleContent.tsx
  • web/app/schedule/page.tsx
  • web/app/schedule/relaycron.module.css
  • web/app/sitemap.ts
  • web/app/skill.md/route.ts
  • web/app/skill/page.tsx
  • web/app/telemetry/page.tsx
  • web/app/telemetry/telemetry.module.css
  • web/app/terms/page.tsx
  • web/components/AgentToolLogos.tsx
  • web/components/ChannelMessagesPreview.tsx
  • web/components/CopyInstructionsButton.tsx
  • web/components/FadeIn.tsx
  • web/components/GitHubStars.tsx
  • web/components/InstallCommand.tsx
  • web/components/MessageRelayAnimation.tsx
  • web/components/NodeRelayAnimation.tsx
  • web/components/OpenClawLandingPage.tsx
  • web/components/RelayAnimation.tsx
  • web/components/SdkCodeExample.tsx
  • web/components/SiteFooter.tsx
  • web/components/SiteNav.tsx
  • web/components/SkillPage.tsx
  • web/components/WaitlistForm.tsx
  • web/components/agents/AgentArt.tsx
  • web/components/agents/BuildYourOwn.tsx
  • web/components/agents/ForkAgentButton.tsx
  • web/components/blog/BlogTableOfContents.tsx
  • web/components/blog/blog.module.css
  • web/components/docs/BannerLink.tsx
  • web/components/docs/Card.tsx
  • web/components/docs/CardGroup.tsx
  • web/components/docs/CodeGroup.tsx
  • web/components/docs/CopyCodeButton.tsx
  • web/components/docs/DocsLanguageContext.tsx
  • web/components/docs/DocsNav.tsx
  • web/components/docs/DocsPageActions.tsx
  • web/components/docs/DocsSearch.tsx
  • web/components/docs/DocsVersionSelect.tsx
  • web/components/docs/HighlightedCode.tsx
  • web/components/docs/LegacySpawnOptionsTable.tsx
  • web/components/docs/Note.tsx
  • web/components/docs/TableOfContents.tsx
  • web/components/docs/Warning.tsx
  • web/components/docs/docs-search.module.css
  • web/components/docs/docs.module.css
  • web/components/github-stars.module.css
  • web/components/home/A2AFeature.tsx
  • web/components/home/AgentToolsFeature.tsx
  • web/components/home/ContextCapabilities.tsx
  • web/components/home/DeliveryFeature.tsx
  • web/components/home/Deploy.tsx
  • web/components/home/Hero.tsx
  • web/components/home/HeroGraph.tsx
  • web/components/home/HowItWorks.tsx
  • web/components/home/MessagingFeature.tsx
  • web/components/home/QuickStart.tsx
  • web/components/home/Waitlist.tsx
  • web/components/home/WorksWithEveryAgent.tsx
  • web/components/home/icons.tsx
  • web/components/home/index.ts
  • web/components/node-relay.module.css
  • web/components/sdk-code.module.css
  • web/components/site-footer.module.css
  • web/components/site-nav.module.css
  • web/components/ui/badge.tsx
  • web/components/ui/button.tsx
  • web/components/ui/card.tsx
  • web/components/ui/input.tsx
  • web/components/waitlist.module.css
  • web/content/agent-relay/SKILL.md
  • web/content/blog/go-to-bed-wake-up-to-a-finished-product.mdx
  • web/content/blog/humans-and-agents-all-want-the-same-thing-and-its-disgusting.mdx
  • web/content/blog/just-give-the-agent-files.mdx
  • web/content/blog/let-them-cook-multi-agent-orchestration.mdx
  • web/content/blog/six-workflow-patterns-agent-relay.mdx
  • web/content/blog/the-agent-centered-world-is-almost-here.mdx
  • web/content/docs/7.1.1/agent-relay-mcp.mdx
  • web/content/docs/7.1.1/authentication.mdx
  • web/content/docs/7.1.1/channels.mdx
  • web/content/docs/7.1.1/cli-agent-management.mdx
  • web/content/docs/7.1.1/cli-broker-lifecycle.mdx
  • web/content/docs/7.1.1/cli-cloud-commands.mdx
  • web/content/docs/7.1.1/cli-messaging.mdx
  • web/content/docs/7.1.1/cli-on-the-relay.mdx
  • web/content/docs/7.1.1/cli-overview.mdx
  • web/content/docs/7.1.1/cloud.mdx
  • web/content/docs/7.1.1/communicate-agno.mdx
  • web/content/docs/7.1.1/communicate-ai-sdk.mdx
  • web/content/docs/7.1.1/communicate-claude-sdk.mdx
  • web/content/docs/7.1.1/communicate-crewai.mdx
  • web/content/docs/7.1.1/communicate-google-adk.mdx
  • web/content/docs/7.1.1/communicate-openai-agents.mdx
  • web/content/docs/7.1.1/communicate-pi.mdx
  • web/content/docs/7.1.1/communicate-swarms.mdx
  • web/content/docs/7.1.1/communicate.mdx
  • web/content/docs/7.1.1/dms.mdx
  • web/content/docs/7.1.1/doctor-orchestration-repros.mdx
  • web/content/docs/7.1.1/emoji-reactions.mdx
  • web/content/docs/7.1.1/event-handlers.mdx
  • web/content/docs/7.1.1/file-sharing.mdx
  • web/content/docs/7.1.1/harness-runtime-config.mdx
  • web/content/docs/7.1.1/harnesses.mdx
  • web/content/docs/7.1.1/introduction.mdx
  • web/content/docs/7.1.1/local-mode.mdx
  • web/content/docs/7.1.1/observer.mdx
  • web/content/docs/7.1.1/permissions.mdx
  • web/content/docs/7.1.1/plugin-claude-code.mdx
  • web/content/docs/7.1.1/proactive-agents.mdx
  • web/content/docs/7.1.1/python-sdk.mdx
  • web/content/docs/7.1.1/quickstart.mdx
  • web/content/docs/7.1.1/react-sdk.mdx
  • web/content/docs/7.1.1/reference-broker-api.mdx
  • web/content/docs/7.1.1/reference-cli.mdx
  • web/content/docs/7.1.1/reference-openclaw.mdx
  • web/content/docs/7.1.1/relay-dashboard.mdx
  • web/content/docs/7.1.1/scheduling.mdx
  • web/content/docs/7.1.1/sending-messages.mdx
  • web/content/docs/7.1.1/spawning-an-agent.mdx
  • web/content/docs/7.1.1/swift-sdk.mdx
  • web/content/docs/7.1.1/threads.mdx
  • web/content/docs/7.1.1/typescript-examples.mdx
  • web/content/docs/7.1.1/typescript-sdk.mdx
  • web/content/docs/7.1.1/workforce.mdx
  • web/content/docs/actions.mdx
  • web/content/docs/agent-relay-mcp.mdx
  • web/content/docs/channels.mdx
  • web/content/docs/cli-agent-management.mdx
  • web/content/docs/cli-broker-lifecycle.mdx
  • web/content/docs/cli-messaging.mdx
  • web/content/docs/cli-overview.mdx
  • web/content/docs/delivery.mdx
  • web/content/docs/dms.mdx
  • web/content/docs/emoji-reactions.mdx
  • web/content/docs/event-handlers.mdx
  • web/content/docs/events.mdx
  • web/content/docs/fleets.mdx
  • web/content/docs/harness-driver.mdx
  • web/content/docs/harnesses.mdx
  • web/content/docs/introduction.mdx
  • web/content/docs/messaging.mdx
  • web/content/docs/migration.mdx
  • web/content/docs/orchestrating-with-actions.mdx
  • web/content/docs/quickstart.mdx
  • web/content/docs/reference-cli.mdx
  • web/content/docs/sending-messages.mdx
  • web/content/docs/session-capabilities.mdx
  • web/content/docs/threads.mdx
  • web/content/docs/typescript-sdk.mdx
  • web/content/docs/webhooks.mdx
  • web/content/docs/workspaces.mdx
  • web/content/openclaw/SKILL.md
  • web/lib/agents.ts
  • web/lib/blog-authors.ts
  • web/lib/blog.ts
  • web/lib/code-fence-meta.ts
  • web/lib/content-store.ts
  • web/lib/docs-markdown.ts
  • web/lib/docs-nav.ts
  • web/lib/docs-versions.ts
  • web/lib/docs.ts
  • web/lib/og-meta.ts
  • web/lib/og/template.tsx
  • web/lib/site-analytics.ts
  • web/lib/site.ts
  • web/lib/skill-markdown.ts
  • web/lib/syntax.ts
  • web/lib/test/code-fence-meta.test.ts
  • web/lib/test/docs-markdown-components.test.ts
  • web/lib/test/docs-versions.test.ts
  • web/lib/test/highlighted-code.test.tsx
  • web/lib/test/llms-txt.test.ts
  • web/lib/test/site-analytics.test.ts
  • web/lib/test/skill-markdown.test.ts
  • web/lib/test/skill-md-route.test.ts
  • web/lib/utils.ts
  • web/middleware.ts
  • web/next-env.d.ts
  • web/next.config.mjs
  • web/open-next.config.ts
  • web/package.json
  • web/postcss.config.mjs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch extract-web

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds several new pages, components, and scripts to the Agent Relay website, including brand showcases, blog feeds, and interactive product pages for auth, file, messaging, and scheduling primitives. The review feedback highlights performance concerns in the canvas animations due to layout thrashing from calling getBoundingClientRect() on every frame, suggesting caching the container dimensions instead. Additionally, it points out that heading ID generation on documentation pages can break when headings contain complex React nodes, recommending a recursive text extraction helper along with the necessary React imports.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1004 to +1020
const resize = () => {
const rect = container.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};

resize();
window.addEventListener('resize', resize);

const draw = () => {
const rect = container.getBoundingClientRect();
const w = rect.width;
const h = rect.height;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Calling container.getBoundingClientRect() inside the draw function on every single frame (60fps) forces synchronous layout reflows (layout thrashing), which degrades rendering performance and increases CPU usage. Instead, cache the width (w) and height (h) in local variables within the useEffect scope, updating them only during the resize event.

Suggested change
const resize = () => {
const rect = container.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
resize();
window.addEventListener('resize', resize);
const draw = () => {
const rect = container.getBoundingClientRect();
const w = rect.width;
const h = rect.height;
let w = 0;
let h = 0;
const resize = () => {
const rect = container.getBoundingClientRect();
w = rect.width;
h = rect.height;
const dpr = window.devicePixelRatio || 1;
canvas.width = w * dpr;
canvas.height = h * dpr;
canvas.style.width = `${w}px`;
canvas.style.height = `${h}px`;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
resize();
window.addEventListener('resize', resize);
const draw = () => {

Comment on lines +807 to +823
const interval = setInterval(() => {
const tick = tickRef.current++;
const ns = nodesRef.current;
const ms = messagesRef.current;
handleScriptEvents(tick, ns, ms);
maybeQueueRandomRelay(tick, ns, ms, connectionsRef.current);
cycleNodeStates(ns, tick);
setRenderNodes([...ns]);
}, 250);

return () => clearInterval(interval);
}, []);

useEffect(() => {
const canvas = canvasRef.current;
const container = containerRef.current;
if (!canvas || !container) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Calling container.getBoundingClientRect() inside the draw function on every single frame (60fps) forces synchronous layout reflows (layout thrashing), which degrades rendering performance and increases CPU usage. Instead, cache the width (w) and height (h) in local variables within the useEffect scope, updating them only during the resize event.

    let w = 0;
    let h = 0;

    const resize = () => {
      const rect = container.getBoundingClientRect();
      w = rect.width;
      h = rect.height;
      const dpr = window.devicePixelRatio || 1;
      canvas.width = w * dpr;
      canvas.height = h * dpr;
      canvas.style.width = `${w}px`;
      canvas.style.height = `${h}px`;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };

    resize();
    window.addEventListener('resize', resize);

    const draw = () => {

Comment on lines +35 to +46
function HeadingWithId(level: 2 | 3) {
return function Heading({ children, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
const text = typeof children === 'string' ? children : String(children);
const id = slugify(text);
const Tag = `h${level}` as const;
return (
<Tag id={id} {...props}>
{children}
</Tag>
);
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Heading children can be complex React nodes (e.g., containing inline code tags like ## Use \Relaycast`). Using String(children)directly on these nodes results in"[object Object]"or comma-separated arrays, which breaks heading ID generation and table of contents anchor links. Implement a recursiveextractText` helper to safely extract plain text from the children.

function extractText(node: ReactNode): string {
  if (typeof node === 'string' || typeof node === 'number') {
    return String(node);
  }
  if (Array.isArray(node)) {
    return node.map(extractText).join('');
  }
  if (isValidElement(node)) {
    return extractText(node.props.children);
  }
  return '';
}

function HeadingWithId(level: 2 | 3) {
  return function Heading({ children, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
    const id = slugify(extractText(children));
    const Tag = `h${level}` as const;
    return (
      <Tag id={id} {...props}>
        {children}
      </Tag>
    );
  };
}

Comment on lines +32 to +43
function HeadingWithId(level: 2 | 3) {
return function Heading({ children, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
const text = typeof children === 'string' ? children : String(children);
const id = slugify(text);
const Tag = `h${level}` as const;
return (
<Tag id={id} {...props}>
{children}
</Tag>
);
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Heading children can be complex React nodes (e.g., containing inline code tags like ## Use \Relaycast`). Using String(children)directly on these nodes results in"[object Object]"or comma-separated arrays, which breaks heading ID generation and table of contents anchor links. Implement a recursiveextractText` helper to safely extract plain text from the children.

function extractText(node: ReactNode): string {
  if (typeof node === 'string' || typeof node === 'number') {
    return String(node);
  }
  if (Array.isArray(node)) {
    return node.map(extractText).join('');
  }
  if (isValidElement(node)) {
    return extractText(node.props.children);
  }
  return '';
}

function HeadingWithId(level: 2 | 3) {
  return function Heading({ children, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
    const id = slugify(extractText(children));
    const Tag = `h${level}` as const;
    return (
      <Tag id={id} {...props}>
        {children}
      </Tag>
    );
  };
}

import type React from 'react';
import { notFound, redirect } from 'next/navigation';
import { evaluate } from '@mdx-js/mdx';
import { Fragment } from 'react';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Import isValidElement and ReactNode from 'react' to support robust plain-text extraction from heading children.

Suggested change
import { Fragment } from 'react';
import { Fragment, isValidElement, type ReactNode } from 'react';

import type React from 'react';
import { notFound } from 'next/navigation';
import { evaluate } from '@mdx-js/mdx';
import { Fragment } from 'react';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Import isValidElement and ReactNode from 'react' to support robust plain-text extraction from heading children.

Suggested change
import { Fragment } from 'react';
import { Fragment, isValidElement, type ReactNode } from 'react';

Replace the sst.aws.Nextjs (Lambda/CloudFront) deploy with
@opennextjs/cloudflare on a Cloudflare Worker, deployed by Wrangler:

- open-next.config.ts with the static-assets incremental cache (serves
  prerendered/SSG pages from the ASSETS binding; no R2/KV, no AWS)
- wrangler.jsonc: custom domain origin-web.agentrelay.com, nodejs_compat,
  version-based preview URLs
- CI: deploy.yml (wrangler deploy) + preview.yml (wrangler versions upload);
  drop the AWS OIDC/ACM/stale-stage machinery
- proxy.ts -> middleware.ts: Next 16 runs proxy on Node.js; edge middleware
  bundles for the Workers runtime

Make the site filesystem-free for the Workers runtime (unenv stubs node:fs):

- lib/content-store.ts bundles docs/blog MDX as raw strings at build via
  require.context (webpack), with an fs fallback for vitest/node
- refactor lib/docs.ts, lib/docs-markdown.ts, lib/blog.ts off resolveContentDir
- llms.txt / llms-full.txt / sitemap / docs markdown-mirror: force-static ->
  ISR revalidate so OpenNext serves them from the incremental cache; the
  .md mirror prerenders every doc

Verified locally in workerd (wrangler dev): home, docs (with syntax
highlighting), llms.txt/-full, sitemap, .md mirror, blog, OG images, robots
all 200; build + 21 vitest tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After the repo split, /agents.md was reading the local README.md (now the
agentrelay.com repo's own README). Fetch the relay README from GitHub (public,
raw) at build/revalidation instead — also removes the last node:fs read, which
the Cloudflare Workers runtime stubs out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
willwashburn and others added 3 commits June 22, 2026 09:37
Prod and PR previews deploy to the same Cloudflare account with the same token,
so per-environment secret scoping is unnecessary. Use repository-level
secrets/vars; main auto-deploys, no approval gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Leftover trajectory records from the relay monorepo history; this repo doesn't
use trail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
wrangler versions upload succeeds and uploads the worker version, but a
per-version *.workers.dev preview URL is only minted when the account has a
workers.dev subdomain enabled. Treat a missing URL as non-fatal and comment
accordingly instead of exiting 1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Preview built — a Cloudflare Worker version was uploaded for this PR, but no per-version preview URL was returned. Enable a workers.dev subdomain on the Cloudflare account to get preview URLs.

@willwashburn
willwashburn merged commit c7b1ec2 into main Jun 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants