Skip to content

Agent Teams and Profile Routing

An Agent Team is an owner-managed group of Agent Lensers connected by typed edges. Teams execute workflows under a four-policy bundle (approval / retry / failure / queue) and emit per-step events the owner can audit. This page covers both the team data model and the routing contract for the /lenser/:handle/ag/overview page that surfaces team operations.

Team data model

Every table on the diagram is committed in supabase/migrations/20260428010000_ai_catalog_agent_control_room.sql.

Team edges

Edge types and semantics:

edge_typeDirectionMeaningDefault is_blocking
delegatessource → targetSource asks target to execute a sub-taskfalse
reviewssource → targetSource's output reviewed by target before continuingtrue
reports_tosource → targetSource surfaces status to target; informationalfalse
shares_contextsource → targetSource's scratchpad/memory readable by targetfalse
handoffsource → targetSource completes; target picks up next phasetrue

is_blocking=true means the source step cannot terminate until the target completes (used to model human-in-the-loop reviews).

Member configuration profiles

A member resolves four profile bundles, each owned at the AI workspace level:

  • agents.personality_profilestone, expertise_level, risk_tolerance, autonomy_level, communication_style, decision_style, escalation_behavior, system_prompt_patch.
  • agents.memory_profilesscope_type, isolation_mode, retention_days, visibility, summary_strategy, reset_policy.
  • agents.tool_profilesallow_tools[], deny_tools[], tool_groups[], provider_overrides, requires_approval.
  • agents.model_profilesprovider_key, model_id, model_key, support_level, params.

Profiles are reusable across teams. Marking is_default=true makes a profile the implicit binding for new members.

Role assignment algorithm

When a workflow is dispatched as a team run, every node must resolve to a member.

When no eligible member exists, the engine writes the node row with status='blocked' and waiting_reason='human_input'. This surfaces as an item in the approval queue.

Autonomy levels

Teams operate at one of four autonomy levels, chosen via agents.workflow_assignments.approval_policy:

Levelapproval_policy shapeSemantics
Manual{"requiresApproval":true,"mode":"every_node"}Owner approves each node before it runs
Assisted{"requiresApproval":true,"mode":"sensitive_actions"}Agents propose; owner approves only sensitive actions (publish / spend / delete / external messaging)
Semi-autonomous{"requiresApproval":true,"mode":"on_block"}Run autonomously; pause for owner only when a node blocks (waiting_reason='human_input')
Autonomous with gates{"requiresApproval":false,"gates":["publish","spend","delete","external_message","schedule_change"]}Run autonomously except for the listed gate actions, which always pause

Gate-action defaults (see approvals):

  • Creating another agent
  • Adding/removing team members
  • Editing permissions or tool/model profiles
  • Publishing public output
  • Sending external messages or emails
  • Spending credits beyond a threshold
  • Modifying CRON schedules
  • Deleting data

Team runs

A agents.team_runs row is created when a workflow assignment is dispatched (manually, by API, or by CRON). It owns:

  • status{queued, running, completed, failed, cancelled, blocked}
  • approval_status{pending, approved, rejected, not_required}
  • scratchpad jsonb — team's working memory
  • workflow_run_id — link to the underlying lenses.workflow_runs row

Per-step rows live in agents.agent_run_steps: one per node × member with lane, current_task, recent_output_summary, blocker_summary, payload. Append-only event log: agents.agent_run_events.

Routing contract — /lenser/:handle/ag/overview

The route is registered at apps/web/src/WebRouter.tsx:386-397:

/lenser/:handle/ag         → AgentControlRoomOverviewRedirect
/lenser/:handle/ag/:section → AgentControlRoomPage

The page must always resolve. It must never redirect on missing agents, must never crash on empty collections, and must surface one of four modes determined by (target.type, viewer.is_owner).

Mode contracts

ModeWhat rendersSource of truth
Human-OwnerTwo tabs. Agents = grid of AgentCards for AI lensers owned by this human, with Create Agent CTA when empty. Activity = cross-agent feed (pending approvals, recent team_runs, schedules).agents.ownerships, agents.team_runs, lenses.workflow_schedules
Human-PublicPublic-visible agents this human owns, no scratchpad, no approvals, no settings.agents.ownerships filtered by lensers.profiles.visibility='public'
Agent-OwnerExisting AgentControlRoomPage with all sections (overview, scratchpad, team, workflows, schedules, memory, personality, tools, models, providers, runs, logs, evaluations, settings).fn_get_agent_workspace_bootstrap(handle)
Agent-PublicStripped-down read-only overview: public lenses, public workflows, public stats. No scratchpad, no approvals, no tool/model bindings, no settings.agents.ai_lensers + filtered lenses.lenses and lenses.workflows where visibility='public'

Empty-state contract

For Human-Owner mode with zero agents:

  • Heading: "No agents yet"
  • Body: "Build your first Agent Lenser to run lenses, workflows, and teams."
  • Primary CTA: Create Agent → opens AgentManageModal (registered as a child route at WebRouter.tsx:373).
  • Never throw because agents.length === 0.
  • Never redirect to /profile or /home.

For Agent-Owner mode with zero teams:

Workspace entry contract (implemented)

Profile split decision (implemented): LenserProfilePage.tsx now redirects all type === 'ai' profiles to /lenser/:handle/ag/overview immediately after the profile resolves. The human-profile page no longer handles AI lensers at all.

Route dispatch (implemented): All four modes are handled by AgentRouteShellAgentWorkspaceShell. The shell reads useAgentRouteMode(handle) and passes the resolved viewMode to the unified workspace provider. No redirects on missing agents, empty collections, or non-owner human visitors.

The "Current vs. desired" deviations listed in older versions of this doc are resolved. The four-mode contract is now the implementation.

Permission decision tree

Authorization helper: agents.can_manage_ai_lenser(uuid).

Future work

The following are Proposed (not yet implemented):

  • Cross-agent activity feed RPCfn_get_human_activity_feed server-side aggregation to back the CrossAgentActivityFeed component currently using a client-side query.
  • Read-only public agent overview polish — richer public stats (run count, last active, public automation feed) for Agent-Public mode.
  • Capability-aware role assignment — uses the proposed instruction_category on lens versions to route validation/research/generation lenses to members whose responsibility tag matches.