Skip to content

Agents, tasks, and subagents

This page reverse-engineers the ordinary task and subagent paths that show how Claude Code delegates work inside a session. The cross-target send/receive lifecycle is documented in Agent messaging and communication, while the gated named-teammate special case is documented end to end in Agent Teams.

Source anchors

Semantic aliasString or symbolMeaning
AgentsCommandFamilyH.command("agents")Background agents command family.
InlineAgentsFlag--agents <json>Custom agent definitions injected from root flags.
TaskCreateToolvar LX="TaskCreate"Task-create tool/action constant.
TaskGetToolvar DQ="TaskGet"Task status/result retrieval constant.
TaskListToolvar UZ="TaskList"Task list constant.
TaskUpdateToolvar J0="TaskUpdate"Task update constant.
SubagentLifecycleHooksSubagentStart, SubagentStopSubagent lifecycle hook events.
TaskLifecycleHooksTaskCreated, TaskCompletedTask lifecycle hook events.
SubagentContextClassifieragentType==="subagent"Runtime subagent context classifier.
UltraReviewCommandH.command("ultrareview [target]")Cloud-hosted multi-agent code-review command.
AgentToolAgents run in the background by defaultDelegated agents launch asynchronously unless run_in_background: false is explicit.
ToolUseConcurrencySchedulerY3g()J3g()Eao(), CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCYConsecutive concurrency-safe tool calls run together, with a default scheduler limit of 10 ~343,247–343,330.
AgentConcurrencyDeclarationAgent.isConcurrencySafe() { return true }Multiple Agent calls in one assistant tool block are eligible for concurrent launch ~395,316.
AgentModelResolveroue(), CLAUDE_CODE_SUBAGENT_MODEL, modelEnvironment, call, definition, and inherited-parent model precedence ~333,311–333,390.
ListAgentsContractListAgents, Names are the address, [ref]Describes conditional roster/reference semantics. No concrete Ai({...}) definition is present in this retained source, and the SendMessage peer-candidate providers are empty.
SendMessagePinGuardsend_message_pin_guard, now resolves to a different agentPrevents a reused display name from silently rebinding an established conversation recipient.
SendMessageResumeGuardAgentStoppedByUserError, ResumeAgentStateErrorUser-stopped agents stay stopped; other stopped or evicted agents can be resumed from retained state/transcript.
AgentCleanupStagesnonShellMonitors, shellTasks, keepaliveGatedAgent-owned monitors and shell tasks can survive the completed turn while keepalive ownership remains active.
ObserverDeclarationobserver, observerMessage, ObserverReportExperimental auto-spawned observer and its one-way report channel.
SessionSpawnCapCLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONSession-wide spawn budget (default 200); /clear resets it.
ImplicitTeamContractteam_name: "Deprecated; ignored. The session has a single implicit team."Explicit model-visible team setup is retired; actual teammate spawning still requires the Agent Teams gate.
ForkAndSubtaskUsage: /fork \<directive\>, Usage: /subtask \<task\>Background conversation copy versus in-session delegated subagent.
WorkflowToolExecute a workflow script that orchestrates multiple subagents deterministicallyStarts resumable deterministic orchestration as a background task.
BuiltInAgentRegistrygetBuiltInAgents()Builds the conditional native-agent roster: up to six normal personas, or the coordinator-only worker roster ~282,836–282,862.
BuiltInAgentClassifierisBuiltInAgent(agent), source === "built-in"Separates bundled definitions from plugin and settings/frontmatter agents ~282,933–282,948.
ForkAgentDefinitionFORK_AGENT, agentType:"fork"Special inherited-context worker outside the normal registry ~259,981–260,006.
WorkflowSubagentDefinitionagentType:"workflow-subagent"Internal default worker for deterministic Workflow agent() calls ~387,968–387,981.

Bundle modules in cli.renamed.js

