feat: add integrations page with ecosystem diagram

- Add /integrations page documenting HLLM, TPMJS, and BlocksAI relationships
- Create EcosystemDiagram D3.js component showing project connections
- Document HLLM multi-agent integration with TPMJS tools
- Document BlocksAI 3-layer validation pipeline
- Add shared technologies section (AI SDK, D3, Prisma)
- Include integration APIs table and data flow documentation
- Add Integrations link to desktop and mobile navigation
- Fix accessibility issue in MobileMenu backdrop

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ajax Davis 2026-01-01 02:17:05 +10:00
parent 579aa99c56
commit 21f1818070
4 changed files with 1459 additions and 1 deletions

View file

@ -0,0 +1,704 @@
import { Button } from '@tpmjs/ui/Button/Button';
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
import { Container } from '@tpmjs/ui/Container/Container';
import Link from 'next/link';
import { AppHeader } from '~/components/AppHeader';
import { EcosystemDiagram } from '~/components/EcosystemDiagram';
export const metadata = {
title: 'Integrations | TPMJS',
description:
'How TPMJS integrates with HLLM multi-agent orchestration and BlocksAI domain-driven validation',
openGraph: {
title: 'Integrations | TPMJS',
description:
'How TPMJS integrates with HLLM multi-agent orchestration and BlocksAI domain-driven validation',
},
};
export default function IntegrationsPage(): React.ReactElement {
return (
<div className="min-h-screen flex flex-col bg-background">
<AppHeader />
<main className="flex-1 py-16">
<Container size="lg" padding="lg">
{/* Hero */}
<div className="text-center mb-16">
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-4 text-foreground">
Ecosystem Integrations
</h1>
<p className="text-xl text-foreground-secondary max-w-3xl mx-auto">
TPMJS is part of a larger AI infrastructure ecosystem. See how it connects with HLLM
for multi-agent orchestration and BlocksAI for domain-driven validation.
</p>
</div>
{/* Ecosystem Diagram */}
<section className="mb-16">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
The Complete Picture
</h2>
<EcosystemDiagram />
</section>
{/* Three Projects Overview */}
<section className="mb-16">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Three Projects, One Vision
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* HLLM */}
<div className="p-6 border-2 border-purple-500/30 rounded-xl bg-purple-500/5">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-purple-500/20 flex items-center justify-center">
<span className="text-xl">🧠</span>
</div>
<div>
<h3 className="font-bold text-foreground">HLLM</h3>
<p className="text-xs text-foreground-tertiary">Multi-Agent Orchestration</p>
</div>
</div>
<p className="text-sm text-foreground-secondary mb-4">
A playground for orchestrating multiple AI agents using 13 different topology
patterns. Agents can use TPMJS tools to extend their capabilities.
</p>
<div className="space-y-2 text-xs">
<div className="flex items-center gap-2">
<span className="text-purple-400"></span>
<span className="text-foreground-secondary">
13 topologies (debate, react, etc.)
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-purple-400"></span>
<span className="text-foreground-secondary">100+ AI models via OpenRouter</span>
</div>
<div className="flex items-center gap-2">
<span className="text-purple-400"></span>
<span className="text-foreground-secondary">Real-time D3 visualizations</span>
</div>
</div>
</div>
{/* TPMJS */}
<div className="p-6 border-2 border-emerald-500/30 rounded-xl bg-emerald-500/5">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-emerald-500/20 flex items-center justify-center">
<span className="text-xl">🔧</span>
</div>
<div>
<h3 className="font-bold text-foreground">TPMJS</h3>
<p className="text-xs text-foreground-tertiary">AI Tool Registry</p>
</div>
</div>
<p className="text-sm text-foreground-secondary mb-4">
The central registry for AI tools. Automatically discovers tools from npm,
validates them, and provides sandboxed execution.
</p>
<div className="space-y-2 text-xs">
<div className="flex items-center gap-2">
<span className="text-emerald-400"></span>
<span className="text-foreground-secondary">106+ official tools</span>
</div>
<div className="flex items-center gap-2">
<span className="text-emerald-400"></span>
<span className="text-foreground-secondary">Deno sandboxed execution</span>
</div>
<div className="flex items-center gap-2">
<span className="text-emerald-400"></span>
<span className="text-foreground-secondary">Auto-discovery from npm</span>
</div>
</div>
</div>
{/* BlocksAI */}
<div className="p-6 border-2 border-amber-500/30 rounded-xl bg-amber-500/5">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-amber-500/20 flex items-center justify-center">
<span className="text-xl"></span>
</div>
<div>
<h3 className="font-bold text-foreground">BlocksAI</h3>
<p className="text-xs text-foreground-tertiary">Domain-Driven Validation</p>
</div>
</div>
<p className="text-sm text-foreground-secondary mb-4">
A validation framework that uses AI to ensure code follows domain semantics. TPMJS
tools are validated using BlocksAI patterns.
</p>
<div className="space-y-2 text-xs">
<div className="flex items-center gap-2">
<span className="text-amber-400"></span>
<span className="text-foreground-secondary">3-layer validation pipeline</span>
</div>
<div className="flex items-center gap-2">
<span className="text-amber-400"></span>
<span className="text-foreground-secondary">AI-powered semantic checks</span>
</div>
<div className="flex items-center gap-2">
<span className="text-amber-400"></span>
<span className="text-foreground-secondary">Domain rules in YAML</span>
</div>
</div>
</div>
</div>
</section>
{/* HLLM Integration Deep Dive */}
<section className="mb-16">
<div className="flex items-center gap-3 mb-6">
<div className="w-8 h-8 rounded bg-purple-500/20 flex items-center justify-center">
<span>🧠</span>
</div>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-foreground">
HLLM Integration
</h2>
</div>
<div className="space-y-8">
{/* Overview */}
<div className="prose max-w-none text-foreground-secondary">
<p className="text-lg">
HLLM (Hierarchical Language Learning Model) is a multi-agent orchestration
platform. It integrates with TPMJS to give agents access to the full tool
registry.
</p>
</div>
{/* How it works */}
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-4 text-foreground">
How HLLM Uses TPMJS Tools
</h3>
<div className="space-y-4">
<div className="flex items-start gap-4">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
1
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Tool Discovery</h4>
<p className="text-sm text-foreground-secondary">
HLLM&apos;s Tool Browser fetches available tools from{' '}
<code className="text-xs bg-surface-secondary px-1 py-0.5 rounded">
/api/tpmjs/tools
</code>{' '}
proxy, which forwards to tpmjs.com
</p>
</div>
</div>
<div className="flex items-start gap-4">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
2
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Agent Configuration</h4>
<p className="text-sm text-foreground-secondary">
Users attach tools to agents in the Agent Studio. Each agent can have
different tools based on its role.
</p>
</div>
</div>
<div className="flex items-start gap-4">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
3
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Tool Execution</h4>
<p className="text-sm text-foreground-secondary">
When an agent calls a tool, HLLM routes the request to{' '}
<code className="text-xs bg-surface-secondary px-1 py-0.5 rounded">
executor.tpmjs.com
</code>{' '}
for sandboxed execution
</p>
</div>
</div>
<div className="flex items-start gap-4">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold text-sm">
4
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Environment Variables</h4>
<p className="text-sm text-foreground-secondary">
User API keys (e.g., FIRECRAWL_API_KEY) are encrypted and injected at
runtime. Keys never leave the server.
</p>
</div>
</div>
</div>
</div>
{/* 13 Topologies */}
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-4 text-foreground">
13 Orchestration Topologies
</h3>
<p className="text-foreground-secondary mb-4">
HLLM supports various multi-agent coordination patterns. Each topology can use
TPMJS tools:
</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ name: 'single', desc: 'One agent' },
{ name: 'sequential', desc: 'Chain of agents' },
{ name: 'parallel', desc: 'Concurrent workers' },
{ name: 'map-reduce', desc: 'Split & aggregate' },
{ name: 'scatter', desc: 'Fan-out only' },
{ name: 'debate', desc: 'Pro vs Con vs Judge' },
{ name: 'reflection', desc: 'Generator + Critic' },
{ name: 'consensus', desc: 'Multi-voter agreement' },
{ name: 'brainstorm', desc: 'Idea generation' },
{ name: 'decomposition', desc: 'Task breakdown' },
{ name: 'rhetorical-triangle', desc: 'Ethos/Pathos/Logos' },
{ name: 'tree-of-thoughts', desc: 'Beam search' },
{ name: 'react', desc: 'Reason + Act loop' },
].map((topology) => (
<div
key={topology.name}
className="p-3 border border-border rounded bg-background"
>
<div className="font-mono text-xs text-purple-500">{topology.name}</div>
<div className="text-xs text-foreground-tertiary mt-1">{topology.desc}</div>
</div>
))}
</div>
</div>
{/* Code Example */}
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-4 text-foreground">
HLLM Tool Execution Code
</h3>
<CodeBlock
language="typescript"
code={`// HLLM's executeTpmjsTool function (simplified)
async function executeTpmjsTool(
toolId: string, // "packageName::exportName" or database ID
params: Record<string, unknown>,
env: Record<string, string>
) {
// Fetch tool metadata from TPMJS registry
const toolMeta = await fetch(\`https://tpmjs.com/api/tools/\${toolId}\`);
// Execute in TPMJS sandbox
const response = await fetch('https://executor.tpmjs.com/execute-tool', {
method: 'POST',
body: JSON.stringify({
packageName: toolMeta.npmPackageName,
name: toolMeta.exportName,
version: toolMeta.version,
params,
env, // User's encrypted API keys injected here
}),
});
// Parse SSE stream for results
return parseExecutionStream(response);
}`}
/>
</div>
</div>
</section>
{/* BlocksAI Integration Deep Dive */}
<section className="mb-16">
<div className="flex items-center gap-3 mb-6">
<div className="w-8 h-8 rounded bg-amber-500/20 flex items-center justify-center">
<span></span>
</div>
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-foreground">
BlocksAI Validation
</h2>
</div>
<div className="space-y-8">
{/* Overview */}
<div className="prose max-w-none text-foreground-secondary">
<p className="text-lg">
BlocksAI provides domain-driven validation for code. TPMJS tools are validated
using BlocksAI&apos;s 3-layer pipeline to ensure quality and consistency.
</p>
</div>
{/* 3-Layer Pipeline */}
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-4 text-foreground">
3-Layer Validation Pipeline
</h3>
<div className="space-y-4">
<div className="flex items-start gap-4 p-4 border border-amber-500/20 rounded-lg bg-amber-500/5">
<span className="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-lg bg-amber-500/20 text-amber-600 font-bold">
1
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Schema Validation</h4>
<p className="text-sm text-foreground-secondary">
Validates blocks.yml structure using Zod schemas. Ensures all required
fields are present with correct types.
</p>
<div className="mt-2 text-xs font-mono text-foreground-tertiary">
Validator: schema.io
</div>
</div>
</div>
<div className="flex items-start gap-4 p-4 border border-amber-500/20 rounded-lg bg-amber-500/5">
<span className="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-lg bg-amber-500/20 text-amber-600 font-bold">
2
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Shape Validation</h4>
<p className="text-sm text-foreground-secondary">
Validates TypeScript file structure. Checks that files exist, have exports,
and follow expected patterns.
</p>
<div className="mt-2 text-xs font-mono text-foreground-tertiary">
Validator: shape.exports.ts
</div>
</div>
</div>
<div className="flex items-start gap-4 p-4 border border-amber-500/20 rounded-lg bg-amber-500/5">
<span className="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-lg bg-amber-500/20 text-amber-600 font-bold">
3
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Domain Validation</h4>
<p className="text-sm text-foreground-secondary">
AI-powered semantic analysis using GPT-4 or Claude. Checks that code aligns
with domain rules and expresses intent clearly.
</p>
<div className="mt-2 text-xs font-mono text-foreground-tertiary">
Validator: domain.validation
</div>
</div>
</div>
</div>
</div>
{/* blocks.yml Example */}
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-4 text-foreground">
TPMJS blocks.yml Configuration
</h3>
<p className="text-foreground-secondary mb-4">
TPMJS tools are defined in blocks.yml with domain rules for validation:
</p>
<CodeBlock
language="yaml"
code={`# packages/tools/official/blocks.yml
name: "TPMJS Official Tools"
root: "."
philosophy:
- "Tools must be small, focused, and composable"
- "Clear interfaces with typed inputs/outputs"
- "Documentation must be comprehensive"
domain:
entities:
ai_tool:
fields: [name, description, inputSchema, execute]
blocks:
domain_rules:
- id: must_export
description: "Must export a valid AI SDK tool"
- id: uses_pattern
description: "Must use tool() + jsonSchema() pattern"
- id: has_description
description: "Tool must have clear description"
webFetchTool:
description: "Fetch content from a URL"
path: "web-fetch/src/index.ts"
domain_rules:
- type: "must_export"
export: "webFetchTool"
- type: "uses_pattern"
pattern: "tool\\\\s*\\\\("
inputs:
- name: url
type: string
description: "URL to fetch"
outputs:
- name: content
type: string
description: "Fetched content"`}
/>
</div>
{/* CLI Usage */}
<div className="p-6 border border-border rounded-lg bg-surface">
<h3 className="text-xl font-semibold mb-4 text-foreground">Running Validation</h3>
<CodeBlock
language="bash"
code={`# Validate all TPMJS tools
npx blocks run --all
# Output:
# webFetchTool [schema ] [shape ] [domain ]
# jsonPathQueryTool [schema ] [shape ] [domain ]
# regexExtractTool [schema ] [shape ] [domain ]
# ...
#
# Summary: 106 blocks passed, 0 failed`}
/>
</div>
</div>
</section>
{/* Shared Technologies */}
<section className="mb-16">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Shared Technologies
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* AI SDK */}
<div className="p-6 border border-border rounded-lg bg-surface">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-gray-500/20 flex items-center justify-center">
<span className="text-xl"></span>
</div>
<h3 className="font-bold text-foreground">Vercel AI SDK v6</h3>
</div>
<p className="text-sm text-foreground-secondary mb-4">
The foundation for all tool definitions. Both HLLM and TPMJS use the same pattern:
</p>
<CodeBlock
language="typescript"
code={`import { tool, jsonSchema } from 'ai';
export const myTool = tool({
description: '...',
inputSchema: jsonSchema<{
param: string;
}>({
type: 'object',
properties: {...},
}),
execute: async ({ param }) => {
// Tool logic
},
});`}
/>
</div>
{/* D3.js */}
<div className="p-6 border border-border rounded-lg bg-surface">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-gray-500/20 flex items-center justify-center">
<span className="text-xl">📊</span>
</div>
<h3 className="font-bold text-foreground">D3.js Visualizations</h3>
</div>
<p className="text-sm text-foreground-secondary mb-4">
Both projects use D3 for interactive diagrams:
</p>
<ul className="space-y-2 text-sm text-foreground-secondary">
<li className="flex items-center gap-2">
<span className="text-purple-400">HLLM:</span>
<span>TopologyGraph, ExecutionTimeline</span>
</li>
<li className="flex items-center gap-2">
<span className="text-emerald-400">TPMJS:</span>
<span>ArchitectureDiagram, SDKFlowDiagram</span>
</li>
</ul>
<p className="text-xs text-foreground-tertiary mt-4">
Real-time animations, hover tooltips, theme-aware colors
</p>
</div>
{/* Prisma */}
<div className="p-6 border border-border rounded-lg bg-surface">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-gray-500/20 flex items-center justify-center">
<span className="text-xl">🗄</span>
</div>
<h3 className="font-bold text-foreground">Prisma + PostgreSQL</h3>
</div>
<p className="text-sm text-foreground-secondary mb-4">Shared database patterns:</p>
<ul className="space-y-2 text-sm text-foreground-secondary">
<li className="flex items-center gap-2">
<span className="text-purple-400">HLLM:</span>
<span>Sessions, traces, prompts, user prefs</span>
</li>
<li className="flex items-center gap-2">
<span className="text-emerald-400">TPMJS:</span>
<span>Tools, packages, health checks, metrics</span>
</li>
</ul>
<p className="text-xs text-foreground-tertiary mt-4">
Both hosted on Neon with connection pooling
</p>
</div>
</div>
</section>
{/* Integration APIs */}
<section className="mb-16">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Integration APIs
</h2>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border">
<th className="text-left py-3 px-4 font-semibold text-foreground">Endpoint</th>
<th className="text-left py-3 px-4 font-semibold text-foreground">Project</th>
<th className="text-left py-3 px-4 font-semibold text-foreground">Purpose</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
<tr>
<td className="py-3 px-4 font-mono text-xs text-emerald-500">/api/tools</td>
<td className="py-3 px-4 text-foreground-secondary">TPMJS</td>
<td className="py-3 px-4 text-foreground-secondary">
Search and list tools with filters
</td>
</tr>
<tr>
<td className="py-3 px-4 font-mono text-xs text-emerald-500">
/api/tools/execute
</td>
<td className="py-3 px-4 text-foreground-secondary">TPMJS</td>
<td className="py-3 px-4 text-foreground-secondary">
Execute tools with SSE streaming
</td>
</tr>
<tr>
<td className="py-3 px-4 font-mono text-xs text-purple-500">
/api/tpmjs/tools
</td>
<td className="py-3 px-4 text-foreground-secondary">HLLM</td>
<td className="py-3 px-4 text-foreground-secondary">
Proxy to TPMJS registry (avoids CORS)
</td>
</tr>
<tr>
<td className="py-3 px-4 font-mono text-xs text-purple-500">
/api/tpmjs/execute
</td>
<td className="py-3 px-4 text-foreground-secondary">HLLM</td>
<td className="py-3 px-4 text-foreground-secondary">
Execute with user&apos;s encrypted env vars
</td>
</tr>
<tr>
<td className="py-3 px-4 font-mono text-xs text-purple-500">
/api/user/tpmjs-env
</td>
<td className="py-3 px-4 text-foreground-secondary">HLLM</td>
<td className="py-3 px-4 text-foreground-secondary">
Store/retrieve encrypted API keys
</td>
</tr>
<tr>
<td className="py-3 px-4 font-mono text-xs text-amber-500">npx blocks run</td>
<td className="py-3 px-4 text-foreground-secondary">BlocksAI</td>
<td className="py-3 px-4 text-foreground-secondary">
Validate tools against domain rules
</td>
</tr>
</tbody>
</table>
</div>
</section>
{/* Data Flow */}
<section className="mb-16">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-6 text-foreground">
Complete Data Flow
</h2>
<div className="space-y-4">
<div className="flex items-start gap-4 p-4 border border-border rounded-lg bg-surface">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-amber-500/10 text-amber-500 font-bold">
1
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">
Tool Development & Validation
</h4>
<p className="text-sm text-foreground-secondary">
Developer creates tool using AI SDK pattern {' '}
<code className="text-xs bg-surface-secondary px-1 py-0.5 rounded">
npx blocks run
</code>{' '}
validates against domain rules Tool published to npm
</p>
</div>
</div>
<div className="flex items-start gap-4 p-4 border border-border rounded-lg bg-surface">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-emerald-500/10 text-emerald-500 font-bold">
2
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Registry Discovery</h4>
<p className="text-sm text-foreground-secondary">
TPMJS changes feed detects new package Schema extraction & health checks
Tool indexed with quality score Available via API
</p>
</div>
</div>
<div className="flex items-start gap-4 p-4 border border-border rounded-lg bg-surface">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-purple-500/10 text-purple-500 font-bold">
3
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Agent Configuration</h4>
<p className="text-sm text-foreground-secondary">
HLLM user browses Tool Browser Selects tools for agent Configures topology
Saves agent configuration
</p>
</div>
</div>
<div className="flex items-start gap-4 p-4 border border-border rounded-lg bg-surface">
<span className="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-blue-500/10 text-blue-500 font-bold">
4
</span>
<div>
<h4 className="font-semibold text-foreground mb-1">Execution</h4>
<p className="text-sm text-foreground-secondary">
Agent decides to use tool HLLM calls TPMJS executor Tool runs in Deno
sandbox Result streamed back Agent continues reasoning
</p>
</div>
</div>
</div>
</section>
{/* CTA */}
<section className="text-center py-12 border border-border rounded-lg bg-surface">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold mb-4 text-foreground">
Explore the Ecosystem
</h2>
<p className="text-lg text-foreground-secondary mb-8 max-w-2xl mx-auto">
Dive deeper into each project or start building with TPMJS tools.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<Link href="/tool/tool-search">
<Button size="lg" variant="default">
Browse Tools
</Button>
</Link>
<Link href="/sdk">
<Button size="lg" variant="outline">
View SDK Docs
</Button>
</Link>
<Link href="/how-it-works">
<Button size="lg" variant="outline">
How It Works
</Button>
</Link>
</div>
</section>
</Container>
</main>
</div>
);
}

