feat: ship agent-first mission control v0.2.57
CI - Build & Test / Backend (.NET) (push) Successful in 42s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 2m46s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Successful in 56s

This commit is contained in:
AzuTear
2026-07-31 22:39:47 +02:00
parent 3bc7622977
commit f5552218bc
535 changed files with 95242 additions and 8791 deletions
+2
View File
@@ -0,0 +1,2 @@
fund=false
audit=false
@@ -0,0 +1,4 @@
{
"d1": null,
"r2": null
}
+22
View File
@@ -0,0 +1,22 @@
# Prototype Instructions
Run the local server yourself and open the preview in the browser available to this environment. Do not give the user server-start instructions when you can run it.
Before making substantial visual changes, use the Product Design plugin's `get-context` skill when the visual source is unclear or no longer matches the current goal. When the user gives durable prototype-specific design feedback, preferences, or decisions, record them in `AGENTS.md`.
When implementing from a selected generated mock, treat that image as the source of truth for layout, component anatomy, density, spacing, color, typography, visible content, and hierarchy.
Build app UI in `src/`. Keep `.openai/hosting.json`, `worker/index.js`, `scripts/prepare-sites-build.mjs`, and `tests/sites-worker.test.mjs` intact so the same local prototype can be handed to Sites. Before a Sites handoff, run `npm run build` and `npm run test:sites`; the build must leave `dist/client/index.html`, `dist/server/index.js`, and `dist/.openai/hosting.json`.
## Selected Nexus direction
- Source visual: `reference/selected-option-2.png`, the second displayed
Run-Control concept selected on 2026-07-27.
- This is a desktop-only controlled prototype. Do not create a mobile view,
phone shell, mobile breakpoint, or standalone Mobile Chat destination.
- Preserve the selected spatial-orchestration composition and the existing
Nexus Galaxy/Glass design contract.
- Agent nodes, approval steps, event rows, and provider/session metadata must
open useful detail views.
- Keep all behavior authored and local. Do not call Nexus, OpenClaw, OpenAI, or
another backend, and label the surface as mock data.
+36
View File
@@ -0,0 +1,36 @@
# Nexus Run Control — Interactive Mockup
Controlled desktop prototype based on the second selected Run Control concept.
It is intentionally isolated from the production Nexus frontend and performs no
Nexus, OpenClaw, OpenAI, authentication, persistence, or network operations.
## Prototype scope
- Spatial Iris-led run topology
- Clickable Iris, Programmer, Reviewer, and Executor nodes
- Clickable run-event details
- Clickable OpenClaw session and OpenAI-via-OpenClaw routing details
- Human-approval review, approval success, and reversible mock decision
- Pause, resume, stop, and restart mock states
- Local Ask Iris dialog with an explicitly authored response
- Desktop-only layout; no Mobile Chat route or mobile interface
## Evidence level
This artifact is a controlled browser prototype with authored fixture data. It
does not prove a live Gateway, provider connection, agent execution, cost,
token count, tool call, approval, or production mutation.
The selected source visual is
[`reference/selected-option-2.png`](reference/selected-option-2.png).
## Local preview
```powershell
npm install
npm run dev -- --host 0.0.0.0 --port 4174 --strictPort
```
The verified visual and interaction report is in
[`design-qa.md`](design-qa.md), with the final side-by-side comparison under
[`evidence/comparison-final.png`](evidence/comparison-final.png).
@@ -0,0 +1,63 @@
# Structural proof preflight
## Product contract
- **Surface:** desktop operational product UI
- **Selected source:** `reference/selected-option-2.png`
- **Concept sentence:** For an owner supervising an OpenClaw run, Run Control
should feel like live mission instrumentation by combining an authoritative
agent topology with one connected decision inspector, while avoiding a
dashboard inventory of unrelated cards.
- **Evidence boundary:** authored controlled prototype; no backend or provider
request
- **Supported width:** 1100 CSS px and wider; primary proof at `1440 x 1024`
- **Explicit exclusion:** no mobile layout and no standalone Mobile Chat page
## Entry mode
`task-first`. The selected run name, runtime state, session reference, active
agent path, and pending approval are visible before the first material action.
The first material action is `Review approval`; it opens a connected dialog
without changing the mock decision.
## Section grammar
| Section | Role | User question | Grammar |
|---|---|---|---|
| Sidebar/topbar | Orient | Where am I and which run is active? | fixed navigation and compact run metadata |
| Status strip | Orient | Is anything waiting or blocked? | one lightweight status row |
| Agent topology | Explain | Who is doing what, and what depends on whom? | spatial nodes with one highlighted execution path |
| Event rail | Prove | What happened and where did the state come from? | chronological rows opening event details |
| Inspector | Explain/Act | What is selected and what can I do safely? | one persistent context rail |
| Approval dialog | Act | What exactly am I approving? | scope, impact, immutable context, explicit decision |
| Toast/recovery | Recover | What changed and can I reverse the mock action? | connected status plus visible undo/restart |
## Signature gesture
The active topology path is the single dominant gesture. A restrained glow may
echo on the selected node and primary approval action. Event rows, dense
metadata, error/recovery copy, and modal body text must remain plain and
high-contrast.
## State and copy truth table
| State | Visible facts | Allowed action | Forbidden claim | Recovery/focus |
|---|---|---|---|---|
| Pending approval | Executor waiting; scope not yet approved | inspect agents/events/session/provider; review approval | deployment started | dialog close restores trigger focus |
| Approval dialog | exact authored scope and no-production-mutation impact | approve, request changes, close | live policy check | close returns to approval trigger |
| Approved | decision recorded; Executor preparing artifacts | inspect; undo mock decision | deployment executed | undo restores pending state |
| Paused | run overlay and paused label | resume; inspect details | agents advancing | Resume in topbar |
| Stopped | stopped overlay and recovery card | inspect; restart mock run | runtime still active | Restart restores pending approval |
| Event detail | actor, time, correlation, source | close | live trace evidence | close restores event-row focus |
| Provider/session detail | authored connected state and explicit no-request copy | close | verified live provider or secret visibility | close restores metadata-row focus |
| Ask Iris | explicit mock label and authored response | submit local text, close | live model response | close restores topbar trigger |
## Proof selectors and settle condition
- Primary canvas: `[data-testid="run-canvas"]`
- Executor node: `[data-testid="agent-executor"]`
- Approval node: `[data-testid="approval-node"]`
- Review trigger: `[data-testid="review-approval"]`
- Confirmation: `[data-testid="confirm-approval"]`
- Settle condition: local fonts loaded, the canvas is visible, no dialog
transition is pending, and the selected node geometry is stable.
+60
View File
@@ -0,0 +1,60 @@
# Design QA — Run Control Option 2
## Comparison contract
- Source: `reference/selected-option-2.png`
- Source dimensions: `1487 x 1058`
- Implementation: `evidence/implementation-final-1440.png`
- Implementation viewport: `1440 x 1024`, CSS pixels, desktop density
- State: running, Executor selected, approval pending
- Full-view comparison: `evidence/comparison-final.png`
- Initial comparison: `evidence/comparison-initial.png`
- Scope: complete desktop viewport; no mobile state exists by design
The source and implementation have the same aspect ratio to rounding. The
combined comparison normalizes both views to the same rendered dimensions.
## Iteration record
The initial comparison identified P2 mismatches in shell proportions, the
full-width status strip, inspector hierarchy, event density, and galaxy
contrast. The implementation was corrected by:
- reducing the sidebar and matching the source workspace/inspector ratio;
- constraining the status strip and removing the large canvas border;
- replacing the generic Executor agent facts with source-aligned step details;
- fitting all six authoritative event rows into the visible rail;
- strengthening the galaxy field and inspector depth;
- preserving the selected source's blue-violet hierarchy and restrained glow.
## Final assessment
- P0: none
- P1: none
- P2: none
- P3: the prototype names the owner Bao rather than the generated source's
placeholder identity, and `Review approval` intentionally opens a detail
dialog before the reversible mock decision.
- Layout: no clipping or unintended horizontal overflow at `1440 x 1024`.
- Typography: bundled Manrope, Space Grotesk, and JetBrains Mono load locally.
- Accessibility: controls have accessible names, modal focus is restored,
Escape closes dialogs, and focus-visible states are present.
- Browser console: no warnings or errors after the complete interaction path.
## Interaction proof
Passed in the in-app browser:
1. Agent node to persistent detail inspector.
2. Event row to correlation/source detail.
3. OpenClaw session and OpenAI-via-OpenClaw routing details.
4. Approval review, approve, updated Executor state, and undo.
5. Ask Iris local authored response and Escape/focus restoration.
6. Pause/resume and stop/restart recovery.
## Automated proof
- `npm run test:sites`: 4/4 passed.
- `npm run build`: passed; 1807 modules transformed.
Result: passed
Binary file not shown.