Semantic aliasLoader lineRepresentative renamed exportsAtlas entry
AgentWorktreeManager631021worktreeBranchName, validateWorktreeSlug, restoreWorktreeSession, persistWorktreeSession, removeAgentWorktree, listRegisteredWorktrees, parsePRReference, killTmuxSessionBundle module map — git, worktree, and daemon
TeammateMailboxIpc286598writeToMailbox, sendShutdownRequestToMailbox, readUnreadMessages, formatTeammateMessages, createIdleNotification, isTaskAssignment, isStructuredProtocolMessage, getInboxPathBundle module map — session, transcript, agent metadata, and teammate IPC
HookEventDispatcher629735getTeammateIdleHookMessage, getTaskCreatedHookMessage, getTaskCompletedHookMessage, hasHookForEvent, persistHookOutputBundle module map — permission, trust, hooks, and policy

Agent/task map

flowchart TD
Root[Root flags/commands] --> Custom[--agents JSON custom agents]
Root --> AgentsCmd[claude agents]
Root --> Ultra[ultrareview]
Runtime[Session runtime] --> TaskTools[TaskCreate / TaskGet / TaskList / TaskUpdate]
TaskTools --> Subagent[Subagent runtime context]
Subagent --> Hooks[SubagentStart / SubagentStop]
TaskTools --> TaskHooks[TaskCreated / TaskCompleted]

Confirmed automation surfaces

SurfaceRuntime role
claude agentsManages background agents; root flags on this command pass settings/MCP/plugins/model/permission defaults into dispatched sessions.
--agents <json>Defines custom agents inline for the current session. The help example includes description and prompt fields.
TaskCreate, TaskGet, TaskList, TaskUpdateTask tool/action names used by agent/task orchestration paths.
SubagentStart, SubagentStopHook events around subagent lifecycle.
TaskCreated, TaskCompletedHook events around task lifecycle.
agentType === "subagent"Runtime context marker distinguishing subagent execution.
ultrareview [target]Cloud-hosted multi-agent code-review command.
AgentSpawns a named/custom subagent in the background by default; can run synchronously, in a worktree, or on a gated remote environment.
Experimental Agent TeamsWith the team gate and lead context active, an eligible named Agent call becomes an asynchronous in-process/tmux/iTerm2 teammate instead of an ordinary named subagent.
ListAgents / SendMessageSendMessage addresses in-process subagents and teammates. The bundle describes a conditional peer roster and retains local/cloud delivery branches, but this exact build’s resolver candidate providers return empty lists; another host/build would have to supply that roster.
ObserverReportSends an observer’s one-way advisory report to its paired task or main conversation; unavailable as a normal peer-message channel.
WorkflowRuns explicit deterministic multi-agent orchestration and returns a background task/run ID.
/fork / /subtask/fork creates a separate background-session copy; /subtask launches the old in-session delegation path.

Background agents command flags

The agents command accepts settings/integration defaults for dispatched sessions, including:

  • --setting-sources
  • --add-dir
  • --plugin-dir
  • --settings
  • --mcp-config
  • --strict-mcp-config
  • --permission-mode
  • --dangerously-skip-permissions
  • --model
  • --effort
  • --agent
  • --json (non-TTY roster output)
  • --all (include completed rows with --json)

This shows that background-agent sessions inherit the same core runtime surfaces as foreground sessions: settings, working directories, plugins, MCP, permissions, and models.

Built-in or “native” agent inventory

In this page, native agent means an agent definition whose source is exactly "built-in". That produces two useful counts for Claude Code 2.1.215:

  • Up to 6 normal built-in personas returned by getBuiltInAgents() in a standard CLI session.
  • 9 built-in role definitions in the bundle when the coordinator-only worker, experimental fork, and internal workflow-subagent roles are included.

The live count can be lower because the registry is mode- and gate-dependent. Custom .claude/agents/*.md, --agents JSON, and plugin agents are dynamic and are not included in either count.

Normal registry: up to six personas

Agent typePurposeTool/model profileAvailability boundary
general-purposeBroad research, code search, analysis, and multi-step execution when no narrower persona fits. It is also the default ordinary Agent definition.Full tool pool; model and permission mode normally inherit from the parent/call.Always seeded in the normal roster ~282,480–282,497.
ExploreFast, read-only file/symbol/pattern discovery. Its prompt explicitly forbids edits, file creation, deletion, and state-changing shell commands.Read/search-oriented surface with mutation, delegation, and Artifact tools removed; normally inherits the parent model, with a provider-aware Explore override; omits CLAUDE.md.Added together with Plan unless CLAUDE_CODE_DISABLE_EXPLORE_PLAN_AGENTS or tengu_slate_ibis disables the pair ~282,443–282,475, ~282,731–282,740.
PlanRead-only software architect: explores existing patterns, evaluates trade-offs, and returns a step-by-step implementation strategy plus critical files.Same read-only restrictions as Explore; inherits the model; omits CLAUDE.md.Shares the Explore/Plan gate ~282,505–282,574.
statusline-setupCreates or updates the user’s Claude Code statusLine configuration, including PS1 conversion and settings edits; it does not execute the later status-line runtime.Exactly Read and Edit; fixed sonnet; orange UI color.Omitted in safe mode because its persisted user setting would not render there ~282,577–282,729.
claude-code-guideDocumentation specialist for Claude Code, Claude Agent SDK, Claude API/Managed Agents, and Claude Tag; fetches official docs and considers the current configuration.Read/search plus WebFetch/WebSearch-style documentation tools; fixed haiku; dontAsk permission mode.Omitted from sdk-ts, sdk-py, and sdk-cli entrypoints ~282,323–282,350, ~282,853–282,859.
claudeCatch-all background-job/FleetView persona. Its extra prompt requires narration and machine-readable result:, needs input:, or failed: state signals.Full tool pool; appends its background-job instructions to the normal prompt.Omitted when CLAUDE_CODE_DISABLE_AGENT_VIEW or managed disableAgentView disables the agents view ~282,807–282,833.

Three special/internal built-in roles

Agent typePurposeWhy it is outside the normal-six count
workerAutonomous research, implementation, or verification for coordinator mode; commits only its own edits and reports back to the coordinator.When coordinator mode is active, getBuiltInAgents() returns only this persona instead of the normal roster ~282,742–282,804, ~282,842–282,846.
forkExecutes one directive with the parent’s full conversation context while keeping intermediate tool output out of the parent context.Defined as built-in but selected by a separate interactive experiment path, not returned by the normal registry; disabled in coordinator/noninteractive modes ~259,901–260,006.
workflow-subagentDefault synchronous return-value worker behind Workflow agent(); its final text is data returned verbatim to the script.Internal to the Workflow runtime, not an ordinary user-selectable registry persona; cannot spawn Agent/Workflow recursively through its default tools ~387,952–387,983.

main, subagent, main-session, team-lead, and teammate are runtime context/task labels, not additional built-in definitions. Observer agents are also not a fixed native persona: an observer names another available built-in, custom, or plugin agent definition.

Registry gates and overrides

ConditionResult
Noninteractive session plus CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=trueReturns no built-in agents.
Coordinator modeReplaces the normal roster with [worker].
Safe modeKeeps built-ins but omits statusline-setup.
Agent view disabledOmits claude; it does not remove the other normal personas.
Explore/Plan env or rollout gate offOmits both Explore and Plan.
SDK TypeScript/Python/CLI entrypointOmits claude-code-guide.
Custom/plugin definition uses the same nameActive-agent precedence can replace a built-in by name; policy/flag sources win later in the merge.

Agent execution contract and limits

The Agent tool’s current contract differs from the older synchronous Task mental model:

  • run_in_background defaults to true. Set it to false only when the parent must block on that result before continuing.
  • name makes a running agent addressable through SendMessage.
  • model overrides the agent definition for one call; otherwise the definition or parent model is used.
  • isolation: "worktree" creates an isolated checkout whose creation, locking, retained-change, and cleanup rules are documented in Worktree isolation and handoffs; gated remote isolation launches in a cloud environment.
  • The deprecated mode input is ignored; subagents inherit the parent permission mode unless agent frontmatter overrides it.
  • The deprecated team_name input is ignored because an enabled session derives one implicit team. This does not mean Agent Teams is always on; without the experimental gate, named calls follow the ordinary Agent path.

Subagents may spawn subagents up to five levels deep. A process-local session budget defaults to 200 spawned agents; exceeding it returns an explicit stop-delegating message and names CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION as the override. This budget is separate from a Workflow run’s own 1,000-agent runaway backstop.

Messages from the launching agent are task direction, not user authorization. Cross-agent messages cannot approve permission requests; permission prompts from background subagents surface in the parent session.

Background-session completion and input-needed states can fire Notification hooks (agent_completed, agent_needs_input). claude agents --json --all exposes active/completed state for scripts, including what a waiting row is blocked on.

How Agent calls select models and run concurrently

Agent model selection is configuration-driven rather than a general task-difficulty classifier. The exact order is:

PrioritySourceBehavior
1CLAUDE_CODE_SUBAGENT_MODELWins for every ordinary subagent when set to a value other than inherit.
2Agent({model})One-call override; ignored for fork agents.
3.claude/agents/*.md or SDK agent-definition modelAgent-specific default.
4Parent runtime modelinherit calls getRuntimeMainLoopModel() with the current permission mode, so plan-mode alias behavior and policy checks still apply.

Every candidate still crosses availableModels and entitlement checks. If an Agent override is denied, the runtime logs the drop and inherits the parent runtime model instead of silently calling the forbidden model. Workflow agent({model, effort}) uses the same model resolver and a separate effort override. See Model selection, calls, usage, quota, and billing.

There are two independent meanings of “parallel”:

  1. Parallel tool-call launch. Agent.isConcurrencySafe() is always true. Y3g() groups consecutive concurrency-safe tool uses from one assistant response, and J3g() runs each group through Eao() with CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY or a default of 10. A non-concurrency-safe tool splits the sequence into an ordered barrier, so safe calls on opposite sides do not leapfrog it.
  2. Background lifetime. Once an Agent call launches asynchronously (the default), its worker remains registered after the tool call returns. The tool scheduler slot is then free, so the default 10 is a concurrent tool-handler/launch limit, not a cap of ten live background agents. Live delegation is instead bounded by nesting depth, the default 200 session spawn budget, permissions, and available resources.

If several run_in_background:false Agent calls appear in one concurrency-safe block, the workers can still execute concurrently, but the parent model does not receive the combined tool-result message until the block settles. For asynchronous calls, it receives launch metadata quickly and later gets independent task notifications. Concurrent writers share the current checkout unless isolation:"worktree" (or an eligible remote environment) is selected; scheduler concurrency is not filesystem isolation.

Peer addressing with ListAgents

The source-visible ListAgents description treats names as the primary address. The resolver’s candidate-index shape can combine:

  • in-process subagents spawned by the current session;
  • other live local Claude sessions;
  • the user’s cloud Claude sessions when cloud access is available;
  • Remote Control bridge sessions, which are reply-only from this path.

That is a capability shape, not proof of a live standalone roster in this artifact. No concrete ListAgents tool definition appears in the retained bundle, Efo() returns an empty local-session list, and r7r() returns an empty cloud-session list. Concrete listLivePeerSessions() and listBridgePeerSessions() implementations exist elsewhere, but are not called by vfo() here. The SendMessage local/cloud switch cases are therefore implemented route code whose normal discovery path is dormant in this exact build.

Callers copy the printed name into SendMessage({to: "<name>", ...}). A row’s bracketed reference is appended only when duplicate names make the bare name ambiguous or an error requests disambiguation. The resolver returns candidate rows rather than guessing.

After a recipient resolves, the runtime can pin that identity. If the same display name later points to a different agent/session, send_message_pin_guard refuses the send and asks for an explicit reference instead of silently redirecting the message. This name-plus-ref contract also appears in the fully implemented SendFile body, but SendFile.isEnabled() is hard-coded false in this build and must not be treated as an available peer capability.

Delivery also distinguishes an explicit user stop from ordinary lifecycle cleanup. SendMessage refuses to resurrect an agent carrying the stopped-by-user marker. An otherwise stopped or evicted agent can be resumed, including by rebuilding resumable state from its transcript; failure to recover that state is reported instead of silently starting an unrelated recipient. The established name pin still applies after resumption.

Observers are intentionally excluded from ordinary messaging: SendMessage rejects observer callers/targets, and observers report only through ObserverReport. See Observer agents for pairing, repeated permission checks, digest framing, and resume behavior.

Hosted review

ultrareview [target] is described as a cloud-hosted multi-agent code review command. Adjacent strings include /ultrareview, /review, and /v1/ultrareview/preflight, indicating both local command UX and remote preflight/API surfaces.

Task and subagent runtime internals

This section deepens the surfaces above by mapping the task tool family, subagent lifecycle events, background/scheduled task mechanics, and hosted review surfaces.

Additional anchors

Semantic aliasString or symbolMeaning
TaskCreatePromptUse this tool to create a structured task list for your current coding sessionTaskCreate prompt/description.
TaskGetDeferredDescriptorTaskGet, shouldDefer:!0, isReadOnly(){return!0}TaskGet is deferred, concurrency-safe, and read-only.
TaskUpdateFreshnessGuardMake sure to read a task's latest state using `TaskGet` before updating it.Task staleness guard in TaskUpdate prompt text.
TaskStoreNotFoundErrorTask not found:Task-store error path.
TaskUpdateWaiter_waitForTaskUpdateTask polling/wait behavior for not-yet-completed task results.
SubagentStartHookSchemaSubagentStart, agent_id, agent_typeSubagent start hook input schema.
SubagentStopHookSchemaSubagentStop, agent_transcript_path, last_assistant_messageSubagent stop hook input schema.
TaskLifecycleHookSchemaTaskCreated, TaskCompletedTask lifecycle hook input schema.
LargeAgentDescriptionWarningLarge agent descriptionsToken-pressure warning for large custom-agent descriptions.
CronSchedulerPromptInjectioncreateCronSchedulerScheduled/recurring task prompt injection inside headless loop.
UltraReviewPreflightApi/v1/ultrareview/preflightHosted review preflight API path.

Task tool family

The task constants are grouped near TodoWrite and Skill, placing task orchestration in the same capability family as planning and skill dispatch: TaskCreate, TaskGet, TaskList, TaskUpdate.

TaskUpdate prompt text defines a status workflow pending → in_progress → completed and also allows deleted. It explicitly tells the model to read fresh task state with TaskGet before updating — a staleness warning implying the runtime expects concurrent/multi-agent task state to change underneath the current agent.

External SDK/MCP task-result waiting

This subsection describes the method-oriented SDK/MCP task protocol, not the local shared-task files used by Agent Teams. Local TaskGet reads one shared JSON task record immediately; see Agent Teams — shared task lifecycle.

The task-store path around line ~99 follows a request handler pattern:

  1. Fetch the task by ID.
  2. If absent, throw Task not found: <id>.
  3. If status is non-terminal, wait via _waitForTaskUpdate(...) and retry.
  4. If terminal, fetch the task result, clear the task queue, and include task metadata in _meta.

This is why the SDK/MCP TaskGet is marked shouldDefer: true — that protocol query may wait for completion/update rather than returning immediately. The local shared-task TaskGet does not use this waiter.

Subagent context and hooks

The runtime has an async-local context classifier agentType === "subagent". Related helpers derive whether the current execution is a built-in or user-defined subagent. Hook schemas then expose subagent lifecycle data:

HookFieldsRuntime meaning
SubagentStartagent_id, agent_typeA subagent execution started.
SubagentStopagent_id, agent_transcript_path, agent_type, last_assistant_messageA subagent stopped and can expose transcript/summary context.
TaskCreatedtask_id, task_subject, optional description/team fieldsA task was created.
TaskCompletedtask_id, task_subject, optional description/team fieldsA task completed.

SubagentStop carrying agent_transcript_path and last_assistant_message indicates that subagent execution is transcript-backed and can surface a concise final message without parsing the full transcript.

Agent-turn completion is staged cleanup rather than an unconditional kill of every descendant resource. Cleanup stages for agent-owned non-shell monitors and shell tasks are keepaliveGated: active ownership can preserve those background resources beyond the agent’s completed turn, after which the normal task cleanup/eviction path can retire them. This is separate from worktree cleanup: an unchanged native Git worktree is removed, while a changed worktree is unlocked and retained so its path and branch can be returned.

Background and scheduled task mechanics

Inside HeadlessControlLoop, when isKairosCronEnabled() is true, the runtime creates a cron scheduler whose onFire callback resolves a loop-default prompt and enqueues it as a later-priority prompt with a workload marker. Scheduled/recurring task behavior is therefore implemented by feeding prompts back into the same headless loop, not by a separate executor.

Custom-agent token pressure

The Large agent descriptions warning is produced when custom-agent descriptions exceed a threshold. It lists the largest contributors and reports total tokens versus threshold — agent descriptions are counted as context budget contributors and can produce warnings before model execution.

Hosted review path

ultrareview [target] is a top-level command described as cloud-hosted multi-agent code review. The preflight API path /v1/ultrareview/preflight checks whether the hosted review can run and returns user-facing blockers such as essential-traffic-only mode and data residency constraints.

Implementation takeaways

  1. Tasks are shared mutable runtime state; TaskGet/TaskUpdate are designed for staleness and concurrency.
  2. Subagents have explicit runtime context, transcript paths, and lifecycle hooks.
  3. Scheduled tasks re-enter the same loop as prompt injections.
  4. Hosted multi-agent review is gated by a preflight API path and traffic/data policy conditions.
  5. Custom-agent descriptions are budgeted as model context and can trigger token-pressure warnings.

Deterministic multi-agent fan-out is owned by the separate Workflow lifecycle. Use individual Agent calls for ordinary delegation; use Workflow only after explicit user opt-in to multi-agent orchestration.

Experimental read-only supervision is owned by Observer agents. It composes with the Agent permission boundary but uses a separate pairing registry and observer-ref transcript record.

Agent communication protocol handoff

Ordinary live subagents receive SendMessage through process-local pending-message queues at a later model/tool boundary. Experimental teammates instead use locked JSON-array inbox files under ~/.claude/teams/<team>/inboxes/, with structured frames serialized inside each envelope’s text; team_permission_update is deliberately dropped. Shared task records are a third, separate file protocol. See Agent messaging and communication for the complete resolve → transport → receive → reply path, Agent Teams for exact team paths and trust checks, and Runtime communication protocols for the cross-module view.

Custom agent definitions lifecycle

The AgentDefinitions module (cli.renamed.js:277638-277700) classifies agent records and decides which agents are visible to a given session, including how each agent’s MCP requirements interact with the configured MCP roster.

Agent provenance

Every agent record carries a provenance tag. The classifiers:

HelperReturns true for
isBuiltInAgent(agent)Agents bundled with Claude Code itself.
isCustomAgent(agent)Agents loaded from a user-supplied JSON (CLI flag --agents, .claude/agents/*.md, etc.).
isPluginAgent(agent)Agents contributed by an installed plugin.

The provenance decides where edits are persisted and which permission tier owns the agent’s frontmatter.

MCP scope translation (agentMcpSpecsToScopedConfigs)

Each custom agent can declare mcpServers (the list of MCP servers it requires). agentMcpSpecsToScopedConfigs(agent) translates those specs into a scoped MCP config bag the MCP coordinator can merge into the session’s roster:

  • Agent-scoped MCP servers (only visible while that agent is active) are mounted with a private scope.
  • Session-scoped MCP servers (declared by the agent but expected to be globally available) are merged into the regular session roster.

This is the mechanism that lets a subagent template say “I need this MCP server” without forcing the operator to register the server globally.

MCP requirement filtering (hasRequiredMcpServers, filterAgentsByMcpRequirements)

Before the session offers an agent to the model, the runtime calls filterAgentsByMcpRequirements(agents, availableMcpServers):

  • For each agent, hasRequiredMcpServers(agent, availableMcpServers) checks that every mcpServers[*] entry is satisfied by the live MCP roster (after agentMcpSpecsToScopedConfigs has run).
  • Agents whose requirements are unmet are dropped from the active list. The UI lists them under “unavailable agents” with the missing-server reason so the operator can install or enable the right MCP server.

Active-agent resolution (getActiveAgentsFromList)

getActiveAgentsFromList(allAgents) is the final selector. It:

  1. Filters by enabled / disabled / explicitly-allowlisted state.
  2. Applies filterAgentsByMcpRequirements(...).
  3. Sorts by source priority (built-in < plugin < custom — later wins on name collision).
  4. Returns the deduped active list the rest of the runtime treats as the source of truth.

Cache invalidation (clearAgentDefinitionsCache)

Agent definitions are cached because parsing .claude/agents/*.md frontmatter on every turn would be wasteful. clearAgentDefinitionsCache() is called when:

  • The user adds/removes an agent file (via the file watcher).
  • A plugin install / uninstall flips the plugin-contributed agent list.
  • The user runs agents reload from the CLI.

After invalidation, the next getActiveAgentsFromList(...) call re-parses everything.

Created and maintained by Yingting Huang.