diff --git a/README.md b/README.md index a805d0d6..329b9add 100644 --- a/README.md +++ b/README.md @@ -2,307 +2,214 @@ AgentField - Kubernetes, for AI Agents -> **πŸ‘‹ Welcome Early Adopter!** -> -> You've discovered AgentField before our official launch. We're currently in private beta, gathering feedback from early users to shape the future of the autonomous software. Feel free to explore and test, and we'd love to hear your thoughts! Share feedback via [GitHub Issues](https://github.com/Agent-Field/agentfield/issues) or email us at contact@agentfield.ai. Please note that features and APIs are still being refined before our public release. - -### Kubernetes for AI Agents - **Deploy, Scale, Observe, and Prove** - -Open-source (Apache-2.0) **control plane** that runs AI agents like microservices. -Every agent gets **REST/gRPC APIs**, **async execution & webhooks**, **built-in observability**, and **cryptographic identity & audit**. +# Kubernetes for AI Agents +### **Deploy, Scale, Observe, and Prove.** [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![Go](https://img.shields.io/badge/go-1.21+-00ADD8.svg)](https://go.dev/) [![Python](https://img.shields.io/badge/python-3.9+-3776AB.svg)](https://www.python.org/) [![Deploy with Docker](https://img.shields.io/badge/deploy-docker-2496ED.svg)](https://docs.docker.com/) -**[πŸ“š Docs](https://agentfield.ai/docs)** β€’ **[⚑ Quickstart](#quickstart-in-60-seconds)** β€’ **[🧠 Why AgentField](#why-agentfield)** +**[πŸ“š Documentation](https://agentfield.ai/docs)** β€’ **[⚑ Quick Start](#-quick-start-in-60-seconds)** β€’ **[🧠 Why AgentField](#-why-agentfield)** --- -## πŸš€ **Ship Production-Ready AI Agents in Minutes** - -βœ… **Write agents in Python/Go** (or any language via REST/gRPC) - -βœ… **Deploy independently** like microservices-zero coordination between teams +> **πŸ‘‹ Welcome Early Adopter!** +> +> You've discovered AgentField before our official launch. We're currently in private beta, gathering feedback from early users to shape the future of the autonomous software. Feel free to explore and test, and we'd love to hear your thoughts! Share feedback via [GitHub Issues](https://github.com/Agent-Field/agentfield/issues) or email us at contact@agentfield.ai. Please note that features and APIs are still being refined before our public release. -βœ… **Get production infrastructure automatically**: -- **IAM & cryptographic audit trails** - W3C DIDs + Verifiable Credentials -- **REST APIs, streaming, async queues** - auto-generated endpoints -- **Built-in observability & metrics** - Prometheus + workflow DAGs +--- -βœ… **Run anywhere**: local dev, Docker, Kubernetes, cloud +## πŸš€ What is AgentField? -```bash -curl -fsSL https://agentfield.ai/install.sh | bash && af init my-agent --defaults -``` +**AgentField is "Kubernetes for AI Agents."** -**[πŸ“š Full Docs](https://agentfield.ai/docs)** β€’ **[⚑ Quick Start](https://agentfield.ai/docs/quick-start)** β€’ **[🎯 Examples](https://github.com/agentfield/agentfield-examples)** +It is an open-source **Control Plane** that treats AI agents as first-class citizens. Instead of building fragile, monolithic scripts, AgentField lets you deploy agents as **independent microservices** that can discover each other, coordinate complex workflows, and scale infinitelyβ€”all with built-in observability and cryptographic trust. ---- -## πŸš€ Try AgentField in 2 Minutes +Write standard Python (or Go). Get a production-grade distributed system automatically. -### Option 1: Local Install +```python +from agentfield import Agent -```bash -# macOS/Linux - install CLI -curl -fsSL https://agentfield.ai/install.sh | bash +# 1. Define an Agent (It's just a microservice) +app = Agent(node_id="researcher", model="gpt-4o") -# Start control plane + create your first agent -af init my-agent --defaults && cd my-agent -af run -``` +# 2. Create a Skill (Deterministic code) +@app.skill() +def fetch_url(url: str) -> str: + return requests.get(url).text -### Option 2: Docker Compose - -```bash -git clone https://github.com/agentfield/agentfield -cd agentfield && docker compose up +# 3. Create a Reasoner (AI-powered logic) +# This automatically becomes a REST API endpoint: POST /execute/researcher.summarize +@app.reasoner() +async def summarize(url: str) -> dict: + content = fetch_url(url) + # Native AI call with structured output + return await app.ai(f"Summarize this content: {content}") + +# 4. Run it +if __name__ == "__main__": + app.run() ``` -Your control plane is running at `http://localhost:8080` +**What you get for free:** +* βœ… **Instant API:** `POST /api/v1/execute/researcher.summarize` +* βœ… **Durable Execution:** Resumes automatically if the server crashes. +* βœ… **Observability:** You get a full execution DAG, metrics, and logs automatically. +* βœ… **Audit:** Every step produces a cryptographically signed Verifiable Credential. -
-export AGENT_CALLBACK_URL="http://host.docker.internal:8001" +--- -**Heads-up:** When your agent nodes run outside the Docker network (local shell, another VM, etc.) they can't be reached through `localhost`. Before starting any agent, set a callback URL that the control plane can dial: +## πŸš€ Quick Start in 60 Seconds +### 1. Install ```bash -export AGENT_CALLBACK_URL="http://host.docker.internal:8001" +curl -fsSL https://agentfield.ai/install.sh | bash ``` -Replace `host.docker.internal` with whatever host/IP the control plane can reach if you're on Linux or a remote machine. - -
- -**[πŸ“š Full quickstart guide β†’](https://agentfield.ai/docs/quick-start)** - -```python -from agentfield import Agent - -# Create an agent -app = Agent(node_id="greeting-agent", - model="openrouter/meta-llama/llama-4-maverick") - -# Decorate a function-becomes a REST endpoint automatically -@app.reasoner() -async def say_hello(name: str) -> dict: - - message = await app.ai(f"Generate a personalized greeting for {name}") - - return {"greeting": message} +### 2. Initialize +```bash +af init my-agent --defaults && cd my-agent ``` -**Deploy:** +### 3. Run ```bash -export OPENROUTER_API_KEY="sk-..." af run ``` -**Call from anywhere** (REST API auto-generated): +### 4. Call ```bash -curl -X POST http://localhost:8080/api/v1/execute/greeting-agent.say_hello \ +curl -X POST http://localhost:8080/api/v1/execute/researcher.summarize \ -H "Content-Type: application/json" \ - -d '{"input": {"name": "Alice"}}' + -d '{"input": {"url": "https://example.com"}}' ``` -**You automatically get:** -- βœ… REST API at `/execute/greeting-agent.say_hello` (OpenAPI spec at `/openapi.yaml`) -- βœ… Async execution: `/execute/async/...` with webhook callbacks (HMAC-signed) -- βœ… Prometheus metrics: `/metrics` -- βœ… Workflow Observability - - -**[πŸ“š Docs](https://agentfield.ai/docs)** β€’ **[⚑ More examples](https://github.com/agentfield/agentfield-examples)** - - - -## Why AgentField? +
+🐳 Docker / Troubleshooting -**TL;DR:** Most agent frameworks are built for prototypes. AgentField is infrastructure for production. If you've tried running multi-agent systems in production, you've hit these problems: agents deployed as a monolith (one team's change redeploys everyone), no proof of what your AI did for auditors, manual service discovery between agents, and building your own queues/webhooks/state management for long running AI nested calls. AgentField ships as a control plane + agent nodes architecture - like Kubernetes for autonomous software. Deploy agents independently, get cryptographic audit trails, and coordinate through zero-config shared memory. Every reasoner becomes a REST endpoint automatically. +If you are running AgentField in Docker, you may need to set a callback URL so the Control Plane can reach your agent: -### From Prototype to Production - -| πŸ”΄ Building Without AgentField | πŸ’š Building With AgentField | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **No cryptographic identity** - agents are names in logs; when regulators ask "prove this AI made this decision," you have editable logs with no tamper-proof record | **DIDs + Verifiable Credentials** - every agent gets a W3C DID; every execution produces a signed VC; export cryptographic proof chains that auditors verify offline with `af verify audit.json` | -| **Monolithic deployments** - all agents in one codebase; Marketing team's update redeploys Support team's agents; coordination nightmare across teams | **Control plane coordinates independent agents** - each team deploys their agent on their own schedule; discovery/routing/orchestration handled by stateless control plane; zero coordination needed | -| **Lost context across agent boundaries** - Agent A calls Agent B, you can't trace the full execution; no visibility into multi-agent workflows | **Context propagation built-in** - `workflow_id`, `execution_id`, `session_id` flow automatically through headers; see complete DAG of which agent called which, when, why - distributed tracing without instrumentation | -| **Manual state management** - set up Redis/database yourself; handle race conditions; write sync logic for agents to share data | **Zero-config shared memory fabric** - `await app.memory.set("key", val)` works across distributed agents; hierarchical scopes (workflow/session/actor/global); real-time change events via `@app.memory.on_change("key")` | -| **DIY async infrastructure** - implement PostgreSQL queues with `FOR UPDATE SKIP LOCKED`, build webhook delivery with HMAC signing and retries, handle backpressure, graceful shutdown | **Durable execution ships working** - PostgreSQL-backed queues, automatic retries with exponential backoff, HMAC webhook delivery (GitHub-style), fair scheduling, Prometheus metrics, health checks for K8s - no assembly required | -| **Timeout hell for nested AI calls** - HTTP timeouts kill long-running reasoners; Agent A calls Agent B calls Agent C fails after 60s; build custom async queues and polling infrastructure | **Durable async execution** - reasoners run for hours/days without timeout; nested agent workflows (Aβ†’Bβ†’C) work natively; async endpoints + webhooks built-in; no external queue infrastructure needed | -| **Hardcoded integrations** - manually wire up service discovery; build custom REST wrappers for frontend teams; maintain API gateway; coordinate URLs across deployments | **Auto-discovery + instant APIs** - call any agent via `await app.call("agent.function")`; every `@app.reasoner()` becomes `/api/v1/execute/agent.function` automatically; React/iOS/Android call via HTTP, no SDK needed | +```bash +export AGENT_CALLBACK_URL="http://host.docker.internal:8001" +``` +
+--- ## 🎨 See It In Action
- -AgentField Dashboard - +AgentField Dashboard
- -**Real-time Scaling/Observability β€’ Execution traces β€’ IAM β€’ Verifiable Credentials** - -*Everything you need to run production AI agents-built in, zero configuration* - +Real-time Observability β€’ Execution DAGs β€’ Verifiable Credentials
+--- +## 🧠 Why AgentField? -## What You Get Out-of-the-Box +**Software is starting to behave less like scripts and more like reasoning systems.** +Once agents act across APIs, data layers, and critical paths, they need infrastructure: identity, routing, retries, observability, policies. We built AgentField because agents should behave as predictably as microservices. -🧩 Scale Infrastructure - deploy like microservices - β€’ Durable async execution for long-running & nested AI workflows (hours/days, no timeouts) - β€’ Auto-generated queues, webhooks (HMAC-signed), event streaming - β€’ Auto-discovery & cross-agent calls; context propagation - β€’ Horizontal scaling & many more..! +### From Prototype to Production -πŸ” Trust & Governance - cryptographic proof for every decision - β€’ W3C IDs & Verifiable Credentials - β€’ Tamper-proof audit trails; runtime policy enforcement - β€’ Offline verification for auditors +Most frameworks (LangChain, CrewAI) are great for prototyping. But when you move to production, you hit walls: **Non-deterministic execution times**, **Multi-agent coordination**, and **Compliance**. -πŸ›° Production Hardening - observability & reliability built in - β€’ Auto-generated workflow DAGs - β€’ Prometheus metrics, structured logs - β€’ Graceful shutdowns, retries, zero-config memory +AgentField isn't a framework you extend. It's **infrastructure** that solves these problems out of the box. -**Learn more:** [Features](https://agentfield.ai/docs/features) β€’ [Identity & Trust](https://agentfield.ai/docs/why-agentfield/vs-agent-frameworks) +| Capability | Traditional Frameworks | AgentField (Infrastructure) | +| :--------------- | :------------------------------- | :-------------------------------------------- | +| **Architecture** | Monolithic application | **Distributed Microservices** | +| **Team Model** | Single team, single repo | **Independent teams & deployments** | +| **Integration** | Custom SDK per language | **Standard REST/gRPC APIs** | +| **Coordination** | Manual message passing | **Service Discovery & Auto-DAGs** | +| **Memory** | Configure vector stores manually | **Zero-config Scoped Memory & Vector Search** | +| **Async** | Roll your own queues | **Durable Queues, Webhooks, Retries** | +| **Trust** | "Trust me" logs | **DIDs & Verifiable Credentials** | --- -## πŸ—οΈ Architecture +## 🎯 Who is this for? -
-AgentField Architecture Diagram -
+* **Backend Engineers** shipping AI into production who want standard APIs, not magic. +* **Platform Teams** who don't want to build another homegrown orchestrator. +* **Enterprise Teams** in regulated industries (Finance, Health) needing audit trails. +* **Frontend Developers** who just want to `fetch()` an agent without Python headaches. --- -| Layer | What It Does | -| ------------- | ------------------------------------------------------------- | -| Control Plane | Stateless Go service; routes, observes, verifies, scales | -| Agent Nodes | Your independent agent microservices (Python/Go/REST/gRPC) | -| Interfaces | Backends via REST; frontends & external APIs via webhooks/SSE | - -Each agent is a microservice. Teams deploy independently; the control plane makes them behave as one coherent system. - -**More:** [Architecture](https://agentfield.ai/docs/architecture) β€’ [API Reference](https://agentfield.ai/docs/api) - +## πŸ’Ž Key Features -## Real-Time & Async +### 🧩 Scale Infrastructure +* **Control Plane:** Stateless Go service that handles routing and state. +* **Async by Default:** Fire-and-forget or wait for results. Handles long-running tasks (hours/days) with **Webhooks**. +* **Shared Memory Fabric:** Built-in, scoped memory (Workflow/Session/User) with **Vector Search** out of the box. No Redis/Pinecone required. -- **Unified API:** `POST /api/v1/execute/{agent.reasoner}` -- **Async runs:** `/execute/async/...` + signed webhooks -- **Live streams:** Server-Sent Events (SSE) for real-time output -- **Auto retries, backpressure, dead-letter queues** +### πŸ›‘οΈ Identity & Trust +* **W3C DIDs:** Every agent has a cryptographic identity. +* **Verifiable Credentials:** Prove *exactly* what the AI did. +* **Policy:** "Only agents signed by 'Finance' can access this tool." -**Docs:** [API Reference](https://agentfield.ai/docs/api) β€’ [Observability](https://agentfield.ai/docs/observability) +### πŸ”­ Observability +* **DAG Visualization:** See the logic flow in real-time. +* **Metrics:** Prometheus endpoints at `/metrics`. +* **Logs:** Structured, correlated logs. +--- -## Identity & Audit (opt-in per agent) - -- DIDs auto-issued for agents (`did:web` / `did:key`) -- Verifiable Credentials (W3C JSON-LD) for each execution -- Input/output hashing for proof integrity -- Offline verification for auditors (`af vc verify audit.json`) - -**Docs:** [Identity & Trust](https://agentfield.ai/docs/why-agentfield/vs-agent-frameworks) - - - -## Installation - -### macOS / Linux - -```bash -curl -fsSL https://agentfield.ai/get | bash -agentfield --version -``` +## πŸ”Œ Interoperability -### Docker Compose +Call your agents from anywhere. No SDK required. -```bash -git clone https://github.com/Agent-Field/agentfield -cd agentfield && docker compose up +**Frontend (React/Next.js):** +```javascript +const response = await fetch("http://localhost:8080/api/v1/execute/researcher.summarize", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ input: { url: "https://example.com" } }), +}); +const result = await response.json(); ``` -**Full guides:** [Installation](https://agentfield.ai/docs/installation) β€’ [Deployment](https://agentfield.ai/docs/deployment) - --- -## When to Use (and When Not) - -### βœ… Use AgentField If: - -- You're building **multi-agent systems** that need to coordinate -- You need **independent deployment**-multiple teams, different schedules -- You need **production infrastructure**: REST APIs, async queues, observability, health checks -- You need **compliance/audit trails** (finance, healthcare, legal) -- You want to **call agents from frontends** (React, mobile) without custom wrappers -- You're scaling to **multiple environments** (dev, staging, prod) and need consistency - -### ❌ Start with a Framework If: - -- You're building a **single-agent chatbot** that will never scale beyond one service -- You don't need REST APIs, observability, or multi-agent coordination -- You're prototyping and don't plan to deploy to production - -### The Bottom Line - -**Frameworks = Build agents** (perfect for learning) -**AgentField = Build and run agents at any scale** (perfect from prototype to production) - -You can start with AgentField and skip migration pain later. Or start with a framework and migrate when you hit the pain points above. - - - -## Community - -We're building AgentField in the open. Join us: - -- **[πŸ“š Documentation](https://agentfield.ai/docs)** - Guides, API reference, examples -- **[πŸ’‘ GitHub Discussions](https://github.com/agentfield/agentfield/discussions)** - Feature requests, Q&A -- **[🐦 Twitter/X](https://x.com/agentfield_dev)** - Updates and announcements - -### Contributing - -Apache 2.0 licensed. Built by developers like you. - -See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and guidelines. - - - - -## πŸ“– Resources +## πŸ—οΈ Architecture -- **[πŸ“š Documentation](https://agentfield.ai/docs)** - Complete guides and API reference -- **[⚑ Quick Start Tutorial](https://agentfield.ai/docs/quick-start)** - Build your first agent in 5 minutes -- **[πŸ—οΈ Architecture Deep Dive](https://agentfield.ai/docs/architecture)** - How AgentField works under the hood -- **[πŸ“¦ Examples Repository](https://github.com/agentfield/agentfield-examples)** - Production-ready agent templates -- **[πŸ“ Blog](https://agentfield.ai/blog)** - Tutorials, case studies, best practices +
+AgentField Architecture Diagram +
--- -
+## βš–οΈ Is AgentField for you? -### ⭐ Star us to follow development +### βœ… YES if: +* You are building **multi-agent systems**. +* You need **independent deployment** (multiple teams). +* You need **compliance/audit trails**. +* You want **production infrastructure** (Queues, Retries, APIs). -**Built by developers who got tired of duct-taping agents together** +### ❌ NO if: +* You are building a **single-agent chatbot**. +* You are just **prototyping** and don't care about scale yet. -**Join the future of autonomous software** +--- -**[🌐 Website](https://agentfield.ai) β€’ [πŸ“š Docs](https://agentfield.ai/docs) β€’ [🐦 Twitter](https://x.com/agentfield_dev)** +## 🀝 Community -**License:** [Apache 2.0](LICENSE) +**Agents are becoming part of production backends. They need identity, governance, and infrastructure. That’s why AgentField exists.** ---- +* **[πŸ“š Documentation](https://agentfield.ai/docs)** +* **[πŸ’‘ GitHub Discussions](https://github.com/agentfield/agentfield/discussions)** +* **[🐦 Twitter/X](https://x.com/agentfield_dev)** +* **[πŸ“¦ Examples](https://github.com/agentfield/agentfield-examples)** -*We believe autonomous software needs infrastructure that respects what makes it different-agents that reason, decide, and coordinate-while providing the same operational excellence that made traditional software successful.* +
+**Built by developers who got tired of duct-taping agents together.** +**[🌐 Website](https://agentfield.ai)**
diff --git a/control-plane/web/client/src/components/EnhancedExecutionsTable.tsx b/control-plane/web/client/src/components/EnhancedExecutionsTable.tsx index 09909455..b471e06b 100644 --- a/control-plane/web/client/src/components/EnhancedExecutionsTable.tsx +++ b/control-plane/web/client/src/components/EnhancedExecutionsTable.tsx @@ -226,11 +226,11 @@ export function EnhancedExecutionsTable({ return (
- + -
+
-
+
VC
-
+
-
+
setHoveredRow(execution.execution_id)} onMouseLeave={() => setHoveredRow(null)} onClick={() => onExecutionClick?.(execution)} > -
+ {/* Hover Accent Bar */} +
+ +
@@ -332,13 +335,13 @@ export function EnhancedExecutionsTable({
- + {execution.relative_time}
- + {execution.duration_display}
@@ -350,7 +353,7 @@ export function EnhancedExecutionsTable({
- + …{execution.execution_id.slice(-8)}
diff --git a/control-plane/web/client/src/components/Navigation/SidebarNew.tsx b/control-plane/web/client/src/components/Navigation/SidebarNew.tsx index a315fcd2..038e7db1 100644 --- a/control-plane/web/client/src/components/Navigation/SidebarNew.tsx +++ b/control-plane/web/client/src/components/Navigation/SidebarNew.tsx @@ -14,6 +14,7 @@ import { useSidebar, } from "@/components/ui/sidebar"; import { Icon } from "@/components/ui/icon"; +import { cn } from "@/lib/utils"; interface SidebarNewProps { sections: NavigationSection[]; @@ -24,19 +25,19 @@ export function SidebarNew({ sections }: SidebarNewProps) { const isCollapsed = state === "collapsed"; return ( - + {/* Header - Add bottom spacing and subtle border separator for visual hierarchy */} - + - + -
+
- AgentField - Open Control Plane + AgentField + v1.0.0
@@ -45,15 +46,15 @@ export function SidebarNew({ sections }: SidebarNewProps) { {/* Content - Add spacing between groups */} - + {sections.map((section) => ( - + {/* Apply caption styling for clear header differentiation */} - + {section.title} {/* Add gap after header */} - + {section.items.map((item) => ( @@ -62,8 +63,9 @@ export function SidebarNew({ sections }: SidebarNewProps) { isActive={false} tooltip={isCollapsed ? item.label : undefined} disabled + className="h-8 text-[13px]" > - {item.icon && } + {item.icon && } {item.label} ) : ( @@ -73,9 +75,18 @@ export function SidebarNew({ sections }: SidebarNewProps) { asChild isActive={isActive} tooltip={isCollapsed ? item.label : undefined} + className={cn( + "h-8 text-[13px] transition-all duration-200 relative", + isActive + ? "bg-sidebar-accent text-sidebar-accent-foreground font-medium shadow-sm" + : "text-muted-foreground hover:text-foreground hover:bg-sidebar-accent/50" + )} > - - {item.icon && } + + {isActive && ( +
+ )} + {item.icon && } {item.label} diff --git a/control-plane/web/client/src/components/SearchWithFilters.tsx b/control-plane/web/client/src/components/SearchWithFilters.tsx index 006ff483..64b390a7 100644 --- a/control-plane/web/client/src/components/SearchWithFilters.tsx +++ b/control-plane/web/client/src/components/SearchWithFilters.tsx @@ -144,8 +144,13 @@ export function SearchWithFilters({ />
- {/* Dropdown indicator */} -
+ {/* Command Hint & Dropdown indicator */} +
+ {!inputValue && tags.length === 0 && ( + + ⌘K + + )} { } const baseShadow = - "0 12px 24px -14px color-mix(in srgb, var(--foreground) 22%, transparent)"; + "0 4px 12px -2px color-mix(in srgb, var(--foreground) 10%, transparent), 0 2px 6px -1px color-mix(in srgb, var(--foreground) 6%, transparent)"; const accentShadow = `0 0 0 1px ${borderColor}`; - const glowShadow = isDimmed ? "" : `0 0 18px ${glowColor}`; + const glowShadow = isDimmed ? "" : `0 0 12px -2px ${glowColor}`; const compositeShadow = [accentShadow, baseShadow, glowShadow].filter(Boolean).join(", "); - const baseBackground = `linear-gradient(135deg, color-mix(in srgb, ${statusColorVar} 12%, transparent), var(--card))`; + const baseBackground = `linear-gradient(145deg, color-mix(in srgb, ${statusColorVar} 8%, transparent), var(--card))`; let background = baseBackground; if (!hasHighlight) { @@ -357,22 +357,22 @@ export const WorkflowNode = memo(({ data, selected }: WorkflowNodeProps) => { return (
{/* Agent color left border accent */}
- + {label} - + {value}
@@ -102,8 +101,6 @@ export function ExecutionHeader({ }: ExecutionHeaderProps) { const navigate = useNavigate(); const normalizedStatus = normalizeStatus(execution.status); - const statusLabel = - normalizedStatus.charAt(0).toUpperCase() + normalizedStatus.slice(1); const workflowTags = execution.workflow_tags ?? []; const webhookEvents = Array.isArray(execution.webhook_events) ? [...execution.webhook_events].sort( @@ -134,7 +131,7 @@ export function ExecutionHeader({ : webhookSuccessCount > 0 ? `${webhookSuccessCount} delivered` : webhookPending - ? "Pending webhook" + ? "Pending" : "Registered"; const handleNavigateBack = () => { @@ -152,266 +149,269 @@ export function ExecutionHeader({ navigate(`/executions?session_id=${execution.session_id}`); return ( -
- {/* Back Navigation */} -
- + + Executions + + / + {truncateId(execution.execution_id)}
- {/* Main Header - Clean Linear Style */} -
-
-

- {execution.reasoner_id} -

- - {statusLabel} - {webhookRegistered && ( - - - 0 - ? "border-destructive/40 text-destructive" - : webhookSuccessCount > 0 - ? "border-emerald-500/40 text-emerald-500" - : "border-border text-muted-foreground", - )} - > - - {webhookBadgeLabel} - - - -
-
-

- {webhookPending - ? "Awaiting first delivery" - : latestWebhookEvent - ? `Last webhook ${formatWebhookStatusLabel(latestWebhookEvent.status)}` - : "Webhook registered"} -

-

- {webhookPending && - "We will display the latest delivery details as soon as the callback is reported."} - {!webhookPending && latestWebhookEvent && ( - <> - {formatWebhookStatusLabel(latestWebhookEvent.status)} - {latestWebhookEvent.http_status ? ` β€’ HTTP ${latestWebhookEvent.http_status}` : ""} - + {/* Main Header */} +

+ {/* Top Row: Title & Status */} +
+
+
+

+ {execution.reasoner_id} +

+ + {webhookRegistered && ( + + + 0 + ? "border-destructive/40 text-destructive" + : webhookSuccessCount > 0 + ? "border-emerald-500/40 text-emerald-500" + : "border-border text-muted-foreground", )} - {!webhookPending && !latestWebhookEvent && "No deliveries recorded yet."} -

-
- {latestWebhookTimestamp && ( - - {latestWebhookTimestamp} - - )} -
+ > + + {webhookBadgeLabel} + + + +
+
+

+ {webhookPending + ? "Awaiting first delivery" + : latestWebhookEvent + ? `Last webhook ${formatWebhookStatusLabel(latestWebhookEvent.status)}` + : "Webhook registered"} +

+

+ {webhookPending && + "We will display the latest delivery details as soon as the callback is reported."} + {!webhookPending && latestWebhookEvent && ( + <> + {formatWebhookStatusLabel(latestWebhookEvent.status)} + {latestWebhookEvent.http_status ? ` β€’ HTTP ${latestWebhookEvent.http_status}` : ""} + + )} + {!webhookPending && !latestWebhookEvent && "No deliveries recorded yet."} +

+
+ {latestWebhookTimestamp && ( + + {latestWebhookTimestamp} + + )} +
-
- 0 ? "success" : "muted"} - /> - 0 ? "danger" : "muted"} - /> - -
+
+ 0 ? "success" : "muted"} + /> + 0 ? "danger" : "muted"} + /> + +
- {latestWebhookEvent?.error_message && ( -
- {latestWebhookEvent.error_message} -
- )} -
- - )} -
+ {latestWebhookEvent?.error_message && ( +
+ {latestWebhookEvent.error_message} +
+ )} + + + )} +
-
-
- Agent: - - {execution.agent_node_id} - - + {/* Tags Row */} + {workflowTags.length > 0 && ( +
+ {workflowTags.map((tag) => ( + + {tag} + + ))} +
+ )}
+ {/* Primary Actions / VC Status */}
- DID: - -
- -
- ID: - - {truncateId(execution.execution_id)} - - + {vcLoading ? ( + Loading VC... + ) : vcStatus?.has_vc ? ( + + ) : null}
+
- {vcLoading ? ( -
- VC: - Loading… + {/* Metadata Grid - The "Developer Dashboard" Look */} +
+ {/* Column 1: Identity */} +
+
+
Execution ID
+
+ + {execution.execution_id} + + +
- ) : vcStatus?.has_vc ? ( -
- VC: - +
+
Agent Node
+
+ + {truncateId(execution.agent_node_id)} + + + +
- ) : null} -
- -
-
- Workflow: - -
- {execution.session_id && ( -
- Session: - - + {/* Column 2: Context */} +
+
+
Workflow
+
+ + +
- )} - -
- Request: - - {execution.agentfield_request_id - ? truncateId(execution.agentfield_request_id) - : "n/a"} - - {execution.agentfield_request_id && ( - + {execution.session_id && ( +
+
Session
+
+ + +
+
)}
-
- {workflowTags.length > 0 && ( -
- Tags: -
- {workflowTags.map((tag) => ( - - {tag} - - ))} + {/* Column 3: Performance */} +
+
+
Duration
+
+ + {formatDuration(execution.duration_ms)} +
+
+
+
Retries
+
+ + {execution.retry_count} +
-
- )} - -
-
- - Duration: - - {formatDuration(execution.duration_ms)} - -
- -
- - Input: - - {formatBytes(execution.input_size)} - -
- -
- - Output: - - {formatBytes(execution.output_size)} -
-
- - Retries: - - {execution.retry_count} - + {/* Column 4: I/O */} +
+
+
Data Transfer
+
+
+ + {formatBytes(execution.input_size)} +
+
+ + {formatBytes(execution.output_size)} +
+
+
+
+
Request ID
+
+ + {execution.agentfield_request_id + ? truncateId(execution.agentfield_request_id) + : "n/a"} + + {execution.agentfield_request_id && ( + + )} +
+
diff --git a/control-plane/web/client/src/components/ui/badge.tsx b/control-plane/web/client/src/components/ui/badge.tsx index b57b3b2b..1f867f47 100644 --- a/control-plane/web/client/src/components/ui/badge.tsx +++ b/control-plane/web/client/src/components/ui/badge.tsx @@ -13,7 +13,7 @@ import { import type { IconComponent, IconWeight } from "@/components/ui/icon-bridge" const badgeVariants = cva( - "inline-flex items-center gap-1.5 rounded-lg border border-transparent px-2.5 py-1 text-xs font-medium transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", + "inline-flex items-center gap-1.5 rounded-md border border-transparent px-2 py-0.5 text-xs font-medium transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", { variants: { variant: { @@ -31,32 +31,32 @@ const badgeVariants = cva( outline: "text-text-primary border border-border bg-transparent hover:bg-bg-hover shadow-sm", metadata: - "rounded-md bg-muted/40 text-text-secondary border border-border/60 px-2 py-1 text-body-small font-medium", + "rounded-md bg-muted/40 text-text-secondary border border-border/60 px-1.5 py-0.5 text-[10px] font-medium font-mono", count: - "rounded-full bg-bg-secondary text-text-primary border border-border-secondary px-2 py-0.5 text-caption font-semibold uppercase tracking-wide", + "rounded-full bg-bg-secondary text-text-primary border border-border-secondary px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide", pill: - "rounded-full bg-muted/30 text-text-primary border border-border/40 px-3 py-1 text-body-small", + "rounded-full bg-muted/30 text-text-primary border border-border/40 px-2.5 py-0.5 text-[11px]", // Status variants with standardized colors and icons success: - cn(getStatusBadgeClasses("success" satisfies StatusTone)), + cn(getStatusBadgeClasses("success" satisfies StatusTone), "font-mono tracking-tight"), failed: - cn(getStatusBadgeClasses("error" satisfies StatusTone)), + cn(getStatusBadgeClasses("error" satisfies StatusTone), "font-mono tracking-tight"), running: - cn(getStatusBadgeClasses("info" satisfies StatusTone)), + cn(getStatusBadgeClasses("info" satisfies StatusTone), "font-mono tracking-tight"), pending: - cn(getStatusBadgeClasses("warning" satisfies StatusTone)), + cn(getStatusBadgeClasses("warning" satisfies StatusTone), "font-mono tracking-tight"), // Additional status variants for degraded states degraded: - cn(getStatusBadgeClasses("warning" satisfies StatusTone)), + cn(getStatusBadgeClasses("warning" satisfies StatusTone), "font-mono tracking-tight"), unknown: - cn(getStatusBadgeClasses("neutral" satisfies StatusTone)), + cn(getStatusBadgeClasses("neutral" satisfies StatusTone), "font-mono tracking-tight"), }, size: { - sm: "px-2 py-0.5 text-xs", - md: "px-2.5 py-1 text-xs", - lg: "px-3 py-1.5 text-sm", + sm: "px-1.5 py-0 text-[10px]", + md: "px-2 py-0.5 text-xs", + lg: "px-3 py-1 text-sm", }, }, defaultVariants: { diff --git a/control-plane/web/client/src/components/ui/button.tsx b/control-plane/web/client/src/components/ui/button.tsx index fe87aacc..9cfef372 100644 --- a/control-plane/web/client/src/components/ui/button.tsx +++ b/control-plane/web/client/src/components/ui/button.tsx @@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 active:scale-[0.98] active:translate-y-[0.5px]", { variants: { variant: { @@ -21,10 +21,11 @@ const buttonVariants = cva( link: "text-primary underline-offset-4 hover:underline", }, size: { - default: "h-9 px-4 py-2", - sm: "h-8 rounded-md px-3 text-sm", + default: "h-8 px-4 py-2", // Compact default for developer tools + sm: "h-7 rounded-md px-3 text-xs", lg: "h-10 rounded-md px-8 text-base", - icon: "h-9 w-9", + icon: "h-8 w-8", + "icon-sm": "h-7 w-7", }, }, defaultVariants: { diff --git a/control-plane/web/client/src/index.css b/control-plane/web/client/src/index.css index 7e83ca4e..22258801 100644 --- a/control-plane/web/client/src/index.css +++ b/control-plane/web/client/src/index.css @@ -187,6 +187,15 @@ main:focus, letter-spacing: 0.05em; } +.text-label { + font-size: 10px; + line-height: 1.2; + color: var(--text-tertiary); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + /* Interactive elements */ .interactive-hover { transition: all var(--transition-fast); diff --git a/control-plane/web/client/src/pages/EnhancedDashboardPage.tsx b/control-plane/web/client/src/pages/EnhancedDashboardPage.tsx index 32285af9..3cd42123 100644 --- a/control-plane/web/client/src/pages/EnhancedDashboardPage.tsx +++ b/control-plane/web/client/src/pages/EnhancedDashboardPage.tsx @@ -242,13 +242,13 @@ export function EnhancedDashboardPage() { const generatedAt = formatTimestamp(data.generated_at); return ( -
+
- + {generatedAt}