New README (#2419)
* docs: refresh README * docs: refine README layout and add actual agent logos * docs: fix image quality and url in twitter journey section * chore: format spawn orchestrator helper * docs: align README tweet screenshots * fix
|
|
@ -1,5 +1,6 @@
|
|||
# Generated — never hand-edit; regenerated by `npm run api` / sqlc / openapi-typescript
|
||||
frontend/src/api/schema.ts
|
||||
frontend/src/renderer/routeTree.gen.ts
|
||||
backend/internal/httpd/apispec/openapi.yaml
|
||||
|
||||
# Build outputs
|
||||
|
|
|
|||
287
README.md
|
|
@ -1,6 +1,5 @@
|
|||
<div align="center">
|
||||
|
||||
<p style="text-align: center;"><img src="ao-logo.svg" alt="Agent Orchestrator" width="200" height="200" style="max-width: 100%; height: auto; margin-left: 50px;" /></p>
|
||||
<img src="ao-logo.svg" alt="Agent Orchestrator" width="160" height="160" />
|
||||
|
||||
# Agent Orchestrator
|
||||
|
||||
|
|
@ -10,12 +9,11 @@
|
|||
[](https://github.com/AgentWrapper/agent-orchestrator/graphs/contributors)
|
||||
[](https://x.com/aoagents)
|
||||
[](https://discord.com/invite/UZv7JjxbwG)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](LICENSE)
|
||||
|
||||
An Agentic IDE that supervises parallel AI coding agents in isolated workspaces, with complete control and automatic feedback loops from CI failures, review comments, and merge conflicts.
|
||||
|
||||

|
||||
|
||||
<img src="docs/assets/readme/dashboard.png" alt="Agent Orchestrator dashboard showing parallel coding agent sessions" width="100%" />
|
||||
</div>
|
||||
|
||||
---
|
||||
|
|
@ -26,8 +24,6 @@ Agent Orchestrator is a meta-harness agent IDE for running AI coding agents in p
|
|||
|
||||
The agents still do the coding. AO provides the harness around them: isolated workspaces, live terminal access, session state, PR awareness, and automatic loops that send CI failures, review comments, and merge conflicts back to the right agent. Instead of manually coordinating a pile of agent terminals, AO turns parallel agent work into a managed workflow.
|
||||
|
||||
---
|
||||
|
||||
## Why Agent Orchestrator?
|
||||
|
||||
AI coding agents become much more useful when they can work in parallel, but parallel work gets messy quickly. Branches overlap, terminals get lost, CI failures need follow-up, review comments need replies, and merge conflicts have to reach the right worker.
|
||||
|
|
@ -40,8 +36,6 @@ Agent Orchestrator is built to keep that loop visible and manageable. It helps y
|
|||
- Route CI failures, review comments, and merge conflicts back to the right session
|
||||
- Use different agent CLIs through one common supervisor
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
At a high level, Agent Orchestrator follows a simple loop:
|
||||
|
|
@ -55,211 +49,116 @@ At a high level, Agent Orchestrator follows a simple loop:
|
|||
|
||||
The result is a local control layer for agentic coding: agents still do the coding, while Agent Orchestrator keeps their workspaces, status, terminals, and feedback loops organized.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
### Witness AO's Journey on X
|
||||
|
||||
<table border="1" style="border-collapse: collapse; width: 100%;">
|
||||
<tr>
|
||||
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
|
||||
<a href="https://x.com/agent_wrapper/status/2026329204405723180"><img src="screenshots/first.png" alt="First" width="400"></a><br><br>
|
||||
<a href="https://x.com/agent_wrapper/status/2026329204405723180">Visit</a>
|
||||
</td>
|
||||
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
|
||||
<a href="https://x.com/agent_wrapper/status/2025986105485733945"><img src="screenshots/second.png" alt="Second" width="400"></a><br><br>
|
||||
<a href="https://x.com/agent_wrapper/status/2025986105485733945">Visit</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
|
||||
<a href="https://x.com/agent_wrapper/status/2064157228400341312"><img src="screenshots/third.png" alt="Third" width="400"></a><br><br>
|
||||
<a href="https://x.com/agent_wrapper/status/2064157228400341312">Visit</a>
|
||||
</td>
|
||||
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
|
||||
<a href="https://x.com/agent_wrapper/status/2024885035774738700?s=20"><img src="screenshots/image.png" alt="Fourth" width="400"></a><br><br>
|
||||
<a href="https://x.com/agent_wrapper/status/2024885035774738700?s=20">Visit</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
[What is Agent Orchestrator?](#what-is-agent-orchestrator) • [Why Agent Orchestrator?](#why-agent-orchestrator) • [How it works](#how-it-works) • [Features](#features) • [Quick Start](#quick-start) • [Architecture](#architecture) • [Documentation](#documentation) • [Contributing](#contributing)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Description |
|
||||
| :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Agent-Agnostic Platform** | 23+ agent adapters including [Claude Code](https://code.claude.com/docs/en/overview), [OpenAI Codex](https://openai.com/), [Cursor](https://cursor.com/), [OpenCode](https://opencode.ai/), [Aider](https://aider.chat/), [Amp](https://ampcode.com/manual), [Goose](https://goose-docs.ai/), [GitHub Copilot](https://github.com/features/copilot), [Grok](https://x.ai/grok), [Qwen Code](https://github.com/QwenLM/qwen-code), [Kimi Code](https://www.kimi.com/code), [Cline](https://cline.bot/), [Continue](https://www.continue.dev/), [Kiro](https://kiro.dev/), and more |
|
||||
| **Isolated Workspaces** | Each session spawns into its own git worktree with dedicated runtime |
|
||||
| **Platform-Native Runtimes** | tmux on Darwin/Linux, conpty on Windows for optimal performance |
|
||||
| **Live PR Observation** | Provider-neutral SCM observer with automatic feedback routing |
|
||||
| **Automatic Feedback Routing** | CI failures, review comments, and merge conflicts routed to the owning agent |
|
||||
| **Durable Facts Storage** | SQLite persists immutable facts with display status derived at read time |
|
||||
| **CDC Broadcasting** | DB triggers append changes to change_log, broadcasted via SSE |
|
||||
| **Desktop Experience** | Native Electron app with React UI and live terminal streaming |
|
||||
| **Loopback-Only Daemon** | HTTP control over 127.0.0.1 with no auth, CORS, or TLS by design |
|
||||
The desktop app is the main control surface: projects on the left, active sessions in the center, and the selected session's terminal, pull request state, review runs, and browser preview in the inspector.
|
||||
|
||||
### Supported Agents
|
||||
<table>
|
||||
<tr>
|
||||
<td width="36%">
|
||||
<h3>Parallel agent sessions</h3>
|
||||
<p>Start multiple coding agents from the same project without mixing files, branches, terminals, or pull request state.</p>
|
||||
</td>
|
||||
<td width="64%">
|
||||
<img src="docs/assets/readme/dashboard.png" alt="Agent Orchestrator board with multiple parallel sessions" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="36%">
|
||||
<h3>Live terminal control</h3>
|
||||
<p>Open any session and attach to the worker terminal while keeping session summary, PR state, and follow-up actions in view.</p>
|
||||
</td>
|
||||
<td width="64%">
|
||||
<img src="docs/assets/readme/session-terminal.png" alt="Session terminal inside Agent Orchestrator" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="36%">
|
||||
<h3>Review feedback loop</h3>
|
||||
<p>Run reviewer agents, inspect review status, and route requested changes back to the right worker session.</p>
|
||||
</td>
|
||||
<td width="64%">
|
||||
<img src="docs/assets/readme/reviews-tab.png" alt="Reviews tab showing reviewer runs and actions" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="36%">
|
||||
<h3>In-app browser preview</h3>
|
||||
<p>Preview a session's local app beside the terminal so UI work, browser state, and agent output stay together.</p>
|
||||
</td>
|
||||
<td width="64%">
|
||||
<img src="docs/assets/readme/browser-preview.png" alt="Browser preview tab showing a local app preview" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Works with 23+ CLI-based coding agents including Claude Code, OpenAI Codex, Cursor, OpenCode, Aider, Amp, Goose, GitHub Copilot, Grok, Qwen Code, Kimi Code, Crush, Cline, Droid, Devin, Auggie, Continue, Kiro, and Kilo Code.
|
||||
## Supported Agents
|
||||
|
||||
For direct CLI usage, including agent readiness checks and context-aware session
|
||||
spawns, see the [CLI Guide](docs/cli/README.md).
|
||||
AO ships adapters for 23 worker agent harnesses:
|
||||
|
||||
<img src="frontend/src/landing/public/docs/logos/claude-code.svg" width="16" valign="middle" /> `claude-code` · <img src="frontend/src/landing/public/docs/logos/codex.svg" width="16" valign="middle" /> `codex` · <img src="frontend/src/landing/public/docs/logos/aider.png" width="16" valign="middle" /> `aider` · <img src="frontend/src/landing/public/docs/logos/opencode.svg" width="16" valign="middle" /> `opencode` · <img src="frontend/src/landing/public/docs/logos/grok.png" width="16" valign="middle" /> `grok` · <img src="frontend/src/landing/public/docs/logos/droid.png" width="16" valign="middle" /> `droid` · `amp` · `agy` · <img src="frontend/src/landing/public/docs/logos/crush.png" width="16" valign="middle" /> `crush` · <img src="frontend/src/landing/public/docs/logos/cursor.svg" width="16" valign="middle" /> `cursor` · <img src="frontend/src/landing/public/docs/logos/qwen.png" width="16" valign="middle" /> `qwen` · <img src="frontend/src/landing/public/docs/logos/copilot.png" width="16" valign="middle" /> `copilot` · <img src="frontend/src/landing/public/docs/logos/goose.png" width="16" valign="middle" /> `goose` · `auggie` · <img src="frontend/src/landing/public/docs/logos/continue.png" width="16" valign="middle" /> `continue` · <img src="frontend/src/landing/public/docs/logos/devin.png" width="16" valign="middle" /> `devin` · `cline` · <img src="frontend/src/landing/public/docs/logos/kimi.png" width="16" valign="middle" /> `kimi` · <img src="frontend/src/landing/public/docs/logos/kiro.png" width="16" valign="middle" /> `kiro` · <img src="frontend/src/landing/public/docs/logos/kilocode.png" width="16" valign="middle" /> `kilocode` · <img src="frontend/src/landing/public/docs/logos/vibe.png" width="16" valign="middle" /> `vibe` · <img src="frontend/src/landing/public/docs/logos/pi.png" width="16" valign="middle" /> `pi` · `autohand`
|
||||
|
||||
Reviewer agents are configured separately. The current reviewer harnesses are:
|
||||
|
||||
<img src="frontend/src/landing/public/docs/logos/claude-code.svg" width="16" valign="middle" /> `claude-code` · <img src="frontend/src/landing/public/docs/logos/codex.svg" width="16" valign="middle" /> `codex` · <img src="frontend/src/landing/public/docs/logos/opencode.svg" width="16" valign="middle" /> `opencode`
|
||||
|
||||
**If it runs in a terminal, it runs on Agent Orchestrator.**
|
||||
|
||||
---
|
||||
## Install
|
||||
|
||||
## Quick Start
|
||||
The fastest path is the same flow used by the installation docs:
|
||||
|
||||
### Prerequisites
|
||||
```bash
|
||||
npm install -g @aoagents/ao
|
||||
ao start
|
||||
```
|
||||
|
||||
| Requirement | Minimum | Recommended |
|
||||
| ----------- | ------- | ----------- |
|
||||
| Go | 1.25+ | Latest |
|
||||
| Node.js | 20+ | Latest LTS |
|
||||
| Git | Any | Latest |
|
||||
| pnpm | Any | Latest |
|
||||
Run `ao start` from the repository you want AO to manage. See the [installation guide](https://aoagents.dev/docs/installation) for pnpm, yarn, source installs, agent CLI setup, and troubleshooting.
|
||||
|
||||
**Optional:**
|
||||
You can also download the latest desktop build for your platform:
|
||||
|
||||
- `tmux` (Darwin/Linux) - For Unix runtime
|
||||
- `gh` (GitHub CLI) - For authenticated GitHub API calls
|
||||
| Platform | Download |
|
||||
| -------- | ------------------------------------------------------------------------------------------------- |
|
||||
| Windows | [Setup.exe](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
|
||||
| macOS | [Agent Orchestrator.dmg](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
|
||||
| Linux | [Agent Orchestrator.AppImage](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
|
||||
|
||||
### Installation
|
||||
## Witness AO's Journey on X
|
||||
|
||||
Download the latest release for your platform:
|
||||
|
||||
| Platform | Download |
|
||||
| ----------- | ------------------------------------------------------------------------------------------------- |
|
||||
| **Windows** | [Setup.exe](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
|
||||
| **macOS** | [Agent Orchestrator.dmg](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
|
||||
| **Linux** | [Agent Orchestrator.AppImage](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
|
||||
|
||||
**Direct Download:** [Latest Release](https://github.com/AgentWrapper/agent-orchestrator/releases/latest)
|
||||
|
||||
---
|
||||
|
||||
## Telemetry
|
||||
|
||||
Agent Orchestrator collects minimal telemetry for reliability and product understanding. Data is stored locally by default; remote transmission is opt-in via environment variables. [Read the full telemetry policy](docs/telemetry.md).
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
Agent Orchestrator is a long-running Go daemon built around **inbound/outbound port contracts** with swappable adapters.
|
||||
|
||||
**Core mental model:** OBSERVE external facts → UPDATE durable facts → DERIVE display status / ACT
|
||||
|
||||
**Key components:**
|
||||
|
||||
- **Frontend** - Electron + React UI with TanStack Router/Query and shadcn/ui
|
||||
- **Backend Daemon** - Go-based HTTP server with controllers, services, and adapters
|
||||
- **Runtime** - Platform-specific: `tmux` on Darwin/Linux, `conpty` on Windows
|
||||
- **Storage** - SQLite with change-data-capture (CDC) for real-time updates
|
||||
- **Adapters** - 23+ agent adapters, git worktree workspace, GitHub SCM integration
|
||||
|
||||
For detailed architecture diagrams, data flows, and load-bearing rules, see [architecture.md](docs/architecture.md).
|
||||
|
||||
---
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%" align="center">
|
||||
<a href="https://x.com/agent_wrapper/status/2026329204405723180">
|
||||
<img src="screenshots/tweet2.png" height="330" alt="Agent Orchestrator journey screenshot one" />
|
||||
</a>
|
||||
</td>
|
||||
<td width="37.5%" align="center">
|
||||
<a href="https://x.com/agent_wrapper/status/2025986105485733945">
|
||||
<img src="screenshots/tweet1.png" height="330" alt="Agent Orchestrator journey screenshot two" />
|
||||
</a>
|
||||
</td>
|
||||
<td width="29.5%" align="center">
|
||||
<a href="https://x.com/agent_wrapper/status/2024885035774738700">
|
||||
<img src="screenshots/tweet3.png" height="330" alt="Agent Orchestrator journey screenshot three" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Documentation
|
||||
|
||||
| Document | Description |
|
||||
| -------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| [Architecture](docs/architecture.md) | System architecture, data flows, and load-bearing rules |
|
||||
| [Backend Code Structure](docs/backend-code-structure.md) | Package-by-package ownership and dependency rules |
|
||||
| [CLI Guide](docs/cli/README.md) | Direct `ao` CLI usage, command routes, and smoke tests |
|
||||
| [AGENTS.md](AGENTS.md) | Contributor and worker-agent contract |
|
||||
| Document | Start here when you need |
|
||||
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| [docs/architecture.md](docs/architecture.md) | Backend mental model, lifecycle, persistence, CDC, status derivation, and daemon boundaries. |
|
||||
| [docs/backend-code-structure.md](docs/backend-code-structure.md) | Package ownership and where each backend concern belongs. |
|
||||
| [docs/cli/README.md](docs/cli/README.md) | CLI behavior and daemon route mapping. |
|
||||
| [docs/STATUS.md](docs/STATUS.md) | What currently ships on `main` and what remains in flight. |
|
||||
| [docs/stack.md](docs/stack.md) | Library, runtime, and dependency decisions. |
|
||||
|
||||
---
|
||||
## Telemetry
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Backend tests
|
||||
cd backend
|
||||
go test -race ./...
|
||||
|
||||
# Frontend tests
|
||||
cd frontend
|
||||
pnpm test
|
||||
|
||||
# Full CI validation locally
|
||||
npx @redwoodjs/agent-ci run --all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration is environment-driven. The daemon takes no config file.
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
| --------------------- | -------------------- | --------------------------- |
|
||||
| `AO_PORT` | `3001` | HTTP bind port |
|
||||
| `AO_REQUEST_TIMEOUT` | `60s` | Per-request timeout |
|
||||
| `AO_SHUTDOWN_TIMEOUT` | `10s` | Graceful shutdown cap |
|
||||
| `AO_RUN_FILE` | `~/.ao/running.json` | PID/port handshake |
|
||||
| `AO_DATA_DIR` | `~/.ao/data` | SQLite data directory |
|
||||
| `AO_AGENT` | `claude-code` | Compatibility agent adapter |
|
||||
| `GITHUB_TOKEN` | - | GitHub auth token |
|
||||
|
||||
### Health Checks
|
||||
|
||||
```bash
|
||||
curl localhost:3001/healthz # Liveness probe
|
||||
curl localhost:3001/readyz # Readiness probe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We love contributions! Join our community on Discord to get started.
|
||||
|
||||
### Join us on Discord
|
||||
|
||||
[](https://discord.com/invite/UZv7JjxbwG)
|
||||
|
||||
**Daily contributor sync:** Every day at **10:00 PM IST**
|
||||
|
||||
Get your issues verified by core contributors, ask questions, share progress, and learn from the community. New contributors are always welcome!
|
||||
|
||||
**Why join Discord?**
|
||||
|
||||
- Get your issues and PRs verified by core contributors before investing time
|
||||
- Learn from experienced contributors in daily sync calls
|
||||
- Share your progress and get feedback
|
||||
- Get help troubleshooting in real-time
|
||||
- Stay updated on the latest developments and roadmap
|
||||
|
||||
### Quick Start
|
||||
|
||||
1. **Join the Discord** - Connect with the community and get guidance
|
||||
2. **Read the contributor contract** - See [AGENTS.md](AGENTS.md) for repo layout, daemon/API boundaries, and coding conventions
|
||||
3. **Pick a focused problem** - Browse [open issues](https://github.com/AgentWrapper/agent-orchestrator/issues) and choose one small enough for a focused PR
|
||||
4. **Open a clear PR** - Keep changes narrow, explain user-visible impact, link issues, include tests
|
||||
5. **Iterate with contributors** - Use review feedback to tighten the PR until verified
|
||||
|
||||
---
|
||||
Agent Orchestrator's Electron renderer sends anonymous usage events to PostHog for reliability and product understanding, and PostHog session recording is enabled with local paths and local URLs redacted before transmission. Set `VITE_AO_POSTHOG_KEY` to an empty string before building to disable transmission. See [docs/telemetry.md](docs/telemetry.md).
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0 - see [LICENSE](LICENSE) for details.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**[Star us on GitHub](https://github.com/AgentWrapper/agent-orchestrator)** • **[Report Issues](https://github.com/AgentWrapper/agent-orchestrator/issues)** • **[Discussions](https://github.com/AgentWrapper/agent-orchestrator/discussions)**
|
||||
|
||||
Made with love by the Agent Orchestrator community
|
||||
|
||||
</div>
|
||||
Apache License 2.0. See [LICENSE](LICENSE).
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 813 B |
|
After Width: | Height: | Size: 519 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 582 B |
|
After Width: | Height: | Size: 926 B |
|
After Width: | Height: | Size: 620 B |
|
After Width: | Height: | Size: 362 B |
|
After Width: | Height: | Size: 571 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 652 B |
|
After Width: | Height: | Size: 785 B |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 377 B |
|
|
@ -38,6 +38,7 @@ export function BrowserPanelView({
|
|||
}: BrowserPanelProps & { browserView: BrowserViewModel }) {
|
||||
const { navState, slotRef, navigate, goBack, goForward, reload, stop } = browserView;
|
||||
const [urlInput, setUrlInput] = useState(navState.url);
|
||||
const showStaticPreview = !window.ao?.browser && navState.url !== "";
|
||||
|
||||
useEffect(() => {
|
||||
setUrlInput(navState.url);
|
||||
|
|
@ -111,6 +112,7 @@ export function BrowserPanelView({
|
|||
</form>
|
||||
<div className="browser-panel__content">
|
||||
<div className="browser-panel__slot" ref={slotRef} />
|
||||
{showStaticPreview ? <StaticPreview url={navState.url} /> : null}
|
||||
{navState.url === "" ? (
|
||||
<div className="browser-panel__overlay">
|
||||
<p>Enter a dev-server URL to preview it here.</p>
|
||||
|
|
@ -121,3 +123,48 @@ export function BrowserPanelView({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StaticPreview({ url }: { url: string }) {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-auto bg-[#f7f8fb] text-[#17202a]">
|
||||
<div className="border-b border-[#dfe4ea] bg-white px-4 py-3">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.08em] text-[#687384]">AO Preview</div>
|
||||
<div className="mt-1 truncate font-mono text-[12px] text-[#2f5b9d]">{url}</div>
|
||||
</div>
|
||||
<div className="mx-auto max-w-[760px] px-5 py-6">
|
||||
<div className="rounded-[8px] border border-[#d7dee8] bg-white p-5 shadow-sm">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 className="text-[22px] font-semibold leading-tight tracking-normal text-[#111827]">
|
||||
Demo app preview
|
||||
</h1>
|
||||
<p className="mt-1 text-[13px] leading-5 text-[#526070]">
|
||||
The worker exposed a local Vite app with <span className="font-mono">ao preview</span>.
|
||||
</p>
|
||||
</div>
|
||||
<span className="rounded-[6px] bg-[#e7f8ed] px-2.5 py-1 text-[11px] font-semibold text-[#177245]">
|
||||
Loaded
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-5 grid grid-cols-3 gap-3">
|
||||
{[
|
||||
["Routes", "12 passing"],
|
||||
["Build", "ready"],
|
||||
["Latency", "42 ms"],
|
||||
].map(([label, value]) => (
|
||||
<div key={label} className="rounded-[7px] border border-[#e1e7ef] bg-[#fbfcfe] p-3">
|
||||
<div className="text-[11px] font-medium uppercase tracking-[0.06em] text-[#687384]">{label}</div>
|
||||
<div className="mt-1 text-[15px] font-semibold text-[#111827]">{value}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-5 rounded-[7px] border border-[#dce4ef] bg-[#0f172a] p-3 font-mono text-[12px] leading-5 text-[#cbd5e1]">
|
||||
<div>$ npm run dev -- --host 127.0.0.1</div>
|
||||
<div className="text-[#86efac]">ready in 418 ms</div>
|
||||
<div>Local: http://localhost:5173/</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ const VIEWS: { id: InspectorView; label: string; icon: ReactNode }[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const usePreviewData = import.meta.env.VITE_NO_ELECTRON === "1";
|
||||
|
||||
const prStateTone: Record<SessionPRSummary["state"], string> = {
|
||||
open: "border-success/40 bg-success/10 text-success",
|
||||
draft: "border-border bg-raised text-muted-foreground",
|
||||
|
|
@ -421,6 +423,7 @@ function ReviewsView({
|
|||
return reviews.some((review) => review.status === "running") ? 2500 : false;
|
||||
},
|
||||
queryFn: async () => {
|
||||
if (usePreviewData) return mockReviewsResponse(session);
|
||||
const { data, error } = await apiClient.GET("/api/v1/sessions/{sessionId}/reviews", {
|
||||
params: { path: { sessionId: session.id } },
|
||||
});
|
||||
|
|
@ -432,6 +435,7 @@ function ReviewsView({
|
|||
queryKey: ["project-config", session.workspaceId],
|
||||
enabled: hasPr,
|
||||
queryFn: async () => {
|
||||
if (usePreviewData) return mockProjectConfig();
|
||||
const { data, error } = await apiClient.GET("/api/v1/projects/{id}", {
|
||||
params: { path: { id: session.workspaceId } },
|
||||
});
|
||||
|
|
@ -491,6 +495,76 @@ function projectConfig(project: components["schemas"]["ProjectOrDegraded"] | und
|
|||
return project.config;
|
||||
}
|
||||
|
||||
function mockProjectConfig(): ProjectConfig {
|
||||
return {
|
||||
worker: { agent: "codex" },
|
||||
orchestrator: { agent: "codex" },
|
||||
reviewers: [{ harness: "codex" }],
|
||||
};
|
||||
}
|
||||
|
||||
function mockReviewsResponse(session: WorkspaceSession): ReviewsResponse {
|
||||
return {
|
||||
reviewerHandleId: `${session.id}-reviewer`,
|
||||
reviews: sortedPRs(session).map((pr, index) => {
|
||||
const targetSha = `demo${pr.number}${index}`;
|
||||
const reviewedAt = new Date(Date.now() - (index + 1) * 11 * 60 * 1000).toISOString();
|
||||
const latestRun =
|
||||
pr.review === "approved" || pr.review === "changes_requested"
|
||||
? {
|
||||
batchId: `demo-batch-${session.id}`,
|
||||
body:
|
||||
pr.review === "approved"
|
||||
? "Demo review approved. The implementation is ready for the README screenshot flow."
|
||||
: "Demo review found polish feedback for the terminal presentation.",
|
||||
createdAt: reviewedAt,
|
||||
githubReviewId: `${pr.number}01`,
|
||||
harness: "codex",
|
||||
id: `demo-review-run-${pr.number}`,
|
||||
prUrl: pr.url,
|
||||
reviewId: `demo-review-${pr.number}`,
|
||||
sessionId: session.id,
|
||||
status: "delivered",
|
||||
targetSha,
|
||||
verdict: pr.review === "approved" ? "approved" : "changes_requested",
|
||||
}
|
||||
: undefined;
|
||||
return {
|
||||
latestRun,
|
||||
prNumber: pr.number,
|
||||
prUrl: pr.url,
|
||||
status:
|
||||
pr.review === "approved"
|
||||
? "up_to_date"
|
||||
: pr.review === "changes_requested"
|
||||
? "changes_requested"
|
||||
: pr.state === "draft"
|
||||
? "ineligible"
|
||||
: "needs_review",
|
||||
targetSha,
|
||||
title: mockReviewTitle(pr.number),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function mockReviewTitle(prNumber: number): string {
|
||||
switch (prNumber) {
|
||||
case 319:
|
||||
return "Browser preview rail renders inside AO";
|
||||
case 320:
|
||||
return "Review tab keeps stacked PR rows visible";
|
||||
case 321:
|
||||
return "Draft child PR waits for parent review";
|
||||
case 318:
|
||||
return "Terminal polish feedback";
|
||||
case 323:
|
||||
return "README screenshot assets ready";
|
||||
default:
|
||||
return `Demo pull request ${prNumber}`;
|
||||
}
|
||||
}
|
||||
|
||||
function ReviewPanel({
|
||||
session,
|
||||
config,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ export function TerminalPane({ session, theme, daemonReady, terminalTarget, font
|
|||
|
||||
if (!window.ao) {
|
||||
const provider = terminalTarget?.kind === "reviewer" ? terminalTarget.harness : (session?.provider ?? "claude");
|
||||
const lines =
|
||||
terminalTarget?.kind === "reviewer" ? reviewerPreviewLines(session) : workerPreviewLines(session, provider);
|
||||
return (
|
||||
<pre
|
||||
className="h-full overflow-auto bg-terminal p-4 font-mono leading-relaxed text-[var(--term-fg)]"
|
||||
|
|
@ -31,11 +33,23 @@ export function TerminalPane({ session, theme, daemonReady, terminalTarget, font
|
|||
<span className="text-[var(--term-dim)]">~/{session?.workspaceName ?? "reverbcode"}</span>{" "}
|
||||
<span className="text-[var(--term-blue)]">{session?.branch || "main"}</span> $ {provider}
|
||||
{"\n"}
|
||||
<span className="text-[var(--term-green)]">✻ Welcome to the agent CLI</span>
|
||||
{"\n\n"}
|
||||
<span className="text-[var(--term-dim)]">
|
||||
Browser preview renders a static terminal surface. Electron attaches the live PTY.
|
||||
</span>
|
||||
{lines.map((line, index) => (
|
||||
<span
|
||||
key={`${line}:${index}`}
|
||||
className={
|
||||
line.startsWith("PASS") || line.startsWith("DONE")
|
||||
? "text-[var(--term-green)]"
|
||||
: line.startsWith("WARN") || line.startsWith("TODO")
|
||||
? "text-[var(--term-amber)]"
|
||||
: line.startsWith("$")
|
||||
? "text-[var(--term-blue)]"
|
||||
: "text-[var(--term-fg)]"
|
||||
}
|
||||
>
|
||||
{line}
|
||||
{"\n"}
|
||||
</span>
|
||||
))}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
|
@ -52,6 +66,57 @@ export function TerminalPane({ session, theme, daemonReady, terminalTarget, font
|
|||
);
|
||||
}
|
||||
|
||||
function workerPreviewLines(session: WorkspaceSession | undefined, provider: string): string[] {
|
||||
if (session?.id === "demo-review-stack") {
|
||||
return [
|
||||
'$ rg "previewUrl|Browser" frontend/src/renderer',
|
||||
"frontend/src/renderer/components/SessionInspector.tsx: Browser tab selected after ao preview",
|
||||
"frontend/src/renderer/hooks/useBrowserView.ts: preview revision re-navigates the view",
|
||||
"$ ao preview http://localhost:5173",
|
||||
"DONE preview target set for demo-review-stack",
|
||||
"$ npm --prefix frontend run typecheck",
|
||||
"PASS TypeScript project references are clean",
|
||||
"TODO wait for reviewer on PR #320 before merging the stack",
|
||||
];
|
||||
}
|
||||
if (session?.id === "demo-working") {
|
||||
return [
|
||||
`$ ${provider} --continue`,
|
||||
"Reading renderer board and inspector components...",
|
||||
"Updated demo workspace data for README screenshots",
|
||||
"$ npm --prefix frontend test -- SessionsBoard SessionInspector",
|
||||
"PASS 18 tests passed",
|
||||
"DONE board has Working, Needs you, In review, and Ready to merge populated",
|
||||
];
|
||||
}
|
||||
if (session?.id === "demo-needs-input") {
|
||||
return [
|
||||
"$ git diff --stat",
|
||||
"frontend/src/renderer/components/TerminalPane.tsx | 41 +++++++++++++++++",
|
||||
"frontend/src/renderer/styles.css | 27 +++++++++++",
|
||||
"WARN reviewer requested a tighter terminal activity sample",
|
||||
"TODO confirm whether to keep the toolbar density change",
|
||||
];
|
||||
}
|
||||
return [
|
||||
`$ ${provider} --status`,
|
||||
"Reading task context and local diff...",
|
||||
"Running focused validation for the current session",
|
||||
"PASS demo terminal is populated for screenshots",
|
||||
];
|
||||
}
|
||||
|
||||
function reviewerPreviewLines(session: WorkspaceSession | undefined): string[] {
|
||||
return [
|
||||
"$ ao review submit --session " + (session?.id ?? "demo-session"),
|
||||
"Reviewing PR #319: browser preview rail renders inside AO",
|
||||
"PASS implementation matches the requested README screenshot flow",
|
||||
"Reviewing PR #320: stacked PR review rows",
|
||||
"WARN keep multiple review rows visible before taking the screenshot",
|
||||
"DONE submitted batched review results",
|
||||
];
|
||||
}
|
||||
|
||||
// Agents whose full-screen TUI keeps its own transcript and scrolls it only by
|
||||
// keyboard, ignoring SGR wheel reports. The terminal routes the wheel to
|
||||
// PageUp/PageDown for these (see XtermTerminal's paneScrollsByKeyboard).
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ export function useBrowserView({
|
|||
const observerRef = useRef<ResizeObserver | null>(null);
|
||||
const previewTriggerRef = useRef<{ revision: number | null; target: string } | null>(null);
|
||||
const hasUrlRef = useRef(false);
|
||||
const hasNativeBrowser = Boolean(window.ao?.browser);
|
||||
|
||||
useEffect(() => {
|
||||
activeRef.current = active;
|
||||
|
|
@ -176,6 +177,21 @@ export function useBrowserView({
|
|||
|
||||
useEffect(() => {
|
||||
let disposed = false;
|
||||
if (!hasNativeBrowser) {
|
||||
const state = {
|
||||
...EMPTY_NAV_STATE,
|
||||
viewId: `preview-${sessionId}`,
|
||||
url: "",
|
||||
title: "",
|
||||
};
|
||||
viewIdRef.current = state.viewId;
|
||||
setViewId(state.viewId);
|
||||
setNavState(state);
|
||||
return () => {
|
||||
disposed = true;
|
||||
viewIdRef.current = "";
|
||||
};
|
||||
}
|
||||
window.ao?.browser.ensure(sessionId).then((state) => {
|
||||
if (disposed) return;
|
||||
viewIdRef.current = state.viewId;
|
||||
|
|
@ -191,7 +207,7 @@ export function useBrowserView({
|
|||
}
|
||||
viewIdRef.current = "";
|
||||
};
|
||||
}, [scheduleSettleMeasure, sendHiddenBounds, sessionId]);
|
||||
}, [hasNativeBrowser, scheduleSettleMeasure, sendHiddenBounds, sessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
return window.ao?.browser.onNavState((state) => {
|
||||
|
|
@ -229,11 +245,29 @@ export function useBrowserView({
|
|||
}, []);
|
||||
|
||||
const navigate = useCallback(
|
||||
(url: string) => withView((id) => window.ao!.browser.navigate({ viewId: id, url })),
|
||||
[withView],
|
||||
(url: string) => {
|
||||
if (!hasNativeBrowser) {
|
||||
const normalized = url.trim();
|
||||
setNavState((current) => ({
|
||||
...current,
|
||||
url: normalized,
|
||||
title: normalized ? "AO preview" : "",
|
||||
isLoading: false,
|
||||
}));
|
||||
return Promise.resolve();
|
||||
}
|
||||
return withView((id) => window.ao!.browser.navigate({ viewId: id, url }));
|
||||
},
|
||||
[hasNativeBrowser, withView],
|
||||
);
|
||||
|
||||
const clear = useCallback(() => withView((id) => window.ao!.browser.clear(id)), [withView]);
|
||||
const clear = useCallback(() => {
|
||||
if (!hasNativeBrowser) {
|
||||
setNavState((current) => ({ ...current, url: "", title: "", isLoading: false }));
|
||||
return Promise.resolve();
|
||||
}
|
||||
return withView((id) => window.ao!.browser.clear(id));
|
||||
}, [hasNativeBrowser, withView]);
|
||||
|
||||
// When the session is terminated, clear the view and stop reacting to
|
||||
// daemon-driven preview changes so stale content does not remain visible.
|
||||
|
|
@ -273,10 +307,10 @@ export function useBrowserView({
|
|||
navState,
|
||||
slotRef,
|
||||
navigate,
|
||||
goBack: () => withView((id) => window.ao!.browser.goBack(id)),
|
||||
goForward: () => withView((id) => window.ao!.browser.goForward(id)),
|
||||
reload: () => withView((id) => window.ao!.browser.reload(id)),
|
||||
stop: () => withView((id) => window.ao!.browser.stop(id)),
|
||||
goBack: () => (hasNativeBrowser ? withView((id) => window.ao!.browser.goBack(id)) : Promise.resolve()),
|
||||
goForward: () => (hasNativeBrowser ? withView((id) => window.ao!.browser.goForward(id)) : Promise.resolve()),
|
||||
reload: () => (hasNativeBrowser ? withView((id) => window.ao!.browser.reload(id)) : Promise.resolve()),
|
||||
stop: () => (hasNativeBrowser ? withView((id) => window.ao!.browser.stop(id)) : Promise.resolve()),
|
||||
destroy,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,285 +2,203 @@ import type { PRState, PullRequestFacts, WorkspaceSummary } from "../types/works
|
|||
import type { SessionPRSummary } from "../hooks/useSessionScmSummary";
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const minutesAgo = (minutes: number) => new Date(Date.now() - minutes * 60 * 1000).toISOString();
|
||||
const hoursAgo = (hours: number) => new Date(Date.now() - hours * 60 * 60 * 1000).toISOString();
|
||||
|
||||
// Single-PR preview helper. Sessions that own a stack (stacked-auth) inline
|
||||
// their facts instead; the daemon aggregates per-PR CI/review server-side.
|
||||
const pr = (number: number, state: PRState, ci = "passing"): PullRequestFacts => ({
|
||||
url: `https://github.com/me/pull/${number}`,
|
||||
const demoPr = (
|
||||
number: number,
|
||||
state: PRState,
|
||||
ci: PullRequestFacts["ci"] = "passing",
|
||||
review: PullRequestFacts["review"] = "none",
|
||||
mergeability: PullRequestFacts["mergeability"] = "mergeable",
|
||||
): PullRequestFacts => ({
|
||||
url: `https://github.com/acme-inc/ao-demo/pull/${number}`,
|
||||
number,
|
||||
state,
|
||||
ci,
|
||||
review: state === "merged" ? "approved" : "none",
|
||||
mergeability: "mergeable",
|
||||
reviewComments: false,
|
||||
review,
|
||||
mergeability,
|
||||
reviewComments: review === "changes_requested",
|
||||
updatedAt: now,
|
||||
});
|
||||
|
||||
export const mockWorkspaces: WorkspaceSummary[] = [
|
||||
{
|
||||
id: "api-gateway",
|
||||
name: "api-gateway",
|
||||
path: "/Users/me/api-gateway",
|
||||
id: "ao-demo",
|
||||
name: "ao-demo",
|
||||
path: "/demo/ao-demo",
|
||||
type: "main",
|
||||
orchestratorAgent: "codex",
|
||||
accentColor: "#6ee7b7",
|
||||
sessions: [
|
||||
{
|
||||
id: "refactor-mux",
|
||||
terminalHandleId: "refactor-mux/terminal_0",
|
||||
workspaceId: "api-gateway",
|
||||
workspaceName: "api-gateway",
|
||||
title: "Split terminal mux responsibilities",
|
||||
provider: "claude-code",
|
||||
branch: "feat/refactor-mux",
|
||||
status: "working",
|
||||
updatedAt: now,
|
||||
createdAt: hoursAgo(4),
|
||||
changedFiles: [
|
||||
{
|
||||
path: "internal/mux/terminal_mux.go",
|
||||
additions: 42,
|
||||
deletions: 8,
|
||||
},
|
||||
],
|
||||
commitMessage: "refactor terminal mux",
|
||||
prs: [],
|
||||
},
|
||||
{
|
||||
id: "stacked-auth",
|
||||
terminalHandleId: "stacked-auth/terminal_0",
|
||||
workspaceId: "api-gateway",
|
||||
workspaceName: "api-gateway",
|
||||
title: "auth stack",
|
||||
provider: "claude-code",
|
||||
branch: "feat/ns",
|
||||
status: "review_pending",
|
||||
updatedAt: now,
|
||||
createdAt: hoursAgo(2),
|
||||
// One session owning a stack: open root, draft child, merged base.
|
||||
prs: [
|
||||
{
|
||||
url: "https://github.com/me/api-gateway/pull/41",
|
||||
number: 41,
|
||||
state: "open",
|
||||
ci: "passing",
|
||||
review: "approved",
|
||||
mergeability: "mergeable",
|
||||
reviewComments: false,
|
||||
updatedAt: now,
|
||||
},
|
||||
{
|
||||
url: "https://github.com/me/api-gateway/pull/42",
|
||||
number: 42,
|
||||
state: "draft",
|
||||
ci: "pending",
|
||||
review: "none",
|
||||
mergeability: "unknown",
|
||||
reviewComments: false,
|
||||
updatedAt: now,
|
||||
},
|
||||
{
|
||||
url: "https://github.com/me/api-gateway/pull/40",
|
||||
number: 40,
|
||||
state: "merged",
|
||||
ci: "passing",
|
||||
review: "approved",
|
||||
mergeability: "mergeable",
|
||||
reviewComments: false,
|
||||
updatedAt: hoursAgo(1),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "fix-auth-timeouts",
|
||||
workspaceId: "api-gateway",
|
||||
workspaceName: "api-gateway",
|
||||
title: "fix auth timeout retry loop",
|
||||
id: "ao-demo-orchestrator",
|
||||
terminalHandleId: "ao-demo-orchestrator/terminal_0",
|
||||
workspaceId: "ao-demo",
|
||||
workspaceName: "ao-demo",
|
||||
title: "Project orchestrator",
|
||||
provider: "codex",
|
||||
branch: "fix/auth-timeouts",
|
||||
status: "ci_failed",
|
||||
updatedAt: hoursAgo(1),
|
||||
kind: "orchestrator",
|
||||
branch: "main",
|
||||
status: "working",
|
||||
createdAt: hoursAgo(6),
|
||||
prs: [pr(184, "open", "failing")],
|
||||
},
|
||||
{
|
||||
id: "rate-limit-headers",
|
||||
workspaceId: "api-gateway",
|
||||
workspaceName: "api-gateway",
|
||||
title: "add rate limit headers",
|
||||
provider: "opencode",
|
||||
branch: "feat/rate-limit-headers",
|
||||
status: "review_pending",
|
||||
updatedAt: hoursAgo(2),
|
||||
createdAt: hoursAgo(9),
|
||||
prs: [pr(185, "open")],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "webgl-preview",
|
||||
name: "webgl-preview",
|
||||
path: "/Users/me/webgl-preview",
|
||||
sessions: [
|
||||
{
|
||||
id: "fix-webgl-fallback",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "Restore fallback renderer after WebGL init fails",
|
||||
provider: "codex",
|
||||
branch: "fix/webgl-fallback",
|
||||
status: "needs_input",
|
||||
updatedAt: now,
|
||||
createdAt: hoursAgo(4),
|
||||
updatedAt: minutesAgo(3),
|
||||
activity: { state: "active", lastActivityAt: minutesAgo(3) },
|
||||
prs: [],
|
||||
},
|
||||
{
|
||||
id: "shader-cache",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "cache compiled shader programs",
|
||||
provider: "claude-code",
|
||||
branch: "feat/shader-cache",
|
||||
id: "demo-working",
|
||||
terminalHandleId: "demo-working/terminal_0",
|
||||
workspaceId: "ao-demo",
|
||||
workspaceName: "ao-demo",
|
||||
title: "Build screenshot-ready dashboard data",
|
||||
provider: "codex",
|
||||
branch: "demo/dashboard-screenshot",
|
||||
status: "working",
|
||||
updatedAt: hoursAgo(0.5),
|
||||
createdAt: hoursAgo(2),
|
||||
changedFiles: [
|
||||
{ path: "src/render/shader-cache.ts", additions: 86, deletions: 12 },
|
||||
{ path: "src/render/webgl-context.ts", additions: 24, deletions: 5 },
|
||||
],
|
||||
prs: [],
|
||||
},
|
||||
{
|
||||
id: "texture-leak",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "stop texture leak on scene reload",
|
||||
provider: "codex",
|
||||
branch: "fix/texture-leak",
|
||||
status: "ci_failed",
|
||||
updatedAt: hoursAgo(1.5),
|
||||
createdAt: hoursAgo(7),
|
||||
prs: [pr(51, "open", "failing")],
|
||||
},
|
||||
{
|
||||
id: "review-camera-pan",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "smooth camera pan controls",
|
||||
provider: "aider",
|
||||
branch: "feat/camera-pan",
|
||||
status: "review_pending",
|
||||
updatedAt: hoursAgo(3),
|
||||
createdAt: hoursAgo(10),
|
||||
prs: [pr(52, "open")],
|
||||
},
|
||||
{
|
||||
id: "draft-webgpu-probe",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "probe WebGPU support before init",
|
||||
provider: "opencode",
|
||||
branch: "feat/webgpu-probe",
|
||||
status: "draft",
|
||||
updatedAt: hoursAgo(5),
|
||||
createdAt: hoursAgo(12),
|
||||
prs: [pr(53, "draft")],
|
||||
},
|
||||
{
|
||||
id: "merge-frame-stats",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "ship frame statistics overlay",
|
||||
provider: "codex",
|
||||
branch: "feat/frame-stats",
|
||||
status: "mergeable",
|
||||
updatedAt: hoursAgo(0.25),
|
||||
createdAt: hoursAgo(14),
|
||||
prs: [pr(54, "open")],
|
||||
},
|
||||
{
|
||||
id: "approved-pixel-ratio",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "respect device pixel ratio",
|
||||
provider: "claude-code",
|
||||
branch: "fix/device-pixel-ratio",
|
||||
status: "approved",
|
||||
updatedAt: hoursAgo(2.5),
|
||||
createdAt: hoursAgo(16),
|
||||
prs: [pr(55, "open")],
|
||||
},
|
||||
{
|
||||
id: "input-pointer-lock",
|
||||
workspaceId: "webgl-preview",
|
||||
workspaceName: "webgl-preview",
|
||||
title: "pointer lock escape handling",
|
||||
provider: "codex",
|
||||
branch: "fix/pointer-lock",
|
||||
status: "changes_requested",
|
||||
updatedAt: hoursAgo(4),
|
||||
createdAt: hoursAgo(18),
|
||||
prs: [pr(56, "open")],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "mobile-shell",
|
||||
name: "mobile-shell",
|
||||
path: "/Users/me/mobile-shell",
|
||||
sessions: [
|
||||
{
|
||||
id: "nav-gesture",
|
||||
workspaceId: "mobile-shell",
|
||||
workspaceName: "mobile-shell",
|
||||
title: "repair back swipe gesture",
|
||||
provider: "codex",
|
||||
branch: "fix/back-swipe",
|
||||
status: "working",
|
||||
updatedAt: hoursAgo(0.75),
|
||||
displayStatus: "working",
|
||||
createdAt: hoursAgo(3),
|
||||
updatedAt: minutesAgo(2),
|
||||
activity: { state: "active", lastActivityAt: minutesAgo(2) },
|
||||
changedFiles: [
|
||||
{ path: "frontend/src/renderer/lib/mock-data.ts", additions: 156, deletions: 22 },
|
||||
{ path: "docs/readme.md", additions: 18, deletions: 4 },
|
||||
],
|
||||
commitMessage: "prepare readme screenshot data",
|
||||
prs: [],
|
||||
},
|
||||
{
|
||||
id: "profile-sheet",
|
||||
workspaceId: "mobile-shell",
|
||||
workspaceName: "mobile-shell",
|
||||
title: "profile sheet accessibility pass",
|
||||
id: "demo-needs-input",
|
||||
terminalHandleId: "demo-needs-input/terminal_0",
|
||||
workspaceId: "ao-demo",
|
||||
workspaceName: "ao-demo",
|
||||
title: "Resolve reviewer feedback on terminal polish",
|
||||
provider: "claude-code",
|
||||
branch: "fix/profile-sheet-a11y",
|
||||
branch: "demo/terminal-polish",
|
||||
status: "changes_requested",
|
||||
displayStatus: "needs_you",
|
||||
createdAt: hoursAgo(5),
|
||||
updatedAt: minutesAgo(18),
|
||||
activity: { state: "waiting_input", lastActivityAt: minutesAgo(18) },
|
||||
changedFiles: [
|
||||
{ path: "frontend/src/renderer/components/TerminalPane.tsx", additions: 41, deletions: 9 },
|
||||
{ path: "frontend/src/renderer/styles.css", additions: 27, deletions: 3 },
|
||||
],
|
||||
commitMessage: "polish terminal screenshots",
|
||||
prs: [demoPr(318, "open", "passing", "changes_requested")],
|
||||
},
|
||||
{
|
||||
id: "demo-review-stack",
|
||||
terminalHandleId: "demo-review-stack/terminal_0",
|
||||
workspaceId: "ao-demo",
|
||||
workspaceName: "ao-demo",
|
||||
title: "Review stacked browser preview flow",
|
||||
provider: "codex",
|
||||
branch: "demo/browser-preview-stack",
|
||||
status: "review_pending",
|
||||
displayStatus: "needs_you",
|
||||
createdAt: hoursAgo(7),
|
||||
updatedAt: minutesAgo(7),
|
||||
activity: { state: "idle", lastActivityAt: minutesAgo(7) },
|
||||
previewUrl: "http://localhost:5173",
|
||||
previewRevision: 4,
|
||||
changedFiles: [
|
||||
{ path: "frontend/src/renderer/components/BrowserPanel.tsx", additions: 52, deletions: 11 },
|
||||
{ path: "frontend/src/renderer/hooks/useBrowserView.ts", additions: 33, deletions: 6 },
|
||||
{ path: "docs/assets/readme/browser-preview.png", additions: 1, deletions: 0 },
|
||||
],
|
||||
commitMessage: "wire readme browser preview",
|
||||
prs: [
|
||||
demoPr(319, "open", "passing", "none"),
|
||||
demoPr(320, "open", "pending", "none", "unknown"),
|
||||
demoPr(321, "draft", "pending", "none", "unknown"),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "demo-in-review",
|
||||
terminalHandleId: "demo-in-review/terminal_0",
|
||||
workspaceId: "ao-demo",
|
||||
workspaceName: "ao-demo",
|
||||
title: "Wait for CI on project settings copy",
|
||||
provider: "opencode",
|
||||
branch: "demo/project-settings-copy",
|
||||
status: "review_pending",
|
||||
displayStatus: "unknown",
|
||||
createdAt: hoursAgo(4),
|
||||
updatedAt: minutesAgo(31),
|
||||
activity: { state: "idle", lastActivityAt: minutesAgo(31) },
|
||||
prs: [demoPr(322, "open", "pending", "none", "unknown")],
|
||||
},
|
||||
{
|
||||
id: "demo-ready",
|
||||
terminalHandleId: "demo-ready/terminal_0",
|
||||
workspaceId: "ao-demo",
|
||||
workspaceName: "ao-demo",
|
||||
title: "Merge README screenshot asset update",
|
||||
provider: "codex",
|
||||
branch: "demo/readme-assets",
|
||||
status: "mergeable",
|
||||
updatedAt: hoursAgo(1.25),
|
||||
displayStatus: "mergeable",
|
||||
createdAt: hoursAgo(9),
|
||||
updatedAt: minutesAgo(5),
|
||||
activity: { state: "idle", lastActivityAt: minutesAgo(5) },
|
||||
changedFiles: [
|
||||
{ path: "docs/assets/readme/dashboard.png", additions: 1, deletions: 0 },
|
||||
{ path: "docs/assets/readme/session-terminal.png", additions: 1, deletions: 0 },
|
||||
],
|
||||
prs: [demoPr(323, "open", "passing", "approved")],
|
||||
},
|
||||
{
|
||||
id: "demo-ci-failed",
|
||||
terminalHandleId: "demo-ci-failed/terminal_0",
|
||||
workspaceId: "ao-demo",
|
||||
workspaceName: "ao-demo",
|
||||
title: "Fix flaky NewTaskDialog smoke test",
|
||||
provider: "codex",
|
||||
branch: "demo/new-task-flake",
|
||||
status: "ci_failed",
|
||||
displayStatus: "needs_you",
|
||||
createdAt: hoursAgo(8),
|
||||
prs: [pr(92, "open")],
|
||||
updatedAt: minutesAgo(46),
|
||||
activity: { state: "idle", lastActivityAt: minutesAgo(46) },
|
||||
prs: [demoPr(324, "open", "failing", "none")],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "billing-portal",
|
||||
name: "billing-portal",
|
||||
path: "/Users/me/billing-portal",
|
||||
id: "docs-site",
|
||||
name: "docs-site",
|
||||
path: "/demo/docs-site",
|
||||
type: "main",
|
||||
orchestratorAgent: "claude-code",
|
||||
accentColor: "#93c5fd",
|
||||
sessions: [
|
||||
{
|
||||
id: "invoice-export",
|
||||
workspaceId: "billing-portal",
|
||||
workspaceName: "billing-portal",
|
||||
title: "invoice CSV export",
|
||||
provider: "opencode",
|
||||
branch: "feat/invoice-export",
|
||||
status: "review_pending",
|
||||
updatedAt: hoursAgo(2.25),
|
||||
createdAt: hoursAgo(11),
|
||||
prs: [pr(117, "open")],
|
||||
id: "docs-installation",
|
||||
terminalHandleId: "docs-installation/terminal_0",
|
||||
workspaceId: "docs-site",
|
||||
workspaceName: "docs-site",
|
||||
title: "Tighten installation guide",
|
||||
provider: "claude-code",
|
||||
branch: "demo/install-docs",
|
||||
status: "working",
|
||||
createdAt: hoursAgo(2),
|
||||
updatedAt: minutesAgo(13),
|
||||
activity: { state: "active", lastActivityAt: minutesAgo(13) },
|
||||
prs: [],
|
||||
},
|
||||
{
|
||||
id: "tax-id-validation",
|
||||
workspaceId: "billing-portal",
|
||||
workspaceName: "billing-portal",
|
||||
title: "tax id validation errors",
|
||||
id: "docs-ready",
|
||||
terminalHandleId: "docs-ready/terminal_0",
|
||||
workspaceId: "docs-site",
|
||||
workspaceName: "docs-site",
|
||||
title: "Publish troubleshooting section",
|
||||
provider: "codex",
|
||||
branch: "fix/tax-id-validation",
|
||||
status: "needs_input",
|
||||
updatedAt: hoursAgo(1.75),
|
||||
createdAt: hoursAgo(5),
|
||||
prs: [],
|
||||
branch: "demo/troubleshooting",
|
||||
status: "approved",
|
||||
createdAt: hoursAgo(12),
|
||||
updatedAt: minutesAgo(22),
|
||||
activity: { state: "idle", lastActivityAt: minutesAgo(22) },
|
||||
prs: [demoPr(411, "open", "passing", "approved")],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,13 +8,7 @@ import { captureRendererEvent } from "./telemetry";
|
|||
// each call site remembering to instrument itself. Keep in sync with the
|
||||
// allowed-source list in telemetry.ts.
|
||||
export type OrchestratorSpawnSource =
|
||||
| "board"
|
||||
| "restore_dialog"
|
||||
| "topbar"
|
||||
| "sidebar"
|
||||
| "project_add"
|
||||
| "settings"
|
||||
| "restart";
|
||||
"board" | "restore_dialog" | "topbar" | "sidebar" | "project_add" | "settings" | "restart";
|
||||
|
||||
/** Spawn the project's orchestrator session via the daemon API. When clean is
|
||||
* true the daemon first tears down any active orchestrator for the project, then
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 452 KiB |
|
After Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 261 KiB |