Skip to content

Commit d104f24

Browse files
authored
Merge pull request #15 from AgentWorkforce/every-tool-ships-an-agent
Add essay: Every tool ships an agent now
2 parents da045b1 + 4b01cbe commit d104f24

6 files changed

Lines changed: 617 additions & 0 deletions

File tree

components/card-illustrations.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const SLUGS: Record<string, React.ComponentType> = {
3030
"agent-moves-first": AgentMovesFirstArt,
3131
"posthog-code": PostHogCodeArt,
3232
"notion-ships-the-primitives": NotionPrimitivesArt,
33+
"every-tool-ships-an-agent": EveryToolAgentArt,
3334
};
3435

3536
export function CardArt({ slug }: { slug: string }) {
@@ -1029,3 +1030,39 @@ function PostHogCodeArt() {
10291030
</svg>
10301031
);
10311032
}
1033+
1034+
function EveryToolAgentArt() {
1035+
return (
1036+
<svg
1037+
viewBox="0 0 500 300"
1038+
className="pointer-events-none absolute inset-0 h-full w-full"
1039+
aria-hidden
1040+
>
1041+
<g
1042+
opacity="0.13"
1043+
stroke={C.ink}
1044+
fill="none"
1045+
strokeLinecap="round"
1046+
strokeLinejoin="round"
1047+
>
1048+
{/* Center hub */}
1049+
<g transform="translate(250, 120)">
1050+
<circle r="22" strokeWidth="1.8" />
1051+
<circle r="3" fill={C.ink} stroke="none" />
1052+
{/* Radiating spokes to tool nodes */}
1053+
{[0, 60, 120, 180, 240, 300].map((angle, i) => {
1054+
const rad = (angle * Math.PI) / 180;
1055+
const x = Math.cos(rad) * 65;
1056+
const y = Math.sin(rad) * 55;
1057+
return (
1058+
<g key={i}>
1059+
<line x1={Math.cos(rad) * 22} y1={Math.sin(rad) * 22} x2={x} y2={y} strokeWidth="1.2" />
1060+
<rect x={x - 12} y={y - 10} width="24" height="20" rx="3" strokeWidth="1.3" />
1061+
</g>
1062+
);
1063+
})}
1064+
</g>
1065+
</g>
1066+
</svg>
1067+
);
1068+
}

