Skip to content

Commit 48de4b6

Browse files
authored
Merge pull request #18 from AgentWorkforce/post/google-remy-background-agent
New essay: Google Remy and the background agent bet
2 parents ba5432c + 973d8d7 commit 48de4b6

11 files changed

Lines changed: 420 additions & 11 deletions

File tree

app/posts/[slug]/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
3838
url: `${SITE_URL}/posts/${slug}/`,
3939
type: "article",
4040
publishedTime: post.date,
41+
modifiedTime: post.lastModified ?? post.date,
4142
authors: ["Khaliq Gant"],
4243
},
4344
twitter: {
@@ -125,7 +126,11 @@ export default async function PostPage({
125126

126127
<div className="mt-12 border-t border-rule pt-8">
127128
<p className="text-xs uppercase tracking-[0.22em] text-ink-faint">
128-
Posted {formatDate(post.date)} &middot; AgentWorkforce
129+
Posted {formatDate(post.date)}
130+
{post.lastModified && post.lastModified !== post.date && (
131+
<> &middot; Updated {formatDate(post.lastModified)}</>
132+
)}
133+
{" "}&middot; AgentWorkforce
129134
</p>
130135
<p className="mt-4 font-serif text-[1.05rem] leading-relaxed text-ink-soft">
131136
Issues, PRs, and arguments welcome on{" "}

app/sitemap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
1414

1515
const postEntries: MetadataRoute.Sitemap = posts.map((p) => ({
1616
url: `${SITE_URL}/posts/${p.slug}/`,
17-
lastModified: new Date(p.date),
17+
lastModified: new Date(p.lastModified ?? p.date),
1818
changeFrequency: "monthly",
1919
priority: 0.8,
2020
}));

components/card-illustrations.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const SLUGS: Record<string, React.ComponentType> = {
3131
"posthog-code": PostHogCodeArt,
3232
"notion-ships-the-primitives": NotionPrimitivesArt,
3333
"every-tool-ships-an-agent": EveryToolAgentArt,
34+
"google-remy-background-agent": RemyBackgroundArt,
3435
};
3536

3637
export function CardArt({ slug }: { slug: string }) {
@@ -1066,3 +1067,51 @@ function EveryToolAgentArt() {
10661067
</svg>
10671068
);
10681069
}
1070+
1071+
function RemyBackgroundArt() {
1072+
return (
1073+
<svg
1074+
viewBox="0 0 500 300"
1075+
className="pointer-events-none absolute inset-0 h-full w-full"
1076+
aria-hidden
1077+
>
1078+
<g
1079+
opacity="0.14"
1080+
stroke={C.ink}
1081+
fill="none"
1082+
strokeLinecap="round"
1083+
strokeLinejoin="round"
1084+
>
1085+
<g transform="translate(250, 115)">
1086+
{/* Central agent — always-on circle */}
1087+
<circle r="20" strokeWidth="1.8" />
1088+
<circle r="2.5" fill={C.ink} stroke="none" />
1089+
{/* Orbiting app dots */}
1090+
{[0, 45, 90, 135, 180, 225, 270, 315].map((angle, i) => {
1091+
const rad = (angle * Math.PI) / 180;
1092+
const r = 52;
1093+
const x = Math.cos(rad) * r;
1094+
const y = Math.sin(rad) * r;
1095+
return (
1096+
<g key={i}>
1097+
<line
1098+
x1={Math.cos(rad) * 20}
1099+
y1={Math.sin(rad) * 20}
1100+
x2={x}
1101+
y2={y}
1102+
strokeWidth="0.8"
1103+
strokeDasharray="2 3"
1104+
/>
1105+
<circle cx={x} cy={y} r="4" fill={C.ink} stroke="none" />
1106+
</g>
1107+
);
1108+
})}
1109+
{/* Outer dashed orbit ring */}
1110+
<circle r="52" strokeWidth="0.8" strokeDasharray="4 4" />
1111+
{/* 24/7 label */}
1112+
<text y="2" textAnchor="middle" fontFamily="inherit" fontSize="8" fill={C.ink} strokeWidth="0">24/7</text>
1113+
</g>
1114+
</g>
1115+
</svg>
1116+
);
1117+
}

components/mdx/figures.tsx

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,3 +3014,183 @@ export function PhaseAgentFigure() {
30143014
</svg>
30153015
);
30163016
}
3017+
3018+
/** Remy — connected-app ecosystem radiating from a central agent. */
3019+
export function RemyEcosystemFigure() {
3020+
const apps = [
3021+
{ label: "Gmail", angle: 0 },
3022+
{ label: "Calendar", angle: 45 },
3023+
{ label: "Drive", angle: 90 },
3024+
{ label: "GitHub", angle: 135 },
3025+
{ label: "WhatsApp", angle: 180 },
3026+
{ label: "Spotify", angle: 225 },
3027+
{ label: "Photos", angle: 270 },
3028+
{ label: "Tasks", angle: 315 },
3029+
];
3030+
const cx = 160, cy = 150, innerR = 36, outerR = 110;
3031+
return (
3032+
<svg viewBox="0 0 320 320" className="w-full" role="img" aria-labelledby="remy-eco-title">
3033+
<title id="remy-eco-title">Google Remy connected-app ecosystem: eight services radiating from a central agent</title>
3034+
<defs>
3035+
<radialGradient id="remyecograd" cx="50%" cy="45%" r="55%">
3036+
<stop offset="0%" stopColor={C.sky} stopOpacity="0.85" />
3037+
<stop offset="100%" stopColor={C.lavender} stopOpacity="0.4" />
3038+
</radialGradient>
3039+
</defs>
3040+
<circle cx={cx} cy={cy} r={outerR + 10} fill="url(#remyecograd)" />
3041+
{/* Connecting lines */}
3042+
{apps.map(({ angle }, i) => {
3043+
const rad = (angle * Math.PI) / 180;
3044+
return (
3045+
<line
3046+
key={`line-${i}`}
3047+
x1={cx + Math.cos(rad) * innerR}
3048+
y1={cy + Math.sin(rad) * innerR}
3049+
x2={cx + Math.cos(rad) * (outerR - 20)}
3050+
y2={cy + Math.sin(rad) * (outerR - 20)}
3051+
stroke={C.faint}
3052+
strokeWidth="1"
3053+
strokeDasharray="3 3"
3054+
/>
3055+
);
3056+
})}
3057+
{/* Center agent circle */}
3058+
<circle cx={cx} cy={cy} r={innerR} fill={C.paper} stroke={C.ink} strokeWidth="2" />
3059+
<text x={cx} y={cy - 4} textAnchor="middle" fontFamily="var(--font-display)" fontSize="10" fill={C.ink}>
3060+
Remy
3061+
</text>
3062+
<text x={cx} y={cy + 8} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="7" fill={C.faint}>
3063+
24/7
3064+
</text>
3065+
{/* App nodes */}
3066+
{apps.map(({ label, angle }, i) => {
3067+
const rad = (angle * Math.PI) / 180;
3068+
const x = cx + Math.cos(rad) * (outerR - 14);
3069+
const y = cy + Math.sin(rad) * (outerR - 14);
3070+
const isExternal = ["GitHub", "WhatsApp", "Spotify"].includes(label);
3071+
return (
3072+
<g key={`app-${i}`}>
3073+
<circle cx={x} cy={y} r="16" fill={C.paper} stroke={isExternal ? C.terracotta : C.ink} strokeWidth={isExternal ? "1.8" : "1.2"} />
3074+
<text x={x} y={y + 3} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="7" fill={isExternal ? C.terracotta : C.inkSoft}>
3075+
{label}
3076+
</text>
3077+
</g>
3078+
);
3079+
})}
3080+
{/* Legend */}
3081+
<g transform="translate(160, 290)">
3082+
<circle cx="-50" cy="0" r="4" fill="none" stroke={C.ink} strokeWidth="1.2" />
3083+
<text x="-42" y="3" fontFamily="var(--font-mono)" fontSize="7" fill={C.faint}>Google</text>
3084+
<circle cx="12" cy="0" r="4" fill="none" stroke={C.terracotta} strokeWidth="1.8" />
3085+
<text x="20" y="3" fontFamily="var(--font-mono)" fontSize="7" fill={C.faint}>external</text>
3086+
</g>
3087+
</svg>
3088+
);
3089+
}
3090+
3091+
/** Remy — three tiers of autonomy (auto, review, approve). */
3092+
export function RemyTrustTierFigure() {
3093+
const tiers = [
3094+
{ label: "auto", desc: "calendar, lookups", color: C.sage, y: 60 },
3095+
{ label: "review", desc: "drafts, scheduling", color: C.butter, y: 140 },
3096+
{ label: "approve", desc: "purchases, messages", color: C.rose, y: 220 },
3097+
];
3098+
return (
3099+
<svg viewBox="0 0 320 320" className="w-full" role="img" aria-labelledby="remy-trust-title">
3100+
<title id="remy-trust-title">Three tiers of autonomy: auto-run for low-risk, review for medium, explicit approval for high-risk</title>
3101+
<defs>
3102+
<radialGradient id="remytrustgrad" cx="50%" cy="40%" r="55%">
3103+
<stop offset="0%" stopColor={C.butter} stopOpacity="0.7" />
3104+
<stop offset="100%" stopColor={C.peach} stopOpacity="0.35" />
3105+
</radialGradient>
3106+
</defs>
3107+
<rect x="20" y="20" width="280" height="280" rx="16" fill="url(#remytrustgrad)" />
3108+
{/* Risk arrow on left */}
3109+
<line x1="48" y1="50" x2="48" y2="240" stroke={C.inkSoft} strokeWidth="1.2" />
3110+
<path d="M44 234 L48 244 L52 234" fill="none" stroke={C.inkSoft} strokeWidth="1.2" strokeLinecap="round" />
3111+
<text x="48" y="262" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="7" fill={C.faint}>risk</text>
3112+
{/* Tier blocks */}
3113+
{tiers.map(({ label, desc, color, y }, i) => (
3114+
<g key={i}>
3115+
<rect x="72" y={y - 28} width="220" height="56" rx="8" fill={C.paper} stroke={C.ink} strokeWidth="1.4" />
3116+
<rect x="72" y={y - 28} width="220" height="56" rx="8" fill={color} opacity="0.25" />
3117+
<text x="100" y={y - 4} fontFamily="var(--font-display)" fontSize="14" fill={C.ink}>{label}</text>
3118+
<text x="100" y={y + 12} fontFamily="var(--font-mono)" fontSize="8" fill={C.faint}>{desc}</text>
3119+
{/* Shield icons — more for higher risk */}
3120+
{Array.from({ length: i + 1 }).map((_, j) => (
3121+
<g key={j} transform={`translate(${252 + j * 16}, ${y - 2})`}>
3122+
<path d="M0 -6 L6 -3 L6 3 Q6 8 0 10 Q-6 8 -6 3 L-6 -3 Z" fill="none" stroke={C.inkSoft} strokeWidth="1" />
3123+
</g>
3124+
))}
3125+
</g>
3126+
))}
3127+
{/* Header */}
3128+
<text x="182" y="300" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8.5" fill={C.terracotta}>
3129+
autonomy narrows as stakes rise
3130+
</text>
3131+
</svg>
3132+
);
3133+
}
3134+
3135+
/** Remy — comparison of three proactive agents (Pulse, Orbit, Remy). */
3136+
export function RemyComparisonFigure() {
3137+
const agents = [
3138+
{ name: "Pulse", primitives: [true, false, false], color: C.peach },
3139+
{ name: "Orbit", primitives: [false, true, false], color: C.lavender },
3140+
{ name: "Remy", primitives: [true, true, true], color: C.sky },
3141+
];
3142+
const labels = ["clock", "listener", "inbox"];
3143+
const colW = 86, startX = 36, topY = 70;
3144+
return (
3145+
<svg viewBox="0 0 320 320" className="w-full" role="img" aria-labelledby="remy-compare-title">
3146+
<title id="remy-compare-title">Three proactive agents compared: Pulse has clock only, Orbit has listener, Remy has all three primitives</title>
3147+
<defs>
3148+
<radialGradient id="remycmpgrad" cx="50%" cy="40%" r="55%">
3149+
<stop offset="0%" stopColor={C.sky} stopOpacity="0.6" />
3150+
<stop offset="100%" stopColor={C.sage} stopOpacity="0.3" />
3151+
</radialGradient>
3152+
</defs>
3153+
<rect x="10" y="10" width="300" height="300" rx="16" fill="url(#remycmpgrad)" />
3154+
{/* Column headers */}
3155+
{agents.map(({ name, color }, i) => {
3156+
const x = startX + i * colW + colW / 2;
3157+
return (
3158+
<g key={`hdr-${i}`}>
3159+
<rect x={x - 34} y={topY - 20} width="68" height="28" rx="6" fill={C.paper} stroke={C.ink} strokeWidth="1.4" />
3160+
<rect x={x - 34} y={topY - 20} width="68" height="28" rx="6" fill={color} opacity="0.3" />
3161+
<text x={x} y={topY - 2} textAnchor="middle" fontFamily="var(--font-display)" fontSize="12" fill={C.ink}>{name}</text>
3162+
</g>
3163+
);
3164+
})}
3165+
{/* Primitive rows */}
3166+
{labels.map((label, row) => {
3167+
const y = topY + 50 + row * 58;
3168+
return (
3169+
<g key={`row-${row}`}>
3170+
<text x="160" y={y - 18} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8" fill={C.faint}>{label}</text>
3171+
{agents.map(({ primitives }, col) => {
3172+
const x = startX + col * colW + colW / 2;
3173+
const has = primitives[row];
3174+
return (
3175+
<g key={`cell-${row}-${col}`}>
3176+
<circle cx={x} cy={y} r="12" fill={C.paper} stroke={has ? C.terracotta : C.rule} strokeWidth={has ? "2" : "1"} />
3177+
{has ? (
3178+
<path d={`M${x - 5} ${y} L${x - 1} ${y + 4} L${x + 6} ${y - 5}`} fill="none" stroke={C.terracotta} strokeWidth="2" strokeLinecap="round" />
3179+
) : (
3180+
<g stroke={C.rule} strokeWidth="1.4">
3181+
<line x1={x - 4} y1={y - 4} x2={x + 4} y2={y + 4} />
3182+
<line x1={x + 4} y1={y - 4} x2={x - 4} y2={y + 4} />
3183+
</g>
3184+
)}
3185+
</g>
3186+
);
3187+
})}
3188+
</g>
3189+
);
3190+
})}
3191+
<text x="160" y="300" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8.5" fill={C.terracotta}>
3192+
same problem, different scope
3193+
</text>
3194+
</svg>
3195+
);
3196+
}

components/mdx/mdx-components.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ import {
6161
JuniorPluginFigure,
6262
AgentScopeFigure,
6363
PhaseAgentFigure,
64+
RemyEcosystemFigure,
65+
RemyTrustTierFigure,
66+
RemyComparisonFigure,
6467
} from "./figures";
6568

6669
export const mdxComponents = {
@@ -127,6 +130,9 @@ export const mdxComponents = {
127130
JuniorPluginFigure,
128131
AgentScopeFigure,
129132
PhaseAgentFigure,
133+
RemyEcosystemFigure,
134+
RemyTrustTierFigure,
135+
RemyComparisonFigure,
130136
LinkedInEmbed: () => (
131137
<iframe
132138
src="https://www.linkedin.com/embed/feed/update/urn:li:share:7429634994467414016?collapsed=1"

content/posts/chatgpt-pulse.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: "ChatGPT Pulse and the missing primitives"
33
summary: "ChatGPT Pulse has one of the three primitives a proactive agent needs. Here's what's missing and what indispensable would look like."
44
date: "2026-05-11"
5-
lastModified: "2026-05-15"
5+
lastModified: "2026-05-18"
66
accent: "sky"
77
dropcap: true
88
---
@@ -85,7 +85,7 @@ That version of Pulse would actually look like the proactive assistant OpenAI de
8585

8686
## The market signal
8787

88-
The most interesting thing about Pulse might be the competitive landscape around it. Anthropic is reportedly building a proactive assistant called Orbit for Claude. Google and Perplexity are developing their own versions. [Notion took a different path entirely](/posts/notion-ships-the-primitives/), shipping composable building blocks instead of a finished product. Everyone is converging on the same insight: reactive AI (ask a question, get an answer) is leaving value on the table.
88+
The most interesting thing about Pulse might be the competitive landscape around it. Anthropic is reportedly building a proactive assistant called [Orbit](https://www.testingcatalog.com/anthropic-is-working-on-orbit-its-upcoming-proactive-assistant/) for Claude. Google is testing [Remy](https://www.absolutegeeks.com/article/tech-news/google-tests-remy-a-proactive-gemini-powered-ai-agent-for-daily-tasks/), a Gemini-powered agent designed to act autonomously in the background, managing errands, sending documents, and coordinating across connected apps without constant user input. [Perplexity](https://www.perplexity.ai) is developing its own version. [Notion took a different path entirely](/posts/notion-ships-the-primitives/), shipping composable building blocks instead of a finished product. Everyone is converging on the same insight: reactive AI (ask a question, get an answer) is leaving value on the table.
8989

9090
That's exciting and it validates what we've been building. But if everyone just ships the clock and calls it done, I'm not sure any of these will feel like more than a tab in an app.
9191

0 commit comments

Comments
 (0)