A DM addressed to one agent that @-mentions another was silently dropping the second agent out of the conversation: * `dispatchAgentMessage` added the mentioned agent to the wake set, so CEO's heartbeat fired — but * `listAgentInboxUnread` only surfaced messages where the current agent was `to_agent_id` or sat in a matching channel/project/ department. Mentions were invisible, so CEO's next turn saw nothing new and returned silently. From the operator's chair this looked exactly like "CEO ignored me; 张工程师 answered instead". * `findMentionedAgentIds` had a latent bug on top: its unique-alias filter used `ids.length === 1`, but a single agent whose name matched its role registered itself twice, tripped the filter, and every `@CEO` in a scaffold with role === name was a no-op. Once inbox visibility was fixed the bug became load-bearing. Root-cause fixes: * New migration v48 adds `agent_message_mentions(message_id, agent_id, company_id, created_at)` so mention fan-out is a first-class join. * `sendAgentMessage` now takes the resolved mention set and writes the row + mentions in a single transaction — a crash between the two writes can no longer leave a message visible to the recipient but invisible to the mentioned agent. * `dispatchAgentMessage` computes the mention set exactly once via `resolveMentionedAgentIds` and hands the same list to both the DB write and `resolveMessageWakeTargets`, so persistence and wake stay in lockstep. * `listAgentInboxUnread` and `listAgentMessages` gain an `id IN (select message_id from agent_message_mentions …)` clause so the mentioned agent's inbox actually contains the message that woke it. * `findMentionedAgentIds` now stores per-alias agent IDs in a `Set` and filters on `ids.size === 1`, dropping the self-duplication bug while preserving the "refuse ambiguous aliases" semantics. * Company teardown drops `agent_message_mentions` alongside the existing `agent_message_reads` cleanup so deletes stay clean. Tests pin the new behaviour: a DM with a `@CEO` mention reaches CEO's inbox and both agents get woken, while a DM whose recipient is `__board__` never attempts to wake the human sentinel.
AgentCompany
Run an AI company from your desktop.
What it does
Type a company name and a goal. A CEO agent boots, hires specialists, forms departments, and runs the business. You approve or steer from the desktop.
Local, SQLite-backed, no external services. Inspired by Paperclip.
26 providers, no CLI
The runtime links pi-mono directly.
OAuth (5): Claude Pro/Max · ChatGPT Plus/Pro (Codex) · GitHub Copilot · Gemini CLI · Antigravity
API key (18): Anthropic · OpenAI · Google · Mistral · Groq · Cerebras · xAI · OpenRouter · Vercel AI Gateway · ZAI · OpenCode · OpenCode Go · Hugging Face · Kimi · MiniMax · MiniMax CN · Fireworks · Fireworks Firepass
Cloud (3): Azure OpenAI · AWS Bedrock · Google Vertex AI
Credentials are SecretVault-encrypted in SQLite.
Two execution modes
- AI CEO — autonomous. Hiring, tasks, deliverables run without you.
- AI Co-Founder — pauses at
hire_agent/document_review/plan_review/strategy_reviewfor your decision.
Switch at Settings → Execution mode.
Features
Self-organizing teams
CEO builds the org chart from one goal.
Agent messaging
Threads and DMs between agents. Direct / department / project / company / incident channels resolve to wake targets; each message lands in the recipient's transcript as a pi AgentMessage turn. If the recipient is already running, the message is delivered live via pi's steer queue; if not, it waits in the inbox until the next prepareRun.
Tasks
Goals break into tasks. Agents pick them up, report progress, escalate blockers.
Approvals
Hiring, deliverables, plan/scope/strategy reviews, budget overrides, production deploys pause for the board. The same approval policy gates the agent's destructive tools (bash / write / edit) via pi's beforeToolCall hook, so a pending approval stops file and shell mutations instead of just flagging them after the fact.
Documents
Plans, strategies, and specs in a shared center, linked to the tasks that produced them.
Launch in a minute
Name, mission, goal, provider, mode.
Quick Start
Download the latest release — macOS (Apple Silicon & Intel), Windows, Linux.
Build from source
# Node.js 22+, pnpm 10+
pnpm install
pnpm dev
First run
- Onboarding probes your shell PATH for
node(agents call the local HTTP API vianode -e). - Settings → Providers. Sign in or drop a key.
- Create a company: name, mission, goal, mode, workspace.
Verification
pnpm typecheck
pnpm lint
pnpm test:unit
pnpm test:integration
pnpm test:e2e
pnpm build:verify
Packaging
pnpm build # Package for current platform → /build/release
pnpm build:dir # Unpackaged directory
pnpm build:current # Current host platform
pnpm release:local # Signed release pipeline — see docs/release/local-r2.md
Targets: macOS .dmg, Windows .exe (NSIS), Linux .AppImage. Built with electron-builder. Local builds use ad-hoc signing (CSC_IDENTITY_AUTO_DISCOVERY=false). Defaults to --publish never.
Architecture
src/
main/
agent-runtime/ RuntimeBridge, SqliteSessionStore, CostWriter
credentials/ CredentialStore, Resolver, OAuthLoginService
api-server/ Agent HTTP API
orchestration/ Scheduler, run dispatch
prompt-builder/ System prompt assembly
thread-integration/ Thread runtime event routing
worker/ utilityProcess worker — direct new Agent(...)
database/ SQLite schema + migrations
preload/ Typed DesktopApi via contextBridge
renderer/ React 19 UI
shared/ Zod schemas shared across processes
- SQLite local; no external services.
contextIsolation: true,nodeIntegration: false, Zod-validated IPC.- Worker constructs
new Agent({...})from@mariozechner/pi-agent-corewith the full contract:sessionId,thinkingLevel, and thebeforeToolCall/afterToolCall/transformContexthooks proxied back toRuntimeBridgeon main. - Sessions persist as
session_entries; long transcripts auto-compact at LLM-call time via a puretransformContextreducer, and rotate at the long-cycle boundary. beforeToolCallenforces the approval gate plus thread wait-state guard at the tool-execution layer; mid-run messages reach live agents through pi's steering queue.- Workspace locks are single-writer-per-repo — every resolved workspace is held exclusively for the duration of the run; contenders queue on the wakeup table.
- Thread runtime (
conversation_threads/conversation_events/thread_wait_states) sits above pi'sAgentMessagestream. - Startup only runs schema migrations plus
interruptStaleRuns; all other data-repair lives at the transition point or in explicit maintenance paths.
Tech Stack
| Layer | Tech |
|---|---|
| Framework | Electron 39.8.0 |
| Language | TypeScript 5.9 |
| UI | React 19, Tailwind CSS 4, Framer Motion, Zustand |
| Build | electron-vite, Vite 7, electron-builder |
| Database | SQLite via node:sqlite (WAL) |
| Validation | Zod 4 |
| Agent runtime | @mariozechner/pi-ai, @mariozechner/pi-agent-core, @mariozechner/pi-coding-agent |
| Testing | Vitest, Playwright |






