Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/blog/ai-agent-memory-vs-rag/og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ const UseCasesPage = lazy(() => import('./pages/UseCasesPage').then(m => ({ defa
const MultiAgentMemoryPage = lazy(() => import('./pages/MultiAgentMemoryPage').then(m => ({ default: m.MultiAgentMemoryPage })))
const PersonalAssistantMemoryPage = lazy(() => import('./pages/PersonalAssistantMemoryPage').then(m => ({ default: m.PersonalAssistantMemoryPage })))
const MultiAgentSharedContextPage = lazy(() => import('./pages/MultiAgentSharedContextPage').then(m => ({ default: m.MultiAgentSharedContextPage })))
const GroundedShopAssistantPage = lazy(() => import('./pages/GroundedShopAssistantPage').then(m => ({ default: m.GroundedShopAssistantPage })))
const StatewaveVsMem0Page = lazy(() => import('./pages/StatewaveVsMem0Page').then(m => ({ default: m.StatewaveVsMem0Page })))
const ConnectorsPage = lazy(() => import('./pages/ConnectorsPage').then(m => ({ default: m.ConnectorsPage })))
const DevelopersPage = lazy(() => import('./pages/DevelopersPage').then(m => ({ default: m.DevelopersPage })))
const CookiesPage = lazy(() => import('./pages/CookiesPage').then(m => ({ default: m.CookiesPage })))
const LaunchPage = lazy(() => import('./pages/LaunchPage').then(m => ({ default: m.LaunchPage })))
const BenchmarksPage = lazy(() => import('./pages/BenchmarksPage').then(m => ({ default: m.BenchmarksPage })))
const PressPage = lazy(() => import('./pages/PressPage').then(m => ({ default: m.PressPage })))
const WhitepaperPage = lazy(() => import('./pages/WhitepaperPage').then(m => ({ default: m.WhitepaperPage })))
const DemoPage = lazy(() => import('./pages/DemoPage').then(m => ({ default: m.DemoPage })))
Expand All @@ -36,9 +39,12 @@ export default function App() {
<Route path="/use-cases/multi-agent-memory" element={<MultiAgentMemoryPage />} />
<Route path="/use-cases/personal-assistant-memory" element={<PersonalAssistantMemoryPage />} />
<Route path="/use-cases/multi-agent-shared-context" element={<MultiAgentSharedContextPage />} />
<Route path="/use-cases/grounded-shop-assistant" element={<GroundedShopAssistantPage />} />
<Route path="/vs/mem0" element={<StatewaveVsMem0Page />} />
<Route path="/connectors" element={<ConnectorsPage />} />
<Route path="/developers" element={<DevelopersPage />} />
<Route path="/launch" element={<LaunchPage />} />
<Route path="/benchmarks" element={<BenchmarksPage />} />
<Route path="/press" element={<PressPage />} />
<Route path="/whitepaper" element={<WhitepaperPage />} />
<Route path="/demo" element={<DemoPage />} />
Expand Down
27 changes: 22 additions & 5 deletions src/components/CodeCopyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,29 @@ import React from 'react'
/**
* Floating "Copy" button for code snippet panels. Copies the given code on
* click and flashes a "Copied" tag for ~1.5s. Falls back gracefully on
* insecure contexts where clipboard access is denied the button still
* insecure contexts where clipboard access is denied; the button still
* flashes feedback so the user knows something happened.
*
* `iconOnly` drops the visible "Copy"/"Copied" word for dense panel headers
* where the label is redundant next to the icon. The accessible name still
* comes through `aria-label`, and the live region still announces the state
* change. Only the visual text is suppressed.
*/
export function CodeCopyButton({ code, label }: { code: string; label: string }) {
export function CodeCopyButton({
code,
label,
iconOnly = false,
}: {
code: string
label: string
iconOnly?: boolean
}) {
const [copied, setCopied] = React.useState(false)
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(code)
} catch {
// ignored clipboard can fail on insecure contexts or denied permissions
// ignored: clipboard can fail on insecure contexts or denied permissions
}
setCopied(true)
window.setTimeout(() => setCopied(false), 1500)
Expand All @@ -23,7 +36,9 @@ export function CodeCopyButton({ code, label }: { code: string; label: string })
onClick={handleCopy}
aria-label={label}
title={copied ? 'Copied' : label}
className="shrink-0 inline-flex items-center gap-1.5 rounded-md border border-transparent px-2 py-0.5 text-[10.5px] font-medium text-theme-muted/80 hover:text-accent hover:border-accent/30 focus-visible:text-accent focus:outline-none transition-colors"
className={`shrink-0 inline-flex items-center rounded-md border border-transparent text-[10.5px] font-medium text-theme-muted/80 hover:text-accent hover:border-accent/30 focus-visible:text-accent focus:outline-none transition-colors ${
iconOnly ? 'p-1.5 hover:bg-accent/10' : 'gap-1.5 px-2 py-0.5'
}`}
>
{copied ? (
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
Expand All @@ -35,7 +50,9 @@ export function CodeCopyButton({ code, label }: { code: string; label: string })
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M5 15V6a2 2 0 0 1 2-2h9" />
</svg>
)}
<span aria-live="polite">{copied ? 'Copied' : 'Copy'}</span>
<span aria-live="polite" className={iconOnly ? 'sr-only' : undefined}>
{copied ? 'Copied' : 'Copy'}
</span>
</button>
)
}
2 changes: 2 additions & 0 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function Footer() {
<li><Link to="/product" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">How it works</Link></li>
<li><Link to="/why" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">Why Statewave</Link></li>
<li><Link to="/use-cases" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">Use Cases</Link></li>
<li><Link to="/vs/mem0" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">vs Mem0</Link></li>
<li><Link to="/connectors" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">Connectors</Link></li>
<li>
<button
Expand All @@ -49,6 +50,7 @@ export function Footer() {
<li><a href="https://github.com/smaramwbc/statewave-py" target="_blank" rel="noopener noreferrer" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">Python SDK</a></li>
<li><a href="https://github.com/smaramwbc/statewave-ts" target="_blank" rel="noopener noreferrer" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">TypeScript SDK</a></li>
<li><Link to="/whitepaper" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">White paper</Link></li>
<li><Link to="/benchmarks" className="text-sm text-theme-muted hover:text-theme-primary transition-colors">Benchmarks</Link></li>
</ul>
</div>

Expand Down
1 change: 1 addition & 0 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface NavLink {
const links: NavLink[] = [
{ to: '/product', label: 'How it works' },
{ to: '/why', label: 'Why Statewave' },
{ to: '/benchmarks', label: 'Benchmarks' },
{ to: '/use-cases', label: 'Use Cases' },
{ to: '/connectors', label: 'Connectors' },
{ to: '/developers', label: 'Developers' },
Expand Down
2 changes: 2 additions & 0 deletions src/content/blog/agent-memory-provenance-audit-trails.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ slug: agent-memory-provenance-audit-trails
date: "2026-05-25T09:00:00+02:00"
description: When an AI agent answers from memory, you need to know where it learned the answer. This post explains the provenance model in Statewave — what we store, what it costs, and what it enables for compliance, debugging, and trust.
author: Statewave team
image: /images/blog/agent-memory-provenance-audit-trails/og.png
headerImage: /images/blog/agent-memory-provenance-audit-trails/header.png
tags:
- provenance
- audit
Expand Down
2 changes: 2 additions & 0 deletions src/content/blog/ai-agent-memory-vs-rag.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ slug: ai-agent-memory-vs-rag
date: "2026-05-25T09:00:00+02:00"
description: Retrieval-augmented generation and an agent memory runtime overlap at the embedding store, but they answer different questions. Here's where the two diverge and which to reach for.
author: Statewave team
image: /images/blog/ai-agent-memory-vs-rag/og.png
headerImage: /images/blog/ai-agent-memory-vs-rag/header.png
tags:
- memory
- rag
Expand Down
2 changes: 2 additions & 0 deletions src/content/blog/episodic-vs-semantic-memory.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ slug: episodic-vs-semantic-memory
date: "2026-08-07T09:00:00+02:00"
description: Episodic memory stores what happened; semantic memory stores what's true. Here's how AI agents use each, when to convert one to the other, and how to store it.
author: Statewave team
image: /images/blog/episodic-vs-semantic-memory/og.png
headerImage: /images/blog/episodic-vs-semantic-memory/header.png
tags:
- memory
- episodic-memory
Expand Down
2 changes: 2 additions & 0 deletions src/content/blog/persistent-memory-for-ai-support-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ slug: persistent-memory-for-ai-support-agents
date: "2026-05-25T09:00:00+02:00"
description: A practical walkthrough of giving a support agent durable memory of customers across sessions — what to record, how to compile it, how to retrieve it, and what to expect from the support workflow benchmark.
author: Statewave team
image: /images/blog/persistent-memory-for-ai-support-agents/og.png
headerImage: /images/blog/persistent-memory-for-ai-support-agents/header.png
tags:
- support
- tutorial
Expand Down
2 changes: 2 additions & 0 deletions src/content/blog/self-hosted-memory-postgres-pgvector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ slug: self-hosted-memory-postgres-pgvector
date: "2026-05-25T09:00:00+02:00"
description: Why Statewave is Postgres-only by design, what pgvector buys you over a dedicated vector database, and what the deployment shape looks like in production.
author: Statewave team
image: /images/blog/self-hosted-memory-postgres-pgvector/og.png
headerImage: /images/blog/self-hosted-memory-postgres-pgvector/header.png
tags:
- postgres
- pgvector
Expand Down
52 changes: 45 additions & 7 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
*
* Border opacity stays at 0.06: against #fafbfc the contrast is gentler
* than against #fff but still defines edges cleanly. Text-primary stays at
* slate-900 — the issue was the bg, not the foreground.
* slate-900. The issue was the bg, not the foreground.
*/
[data-theme="light"] {
--theme-surface-0: #FAFBFD;
Expand All @@ -97,7 +97,7 @@
--theme-selection: rgba(99, 102, 241, 0.15);
--theme-hero-particle: #4f46e5;
--theme-hero-opacity: 0.35;
/* Slightly stronger in light mode the near-white surface needs the
/* Slightly stronger in light mode: the near-white surface needs the
texture more than the dark theme does. */
--theme-hero-dot: rgba(2, 6, 23, 0.06);
--theme-hero-overlay-from: rgba(250, 251, 252, 0.5);
Expand Down Expand Up @@ -146,6 +146,16 @@
--viz-green: var(--color-success);
--viz-amber: #F2D16B;

/* Benchmark chart series (/benchmarks scoreboard, /vs/mem0). Categorical:
* each hue identifies one system, never its rank. Dark-mode steps are
* chosen against the --theme-surface-1 card (#0C1833), not flipped from
* the light set, and validated for the OKLCH lightness band, chroma
* floor, adjacent-pair CVD separation (worst pair ΔE 14.3 deutan, target
* >= 8), and 3:1 contrast. Re-run the validator if you change these. */
--series-statewave: #5E8CFF;
--series-mem0-cloud: #CC7A0D;
--series-mem0-oss: #12A594;

--viz-code-text: #C7D6F2;
--viz-code-muted: #7385A8;
--viz-code-keyword: #8FA5FF;
Expand Down Expand Up @@ -183,6 +193,12 @@
--viz-green: #059669;
--viz-amber: #B45309;

/* Benchmark chart series, light-mode steps, validated against the
* --theme-surface-1 card (#F2F6FC). See the dark block for the contract. */
--series-statewave: #4A72E8;
--series-mem0-cloud: #B86A08;
--series-mem0-oss: #0D9488;

--viz-code-text: #334155;
--viz-code-muted: #64748B;
--viz-code-keyword: #4F46E5;
Expand All @@ -198,7 +214,7 @@ html {
scroll-behavior: smooth;
/* Anchor links (e.g. /product#privacy) and programmatic scrolls land
* below the 60px fixed navbar instead of being clipped by it. No snap
* here — proximity-based scroll-snap fought trackpad momentum and made
* here. Proximity-based scroll-snap fought trackpad momentum and made
* the page feel sticky. */
scroll-padding-top: 80px;
/* Prevents iOS Safari bouncing the body (and its rubber-band background)
Expand Down Expand Up @@ -290,7 +306,7 @@ html[data-scroll-lock="true"] body {
* element with a `transition` on a property tied to those variables (body
* bg, card backgrounds, borders) starts an interpolation. The custom
* scrollbar thumb is alpha-tinted, so as the body background transitions
* underneath it, the thumb appears to change color through wrong shades
* underneath it, the thumb appears to change color through wrong shades,
* reading as "flicker."
*
* Standard fix: ThemeProvider toggles `theme-switching` on <html> for one
Expand Down Expand Up @@ -345,7 +361,7 @@ textarea:disabled {
cursor: not-allowed;
}

/* Hero hint chip attention bounce + subtle scale pulse */
/* Hero hint chip: attention bounce + subtle scale pulse */
@keyframes heroHintBounce {

0%,
Expand Down Expand Up @@ -396,7 +412,7 @@ textarea:disabled {
z-index: 1;
}

/* For elements that already have a corner radius keep theirs. */
/* For elements that already have a corner radius, keep theirs. */
.tour-pulse--inherit-radius {
border-radius: inherit;
}
Expand Down Expand Up @@ -444,7 +460,7 @@ textarea:disabled {
*
* Theme-switch flicker fix: the scrollbar properties live on `html` (they're
* inherited, so a global `*` selector is wasteful and causes unnecessary
* style recalc on every theme swap). The thumb has NO transition either
* style recalc on every theme swap). The thumb has NO transition either,
* WebKit pseudo-element transitions are unreliable and produce a visible
* flash when interpolating between the dark and light alpha values during a
* theme change. Snappier is better here; the body's bg fade carries the
Expand Down Expand Up @@ -633,6 +649,17 @@ h6 {
opacity: .45;
}

/* For sections that bring their own color — the /benchmarks scoreboard and
/vs/mem0 comparison charts run saturated categorical series hues — so the
ambient wash stays behind the data instead of washing over it. */
.section-glow--soft {
opacity: .4;
}

[data-theme="light"] .section-glow--soft {
opacity: .2;
}

.install-cmd-field {
box-shadow: 0 12px 35px rgba(0, 0, 0, .14);
}
Expand All @@ -641,6 +668,17 @@ h6 {
box-shadow: 0 6px 18px rgba(30, 41, 59, .06);
}

/* For sections that bring their own color — the /benchmarks scoreboard runs
three saturated categorical hues — so the ambient wash stays behind the data
instead of washing over it. */
.section-glow--soft {
opacity: .4;
}

[data-theme="light"] .section-glow--soft {
opacity: .2;
}

/* ==========================================================================
Home page
========================================================================== */
Expand Down
6 changes: 6 additions & 0 deletions src/lib/blog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ export interface BlogPostFrontmatter {
* "/blog/my-post/cover.png"). Optional — falls back to the site-wide
* default OG image (DEFAULT_OG_IMAGE in lib/seo-meta.ts) when unset. */
image?: string
/** Site-relative path to this post's banner image, shown inline at the
* top of the post itself (below the title card, above the article
* body). Separate from `image` (the OG/social-share card) because the
* two can reasonably differ — this one renders on the page, `image`
* never does. Optional — posts without one just skip the banner. */
headerImage?: string
}

export interface BlogPost {
Expand Down
36 changes: 36 additions & 0 deletions src/lib/seo-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ export type RouteKey =
| '/'
| '/product'
| '/why'
| '/benchmarks'
| '/use-cases'
| '/use-cases/multi-agent-memory'
| '/use-cases/personal-assistant-memory'
| '/use-cases/multi-agent-shared-context'
| '/use-cases/grounded-shop-assistant'
| '/vs/mem0'
| '/connectors'
| '/developers'
| '/about'
Expand All @@ -58,10 +61,13 @@ export const PUBLIC_ROUTES: readonly RouteKey[] = [
'/',
'/product',
'/why',
'/benchmarks',
'/use-cases',
'/use-cases/multi-agent-memory',
'/use-cases/personal-assistant-memory',
'/use-cases/multi-agent-shared-context',
'/use-cases/grounded-shop-assistant',
'/vs/mem0',
'/connectors',
'/developers',
'/about',
Expand Down Expand Up @@ -117,6 +123,18 @@ export const PAGE_META: Record<RouteKey, PageMeta> = {
priority: 0.8,
changefreq: 'monthly',
},
'/benchmarks': {
title: 'Statewave Benchmarks: LoCoMo & LongMemEval vs. mem0',
description:
"Statewave beats mem0 OSS on both LoCoMo and LongMemEval, run on mem0's own harness at gpt-4o, and edges the paid mem0 cloud tier while staying free and self-hosted. Apache-2.0, fully reproducible.",
breadcrumbLabel: 'Benchmarks',
// Long-form editorial with a methodology and a scope section, not a
// landing page: 'article' is what tells social scrapers and answer
// engines to treat it as the writeup it is.
ogType: 'article',
priority: 0.9,
changefreq: 'monthly',
},
'/use-cases': {
title: 'Use Cases — Memory for Support Agents, Copilots, and AI Apps',
description:
Expand Down Expand Up @@ -153,6 +171,24 @@ export const PAGE_META: Record<RouteKey, PageMeta> = {
priority: 0.7,
changefreq: 'monthly',
},
'/use-cases/grounded-shop-assistant': {
title: 'Grounded Shop Assistant — Cited Answers with a Closed-Loop Coverage Gap',
description:
'A shopper and ops assistant pair that answers strictly from retrieved evidence, validates every citation against what was actually retrieved, and turns ungrounded questions into coverage-gap episodes the content team resolves.',
breadcrumbLabel: 'Grounded Shop Assistant',
ogType: 'article',
priority: 0.7,
changefreq: 'monthly',
},
'/vs/mem0': {
title: 'Statewave vs. Mem0 — Deterministic Context vs. Ranked Retrieval',
description:
'How Statewave compares to Mem0: deterministic, token-bounded context assembly with policy enforcement and integrity-hashed receipts vs. ranked similarity retrieval — plus LoCoMo and LongMemEval benchmark results measured on Mem0’s own harness.',
breadcrumbLabel: 'vs Mem0',
ogType: 'article',
priority: 0.7,
changefreq: 'monthly',
},
'/connectors': {
title: 'Connectors — Feed GitHub, Docs, Slack, and More into Statewave Memory',
description:
Expand Down
5 changes: 5 additions & 0 deletions src/lib/use-case-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export const USE_CASE_DETAIL_PAGES: readonly UseCaseDetailPage[] = [
label: 'Shared Context',
path: '/use-cases/multi-agent-shared-context',
},
{
slug: 'grounded-shop-assistant',
label: 'Grounded Shop Assistant',
path: '/use-cases/grounded-shop-assistant',
},
] as const

export function useCaseDetailPage(
Expand Down
Loading