After

Width:  |  Height:  |  Size: 760 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

+12
View File
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Prototype</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
{
"name": "run-control-option-2",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build && node scripts/prepare-sites-build.mjs",
"preview": "vite preview",
"test:sites": "node --test tests/sites-worker.test.mjs"
},
"dependencies": {
"@fontsource/jetbrains-mono": "^5.3.0",
"@fontsource/manrope": "^5.3.0",
"@fontsource/space-grotesk": "^5.3.0",
"@vitejs/plugin-react": "5.0.4",
"lucide-react": "^1.27.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"vite": "6.4.2"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

@@ -0,0 +1,21 @@
#!/usr/bin/env node
import { copyFileSync, existsSync, mkdirSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const dist = path.join(root, "dist");
const index = path.join(dist, "client", "index.html");
const worker = path.join(root, "worker", "index.js");
const hosting = path.join(root, ".openai", "hosting.json");
for (const file of [index, worker, hosting]) {
if (!existsSync(file)) throw new Error("Missing Sites build input: " + file);
}
mkdirSync(path.join(dist, "server"), { recursive: true });
mkdirSync(path.join(dist, ".openai"), { recursive: true });
copyFileSync(worker, path.join(dist, "server", "index.js"));
copyFileSync(hosting, path.join(dist, ".openai", "hosting.json"));
console.log("Prepared Sites build: dist/server/index.js and dist/.openai/hosting.json");
+966
View File
@@ -0,0 +1,966 @@
import { useMemo, useRef, useState } from "react";
import {
Activity,
ArrowRight,
BadgeCheck,
Bell,
BookOpen,
Bot,
Boxes,
Brain,
CalendarDays,
Check,
CheckCircle2,
ChevronRight,
CircleDollarSign,
Clock3,
Code2,
Cpu,
FileCheck2,
FolderKanban,
History,
KeyRound,
LayoutDashboard,
Link2,
ListTodo,
MessageSquareText,
Network,
Pause,
Play,
RotateCcw,
Settings,
ShieldCheck,
SlidersHorizontal,
Sparkles,
Square,
Terminal,
TriangleAlert,
UserRound,
Wrench,
X,
} from "lucide-react";
import { Modal } from "./Modal.jsx";
import {
agents,
approval,
navGroups,
runDetails,
timelineEvents,
} from "./data.js";
const iconMap = {
dashboard: LayoutDashboard,
runs: Activity,
agents: Bot,
projects: FolderKanban,
tasks: ListTodo,
memory: Brain,
docs: BookOpen,
models: SlidersHorizontal,
activity: Activity,
calendar: CalendarDays,
security: ShieldCheck,
incidents: TriangleAlert,
notifications: Bell,
iris: Sparkles,
programmer: Code2,
reviewer: FileCheck2,
executor: Terminal,
};
function Sidebar({ onPrototypeNav }) {
return (
<aside className="sidebar" aria-label="Nexus navigation">
<div className="brand">
<span className="brand-mark" aria-hidden="true">
<Sparkles size={22} />
</span>
<span>
<strong>NEXUS</strong>
<small>Mission Control</small>
</span>
</div>
<nav className="navigation">
{navGroups.map((group) => (
<section className="nav-group" key={group.label}>
<h2>{group.label}</h2>
<div className="nav-list">
{group.items.map((item) => {
const Icon = iconMap[item.icon];
return (
<button
className={`nav-item${item.active ? " is-active" : ""}`}
type="button"
key={item.id}
aria-current={item.active ? "page" : undefined}
onClick={() => onPrototypeNav(item.label, item.active)}
>
<Icon size={17} strokeWidth={1.8} aria-hidden="true" />
<span>{item.label}</span>
{item.count !== undefined ? (
<span className={`nav-count${item.count > 0 ? " has-items" : ""}`}>
{item.count}
</span>
) : null}
</button>
);
})}
</div>
</section>
))}
</nav>
<div className="sidebar-footer">
<button
className="nav-item"
type="button"
onClick={() => onPrototypeNav("Settings", false)}
>
<Settings size={17} strokeWidth={1.8} aria-hidden="true" />
<span>Settings</span>
</button>
<button
className="owner"
type="button"
onClick={() => onPrototypeNav("Owner profile", false)}
>
<span className="owner-avatar">B</span>
<span>
<strong>Bao</strong>
<small>Owner</small>
</span>
<ChevronRight size={16} aria-hidden="true" />
</button>
</div>
</aside>
);
}
function Topbar({ runState, onPause, onStop, onAskIris }) {
const stateLabel =
runState === "paused" ? "Paused" : runState === "stopped" ? "Stopped" : "Running";
return (
<header className="topbar">
<div className="run-identity">
<span className={`runtime-dot runtime-dot--${runState}`} aria-hidden="true" />
<span>
<small>Run</small>
<strong>{runDetails.title}</strong>
</span>
<span className={`run-state run-state--${runState}`}>{stateLabel}</span>
</div>
<dl className="topbar-meta">
<div>
<dt>Elapsed</dt>
<dd>{runDetails.elapsed}</dd>
</div>
<div>
<dt>Started</dt>
<dd>{runDetails.started}</dd>
</div>
<div>
<dt>Session</dt>
<dd>{runDetails.sessionId}</dd>
</div>
</dl>
<div className="topbar-actions">
<button
className="button button--secondary"
type="button"
onClick={onPause}
disabled={runState === "stopped"}
>
{runState === "paused" ? (
<Play size={17} aria-hidden="true" />
) : (
<Pause size={17} aria-hidden="true" />
)}
{runState === "paused" ? "Resume" : "Pause"}
</button>
<button
className="button button--danger"
type="button"
onClick={onStop}
disabled={runState === "stopped"}
>
<Square size={15} aria-hidden="true" />
Stop
</button>
<button className="button button--primary" type="button" onClick={onAskIris}>
<Sparkles size={17} aria-hidden="true" />
Ask Iris
</button>
</div>
</header>
);
}
function AgentNode({ agent, selected, approvalState, onSelect }) {
const Icon = iconMap[agent.icon];
const status =
agent.id === "executor" && approvalState === "approved"
? "Preparing artifacts"
: agent.status;
const progress =
agent.id === "executor" && approvalState === "approved" ? 16 : agent.progress;
const state =
agent.id === "executor" && approvalState === "approved" ? "working" : agent.state;
return (
<button
className={`agent-node agent-node--${agent.id} state-${state}${
selected ? " is-selected" : ""
}`}
type="button"
aria-pressed={selected}
aria-label={`Open ${agent.name} details`}
onClick={() => onSelect(agent.id)}
data-testid={`agent-${agent.id}`}
>
<span className="node-heading">
<span className="agent-icon">
<Icon size={18} strokeWidth={1.8} aria-hidden="true" />
</span>
<span className="node-title">
<strong>{agent.name}</strong>
<small>{agent.role}</small>
</span>
<span className={`status-dot status-dot--${state}`} aria-hidden="true" />
</span>
<span className="node-status">{status}</span>
<span className="progress-track" aria-hidden="true">
<span className="progress-value" style={{ width: `${progress}%` }} />
</span>
<span className="node-footer">
<span>{state === "completed" ? "Completed" : state === "waiting" ? "Waiting" : "Active"}</span>
<span>{progress}%</span>
</span>
</button>
);
}
function OrchestrationMap({
selectedAgent,
approvalState,
runState,
onSelectAgent,
onOpenApproval,
}) {
return (
<section
className={`orchestration-map run-${runState}`}
aria-label="Live agent orchestration"
data-testid="run-canvas"
>
<div className="connection connection--iris-programmer" aria-hidden="true" />
<div className="connection connection--iris-reviewer" aria-hidden="true" />
<div className="connection connection--iris-executor" aria-hidden="true" />
<div className="connection connection--executor-approval" aria-hidden="true" />
<div className="connection connection--approval-deploy" aria-hidden="true" />
{Object.values(agents).map((agent) => (
<AgentNode
agent={agent}
key={agent.id}
selected={selectedAgent === agent.id}
approvalState={approvalState}
onSelect={onSelectAgent}
/>
))}
<button
className={`approval-node approval-node--${approvalState}`}
type="button"
onClick={onOpenApproval}
data-testid="approval-node"
>
<span className="approval-kicker">
{approvalState === "pending" ? "Pending approval" : "Approval granted"}
</span>
<strong>{approval.title}</strong>
<small>
{approvalState === "pending"
? "Owner decision required before Executor continues."
: "Executor may prepare versioned deployment artifacts."}
</small>
<span className="approval-meta">
<span>{approval.requestedBy}</span>
<span>{approvalState === "pending" ? "Review details" : "View decision"}</span>
</span>
</button>
<div className={`deploy-node deploy-node--${approvalState}`}>
<span>{approvalState === "pending" ? "Pending" : "In progress"}</span>
<strong>Prepare deployment</strong>
<small>
{approvalState === "pending" ? "Awaiting owner approval" : "Executor started"}
</small>
</div>
</section>
);
}
function EventRail({ approvalState, onOpenEvent }) {
const visibleEvents = useMemo(() => {
if (approvalState === "approved") {
return [
...timelineEvents,
{
id: "event-approved",
time: "17:21:12",
actor: "Bao",
title: "Approval granted",
detail: "Executor may prepare deployment artifacts.",
type: "success",
correlation: "evt_01JZR7K0B4",
},
];
}
return timelineEvents;
}, [approvalState]);
return (
<section className="event-rail" aria-labelledby="event-rail-title">
<header>
<div>
<p className="eyebrow">Authoritative mock timeline</p>
<h2 id="event-rail-title">Run events</h2>
</div>
<span>{visibleEvents.length} events</span>
</header>
<div className="event-list">
{visibleEvents.map((event) => (
<button
type="button"
className={`event-row event-row--${event.type}`}
key={event.id}
onClick={() => onOpenEvent(event)}
data-testid={`event-${event.id}`}
>
<time>{event.time}</time>
<span className="event-marker" aria-hidden="true" />
<span className="event-copy">
<strong>{event.actor}</strong>
<span>{event.title}</span>
<small>{event.detail}</small>
</span>
<ChevronRight size={16} aria-hidden="true" />
</button>
))}
</div>
</section>
);
}
function DetailRow({ icon: Icon, label, value, actionLabel, onClick }) {
const content = (
<>
<Icon size={15} strokeWidth={1.8} aria-hidden="true" />
<span>{label}</span>
<strong>{value}</strong>
{onClick ? <ChevronRight size={15} aria-hidden="true" /> : null}
</>
);
if (onClick) {
return (
<button
className="detail-row detail-row--action"
type="button"
aria-label={actionLabel}
onClick={onClick}
>
{content}
</button>
);
}
return <div className="detail-row">{content}</div>;
}
function DetailPanel({
selectedAgentId,
approvalState,
runState,
onSelectAgent,
onOpenApproval,
onOpenProvider,
onOpenSession,
onUndoApproval,
onRestart,
}) {
const selectedAgent = agents[selectedAgentId];
const Icon = iconMap[selectedAgent.icon];
const executorApproved = selectedAgentId === "executor" && approvalState === "approved";
const selectedState = executorApproved ? "working" : selectedAgent.state;
return (
<aside className="detail-panel" aria-label={`${selectedAgent.name} details`}>
<header className="detail-agent-header">
<span className="detail-agent-icon">
<Icon size={20} strokeWidth={1.8} aria-hidden="true" />
</span>
<span>
<strong>{selectedAgent.name}</strong>
<small>{executorApproved ? "Preparing artifacts" : selectedAgent.status}</small>
</span>
<span className={`status-dot status-dot--${selectedState}`} aria-hidden="true" />
<button
className="icon-button"
type="button"
aria-label="Return to Iris details"
onClick={() => onSelectAgent("iris")}
>
<X size={17} aria-hidden="true" />
</button>
</header>
<section className="detail-section">
{selectedAgentId === "executor" ? (
<>
<p className="eyebrow">Step details</p>
<span className={`step-badge step-badge--${approvalState}`}>
<ShieldCheck size={13} aria-hidden="true" />
{approvalState === "pending" ? "Pending approval" : "Approval granted"}
</span>
<h2>{approval.title}</h2>
<p className="step-description">
Assemble artifacts and configure release targets.
</p>
<dl className="agent-facts approval-facts">
<div>
<dt>Requested by</dt>
<dd>{approval.requestedBy}</dd>
</div>
<div>
<dt>Requested at</dt>
<dd>{approval.requestedAt}</dd>
</div>
<div>
<dt>Estimated duration</dt>
<dd>{approval.duration}</dd>
</div>
<div>
<dt>Status</dt>
<dd>{approvalState === "pending" ? "Waiting for approval" : "Preparing"}</dd>
</div>
<div>
<dt>Approval needed</dt>
<dd>Bao (you)</dd>
</div>
</dl>
</>
) : (
<>
<p className="eyebrow">Agent details</p>
<h2>{selectedAgent.task}</h2>
<dl className="agent-facts">
<div>
<dt>Role</dt>
<dd>{selectedAgent.role}</dd>
</div>
<div>
<dt>Started</dt>
<dd>{selectedAgent.started}</dd>
</div>
<div>
<dt>Model policy</dt>
<dd>{selectedAgent.model}</dd>
</div>
</dl>
<div className="tool-chips" aria-label="Allowed tools">
{selectedAgent.tools.map((tool) => (
<span key={tool}>{tool}</span>
))}
</div>
</>
)}
</section>
<section className="detail-section">
<p className="eyebrow">Run context</p>
<DetailRow icon={Boxes} label="Run" value={runDetails.title} />
<DetailRow
icon={Link2}
label="Session"
value={runDetails.sessionId}
actionLabel="Open OpenClaw session details"
onClick={onOpenSession}
/>
<DetailRow
icon={Cpu}
label="Provider"
value={`${runDetails.provider} via OpenClaw`}
actionLabel="Open provider and routing details"
onClick={onOpenProvider}
/>
<DetailRow icon={Bot} label="Model" value="gpt-5.2-codex" />
<DetailRow icon={FolderKanban} label="Project" value={runDetails.project} />
<DetailRow icon={ShieldCheck} label="Environment" value={runDetails.environment} />
</section>
<section className="detail-section usage-section">
<p className="eyebrow">Usage (run total)</p>
<DetailRow icon={Clock3} label="Duration" value={runDetails.elapsed} />
<DetailRow icon={Activity} label="Tokens" value={runDetails.tokens} />
<DetailRow icon={CircleDollarSign} label="Estimated cost" value={runDetails.cost} />
<DetailRow icon={Wrench} label="Tool calls" value={String(runDetails.toolCalls)} />
</section>
{selectedAgentId === "executor" ? (
<section
className={`decision-card decision-card--${approvalState}`}
aria-live="polite"
>
{approvalState === "pending" ? (
<>
<p className="eyebrow">Pending approval</p>
<h2>{approval.title}</h2>
<p>{approval.impact}</p>
<button
className="button button--primary button--full"
type="button"
onClick={onOpenApproval}
data-testid="review-approval"
>
<ShieldCheck size={17} aria-hidden="true" />
Review approval
</button>
</>
) : (
<>
<p className="eyebrow">Decision recorded</p>
<h2>Approval granted</h2>
<p>Executor is preparing artifacts. No deployment was executed.</p>
<button
className="button button--secondary button--full"
type="button"
onClick={onUndoApproval}
>
<RotateCcw size={16} aria-hidden="true" />
Undo mock decision
</button>
</>
)}
</section>
) : null}
{runState === "stopped" ? (
<section className="decision-card decision-card--stopped">
<p className="eyebrow">Run stopped</p>
<h2>The mock run is no longer advancing.</h2>
<button
className="button button--primary button--full"
type="button"
onClick={onRestart}
>
<RotateCcw size={16} aria-hidden="true" />
Restart mock run
</button>
</section>
) : null}
</aside>
);
}
function ApprovalDialog({ approvalState, onApprove, onUndo, onClose }) {
return (
<Modal
title={approvalState === "pending" ? "Review deployment preparation" : "Approval decision"}
eyebrow="Human approval"
onClose={onClose}
size="wide"
>
<div className="approval-layout">
<section>
<div className={`approval-summary approval-summary--${approvalState}`}>
{approvalState === "pending" ? (
<KeyRound size={22} aria-hidden="true" />
) : (
<CheckCircle2 size={22} aria-hidden="true" />
)}
<div>
<strong>
{approvalState === "pending" ? "Decision required" : "Decision recorded"}
</strong>
<p>{approval.impact}</p>
</div>
</div>
<h3>Requested scope</h3>
<ul className="scope-list">
{approval.scope.map((item) => (
<li key={item}>
<Check size={16} aria-hidden="true" />
<span>{item}</span>
</li>
))}
</ul>
</section>
<aside className="decision-context">
<dl>
<div>
<dt>Requested by</dt>
<dd>{approval.requestedBy}</dd>
</div>
<div>
<dt>Requested at</dt>
<dd>{approval.requestedAt}</dd>
</div>
<div>
<dt>Estimated duration</dt>
<dd>{approval.duration}</dd>
</div>
<div>
<dt>Session</dt>
<dd>{runDetails.sessionId}</dd>
</div>
</dl>
</aside>
</div>
<footer className="modal-actions">
<button className="button button--ghost" type="button" onClick={onClose}>
Close
</button>
{approvalState === "pending" ? (
<>
<button className="button button--secondary" type="button" onClick={onClose}>
Request changes
</button>
<button
className="button button--primary"
type="button"
onClick={onApprove}
data-testid="confirm-approval"
>
<Check size={17} aria-hidden="true" />
Approve and continue
</button>
</>
) : (
<button className="button button--secondary" type="button" onClick={onUndo}>
<RotateCcw size={16} aria-hidden="true" />
Undo mock decision
</button>
)}
</footer>
</Modal>
);
}
function EventDialog({ event, onClose }) {
return (
<Modal title={event.title} eyebrow="Run event detail" onClose={onClose}>
<div className="event-detail">
<span className={`event-detail-icon event-detail-icon--${event.type}`}>
<History size={20} aria-hidden="true" />
</span>
<p>{event.detail}</p>
<dl className="detail-list">
<div>
<dt>Actor</dt>
<dd>{event.actor}</dd>
</div>
<div>
<dt>Timestamp</dt>
<dd>27 Jul 2026, {event.time}</dd>
</div>
<div>
<dt>Correlation ID</dt>
<dd>{event.correlation}</dd>
</div>
<div>
<dt>Source</dt>
<dd>OpenClaw session projection</dd>
</div>
</dl>
</div>
</Modal>
);
}
function RuntimeDialog({ type, onClose }) {
const isProvider = type === "provider";
return (
<Modal
title={isProvider ? "Provider routing" : "OpenClaw session"}
eyebrow={isProvider ? "Runtime policy detail" : "Session detail"}
onClose={onClose}
size="wide"
>
<div className="runtime-detail-grid">
<section className="runtime-overview">
<span className="runtime-large-icon">
{isProvider ? <Cpu size={26} aria-hidden="true" /> : <Link2 size={26} aria-hidden="true" />}
</span>
<p className="runtime-status">
<span className="status-dot status-dot--active" aria-hidden="true" />
Authored connected state
</p>
<h3>{isProvider ? "OpenAI through OpenClaw" : runDetails.sessionId}</h3>
<p>
{isProvider
? "Nexus displays the effective OpenClaw routing policy. This prototype performs no provider request."
: "This mock projection represents the authoritative OpenClaw session without duplicating runtime state."}
</p>
</section>
<dl className="runtime-facts">
<div>
<dt>{isProvider ? "Primary provider" : "Session owner"}</dt>
<dd>{isProvider ? runDetails.provider : "Iris"}</dd>
</div>
<div>
<dt>{isProvider ? "Access path" : "Parent run"}</dt>
<dd>{isProvider ? runDetails.access : runDetails.runId}</dd>
</div>
<div>
<dt>{isProvider ? "Model policy" : "Started"}</dt>
<dd>{isProvider ? runDetails.modelPolicy : runDetails.started}</dd>
</div>
<div>
<dt>{isProvider ? "Secret visibility" : "Freshness"}</dt>
<dd>{isProvider ? "Hidden · reference only" : "Observed 8 seconds ago"}</dd>
</div>
</dl>
</div>
<footer className="modal-actions">
<button className="button button--primary" type="button" onClick={onClose}>
Done
</button>
</footer>
</Modal>
);
}
function AskIrisDialog({ onClose }) {
const [message, setMessage] = useState("");
const [reply, setReply] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
const trimmed = message.trim();
if (!trimmed) return;
setReply(
"Mock response: I would keep the approval paused until you review the requested deployment-preparation scope.",
);
};
return (
<Modal title="Ask Iris" eyebrow="Interactive mockup · no backend calls" onClose={onClose}>
<div className="iris-dialog">
<div className="iris-message">
<span className="detail-agent-icon">
<Sparkles size={18} aria-hidden="true" />
</span>
<p>
I am supervising <strong>{runDetails.title}</strong>. Ask about the current
plan, agents, or pending approval.
</p>
</div>
{reply ? (
<div className="iris-message iris-message--reply" aria-live="polite">
<span className="detail-agent-icon">
<Sparkles size={18} aria-hidden="true" />
</span>
<p>{reply}</p>
</div>
) : null}
<form onSubmit={handleSubmit}>
<label htmlFor="iris-message">Message</label>
<textarea
id="iris-message"
value={message}
onChange={(event) => setMessage(event.target.value)}
placeholder="What is blocking this run?"
rows={3}
/>
<button className="button button--primary" type="submit">
<MessageSquareText size={17} aria-hidden="true" />
Send mock message
</button>
</form>
</div>
</Modal>
);
}
export function App() {
const [selectedAgent, setSelectedAgent] = useState("executor");
const [approvalState, setApprovalState] = useState("pending");
const [runState, setRunState] = useState("running");
const [modal, setModal] = useState(null);
const [toast, setToast] = useState("");
const toastTimer = useRef(null);
const announce = (message) => {
window.clearTimeout(toastTimer.current);
setToast(message);
toastTimer.current = window.setTimeout(() => setToast(""), 3200);
};
const closeModal = () => setModal(null);
const approve = () => {
setApprovalState("approved");
setRunState("running");
setSelectedAgent("executor");
closeModal();
announce("Mock approval recorded. Executor is preparing artifacts.");
};
const undoApproval = () => {
setApprovalState("pending");
setSelectedAgent("executor");
closeModal();
announce("Mock approval restored to pending.");
};
const togglePause = () => {
const nextState = runState === "paused" ? "running" : "paused";
setRunState(nextState);
announce(nextState === "paused" ? "Mock run paused." : "Mock run resumed.");
};
const stopRun = () => {
setRunState("stopped");
closeModal();
announce("Mock run stopped. You can restart it from the detail panel.");
};
const restart = () => {
setRunState("running");
setApprovalState("pending");
setSelectedAgent("executor");
announce("Mock run restarted at the pending approval.");
};
const handlePrototypeNav = (label, active) => {
if (active) {
announce("You are already viewing Run Control.");
return;
}
announce(`${label} is outside this focused mockup.`);
};
return (
<div className="prototype-root">
<div className="desktop-gate">
<Network size={30} aria-hidden="true" />
<h1>Desktop mockup</h1>
<p>This selected concept intentionally has no mobile surface.</p>
</div>
<div className="app-shell">
<Sidebar onPrototypeNav={handlePrototypeNav} />
<div className="application">
<Topbar
runState={runState}
onPause={togglePause}
onStop={() => setModal({ type: "stop" })}
onAskIris={() => setModal({ type: "ask" })}
/>
<main className="run-workspace">
<section className="workspace-stage">
<div className="status-strip" aria-label="Run status summary">
<span>
<i className="status-dot status-dot--active" />
5 active
</span>
<span>
<i className="status-dot status-dot--waiting" />
{approvalState === "pending" ? "1 waiting" : "0 waiting"}
</span>
<span>
<i className="status-dot status-dot--idle" />0 blocked
</span>
<button
type="button"
onClick={() => announce("Auto-advance remains on for reversible mock steps.")}
>
Auto-advance <strong>On</strong>
</button>
</div>
<OrchestrationMap
selectedAgent={selectedAgent}
approvalState={approvalState}
runState={runState}
onSelectAgent={setSelectedAgent}
onOpenApproval={() => setModal({ type: "approval" })}
/>
<EventRail
approvalState={approvalState}
onOpenEvent={(event) => setModal({ type: "event", event })}
/>
</section>
<DetailPanel
selectedAgentId={selectedAgent}
approvalState={approvalState}
runState={runState}
onSelectAgent={setSelectedAgent}
onOpenApproval={() => setModal({ type: "approval" })}
onOpenProvider={() => setModal({ type: "provider" })}
onOpenSession={() => setModal({ type: "session" })}
onUndoApproval={undoApproval}
onRestart={restart}
/>
</main>
</div>
</div>
{modal?.type === "approval" ? (
<ApprovalDialog
approvalState={approvalState}
onApprove={approve}
onUndo={undoApproval}
onClose={closeModal}
/>
) : null}
{modal?.type === "event" ? (
<EventDialog event={modal.event} onClose={closeModal} />
) : null}
{modal?.type === "provider" || modal?.type === "session" ? (
<RuntimeDialog type={modal.type} onClose={closeModal} />
) : null}
{modal?.type === "ask" ? <AskIrisDialog onClose={closeModal} /> : null}
{modal?.type === "stop" ? (
<Modal title="Stop this mock run?" eyebrow="Reversible prototype action" onClose={closeModal}>
<div className="stop-dialog">
<span className="event-detail-icon event-detail-icon--approval">
<Square size={20} aria-hidden="true" />
</span>
<p>
The run will enter a stopped state. No backend or OpenClaw action is
performed, and the mock state can be restarted immediately.
</p>
</div>
<footer className="modal-actions">
<button className="button button--ghost" type="button" onClick={closeModal}>
Keep running
</button>
<button className="button button--danger" type="button" onClick={stopRun}>
<Square size={15} aria-hidden="true" />
Stop mock run
</button>
</footer>
</Modal>
) : null}
<div className={`toast${toast ? " is-visible" : ""}`} role="status" aria-live="polite">
<BadgeCheck size={17} aria-hidden="true" />
<span>{toast}</span>
</div>
</div>
);
}
@@ -0,0 +1,60 @@
import { useEffect, useId, useRef } from "react";
import { X } from "lucide-react";
export function Modal({ title, eyebrow, children, onClose, size = "medium" }) {
const titleId = useId();
const closeRef = useRef(null);
useEffect(() => {
const previousFocus = document.activeElement;
const handleKeyDown = (event) => {
if (event.key === "Escape") {
onClose();
}
};
document.addEventListener("keydown", handleKeyDown);
document.body.classList.add("dialog-open");
closeRef.current?.focus();
return () => {
document.removeEventListener("keydown", handleKeyDown);
document.body.classList.remove("dialog-open");
previousFocus?.focus?.();
};
}, [onClose]);
return (
<div className="modal-layer" role="presentation">
<button
className="modal-backdrop"
type="button"
aria-label="Close detail view"
onClick={onClose}
/>
<section
className={`modal-panel modal-panel--${size}`}
role="dialog"
aria-modal="true"
aria-labelledby={titleId}
>
<header className="modal-header">
<div>
{eyebrow ? <p className="eyebrow">{eyebrow}</p> : null}
<h2 id={titleId}>{title}</h2>
</div>
<button
ref={closeRef}
className="icon-button"
type="button"
aria-label="Close detail view"
onClick={onClose}
>
<X size={18} aria-hidden="true" />
</button>
</header>
<div className="modal-content">{children}</div>
</section>
</div>
);
}
+181
View File
@@ -0,0 +1,181 @@
export const navGroups = [
{
label: "Operations",
items: [
{ id: "dashboard", label: "Dashboard", icon: "dashboard" },
{ id: "runs", label: "Runs", icon: "runs", active: true },
{ id: "agents", label: "Agents", icon: "agents" },
{ id: "projects", label: "Projects", icon: "projects" },
{ id: "tasks", label: "Task Board", icon: "tasks", count: 3 },
],
},
{
label: "Knowledge",
items: [
{ id: "memory", label: "Memory", icon: "memory" },
{ id: "docs", label: "Docs", icon: "docs" },
],
},
{
label: "Infrastructure",
items: [
{ id: "models", label: "Models", icon: "models" },
{ id: "activity", label: "Activity", icon: "activity" },
{ id: "calendar", label: "Calendar", icon: "calendar" },
],
},
{
label: "Governance",
items: [
{ id: "security", label: "Security", icon: "security" },
{ id: "incidents", label: "Incidents", icon: "incidents", count: 0 },
{ id: "notifications", label: "Notifications", icon: "notifications", count: 3 },
],
},
];
export const agents = {
iris: {
id: "iris",
name: "Iris",
initials: "IR",
role: "Lead · Orchestrator",
status: "Delegating work",
state: "active",
progress: 100,
icon: "iris",
model: "OpenAI primary",
task: "Coordinates the release-validation plan and human approvals.",
started: "17:02:13",
tools: ["sessions_send", "tasks_delegate", "approvals_request"],
},
programmer: {
id: "programmer",
name: "Programmer",
initials: "PR",
role: "Code Engineer",
status: "Validation complete",
state: "completed",
progress: 100,
icon: "programmer",
model: "OpenAI coding policy",
task: "Validated the change set and completed 128 unit and integration checks.",
started: "17:02:28",
tools: ["workspace_read", "exec_sandboxed", "test_report"],
},
reviewer: {
id: "reviewer",
name: "Reviewer",
initials: "RV",
role: "Quality Gate",
status: "Checking changes",
state: "working",
progress: 43,
icon: "reviewer",
model: "OpenAI review policy",
task: "Reviews release notes, policy constraints, and validation evidence.",
started: "17:06:10",
tools: ["diff_read", "policy_check", "review_submit"],
},
executor: {
id: "executor",
name: "Executor",
initials: "EX",
role: "Release Engineer",
status: "Waiting for approval",
state: "waiting",
progress: 0,
icon: "executor",
model: "OpenAI execution policy",
task: "Will prepare deployment artifacts after explicit owner approval.",
started: "Queued",
tools: ["artifact_prepare", "release_validate", "deployment_stage"],
},
};
export const timelineEvents = [
{
id: "event-start",
time: "17:02:13",
actor: "Iris",
title: "Run started",
detail: "Created the execution plan for production release validation.",
type: "system",
correlation: "evt_01JZR6C90A",
},
{
id: "event-delegate-programmer",
time: "17:02:28",
actor: "Iris",
title: "Delegated to Programmer",
detail: "Validate changes and run the complete test suite.",
type: "delegation",
correlation: "evt_01JZR6D2KR",
},
{
id: "event-tests",
time: "17:05:21",
actor: "Programmer",
title: "Validation complete",
detail: "All 128 unit and integration checks passed.",
type: "success",
correlation: "evt_01JZR6M8QF",
},
{
id: "event-review",
time: "17:06:10",
actor: "Reviewer",
title: "Review started",
detail: "Checking release notes, changed files, and policy evidence.",
type: "working",
correlation: "evt_01JZR6Q8HN",
},
{
id: "event-review-ready",
time: "17:18:34",
actor: "Reviewer",
title: "Quality gate clear",
detail: "Release evidence is complete and the deployment step can be prepared.",
type: "success",
correlation: "evt_01JZR7C0PD",
},
{
id: "event-approval",
time: "17:20:55",
actor: "Iris",
title: "Approval requested",
detail: "Executor needs permission to prepare deployment artifacts.",
type: "approval",
correlation: "evt_01JZR7J4SX",
},
];
export const runDetails = {
title: "Production release validation",
runId: "run_01JZR6C7Q8M9",
sessionId: "oc_7f3a2d1e",
started: "27 Jul 2026, 17:02:13",
elapsed: "00:18:42",
provider: "OpenAI",
access: "OpenClaw Gateway",
modelPolicy: "openai-primary / release-safe",
project: "Nexus Platform",
environment: "production",
tokens: "1.42M in / 368k out",
cost: "$0.186",
toolCalls: 18,
};
export const approval = {
id: "approval_01JZR7J5B2",
title: "Prepare deployment",
requestedBy: "Iris",
requestedAt: "27 Jul 2026, 17:20:55",
duration: "~ 6 min",
scope: [
"Assemble versioned release artifacts",
"Validate production configuration",
"Prepare, but do not execute, the deployment",
],
impact: "No production mutation is included in this approval.",
};
+19
View File
@@ -0,0 +1,19 @@
import React from "react";
import { createRoot } from "react-dom/client";
import "@fontsource/manrope/400.css";
import "@fontsource/manrope/500.css";
import "@fontsource/manrope/600.css";
import "@fontsource/manrope/700.css";
import "@fontsource/space-grotesk/500.css";
import "@fontsource/space-grotesk/600.css";
import "@fontsource/space-grotesk/700.css";
import "@fontsource/jetbrains-mono/400.css";
import "@fontsource/jetbrains-mono/500.css";
import { App } from "./App.jsx";
import "./styles.css";
createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,68 @@
import assert from "node:assert/strict";
import { access } from "node:fs/promises";
import test from "node:test";
import worker from "../worker/index.js";
test("serves existing static assets without a fallback", async () => {
const calls = [];
const response = await worker.fetch(new Request("https://example.test/assets/app.js"), {
ASSETS: {
fetch: async (request) => {
calls.push(new URL(request.url).pathname);
return new Response("asset", { status: 200 });
},
},
});
assert.equal(response.status, 200);
assert.deepEqual(calls, ["/assets/app.js"]);
});
test("falls back to index.html for an unknown app route", async () => {
const calls = [];
const response = await worker.fetch(
new Request("https://example.test/flow/step-two?source=share", {
headers: { accept: "text/html" },
}),
{
ASSETS: {
fetch: async (request) => {
const url = new URL(request.url);
calls.push(url.pathname + url.search);
return new Response(url.pathname === "/index.html" ? "app" : "missing", {
status: url.pathname === "/index.html" ? 200 : 404,
});
},
},
},
);
assert.equal(response.status, 200);
assert.deepEqual(calls, ["/flow/step-two?source=share", "/index.html"]);
});
test("does not turn missing API or write requests into the app shell", async () => {
for (const request of [
new Request("https://example.test/api/missing", { headers: { accept: "application/json" } }),
new Request("https://example.test/flow", { method: "POST", headers: { accept: "text/html" } }),
]) {
let calls = 0;
const response = await worker.fetch(request, {
ASSETS: {
fetch: async () => {
calls += 1;
return new Response("missing", { status: 404 });
},
},
});
assert.equal(response.status, 404);
assert.equal(calls, 1);
}
});
test("emits the files required by Sites packaging", async () => {
await access(new URL("../dist/client/index.html", import.meta.url));
await access(new URL("../dist/server/index.js", import.meta.url));
await access(new URL("../dist/.openai/hosting.json", import.meta.url));
});
@@ -0,0 +1,19 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
build: {
outDir: "dist/client",
},
optimizeDeps: {
include: ["react", "react-dom/client"],
},
server: {
host: "0.0.0.0",
allowedHosts: ["terminal.local"],
warmup: {
clientFiles: ["./src/main.jsx"],
},
},
plugins: [react()],
});
@@ -0,0 +1,15 @@
export default {
async fetch(request, env) {
const response = await env.ASSETS.fetch(request);
const acceptsHtml = request.headers.get("accept")?.includes("text/html");
if (response.status !== 404 || !acceptsHtml || !["GET", "HEAD"].includes(request.method)) {
return response;
}
const indexUrl = new URL(request.url);
indexUrl.pathname = "/index.html";
indexUrl.search = "";
return env.ASSETS.fetch(new Request(indexUrl, request));
},
};