Orchestration
Orchestration
The multi-agent orchestration layer — companies, agents, wakeups, runs, live events — and the operational sequences that move work through it.
The multi-agent orchestration layer and the sequences that move work through it. Use the Full screen button above, or open the orchestration diagram in a new tab ↗.
Company Orchestration System
The company system is the multi-agent orchestration layer. It owns companies, agents, projects, goals, issues, routines, activity, runs, approvals, and company-scoped files.
Core Domain Objects
| Object | Purpose |
|---|---|
| Company | Tenant boundary for agents, issues, projects, secrets, channels, and files |
| Agent | Company teammate with role, adapter config, runtime config, instructions, skills, and env |
| Project | Workspace and project-scoped configuration for issues and files |
| Goal | High-level objective that can group issues and routines |
| Issue | Unit of company work assigned to an agent or moved through lifecycle states |
| Routine | Scheduled or trigger-based recurring work owned by an agent |
| Wakeup request | Durable queue item that tells an agent why it should run |
| Heartbeat run | Execution record for one agent invocation |
| Activity log | Audit/event history for user, agent, and system actions |
| Company secret | Company-scoped secret reference used by env bindings |
Wake Sources
An agent run can be started from several sources:
- Issue assigned to an agent.
- Issue comment or mention.
- Approval stage or review decision.
- User clicks run heartbeat.
- Background heartbeat timer fires.
- Routine timer fires.
- Channel inbound message is routed to the agent.
- Direct company-agent chat message is sent.
All of these are normalized into wake context before execution. The agent should not need to know whether the wake came from the UI, a timer, an issue, or a channel except through the wake fields it receives.
Wakeup Queue Flow
apps/api/src/orchestration/engine/wakeup.ts owns the durable queue helpers.
enqueueWakeup(agentId, source, reason, payload, contextSnapshot)agentWakeupRequest status queuedheartbeat_runs row is created or updatedagent-invoker starts runtime processThe wakeup record captures:
agentIdsourcetriggerDetailreasonpayloadcontextSnapshot- queue status and timestamps
The context snapshot is important because it is the stable handoff from product events to runtime execution. For example, an issue assignment wakeup includes the issue id and source context even if the issue later changes.
Run Lifecycle
Run status is not the same as agent status.
queued -> running -> succeeded | failed | cancelledRun status describes one execution attempt. Agent status describes the visible state of the agent. A queued run should not overwrite an already-running agent as if the agent itself stopped running. The UI and event consumers should treat run lifecycle events and agent lifecycle events as separate streams.
Agent Status
Agent status should be updated only when the agent state actually changes, for example:
- idle
- active
- running
- paused
- error
Queued work is a run property. It may be displayed near the agent, but it should not be treated as the agent’s primary lifecycle unless the backend deliberately sets an agent status event.
Live Event Bus
apps/api/src/orchestration/engine/event-bus.ts publishes company live events.
The shared types live in packages/shared/src/orchestration/types/live.ts, and
event names live in packages/shared/src/orchestration/constants.ts.
Important event families:
| Event family | Purpose |
|---|---|
heartbeat.run.queued | A run exists and is waiting or starting |
heartbeat.run.running | A run has started |
heartbeat.run.log | Runtime SDK stream event, tool event, text, thinking, or system event |
heartbeat.run.completed | Run completed successfully |
heartbeat.run.failed | Run failed |
heartbeat.run.cancelled | Run was cancelled |
agent.status | Agent state changed |
issue.created | Agent or user created an issue |
issue.updated | Issue fields changed, including status or assignee |
issue.comment.created | Comment was written |
issue.deleted | Issue was soft or hard deleted |
activity.logged | Audit log entry was created |
Live event payloads should include enough denormalized data for subscribers to update without refetching the entire company:
- run id, run status, session id, adapter type, timestamps
- agent id, agent name, role, status, adapter/runtime metadata
- issue id, identifier, title, status, assignee, project, goal
- activity actor, action, entity, details
Refetching is still acceptable for recovery, but the normal path should consume the event payload directly.
Activity Log
Activity logs are audit records, not only UI feed items. They preserve:
- company id
- actor type
- actor id
- agent id
- run id
- action
- entity type
- entity id
- details
They are used to reconstruct what happened around an issue, run, routine, or agent action even after live events are gone from memory.
Operational Sequences
Issue Assignment
activity.logged is emittedqueueIssueAssignmentWakeup enqueues wakeupheartbeat.run.queued eventagent-invoker starts runtimeheartbeat.run.running eventheartbeat.run.completed or heartbeat.run.failedDirect Company-Agent Chat
Direct chat does not have to create a company issue. It can still use company context, agent instructions, skills, files, env, and channel tools.
Heartbeat Timer
runtime_config.heartbeat.enabledenqueueWakeup(source=“timer”, reason=“heartbeat_timer”)Turning heartbeat on in the agent configuration updates scheduler input. The next scheduler tick should observe the new runtime config without requiring the agent process to stay alive.
Routine Timer
nextFireAtnextFireAt in UTCUTC is the storage format. The routine timezone controls when the schedule fires. Browser timezone can be used as a default when the user or agent did not specify a timezone.