Agents
Orchestrator — The Manager Agent
The brain that routes “What should I do now?” to the right specialist, builds context packs, and ensures workspace isolation. Beginner to advanced.
What Orchestrator Does
Beginner: One Brain, Many Hands
You talk to one assistant (
/w/[workspace]/assistant), but behind it the Orchestrator picks which of 9 agents acts, what tools they can use, and when to ask you for confirmation.Located: lib/agents/orchestrator.ts. Single entry: runOrchestrator(workspaceId, userInput).
Flow — Intent to Tools
User: "What should I do now?" or Gmail webhook
│
▼
Orchestrator (Step 1: Intent Detection)
"Is this: create_task? query? set_reminder? finance?"
│
▼
Select Agent(s) — e.g., Task Agent + Calendar Agent
│
▼
Gather Context (Step 2)
SELECT * FROM tasks WHERE workspace_id = 'my-studio' AND due_date = today
SELECT * FROM calendar_events WHERE starts_at = today
│
▼
Rank & Propose (Step 3)
Task scoring 0–100 + available time → "Do GB Reel now (score 88)"
│
▼
Execute Safe Tools (Step 4)
create_task, search_drive → auto
│
▼
Pause for Sensitive (Step 5)
send_email → modal: "Send this?" → user confirms → execute
│
▼
Audit & Notify (Step 6)
Write activity_log, push via Redis → UI badgePrompt & Context Pack
Each agent gets workspace-scoped context, not full DB dump:
Prompt for Task Agent:
"You are Task Agent for workspace my-studio.
Context Pack for task GB-reel-123:
- client: GB Banquet, project: Social Media, deadline: 2025-12-06, priority: High 84
- checklist: [x] Download [ ] Select [ ] Edit
- related emails: 2, files: GB-logo.png
- user recently asked: 'What should I do now?'
Available tools: create_task, update_task, prioritize
Do not use finance tools."
Token saving: only GB task, not all 200 tasks.Keyword: Context Pack = Token Efficient
Narrow context → cheaper, more accurate. The orchestrator builds a mini-pack per request, not “send entire DB to LLM”.
Multi-Agent Coordination
User: "Make 3 reels for GB before Saturday"
Orchestrator → Inbox Agent (tools: extract_entities)
→ Returns: 3 tasks JSON
Orchestrator → Task Agent (tools: create_task x3)
→ Creates tasks, returns IDs
Orchestrator → Notification Agent (tool: create_reminder)
→ Schedules 7d/3d/1d reminders
Final: "Created 3 tasks for GB Banquet, due Saturday. Reminders set."Code Walkthrough
Entry: lib/agents/orchestrator.ts
export async function runOrchestrator(ctx: AgentRuntimeContext, input: string) {
const intent = await detectIntent(input) // LLM or heuristic
const agents = selectAgents(intent) // e.g., ["task", "calendar"]
const contextPack = await buildContextPack(ctx.workspaceId, agents)
const plan = await planActions(intent, contextPack, agents)
for (const action of plan) {
if (action.risk === "SENSITIVE") await askConfirmation(action)
else await executeTool(action)
}
}Tool Execution: lib/agents/tools.ts
tools = registry of { name, risk, input: Zod, handler: (args, { db, ctx }) => result }
// ctx: { workspaceId, userId } — enforced tenancy
handler always does: db.task.findMany({ where: { workspaceId: ctx.workspaceId } })Tenancy & Safety
// Every DB call in handler:
await db.task.create({ data: { workspace_id: ctx.workspaceId, title: args.title } })
// Check: npm run check:tenancy
// Scans lib/** for `db.*.findMany(` without workspace_id → fails CI🔒 Design Rule: No Global Queries
If you add a new query, always include
workspace_id. The isolation script is the gate. This is why beginners should copy existing handler patterns.