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 description is 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 are plugin:name.

  • Tools. tools and disallowedTools take 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 in unknown_tools. tools absent, 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: worktree makes 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 skills first, then briefing.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, Explore and Plan ("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-purpose has every tool and the caller's memory. Explore and Plan are read-only by their instructions and their disallowed list (agent, send_message, write_file, edit_file, multi_edit, exit_plan_mode), one-shot (one_shot: they cannot be continued) and without memory (memory 0: their mission carries no Claude memory, no AGENTS.md and no packs, see "compile_mission" in Langertha::Raider::Application). File agents have one_shot 0 and memory 1.

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.

https://raudssus.de/