components/mdx/figures.tsx

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,3 +2699,318 @@ export function NotionOpenFigure() {
26992699
</svg>
27002700
);
27012701
}
2702+
2703+
/** Vendor silo problem — four tools, each seeing only its own data, engineer bridges them. */
2704+
export function VendorSiloFigure() {
2705+
const tools = [
2706+
{ label: "Sentry", color: C.rose, y: 70 },
2707+
{ label: "GitHub", color: C.sage, y: 70 },
2708+
{ label: "Datadog", color: C.butter, y: 70 },
2709+
{ label: "Linear", color: C.lavender, y: 70 },
2710+
];
2711+
const colW = 58;
2712+
const gap = 12;
2713+
const totalW = tools.length * colW + (tools.length - 1) * gap;
2714+
const startX = (320 - totalW) / 2;
2715+
2716+
return (
2717+
<svg viewBox="0 0 320 320" className="w-full" role="img" aria-labelledby="vendorsilo-title">
2718+
<title id="vendorsilo-title">Vendor chatbots as isolated silos: each sees only its own data</title>
2719+
2720+
<text x="160" y="24" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill={C.faint} letterSpacing="0.06em">
2721+
the incident spans all four
2722+
</text>
2723+
2724+
{/* Incident line spanning all silos */}
2725+
<line x1={startX - 8} y1="42" x2={startX + totalW + 8} y2="42" stroke={C.terracotta} strokeWidth="1.4" strokeDasharray="6 4" />
2726+
<circle cx={startX - 8} cy="42" r="3" fill={C.terracotta} />
2727+
<circle cx={startX + totalW + 8} cy="42" r="3" fill={C.terracotta} />
2728+
2729+
{/* Silo columns */}
2730+
{tools.map((tool, i) => {
2731+
const x = startX + i * (colW + gap);
2732+
return (
2733+
<g key={tool.label}>
2734+
<rect x={x} y={54} width={colW} height={130} rx="6" fill={tool.color} opacity="0.35" stroke={C.ink} strokeWidth="1" />
2735+
<text x={x + colW / 2} y={72} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8.5" fill={C.ink} fontWeight="600">
2736+
{tool.label}
2737+
</text>
2738+
<line x1={x + 8} y1={84} x2={x + colW - 8} y2={84} stroke={C.faint} strokeWidth="0.8" />
2739+
<line x1={x + 8} y1={96} x2={x + colW - 12} y2={96} stroke={C.faint} strokeWidth="0.8" />
2740+
<line x1={x + 8} y1={108} x2={x + colW - 8} y2={108} stroke={C.faint} strokeWidth="0.8" />
2741+
{/* Chatbot icon */}
2742+
<circle cx={x + colW / 2} cy={148} r="12" fill={C.paper} stroke={C.ink} strokeWidth="1" />
2743+
<circle cx={x + colW / 2 - 4} cy={146} r="1.5" fill={C.ink} />
2744+
<circle cx={x + colW / 2 + 4} cy={146} r="1.5" fill={C.ink} />
2745+
<path d={`M${x + colW / 2 - 4} ${152} Q${x + colW / 2} ${155} ${x + colW / 2 + 4} ${152}`} stroke={C.ink} strokeWidth="0.8" fill="none" />
2746+
{/* Vertical wall lines */}
2747+
<line x1={x} y1={54} x2={x} y2={184} stroke={C.ink} strokeWidth="1" />
2748+
<line x1={x + colW} y1={54} x2={x + colW} y2={184} stroke={C.ink} strokeWidth="1" />
2749+
</g>
2750+
);
2751+
})}
2752+
2753+
{/* Engineer at bottom bridging silos */}
2754+
<circle cx="160" cy="230" r="14" fill={C.paper} stroke={C.ink} strokeWidth="1.4" />
2755+
<circle cx="156" cy="228" r="1.5" fill={C.ink} />
2756+
<circle cx="164" cy="228" r="1.5" fill={C.ink} />
2757+
<line x1="155" y1="234" x2="165" y2="234" stroke={C.ink} strokeWidth="1" />
2758+
2759+
{/* Arrows from engineer to each silo */}
2760+
{tools.map((_, i) => {
2761+
const x = startX + i * (colW + gap) + colW / 2;
2762+
return (
2763+
<g key={i} stroke={C.faint} strokeWidth="1" fill="none" strokeLinecap="round">
2764+
<line x1="160" y1="216" x2={x} y2="188" />
2765+
<circle cx={x} cy="186" r="2" fill={C.faint} />
2766+
</g>
2767+
);
2768+
})}
2769+
2770+
<text x="160" y="264" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill={C.faint}>
2771+
engineer = integration layer
2772+
</text>
2773+
2774+
<text x="160" y="280" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8" fill={C.terracotta}>
2775+
each bot sees 1/4 of the picture
2776+
</text>
2777+
</svg>
2778+
);
2779+
}
2780+
2781+
/** Junior plugin architecture — hub-and-spoke with egress proxy ring. */
2782+
export function JuniorPluginFigure() {
2783+
const plugins = [
2784+
{ label: "GitHub", angle: -90, type: "app", color: C.sage },
2785+
{ label: "Sentry", angle: -30, type: "oauth", color: C.rose },
2786+
{ label: "Datadog", angle: 30, type: "api-key", color: C.butter },
2787+
{ label: "Linear", angle: 90, type: "MCP", color: C.lavender },
2788+
{ label: "Notion", angle: 150, type: "MCP", color: C.sky },
2789+
{ label: "Hex", angle: 210, type: "MCP", color: C.peach },
2790+
];
2791+
const cx = 160;
2792+
const cy = 150;
2793+
const innerR = 28;
2794+
const proxyR = 60;
2795+
const outerR = 110;
2796+
2797+
return (
2798+
<svg viewBox="0 0 320 320" className="w-full" role="img" aria-labelledby="juniorplugin-title">
2799+
<title id="juniorplugin-title">Junior architecture: one agent hub with plugins connected through an egress proxy</title>
2800+
2801+
<text x="160" y="20" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill={C.faint} letterSpacing="0.06em">
2802+
one runtime, many plugins
2803+
</text>
2804+
2805+
{/* Egress proxy ring */}
2806+
<circle cx={cx} cy={cy} r={proxyR} fill="none" stroke={C.terracotta} strokeWidth="1.2" strokeDasharray="5 3" opacity="0.6" />
2807+
<text x={cx + proxyR - 12} y={cy - proxyR + 14} fontFamily="var(--font-mono)" fontSize="7" fill={C.terracotta} opacity="0.8">
2808+
egress proxy
2809+
</text>
2810+
2811+
{/* Center agent */}
2812+
<circle cx={cx} cy={cy} r={innerR} fill={C.sage} opacity="0.5" stroke={C.ink} strokeWidth="1.4" />
2813+
<text x={cx} y={cy - 4} textAnchor="middle" fontFamily="var(--font-display)" fontSize="14" fill={C.ink} fontWeight="600">jr</text>
2814+
<text x={cx} y={cy + 10} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="7" fill={C.inkSoft}>slack bot</text>
2815+
2816+
{/* Plugin nodes */}
2817+
{plugins.map((p) => {
2818+
const rad = (p.angle * Math.PI) / 180;
2819+
const px = cx + Math.cos(rad) * outerR;
2820+
const py = cy + Math.sin(rad) * outerR;
2821+
const proxyX = cx + Math.cos(rad) * proxyR;
2822+
const proxyY = cy + Math.sin(rad) * proxyR;
2823+
return (
2824+
<g key={p.label}>
2825+
{/* Connection line */}
2826+
<line x1={proxyX} y1={proxyY} x2={px} y2={py} stroke={C.faint} strokeWidth="1" />
2827+
{/* Proxy crossing dot */}
2828+
<circle cx={proxyX} cy={proxyY} r="2.5" fill={C.terracotta} opacity="0.7" />
2829+
{/* Plugin node */}
2830+
<circle cx={px} cy={py} r="20" fill={p.color} opacity="0.4" stroke={C.ink} strokeWidth="0.8" />
2831+
<text x={px} y={py - 4} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8" fill={C.ink} fontWeight="600">
2832+
{p.label}
2833+
</text>
2834+
<text x={px} y={py + 7} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="6.5" fill={C.faint}>
2835+
{p.type}
2836+
</text>
2837+
</g>
2838+
);
2839+
})}
2840+
2841+
{/* Sandbox label */}
2842+
<rect x="50" y="278" width="220" height="22" rx="4" fill={C.paper} stroke={C.faint} strokeWidth="0.8" />
2843+
<text x="160" y="293" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8" fill={C.faint}>
2844+
sandbox: bash, readFile, editFile, grep, browser
2845+
</text>
2846+
</svg>
2847+
);
2848+
}
2849+
2850+
/** Agent scope spectrum — narrow to generalized, four vendors positioned. */
2851+
export function AgentScopeFigure() {
2852+
const axisY = 160;
2853+
const axisX1 = 30;
2854+
const axisX2 = 290;
2855+
2856+
const vendors = [
2857+
{ label: "PostHog", x: 62, desc: "in-app only", color: C.rose },
2858+
{ label: "CodeRabbit", x: 138, desc: "expanding", color: C.butter },
2859+
{ label: "Notion", x: 210, desc: "hub model", color: C.lavender },
2860+
{ label: "Junior", x: 264, desc: "open runtime", color: C.sage },
2861+
];
2862+
2863+
return (
2864+
<svg viewBox="0 0 320 320" className="w-full" role="img" aria-labelledby="agentscope-title">
2865+
<title id="agentscope-title">Spectrum from narrow vendor chatbot to generalized agent runtime</title>
2866+
2867+
<text x="160" y="28" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill={C.faint} letterSpacing="0.06em">
2868+
scope vs. depth
2869+
</text>
2870+
2871+
{/* Gradient background bar */}
2872+
<defs>
2873+
<linearGradient id="scope-grad" x1="0" x2="1" y1="0" y2="0">
2874+
<stop offset="0%" stopColor={C.rose} stopOpacity="0.25" />
2875+
<stop offset="50%" stopColor={C.butter} stopOpacity="0.2" />
2876+
<stop offset="100%" stopColor={C.sage} stopOpacity="0.3" />
2877+
</linearGradient>
2878+
</defs>
2879+
<rect x={axisX1} y={axisY - 40} width={axisX2 - axisX1} height="80" rx="8" fill="url(#scope-grad)" />
2880+
2881+
{/* Axis line */}
2882+
<line x1={axisX1} y1={axisY} x2={axisX2} y2={axisY} stroke={C.ink} strokeWidth="1.2" />
2883+
{/* Arrow */}
2884+
<path d={`M${axisX2 - 6} ${axisY - 4} L${axisX2} ${axisY} L${axisX2 - 6} ${axisY + 4}`} stroke={C.ink} strokeWidth="1.2" fill="none" />
2885+
2886+
{/* Axis labels */}
2887+
<text x={axisX1 + 4} y={axisY + 56} fontFamily="var(--font-mono)" fontSize="8" fill={C.faint}>narrow</text>
2888+
<text x={axisX2 - 4} y={axisY + 56} textAnchor="end" fontFamily="var(--font-mono)" fontSize="8" fill={C.faint}>generalized</text>
2889+
2890+
{/* Vendor markers */}
2891+
{vendors.map((v) => (
2892+
<g key={v.label}>
2893+
{/* Vertical line */}
2894+
<line x1={v.x} y1={axisY - 32} x2={v.x} y2={axisY - 4} stroke={C.ink} strokeWidth="1" opacity="0.4" />
2895+
{/* Dot on axis */}
2896+
<circle cx={v.x} cy={axisY} r="6" fill={v.color} stroke={C.ink} strokeWidth="1.2" />
2897+
{/* Label above */}
2898+
<text x={v.x} y={axisY - 38} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill={C.ink} fontWeight="600">
2899+
{v.label}
2900+
</text>
2901+
{/* Description below */}
2902+
<text x={v.x} y={axisY + 22} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="7" fill={C.inkSoft}>
2903+
{v.desc}
2904+
</text>
2905+
</g>
2906+
))}
2907+
2908+
{/* Key insight */}
2909+
<text x="160" y="260" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8.5" fill={C.faint}>
2910+
different bets on where the value lives
2911+
</text>
2912+
2913+
{/* MCP convergence note */}
2914+
<g>
2915+
<rect x="60" y="272" width="200" height="20" rx="4" fill={C.paper} stroke={C.faint} strokeWidth="0.8" />
2916+
<text x="160" y="286" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="7.5" fill={C.terracotta}>
2917+
MCP narrows the gap between all four
2918+
</text>
2919+
</g>
2920+
</svg>
2921+
);
2922+
}
2923+
2924+
/** Phase-specific agents — PDERO pipeline with cross-phase integrations. */
2925+
export function PhaseAgentFigure() {
2926+
const phases = [
2927+
{ label: "Plan", short: "P", color: C.lavender },
2928+
{ label: "Delegate", short: "D", color: C.butter },
2929+
{ label: "Execute", short: "E", color: C.sage },
2930+
{ label: "Review", short: "R", color: C.rose },
2931+
{ label: "Observe", short: "O", color: C.sky },
2932+
];
2933+
const boxW = 46;
2934+
const gap = 8;
2935+
const totalW = phases.length * boxW + (phases.length - 1) * gap;
2936+
const startX = (320 - totalW) / 2;
2937+
const cy = 130;
2938+
const boxH = 64;
2939+
2940+
return (
2941+
<svg viewBox="0 0 320 320" className="w-full" role="img" aria-labelledby="phaseagent-title">
2942+
<title id="phaseagent-title">Phase-specific agents: Plan, Delegate, Execute, Review, Observe with cross-phase integrations</title>
2943+
2944+
<text x="160" y="22" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill={C.faint} letterSpacing="0.06em">
2945+
agents per phase, not per tool
2946+
</text>
2947+
2948+
{/* Phase boxes */}
2949+
{phases.map((phase, i) => {
2950+
const x = startX + i * (boxW + gap);
2951+
return (
2952+
<g key={phase.label}>
2953+
<rect
2954+
x={x} y={cy - boxH / 2} width={boxW} height={boxH}
2955+
rx="6" fill={phase.color} opacity="0.4"
2956+
stroke={C.ink} strokeWidth="1"
2957+
/>
2958+
<text x={x + boxW / 2} y={cy - 10} textAnchor="middle" fontFamily="var(--font-display)" fontSize="16" fill={C.ink} fontWeight="600">
2959+
{phase.short}
2960+
</text>
2961+
<text x={x + boxW / 2} y={cy + 8} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="7" fill={C.inkSoft}>
2962+
{phase.label}
2963+
</text>
2964+
{/* Agent dot */}
2965+
<circle cx={x + boxW / 2} cy={cy + 22} r="5" fill={C.paper} stroke={C.ink} strokeWidth="0.8" />
2966+
<circle cx={x + boxW / 2 - 2} cy={cy + 21} r="0.8" fill={C.ink} />
2967+
<circle cx={x + boxW / 2 + 2} cy={cy + 21} r="0.8" fill={C.ink} />
2968+
{/* Forward arrow to next phase */}
2969+
{i < phases.length - 1 && (
2970+
<g stroke={C.terracotta} strokeWidth="1.2" fill="none" strokeLinecap="round">
2971+
<line x1={x + boxW + 1} y1={cy} x2={x + boxW + gap - 1} y2={cy} />
2972+
<path d={`M${x + boxW + gap - 4} ${cy - 3} L${x + boxW + gap - 1} ${cy} L${x + boxW + gap - 4} ${cy + 3}`} />
2973+
</g>
2974+
)}
2975+
</g>
2976+
);
2977+
})}
2978+
2979+
{/* Cross-phase integration arcs */}
2980+
<g stroke={C.faint} strokeWidth="0.8" fill="none" strokeDasharray="3 3">
2981+
{/* Plan → Review arc */}
2982+
<path d={`M${startX + boxW / 2} ${cy - boxH / 2 - 2} Q160 ${cy - boxH / 2 - 30} ${startX + 3 * (boxW + gap) + boxW / 2} ${cy - boxH / 2 - 2}`} />
2983+
{/* Execute → Observe arc */}
2984+
<path d={`M${startX + 2 * (boxW + gap) + boxW / 2} ${cy + boxH / 2 + 2} Q${startX + 3.5 * (boxW + gap)} ${cy + boxH / 2 + 26} ${startX + 4 * (boxW + gap) + boxW / 2} ${cy + boxH / 2 + 2}`} />
2985+
</g>
2986+
2987+
{/* Arc labels */}
2988+
<text x="160" y={cy - boxH / 2 - 32} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="6.5" fill={C.faint}>
2989+
traces back to intent
2990+
</text>
2991+
<text x={startX + 3.25 * (boxW + gap)} y={cy + boxH / 2 + 32} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="6.5" fill={C.faint}>
2992+
closes the loop
2993+
</text>
2994+
2995+
{/* Tool integrations below */}
2996+
<text x="160" y="228" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8" fill={C.inkSoft}>
2997+
tools cross phase boundaries
2998+
</text>
2999+
{["tickets", "code", "runtime", "metrics", "logs"].map((tool, i) => {
3000+
const x = startX + i * (boxW + gap) + boxW / 2;
3001+
return (
3002+
<g key={tool}>
3003+
<line x1={x} y1={cy + boxH / 2 + 4} x2={x} y2="238" stroke={C.faint} strokeWidth="0.6" strokeDasharray="2 2" />
3004+
<text x={x} y="250" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="6.5" fill={C.faint}>
3005+
{tool}
3006+
</text>
3007+
</g>
3008+
);
3009+
})}
3010+
3011+
<text x="160" y="278" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8.5" fill={C.terracotta}>
3012+
narrow in purpose, broad in reach
3013+
</text>
3014+
</svg>
3015+
);
3016+
}

components/mdx/mdx-components.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ import {
5757
NotionStackFigure,
5858
NotionPrimitiveMapFigure,
5959
NotionOpenFigure,
60+
VendorSiloFigure,
61+
JuniorPluginFigure,
62+
AgentScopeFigure,
63+
PhaseAgentFigure,
6064
} from "./figures";
6165

6266
export const mdxComponents = {
@@ -119,6 +123,10 @@ export const mdxComponents = {
119123
NotionStackFigure,
120124
NotionPrimitiveMapFigure,
121125
NotionOpenFigure,
126+
VendorSiloFigure,
127+
JuniorPluginFigure,
128+
AgentScopeFigure,
129+
PhaseAgentFigure,
122130
LinkedInEmbed: () => (
123131
<iframe
124132
src="https://www.linkedin.com/embed/feed/update/urn:li:share:7429634994467414016?collapsed=1"

0 commit comments

Comments
 (0)