View file

@ -45,6 +45,11 @@ export function AppHeader(): React.ReactElement {
How It Works
</Button>
</Link>
<Link href="/integrations">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Integrations
</Button>
</Link>
<a href="https://playground.tpmjs.com" target="_blank" rel="noopener noreferrer">
<Button variant="ghost" size="sm" className="text-foreground hover:text-foreground">
Playground

View file

@ -0,0 +1,747 @@
'use client';
import * as d3 from 'd3';
import { useEffect, useRef, useState } from 'react';
interface Node {
id: string;
label: string;
sublabel?: string;
x: number;
y: number;
width: number;
height: number;
type: 'hllm' | 'tpmjs' | 'blocksai' | 'shared' | 'flow';
cluster?: string;
}
interface Connection {
from: string;
to: string;
label?: string;
animated?: boolean;
dashed?: boolean;
}
const nodeDescriptions: Record<string, { title: string; description: string }> = {
// HLLM cluster
'hllm-ui': {
title: 'HLLM Playground',
description:
'Next.js web app for multi-agent orchestration. Users can configure agents, select topologies, and execute workflows with real-time visualization.',
},
'hllm-topologies': {
title: '13 Topology Patterns',
description:
'Sequential, parallel, debate, reflection, consensus, brainstorm, decomposition, tree-of-thoughts, react, and more orchestration patterns.',
},
'hllm-agents': {
title: 'Agent Configuration',
description:
'Configure agents with custom models, system prompts, temperature, tools, and memory windows. Each agent can use different AI providers via OpenRouter.',
},
'hllm-tools': {
title: 'TPMJS Tool Browser',
description:
'In-app tool selector that fetches from the TPMJS registry. Browse, search, and attach tools to agents by category.',
},
// TPMJS cluster
'tpmjs-registry': {
title: 'Tool Registry',
description:
'100+ AI tools automatically discovered from npm. Searchable by category, quality score, and health status.',
},
'tpmjs-executor': {
title: 'Sandbox Executor',
description:
'Deno-based sandboxed execution environment on Railway. Tools are loaded dynamically from esm.sh and executed securely.',
},
'tpmjs-api': {
title: 'REST APIs',
description:
'/api/tools for search, /api/tools/execute for execution. Supports filtering, pagination, and SSE streaming.',
},
'tpmjs-tools': {
title: '106 Official Tools',
description:
'Web scraping, data processing, text analysis, security, statistics, agent utilities, and more.',
},
// BlocksAI cluster
'blocks-yml': {
title: 'blocks.yml',
description:
'Domain-driven configuration file defining blocks, domain rules, entities, signals, and measures.',
},
'blocks-validators': {
title: '3-Layer Validation',
description:
'Schema validation (Zod), Shape validation (TypeScript structure), Domain validation (AI-powered semantic analysis).',
},
'blocks-cli': {
title: 'blocks CLI',
description:
'npx blocks run --all validates all blocks. Supports parallel rule validation and JSON output.',
},
'blocks-ai': {
title: 'AI Validation',
description:
'Uses GPT-4 or Claude to analyze source code against domain rules. Checks semantic alignment and domain intent.',
},
// Shared technologies
'shared-d3': {
title: 'D3.js Visualizations',
description:
'Both HLLM and TPMJS use D3 for interactive diagrams: topology graphs, architecture diagrams, and flow visualizations.',
},
'shared-ai-sdk': {
title: 'Vercel AI SDK v6',
description:
'Common foundation for tool definitions using tool() + jsonSchema(). Enables streaming, multi-step execution, and tool calling.',
},
'shared-prisma': {
title: 'Prisma + PostgreSQL',
description:
'Both projects use Prisma ORM with PostgreSQL for data persistence. HLLM for sessions/traces, TPMJS for tool metadata.',
},
// Flow nodes
'flow-execute': {
title: 'Tool Execution',
description:
'HLLM calls TPMJS executor to run tools. Environment variables are encrypted per-user and injected at runtime.',
},
'flow-validate': {
title: 'Tool Validation',
description:
'TPMJS tools are validated using BlocksAI patterns. blocks.yml defines domain rules for tool quality.',
},
};
function getThemeColors(element: HTMLElement) {
const styles = getComputedStyle(element);
return {
background: styles.getPropertyValue('--color-background').trim() || '#ffffff',
foreground: styles.getPropertyValue('--color-foreground').trim() || '#0a0a0a',
foregroundSecondary:
styles.getPropertyValue('--color-foreground-secondary').trim() || '#525252',
foregroundTertiary: styles.getPropertyValue('--color-foreground-tertiary').trim() || '#737373',
border: styles.getPropertyValue('--color-border').trim() || '#e5e5e5',
primary: styles.getPropertyValue('--color-primary').trim() || '#2563eb',
};
}
export function EcosystemDiagram(): React.ReactElement {
const svgRef = useRef<SVGSVGElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [hoveredNode, setHoveredNode] = useState<string | null>(null);
const [dimensions, setDimensions] = useState({ width: 900, height: 700 });
useEffect(() => {
const updateDimensions = () => {
if (containerRef.current) {
const width = Math.min(containerRef.current.clientWidth, 950);
setDimensions({ width, height: 720 });
}
};
updateDimensions();
window.addEventListener('resize', updateDimensions);
return () => window.removeEventListener('resize', updateDimensions);
}, []);
useEffect(() => {
if (!svgRef.current || !containerRef.current) return;
const colors = getThemeColors(containerRef.current);
const svg = d3.select(svgRef.current);
svg.selectAll('*').remove();
const { width } = dimensions;
const scale = Math.min(1, width / 900);
// Cluster colors
const clusterColors = {
hllm: '#8B5CF6', // Purple
tpmjs: '#10B981', // Green
blocksai: '#F59E0B', // Amber
shared: '#6B7280', // Gray
flow: '#3B82F6', // Blue
};
// Layout positions
const nodes: Node[] = [
// HLLM cluster (top-left)
{
id: 'hllm-ui',
label: 'HLLM Playground',
sublabel: 'Multi-Agent UI',
x: 160 * scale,
y: 80,
width: 150 * scale,
height: 50,
type: 'hllm',
cluster: 'hllm',
},
{
id: 'hllm-topologies',
label: '13 Topologies',
sublabel: 'debate · react · etc',
x: 160 * scale,
y: 160,
width: 145 * scale,
height: 50,
type: 'hllm',
cluster: 'hllm',
},
{
id: 'hllm-agents',
label: 'Agent Studio',
sublabel: '100+ Models',
x: 80 * scale,
y: 240,
width: 120 * scale,
height: 50,
type: 'hllm',
cluster: 'hllm',
},
{
id: 'hllm-tools',
label: 'Tool Browser',
sublabel: 'TPMJS Integration',
x: 240 * scale,
y: 240,
width: 135 * scale,
height: 50,
type: 'hllm',
cluster: 'hllm',
},
// TPMJS cluster (top-right)
{
id: 'tpmjs-registry',
label: 'Tool Registry',
sublabel: 'tpmjs.com',
x: 740 * scale,
y: 80,
width: 130 * scale,
height: 50,
type: 'tpmjs',
cluster: 'tpmjs',
},
{
id: 'tpmjs-api',
label: 'REST APIs',
sublabel: '/api/tools',
x: 740 * scale,
y: 160,
width: 120 * scale,
height: 50,
type: 'tpmjs',
cluster: 'tpmjs',
},
{
id: 'tpmjs-executor',
label: 'Sandbox',
sublabel: 'Deno Runtime',
x: 650 * scale,
y: 240,
width: 115 * scale,
height: 50,
type: 'tpmjs',
cluster: 'tpmjs',
},
{
id: 'tpmjs-tools',
label: '106 Tools',
sublabel: '@tpmjs/tools-*',
x: 820 * scale,
y: 240,
width: 115 * scale,
height: 50,
type: 'tpmjs',
cluster: 'tpmjs',
},
// BlocksAI cluster (bottom)
{
id: 'blocks-yml',
label: 'blocks.yml',
sublabel: 'Domain Config',
x: 350 * scale,
y: 560,
width: 120 * scale,
height: 50,
type: 'blocksai',
cluster: 'blocksai',
},
{
id: 'blocks-validators',
label: '3-Layer Pipeline',
sublabel: 'Schema→Shape→Domain',
x: 550 * scale,
y: 560,
width: 160 * scale,
height: 50,
type: 'blocksai',
cluster: 'blocksai',
},
{
id: 'blocks-cli',
label: 'blocks CLI',
sublabel: 'npx blocks run',
x: 350 * scale,
y: 640,
width: 120 * scale,
height: 50,
type: 'blocksai',
cluster: 'blocksai',
},
{
id: 'blocks-ai',
label: 'AI Validation',
sublabel: 'GPT-4 / Claude',
x: 550 * scale,
y: 640,
width: 130 * scale,
height: 50,
type: 'blocksai',
cluster: 'blocksai',
},
// Shared technologies (middle)
{
id: 'shared-ai-sdk',
label: 'AI SDK v6',
sublabel: 'tool() + jsonSchema()',
x: 450 * scale,
y: 340,
width: 145 * scale,
height: 45,
type: 'shared',
cluster: 'shared',
},
{
id: 'shared-d3',
label: 'D3.js',
sublabel: 'Visualizations',
x: 320 * scale,
y: 400,
width: 100 * scale,
height: 45,
type: 'shared',
cluster: 'shared',
},
{
id: 'shared-prisma',
label: 'Prisma',
sublabel: 'PostgreSQL',
x: 580 * scale,
y: 400,
width: 100 * scale,
height: 45,
type: 'shared',
cluster: 'shared',
},
// Flow nodes (integration points)
{
id: 'flow-execute',
label: 'Tool Execution',
sublabel: 'HLLM → TPMJS',
x: 450 * scale,
y: 200,
width: 140 * scale,
height: 45,
type: 'flow',
cluster: 'flow',
},
{
id: 'flow-validate',
label: 'Validation',
sublabel: 'TPMJS → BlocksAI',
x: 650 * scale,
y: 470,
width: 130 * scale,
height: 45,
type: 'flow',
cluster: 'flow',
},
];
const connections: Connection[] = [
// HLLM internal
{ from: 'hllm-ui', to: 'hllm-topologies', animated: true },
{ from: 'hllm-topologies', to: 'hllm-agents', animated: true },
{ from: 'hllm-topologies', to: 'hllm-tools', animated: true },
// TPMJS internal
{ from: 'tpmjs-registry', to: 'tpmjs-api', animated: true },
{ from: 'tpmjs-api', to: 'tpmjs-executor', animated: true },
{ from: 'tpmjs-api', to: 'tpmjs-tools', animated: true },
// BlocksAI internal
{ from: 'blocks-yml', to: 'blocks-validators', animated: true },
{ from: 'blocks-yml', to: 'blocks-cli', animated: true },
{ from: 'blocks-validators', to: 'blocks-ai', animated: true },
// Cross-project integrations
{ from: 'hllm-tools', to: 'flow-execute', animated: true, label: 'fetch tools' },
{ from: 'flow-execute', to: 'tpmjs-api', animated: true, label: 'execute' },
{ from: 'tpmjs-tools', to: 'flow-validate', animated: true, label: 'validate' },
{ from: 'flow-validate', to: 'blocks-validators', animated: true },
// Shared technology connections (dashed)
{ from: 'hllm-topologies', to: 'shared-ai-sdk', dashed: true },
{ from: 'tpmjs-tools', to: 'shared-ai-sdk', dashed: true },
{ from: 'hllm-ui', to: 'shared-d3', dashed: true },
{ from: 'tpmjs-registry', to: 'shared-d3', dashed: true },
{ from: 'hllm-ui', to: 'shared-prisma', dashed: true },
{ from: 'tpmjs-registry', to: 'shared-prisma', dashed: true },
];
// Create defs
const defs = svg.append('defs');
// Glow filter
const glow = defs
.append('filter')
.attr('id', 'eco-glow')
.attr('x', '-50%')
.attr('y', '-50%')
.attr('width', '200%')
.attr('height', '200%');
glow.append('feGaussianBlur').attr('stdDeviation', '3').attr('result', 'coloredBlur');
const glowMerge = glow.append('feMerge');
glowMerge.append('feMergeNode').attr('in', 'coloredBlur');
glowMerge.append('feMergeNode').attr('in', 'SourceGraphic');
// Shadow filter
const shadow = defs
.append('filter')
.attr('id', 'eco-shadow')
.attr('x', '-20%')
.attr('y', '-20%')
.attr('width', '140%')
.attr('height', '140%');
shadow
.append('feDropShadow')
.attr('dx', '0')
.attr('dy', '2')
.attr('stdDeviation', '4')
.attr('flood-color', colors.foreground)
.attr('flood-opacity', '0.08');
// Arrow markers for each cluster
Object.entries(clusterColors).forEach(([key, color]) => {
defs
.append('marker')
.attr('id', `eco-arrow-${key}`)
.attr('viewBox', '0 -5 10 10')
.attr('refX', 8)
.attr('refY', 0)
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('fill', color);
});
const mainGroup = svg.append('g');
// Draw cluster backgrounds
const clusters = [
{
id: 'hllm',
label: 'HLLM',
x: 30 * scale,
y: 40,
width: 300 * scale,
height: 280,
color: clusterColors.hllm,
},
{
id: 'tpmjs',
label: 'TPMJS',
x: 580 * scale,
y: 40,
width: 300 * scale,
height: 280,
color: clusterColors.tpmjs,
},
{
id: 'blocksai',
label: 'BlocksAI',
x: 280 * scale,
y: 520,
width: 350 * scale,
height: 190,
color: clusterColors.blocksai,
},
];
clusters.forEach((cluster) => {
const clusterGroup = mainGroup.append('g');
// Background
clusterGroup
.append('rect')
.attr('x', cluster.x)
.attr('y', cluster.y)
.attr('width', cluster.width)
.attr('height', cluster.height)
.attr('rx', 12)
.attr('fill', cluster.color)
.attr('opacity', 0.05)
.attr('stroke', cluster.color)
.attr('stroke-width', 1)
.attr('stroke-opacity', 0.3);
// Label
clusterGroup
.append('text')
.attr('x', cluster.x + 12)
.attr('y', cluster.y + 24)
.attr('fill', cluster.color)
.attr('font-size', '14px')
.attr('font-weight', '700')
.attr('font-family', 'ui-monospace, monospace')
.text(cluster.label);
});
// Draw connections
connections.forEach((conn) => {
const fromNode = nodes.find((n) => n.id === conn.from);
const toNode = nodes.find((n) => n.id === conn.to);
if (!fromNode || !toNode) return;
const fromCluster = fromNode.cluster || 'shared';
const color = clusterColors[fromCluster as keyof typeof clusterColors];
// Calculate connection points
const dx = toNode.x - fromNode.x;
const dy = toNode.y - fromNode.y;
const angle = Math.atan2(dy, dx);
const startX = fromNode.x + (fromNode.width / 2) * Math.cos(angle);
const startY = fromNode.y + (fromNode.height / 2) * Math.sin(angle);
const endX = toNode.x - (toNode.width / 2 + 8) * Math.cos(angle);
const endY = toNode.y - (toNode.height / 2 + 8) * Math.sin(angle);
const midX = (startX + endX) / 2;
const midY = (startY + endY) / 2;
// Curved path
const pathData = `M ${startX} ${startY} Q ${midX} ${midY - 20}, ${endX} ${endY}`;
// Background path
mainGroup
.append('path')
.attr('d', pathData)
.attr('fill', 'none')
.attr('stroke', colors.border)
.attr('stroke-width', 1.5)
.attr('opacity', 0.3);
// Main path
const mainPath = mainGroup
.append('path')
.attr('d', pathData)
.attr('fill', 'none')
.attr('stroke', color)
.attr('stroke-width', conn.dashed ? 1 : 1.5)
.attr('stroke-dasharray', conn.dashed ? '4,4' : conn.animated ? '6,10' : 'none')
.attr('stroke-opacity', conn.dashed ? 0.4 : 0.7)
.attr('marker-end', `url(#eco-arrow-${fromCluster})`);
// Animate
if (conn.animated && !conn.dashed) {
const animate = () => {
mainPath
.attr('stroke-dashoffset', 0)
.transition()
.duration(2000)
.ease(d3.easeLinear)
.attr('stroke-dashoffset', -32)
.on('end', animate);
};
animate();
}
// Connection label
if (conn.label) {
mainGroup
.append('text')
.attr('x', midX)
.attr('y', midY - 8)
.attr('text-anchor', 'middle')
.attr('fill', colors.foregroundTertiary)
.attr('font-size', '9px')
.attr('font-family', 'ui-monospace, monospace')
.text(conn.label);
}
});
// Draw nodes
nodes.forEach((node, i) => {
const nodeColor = clusterColors[node.type as keyof typeof clusterColors];
const nodeGroup = mainGroup
.append('g')
.attr('transform', `translate(${node.x}, ${node.y})`)
.style('cursor', 'pointer')
.on('mouseenter', function () {
setHoveredNode(node.id);
d3.select(this).select('.main-rect').transition().duration(200).attr('stroke-width', 2.5);
d3.select(this).select('.node-glow').transition().duration(200).attr('opacity', 0.3);
})
.on('mouseleave', function () {
setHoveredNode(null);
d3.select(this).select('.main-rect').transition().duration(200).attr('stroke-width', 1.5);
d3.select(this).select('.node-glow').transition().duration(200).attr('opacity', 0);
});
// Glow effect
nodeGroup
.append('rect')
.attr('class', 'node-glow')
.attr('x', -node.width / 2 - 4)
.attr('y', -node.height / 2 - 4)
.attr('width', node.width + 8)
.attr('height', node.height + 8)
.attr('rx', 10)
.attr('fill', nodeColor)
.attr('opacity', 0)
.attr('filter', 'url(#eco-glow)');
// Main rectangle
nodeGroup
.append('rect')
.attr('class', 'main-rect')
.attr('x', -node.width / 2)
.attr('y', -node.height / 2)
.attr('width', node.width)
.attr('height', node.height)
.attr('rx', 8)
.attr('fill', colors.background)
.attr('stroke', nodeColor)
.attr('stroke-width', 1.5)
.attr('filter', 'url(#eco-shadow)');
// Label
nodeGroup
.append('text')
.attr('x', 0)
.attr('y', node.sublabel ? -4 : 4)
.attr('text-anchor', 'middle')
.attr('fill', colors.foreground)
.attr('font-size', '11px')
.attr('font-weight', '600')
.attr('font-family', 'ui-monospace, monospace')
.text(node.label);
// Sublabel
if (node.sublabel) {
nodeGroup
.append('text')
.attr('x', 0)
.attr('y', 12)
.attr('text-anchor', 'middle')
.attr('fill', colors.foregroundTertiary)
.attr('font-size', '9px')
.attr('font-family', 'ui-monospace, monospace')
.text(node.sublabel);
}
// Entrance animation
nodeGroup
.attr('opacity', 0)
.attr('transform', `translate(${node.x}, ${node.y - 10})`)
.transition()
.delay(100 + i * 40)
.duration(400)
.ease(d3.easeCubicOut)
.attr('opacity', 1)
.attr('transform', `translate(${node.x}, ${node.y})`);
});
// Legend
const legendY = 680;
const legendItems = [
{ label: 'HLLM', color: clusterColors.hllm },
{ label: 'TPMJS', color: clusterColors.tpmjs },
{ label: 'BlocksAI', color: clusterColors.blocksai },
{ label: 'Integration', color: clusterColors.flow },
{ label: 'Shared Tech', color: clusterColors.shared },
];
const legendGroup = mainGroup
.append('g')
.attr('transform', `translate(${width / 2 - 200}, ${legendY})`);
legendItems.forEach((item, index) => {
const itemGroup = legendGroup.append('g').attr('transform', `translate(${index * 85}, 0)`);
itemGroup
.append('rect')
.attr('width', 12)
.attr('height', 12)
.attr('rx', 3)
.attr('fill', item.color)
.attr('opacity', 0.8);
itemGroup
.append('text')
.attr('x', 16)
.attr('y', 10)
.attr('fill', colors.foregroundSecondary)
.attr('font-size', '10px')
.attr('font-family', 'ui-monospace, monospace')
.text(item.label);
});
}, [dimensions]);
const hoveredInfo = hoveredNode ? nodeDescriptions[hoveredNode] : null;
return (
<div ref={containerRef} className="w-full">
<div className="relative p-4 md:p-6 border border-border rounded-xl bg-surface/50 backdrop-blur overflow-hidden">
{/* Grid background */}
<div
className="absolute inset-0 opacity-[0.02]"
style={{
backgroundImage: `
linear-gradient(to right, currentColor 1px, transparent 1px),
linear-gradient(to bottom, currentColor 1px, transparent 1px)
`,
backgroundSize: '32px 32px',
}}
/>
<svg
ref={svgRef}
width={dimensions.width}
height={dimensions.height}
viewBox={`0 0 ${dimensions.width} ${dimensions.height}`}
className="mx-auto relative"
style={{ maxWidth: '100%', height: 'auto' }}
role="img"
aria-label="Ajax's AI Infrastructure Ecosystem diagram showing HLLM, TPMJS, and BlocksAI"
/>
</div>
{/* Tooltip */}
{hoveredInfo && (
<div className="mt-4 px-4 py-3 bg-background border border-border rounded-lg shadow-lg text-sm">
<div className="text-foreground-secondary">
<span className="font-semibold text-foreground">{hoveredInfo.title}</span> {' '}
{hoveredInfo.description}
</div>
</div>
)}
</div>
);
}

View file

@ -14,6 +14,7 @@ const navLinks = [
{ href: '/tool/tool-search', label: 'Tools' },
{ href: '/docs', label: 'Docs' },
{ href: '/how-it-works', label: 'How It Works' },
{ href: '/integrations', label: 'Integrations' },
{ href: 'https://playground.tpmjs.com', label: 'Playground', external: true },
{ href: '/spec', label: 'Spec' },
{ href: '/sdk', label: 'SDK' },
@ -63,7 +64,8 @@ export function MobileMenu({ isOpen, onClose }: MobileMenuProps): React.ReactEle
isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'
}`}
onClick={onClose}
aria-hidden="true"
onKeyDown={(e) => e.key === 'Escape' && onClose()}
role="presentation"
/>
{/* Slide-out drawer */}