How the Magnitude Agent Runtime Handles Worker Specialization Through Roles
Magnitude's agent runtime uses a dedicated roles package to define worker capabilities, map roles to execution slots, and enforce security policies through a centralized registry that spawns specialized workers with specific toolkits and lifecycle hooks.
The magnitudedev/magnitude repository implements a sophisticated worker specialization system where distinct RoleDefinition objects encapsulate everything a worker needs to operate. The agent runtime consumes these definitions to instantiate specialized agents with appropriate permissions, prompts, and execution contexts.
Role Definitions and Structure
Each worker specialization begins with a RoleDefinition object that declaratively specifies the worker's purpose, constraints, and behavior.
The RoleDefinition Interface
A complete role definition includes an identifier, description, prompt template, security policy array, and lifecycle messages. In packages/roles/src/roles/engineer.ts, the createEngineerRole() function returns a definition that implements this interface:
export function createEngineerRole(): RoleDefinition {
return {
id: 'engineer',
description: 'Implements code changes',
prompt: definePrompt<'SKILLS_SECTION' | 'THINKING_LIMIT' | 'CHECKPOINT_SECTION'>(engineerPromptRaw),
defaultRecipient: 'coordinator',
agentKind: 'worker',
spawnable: true,
maxThoughtChars: 20000,
policy: [
denyForbiddenCommands(),
denyMutatingGit(),
denyWritesOutside(ctx => [ctx.cwd, ctx.scratchpadPath, join(homedir(), '.magnitude')]),
denyMassDestructiveIn(ctx => [join(homedir(), '.magnitude')]),
allowAll(),
],
lifecycle: {
coordinatorOnSpawn: 'If there are other independent changes to make, spawn additional engineers in parallel.',
coordinatorOnIdle: "Review the engineer's work for correctness and quality.",
},
initialContext: { coordinatorConversation: true },
}
}
The policy array defines security boundaries through composable restriction functions, while the lifecycle object provides contextual instructions for the coordinator agent when spawning or checking idle workers.
Role Registration and Discovery
The runtime discovers available specializations through a centralized catalog that aggregates all role definitions.
Aggregating Roles via createRoles()
In packages/roles/src/index.ts, the createRoles() function instantiates every role definition and returns a record keyed by RoleId. This catalog serves as the single source of truth for available worker types:
export function createRoles(): Record<RoleId, RoleDefinition> {
return {
engineer: createEngineerRole(),
leader: createLeaderRole(),
critic: createCriticRole(),
// Additional roles...
}
}
The agent runtime imports this function to build its internal representation of available specializations.
Validating Role Identifiers
The packages/roles/src/constants.ts file exports ROLE_IDS, a runtime array of all valid role identifiers. The agent uses the isRoleId() type guard imported from @magnitudedev/roles to validate strings against this canonical list before attempting to spawn workers.
Mapping Roles to Execution Slots
Worker specialization extends beyond behavior definitions to physical execution scheduling through slot assignment.
ROLE_TO_SLOT Constants
The constants file defines ROLE_TO_SLOT, a mapping that assigns each role identifier to a specific execution slot (such as primary or secondary). This allows the runtime to schedule resource-intensive roles on appropriate hardware:
// packages/roles/src/constants.ts
export const ROLE_TO_SLOT = {
engineer: 'primary',
critic: 'secondary',
leader: 'primary',
} as const;
Slot-Based Toolkit Assignment
In packages/agent/src/tools/toolkits.ts, the runtime imports ROLE_TO_SLOT to compose tool sets appropriate to the worker's assigned slot. Workers receive different capabilities based on whether they occupy primary or secondary slots:
import { ROLE_TO_SLOT } from '@magnitudedev/roles'
function getToolsForRole(roleId: RoleId) {
const slot = ROLE_TO_SLOT[roleId];
return mergeToolkits(defaultToolkit, slotSpecificToolkits[slot]);
}
Runtime Execution and Worker Spawning
The agent runtime transforms static role definitions into active worker processes through a coordinated registration and spawning pipeline.
The Role Registry
packages/agent/src/agents/registry.ts initializes the runtime's role awareness by importing the roles package and building an internal registry:
import { createRoles, isRoleId, type RoleId, type RoleDefinition } from '@magnitudedev/roles'
class RoleRegistry {
private roles: Record<RoleId, RoleDefinition>;
constructor() {
this.roles = createRoles();
}
getRole(id: RoleId): RoleDefinition | undefined {
if (isRoleId(id)) {
return this.roles[id];
}
return undefined;
}
}
This registry validates role identifiers using isRoleId() and provides type-safe access to RoleDefinition objects.
Spawning Workers by Role
The execution manager in packages/agent/src/execution/execution-manager.ts orchestrates the instantiation process. When the coordinator decides to parallelize work, it queries the registry for a spawnable role, resolves its slot via ROLE_TO_SLOT, and creates the worker process:
import { ROLE_TO_SLOT } from '@magnitudedev/roles'
class ExecutionManager {
spawnWorker(roleId: RoleId) {
const role = roleRegistry.getRole(roleId);
if (!role?.spawnable) {
throw new Error(`Role ${roleId} is not spawnable`);
}
const slot = ROLE_TO_SLOT[roleId];
return this.createWorkerProcess({ role, slot });
}
}
Enforcing Role Policies
Once spawned, workers operate under their role's security constraints. The packages/agent/src/execution/permission-gate.ts module evaluates the policy array using the evaluatePolicy function from the roles package:
import { evaluatePolicy, type RoleDefinition } from '@magnitudedev/roles'
function checkPermission(role: RoleDefinition, action: Action): boolean {
return evaluatePolicy(role.policy, action);
}
This ensures that an engineer role cannot execute forbidden shell commands or mutate Git history, as specified in its policy definition.
Creating Custom Worker Roles
Developers can extend the specialization system by defining new roles and registering them in the catalog. Here is a complete example implementing a "reviewer" role:
// packages/roles/src/roles/reviewer.ts
import { definePrompt } from '../prompt'
import { allowAll } from '../policy'
import type { RoleDefinition } from '../types'
export function createReviewerRole(): RoleDefinition {
return {
id: 'reviewer',
description: 'Reviews code changes and suggests improvements',
prompt: definePrompt<'SKILLS_SECTION'>('You are a reviewer...'),
defaultRecipient: 'coordinator',
agentKind: 'worker',
spawnable: true,
maxThoughtChars: 15000,
policy: [allowAll()],
lifecycle: {
coordinatorOnSpawn: 'Spawn additional reviewers if many PRs are pending.',
coordinatorOnIdle: 'Ask the reviewer for feedback on recent changes.',
},
initialContext: { coordinatorConversation: true },
}
}
Register the new role in packages/roles/src/index.ts:
export function createRoles(): Record<RoleId, RoleDefinition> {
return {
engineer: createEngineerRole(),
reviewer: createReviewerRole(), // Added
// ...other roles
}
}
Summary
- RoleDefinition objects encapsulate worker specialization through id, description, prompt templates, security policies, and lifecycle hooks defined in
packages/roles/src/roles/. - The createRoles() function in
packages/roles/src/index.tsaggregates all available roles into a type-safe catalog consumed by the agent runtime. - ROLE_TO_SLOT mapping in
packages/roles/src/constants.tsassigns roles to execution slots, enabling resource-aware scheduling. - The RoleRegistry in
packages/agent/src/agents/registry.tsvalidates role identifiers usingisRoleId()and provides runtime access to role metadata. - The ExecutionManager spawns workers by looking up role definitions, verifying
spawnablestatus, and resolving slots throughROLE_TO_SLOT. - Permission-gate enforcement via
evaluatePolicyinpackages/agent/src/execution/permission-gate.tsapplies role-specific security constraints to all worker actions.
Frequently Asked Questions
What is a RoleDefinition in Magnitude?
A RoleDefinition is a TypeScript interface that declaratively specifies a worker's capabilities, constraints, and operational context. It includes an identifier, prompt template, security policy array, lifecycle messages for coordinator interaction, and execution metadata like maxThoughtChars and spawnable status. According to the source code in packages/roles/src/types.ts, these definitions enable the agent runtime to instantiate workers with specific behavioral guarantees.
How does the agent runtime validate role identifiers?
The runtime uses the isRoleId() type guard function exported from @magnitudedev/roles to check if a string matches the ROLE_IDS array defined in packages/roles/src/constants.ts. The RoleRegistry in packages/agent/src/agents/registry.ts performs this validation before retrieving RoleDefinition objects, ensuring only registered specializations can be instantiated.
What is the difference between ROLE_IDS and ROLE_TO_SLOT?
ROLE_IDS is a runtime array containing all valid role identifier strings, used for validation and iteration. ROLE_TO_SLOT is a mapping object that assigns each role identifier to an execution slot (such as primary or secondary). While ROLE_IDS answers "what roles exist," ROLE_TO_SLOT answers "where should this role execute," enabling the runtime to allocate appropriate computational resources and toolkits.
How are security policies enforced for specialized workers?
Security policies defined in a role's policy array are enforced by the permission gate in packages/agent/src/execution/permission-gate.ts. This module imports evaluatePolicy from @magnitudedev/roles and applies it to every sensitive action. The policy array uses composable functions like denyForbiddenCommands(), denyMutatingGit(), and allowAll() to create layered security restrictions specific to each worker specialization.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →