NAME
Langertha::Raider::Context::Agents - Internal catalogue of the agent files a run can start as subagents
VERSION
version 0.504
SYNOPSIS
# Internal to Langertha-Raider -- no API promise.
my $agents = Langertha::Raider::Context::Agents->new(
roots => [
{ dir => "$root/.raider/agents", source => '.raider/agents' },
{ dir => "$root/.claude/agents", source => '.claude/agents' },
],
plugins => [ { name => 'superpowers', dirs => [ @agent_dirs ] } ],
);
my $agent = $agents->find('raider-worker');
my $model = $agents->resolve_model($agent->{model},
engine_class => 'Langertha::Engine::Anthropic', engine_name => 'anthropic');
DESCRIPTION
Internal module. Its interface may change without notice.
The agent catalogue of ADR 0020 §1 and §2. It takes agent roots in precedence order and the agent paths of plugins, and knows every agent's name, description, instructions, tool restriction, model and declared skills. Which roots exist is the caller's decision ("agent_roots" in Langertha::Raider::Context); a root that does not exist is skipped silently. Nothing here starts an agent.
An agent is a plain hash ref:
{
name => 'raider-worker',
description => 'Default Raider worker ...',
file => '/abs/.claude/agents/raider-worker.md', # undef for the built-in
dir => '/abs/.claude/agents', # the file's directory
source => '.claude/agents', # root label, 'plugin NAME' or 'built-in'
body => 'You are ...', # the instructions, without frontmatter
frontmatter => { ... }, # every key kept
tools => undef | [ 'read_file', 'bash', ... ], # undef: every tool
disallowed => [ 'write_file', ... ],
unknown_tools => [ 'NotebookEdit', ... ], # tools/disallowedTools names not mapped
model => 'sonnet' | undef, # as written; see resolve_model
isolation => 'worktree' | undef, # as written (trimmed); a list or mapping as 'a list' / 'a mapping'
skills => [ 'getty-perl-core', ... ], # declared, not resolved
ignored_keys => [ 'color', ... ], # frontmatter keys raider does not use
builtin => 0 | 1,
one_shot => 0 | 1, # 1: cannot be continued (Explore, Plan)
memory => 1 | 0, # 0: mission without Claude memory (Explore, Plan)
}
Files. Every root and every plugin directory is walked recursively for *.md -- the files of a directory sorted by name, then its subdirectories sorted by name (the order of the briefing hook). Hidden files and directories (a leading
.) are skipped. A plugin path may also be one *.md file.Frontmatter. Read by Langertha::Raider::Context::Frontmatter (YAML, else the lenient line reader). A file without a
descriptionis not an agent: it is skipped and listed in "problems", as are unreadable files.Name. The frontmatter
name, else the file name without .md. Plugin agents areplugin:name.Tools.
toolsanddisallowedToolstake a comma-separated string or a list. A Claude Code name goes through Langertha::Raider::ToolNames (Read->read_file); a raider name -- one of "raider_names" in Langertha::Raider::ToolNames or of the built-in tool table "tool_names" in Langertha::Raider::ToolEffects (perl_eval,raider_*, ...) -- is taken as it is;mcp__*passes through. Any other name is ignored and listed inunknown_tools.toolsabsent, empty or*means every tool of the caller; a list whose names are all unknown means none. Names are unique, in the order written.Isolation.
isolation: worktreemakes every spawn of the agent run in a git worktree of its own (ADR 0032 §2). The value is kept as written; any other one refuses the spawn ("isolation_refusal" in Langertha::Raider::Subagent), nothing here judges it.Skills. Claude Code's top-level
skillsfirst, thenbriefing.skills; a string is split on commas, a[a, b]flow left as a string by the lenient reader too; each name once.Precedence. Roots in the given order, then plugins, then the built-ins
general-purpose,ExploreandPlan("builtin_agents"). The first agent of a name wins; later ones are listed by "shadowed", a built-in included when a file shadows it.Built-ins (ADR 0020 §1, ADR 0027 §6).
general-purposehas every tool and the caller's memory.ExploreandPlanare read-only by their instructions and theirdisallowedlist (agent,send_message,write_file,edit_file,multi_edit,exit_plan_mode), one-shot (one_shot: they cannot be continued) and without memory (memory0: their mission carries no Claude memory, no AGENTS.md and no packs, see "compile_mission" in Langertha::Raider::Application). File agents haveone_shot0 andmemory1.
roots
Agent roots, highest precedence first. Required. Each a hash ref { dir => '/abs/dir', source => 'label' }.
plugins
Plugin agent paths, after all roots. Defaults to none. Each a hash ref { name => 'plugin', dirs => [ '/abs/agents', '/abs/x.md', ... ] } -- the name and "agent_dirs" in Langertha::Raider::Context::Plugins of a plugin.
builtin_name
The name of the default built-in agent: general-purpose, what the agent tool starts without subagent_type.
builtin_description
Its description, Claude Code's word for word.
builtin_body
Its instructions: a short general-purpose system prompt.
explore_description
plan_description
The descriptions of the built-ins Explore and Plan, Claude Code's word for word.
explore_body
plan_body
Their instructions, after Claude Code's prompts for these agents in raider's tool names: Explore a read-only file-search specialist, Plan a read-only architect that ends with the critical files.
builtin_agents
for my $spec ($agents->builtin_agents) { ... }
The built-in agents in catalogue order -- general-purpose, Explore, Plan -- each a hash ref name, description, body, disallowed, one_shot, memory.
read_only_disallowed
The tools Explore and Plan go without: agent, send_message, write_file, edit_file, multi_edit, exit_plan_mode.
raider_tool_name
my $name = $agents->raider_tool_name('Read'); # 'read_file'
The raider tool an agent file's tool name means (see "DESCRIPTION"), or undef for a name that is neither a Claude Code name of the table, a raider tool nor mcp__*.
agents
for my $a ($agents->agents) { say $a->{name}.' ('.$a->{source}.')' }
Every agent that won its name, in precedence order: roots in the given order, plugins in the given order, the built-ins last.
shadowed
Agents that lost their name to an earlier one, each { name, source, file, by } -- by is the winner's source. A shadowed built-in has file undef.
problems
Agent files that are not agents -- unreadable, or without a description (with the frontmatter error when there was one) -- each { file, reason }.
find
my $agent = $agents->find('general-purpose');
The agent of exactly that name, or undef.
model_aliases
my $table = $agents->model_aliases; # { opus => 'claude-opus-5-5', ... }
The alias table of ADR 0020 §5, a new hash ref.
anthropic_class
The engine class a model alias needs: Langertha::Engine::Anthropic (and its subclasses), not the other Anthropic-dialect engines.
resolve_model
my $r = $agents->resolve_model('sonnet',
engine_class => 'Langertha::Engine::Anthropic', # or an engine object
engine_name => 'anthropic', # for the reason text
agent_models => { sonnet => 'claude-sonnet-4-6' }, # config agent_models
);
# { model => 'claude-sonnet-4-6' }
# or { inherit => 1, reason => 'model sonnet: inherited, engine openai is not the Anthropic API' }
What an agent's model means under the caller's engine (ADR 0020 §5); never an error. Absent or inherit: inherit. An alias (opus, sonnet, haiku, any case) goes through "model_aliases", each overridable by agent_models (other keys there are ignored); a full claude-* id stays as it is. Either gives model only when the engine class is (a subclass of) "anthropic_class"; in every other case the agent inherits and reason says why. Builds nothing; loads the engine class when given as a name.
report
my $r = $agents->report(engine_class => $class, engine_name => 'openai', agent_models => {});
What config explain shows of the agents, plain data (JSON-safe):
{
agents => [ {
name, source, file, # file undef for the built-in
tools => '*' | [ 'read_file', ... ],
disallowed => [ ... ],
model => { declared => 'sonnet' | undef, model => '...' }
| { declared => ..., inherit => 1, reason => '...' },
skills => [ ... ], # declared
unknown_tools => [ ... ],
ignored_keys => [ ... ],
}, ... ],
shadowed => [ { name, source, file, by }, ... ],
problems => [ { file, reason }, ... ],
}
The arguments go to "resolve_model".
SEE ALSO
SUPPORT
Issues
Please report bugs and feature requests at https://github.com/Getty/langertha-raider/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.