NAME
Langertha::Raider::Application - Internal application service that builds and runs a raider for a workspace
VERSION
version 0.503
SYNOPSIS
# Internal to Langertha-Raider -- no API promise.
my $app = Langertha::Raider::Application->new(
root => '/path/to/project',
engine_options => { temperature => 0.2 },
);
my $result = $app->run('Explore the repo and summarize it.');
my $raider = $app->raider; # the Langertha::Raider
DESCRIPTION
Internal module. Its interface may change without notice.
The application service the surfaces share (ADR 0002): for one workspace ("root") it reads the project config file (Langertha::Raider::Config: .raider/config.yml, else the legacy .raider.yml; below, .raider.yml stands for whichever is in use, laid over the home file ~/.raider/config.yml), picks the engine through Langertha::Raider::EngineResolver, activates the packs (Langertha::Raider::Packs, ADR 0012), compiles the mission (ADR 0004, ADR 0014), mounts the tool servers -- files (Langertha::Raider::FileTools), bash (MCP::Run::Bash), web (Langertha::Raider::WebTools), the Perl tools (Langertha::Raider::PerlTools) when granted, the Hall tools when spawned by a Hall -- and builds the Langertha::Raider that runs the raids. It opens, creates and replays the sessions of the project ("session_store", ADR 0015) and records every run in the session's journal ("run_prompt"), handing the run's events on to the surface. It prints nothing; presentation such as the live trace belongs to the surface, see Langertha::Raider::CLI.
engine_name
Langertha engine class shortcut (e.g. 'anthropic', 'openai', 'deepseek', 'groq', 'mistral', 'gemini', 'ollama'), passed as engine. Defaults to -o engine=, then to engine: in .raider.yml, then to the first *_API_KEY environment variable found, then to 'anthropic' ("engine_name" in Langertha::Raider::EngineResolver).
engine_resolver
The Langertha::Raider::EngineResolver that picks engine, model and API key from the flags, the -o options, .raider.yml and the environment, and builds the engine.
provider
The completed provider activation of raider --provider ("activate_f" in Langertha::Raider::Provider::Activation), or none. It decides engine, model and URL; see "provider" in Langertha::Raider::EngineResolver.
model
Model identifier to pass to the engine. Defaults to model in "engine_options", then model: in .raider.yml, then the per-engine cheap default. An explicit model always wins; this is the model the engine is built with.
api_key_env
Name of the environment variable used for the current engine's API key (for display / debugging). Returns undef for engines that don't use an API key (e.g. ollama).
api_key
API key for the engine. Defaults to api_key in "engine_options", then api_key: in .raider.yml, then an engine-appropriate environment variable. An explicit api_key always wins.
mission
System prompt of the Raider, compiled from separate items (ADR 0004, ADR 0014): the instructions (a generic assistant persona plus the project instructions file, "instructions"), the tool description, the loaded skills and the active packs. The tool description lists the tools of the mounted tool servers -- the same servers the engine gets -- each as name(required, [optional]) from its input schema (ADR 0005); a tool that is not mounted is not described. A mission passed to the constructor (-M) replaces the instructions item only, also across "reload_mission"; the other items still apply. With "bare" the skills, the instructions file and all packs not switched on by --pack or /pack are left out.
bare
--bare: an isolated context. No instructions file (.raider.md, .raider/instructions.md), no skills, no pack detection, and no packs from packs: or enabled_by_default; --pack NAME and /pack NAME still switch a pack on explicitly. What remains is the instructions (the default persona, or the -M text) and the tool description.
persona_intro
The first paragraph of the default persona: who the agent is and where it runs. The command line (Langertha::Raider::CLI) says it is a CLI.
persona_turn_end
The last paragraph of the default persona: how the agent ends a turn and who answers next.
root
Working directory for tool operations. Defaults to the current process cwd. File tools are confined to this directory, including realpath checks for symlink escapes; bash commands inherit it as their default working directory.
allowed_commands
Optional arrayref restricting which bash commands may run (first word match). When undef, any command is allowed.
max_iterations
Maximum tool-calling iterations per raid. Defaults to 10_000 — effectively unlimited, so a raid only ends when the model itself stops emitting tool calls. The conversation history is preserved between raids, so the next user message in the REPL simply continues the same thread.
Set this to a smaller number if you want a hard safety cap.
on_event
Optional code reference called as $on_event->($type, %payload) for every event of the application ("event"): the run events of "run_prompt" and every tool.call and tool.result of a raid, which come from Langertha::Raider::Plugin::Events. A surface that follows one run passes its consumer to "run_prompt" instead.
on_journal_error
Optional code reference called as $on_journal_error->($session, $error) when an event cannot be written to a session journal (a full disk): once per run, and once per "record". The run goes on. Without it the error is a warn.
perl
Enable the PerlTools MCP server (perl_eval, perl_check, perl_cpanm). Off by default; set via --perl CLI flag or perl: true in .raider.yml. Without either, the tools also come with the perl pack, which is detected in a Perl workspace; see "perl_tools_enabled".
perl_tools_enabled
Whether the PerlTools server is mounted: --perl turns it on; else an explicit perl: (in .raider.yml or -o perl=) decides either way; else it is on when an active pack requests the perl tools (the bundled perl pack, detected by cpanfile, dist.ini, Makefile.PL or lib/**/*.pm). Granting a pack's request here stands in for the local tool policy of ADR 0005, which does not exist yet; perl: false is the local denial. A pack switched on or off later is followed by "reload_mission", which remounts the server to match.
perl_tools_grant
my $grant = $app->perl_tools_grant;
# { enabled => 1, reason => 'pack perl (detected)' }
"perl_tools_enabled" with the reason: "perl" by its "source_label" (--perl on the command line), perl: true or perl: false with where it was set (.raider.yml or the label of engine_options, -o), the active packs requesting the tools with their activation source, or not requested.
preferred_lib_target
Override the default local::lib target for perl_cpanm. When unset, defaults to .raider/lib/ for standalone raiders. Can be set via preferred_lib_target in .raider.yml.
pack_names
Optional list of pack names supplied by the CLI, usually from repeatable --pack NAME. When present, these override the packs: list in .raider.yml.
no_pack_names
Pack names switched off from the command line (repeatable --no-pack NAME). They win over --pack, packs:, the bundled defaults and detection.
detect
Pack detection from the command line: 0 for --no-detect, 1 for --detect. When not given, detect: in .raider.yml decides; the default is on.
packs
Langertha::Raider::Packs::Collection of the installed packs: those in "root"/.raider/packs/, ~/.raider/packs/, the bundled share/packs/ and $RAIDER_PACK_DIRS, the first place winning for a name ("build_packs" in Langertha::Raider::Packs). Which are enabled, highest priority first (ADR 0012); with "bare" only --pack NAME applies:
- 1.
--no-pack NAMEswitches a pack off;--pack NAME(or-o packs=a,b) enables the listed ones exclusively;--no-detect/--detectswitch detection off or on. - 2.
packs:in .raider.yml ([caveman, git-guru]) enables the listed ones exclusively when no flag named packs;detect: falseandno_detect: [NAME]switch detection off entirely or per pack. - 3. Packs whose detection rule matches "root" are added.
Without explicit packs the bundled defaults (enabled_by_default) are on.
Detection rules come from a pack's pack.yml (detect:, the pack default) and from detect: in ~/.raider/config.yml and .raider.yml, which replace the pack default per pack name, the project's rule over the home's. Each rule is evaluated against "root" with Langertha::Raider::Detect when "packs" is built and on "redetect_packs" (/reload), never per model call. A detected pack is added to the enabled ones; in an exclusive group it gives way to an explicit pack and replaces a bundled default. The outcome per pack is in "activation_report" in Langertha::Raider::Packs::Collection. An invalid rule croaks.
Detection only decides which packs are active, it grants nothing (ADR 0005). Rules from the project's .raider.yml or .raider/config.yml are evaluated and packs from its .raider/packs/ are loaded right away: the workspace trust decision of ADR 0004, which is meant to gate both, does not exist yet.
detection_state
my ( $on, $why ) = $app->detection_state;
Whether pack detection runs, and what decided it: "bare" or "detect" by their "source_label" (--bare, --detect, --no-detect on the command line), detect: false or default.
redetect_packs
my @detected = $app->redetect_packs;
Drops the packs that were enabled by detection, evaluates the rules again against "root" and returns the names of the packs detected now. Packs enabled any other way stay as they are. /reload calls it.
max_context_tokens
Trigger history auto-compression once the last prompt exceeds context_compress_threshold * max_context_tokens. Defaults to 40_000, which keeps the running session comfortably under typical per-minute rate limits (Anthropic org default: 50k input tokens/min on Haiku).
context_compress_threshold
Fraction of "max_context_tokens" at which compression kicks in. Defaults to 0.7.
skill_sources
ArrayRef of skill-source specs to load and append to the mission. Each spec is a hashref:
{ type => 'claude', path => '.claude/skills' } # Claude Code SKILL.md tree
{ type => 'dir', path => 'my-skills', glob => '*.md' }
Defaults to the skills entries of .raider.yml (see Langertha::Raider::Config) followed by "cli_skill_sources". Passing skill_sources explicitly replaces both. With "bare" there are none.
cli_skill_sources
ArrayRef of skill-source specs from the command line (--claude, --openai, --skills DIR). They are added to the .raider.yml skills, duplicates dropped.
config
The Langertha::Raider::Config of "root": .raider/config.yml, else .raider.yml, over ~/.raider/config.yml.
instructions
The Langertha::Raider::Instructions of "root": the project instructions file, .raider/instructions.md, else .raider.md.
engine_options
HashRef of the -o KEY=VALUE options. Engine attributes (e.g. temperature, response_size, seed) are forwarded to the engine constructor, merged on top of values loaded from .raider.yml in the working directory. Raider's own keys (see "is_app_key" in Langertha::Raider::Config) configure raider like their .raider.yml counterparts and override them; packs and skills take a comma-separated list.
raid_f
my $result = await $app->raid_f($prompt);
Async variant: drives one raid iteration and returns the Langertha::Raider::Result.
run
my $result = $app->run($prompt);
Synchronous convenience wrapper around "raid_f". Runs the I/O loop until the raid completes and returns the result (which stringifies to the final text).
run_prompt
my $outcome = $app->run_prompt($text,
session => $session, # optional
on_event => sub { my ( $type, %payload ) = @_; ... }, # optional
);
Runs $text as one run ("run") and records it, whichever surface asks: in the journal of the Langertha::Raider::Session as the session's next run (ADR 0015) -- run.started, the user input as message, every tool.call and tool.result with the whole result text, the final answer as message, and run.finished with the end state and the raider's metrics, also when the run failed. A journal write that fails goes to "on_journal_error"; the run goes on.
Every event of the run goes to on_event (and "on_event") through "event": the journal types and run.state (running, then the end state), as ADR 0013 names them.
Returns the outcome as a hash reference: status (completed, failed, or cancelled after "cancel_run"), elapsed (seconds, to the millisecond), result (the Langertha::Raider::Result, also of a cancelled run), response (its text) and metrics of a completed run, error of a failed one, and session (id, path) when there is one. An empty $text runs nothing and returns nothing.
begin_run
$app->begin_run(session => $session, on_event => $consumer);
Starts a run as "run_prompt" does, for a surface that drives "run" itself: the run gets the next run id of session, run.started (engine and model) and run.state running. "end_run" ends it.
end_run
my $end = $app->end_run(interrupted => signal => 'TERM');
Ends the run in progress, if there is one: run.finished with $status and the given error and signal into the journal, with the given metrics or else the raider's, then run.state with $status. Returns status, elapsed, the error and signal given and, with a session, session (id, path) -- or nothing outside a run. A surface calls it for a run it ends itself, as the command line does for a signal.
cancel_run
$app->cancel_run;
Cancels the run in progress (ADR 0009): its raid stops at the next safe point ("cancel" in Langertha::Raider) and "run_prompt" ends the run as cancelled -- run.finished with that status, and a tool call cut off gets its tool.result with status cancelled. Tool subprocesses are not signalled here; that is the surface's to do. Only records the request, so a signal handler may call it. Outside a run it does nothing and returns false.
event
$app->event('tool.call', call => 'c1', name => 'bash', arguments => { ... });
One event of the application, handed to every consumer: the session journal of the run in progress for the types it records (with the run's run id), the on_event of that run, and "on_event".
record
$app->record($session, 'history.cleared');
Appends one event outside a run to the session journal. A write that fails goes to "on_journal_error" and returns false.
raider
Returns the underlying Langertha::Raider instance (lazily built).
loaded_skill_names
Returns a list of skill names currently discoverable from the configured "skill_sources". Intended for banner/status display.
session_store
The Langertha::Raider::SessionStore of the project in "root" (ADR 0003, ADR 0015), built on first use.
create_session
my $session = $app->create_session;
A new session in "session_store", open for writing and locked ("create" in Langertha::Raider::SessionStore).
open_session
my $session = $app->open_session($id);
Opens the session $id of "session_store" for writing; croaks ... is in use while another raider holds it ("open" in Langertha::Raider::SessionStore).
replay_session
my $journal = $app->replay_session($session);
Replays the journal the Langertha::Raider::Session was opened with into "raider" (ADR 0015): history from the message events of the runs that got an answer, session_history from all events. Nothing is executed. Returns the Langertha::Raider::Session::Journal.
fork_session
my $fork = $app->fork_session($id);
# { id, path, forked_from, messages }
A new session in "session_store" whose session.created names the session $id in forked_from, and which takes over its working history -- what a resume would replay into history ("history_messages" in Langertha::Raider::Session::Journal) -- as message events outside any run. The original is only read (no lock needed) and never changed. Returns the new session's id and path, forked_from and how many messages it took over.
remove_session
my $path = $app->remove_session($id);
Deletes the session $id of "session_store", journal and lock file ("remove" in Langertha::Raider::SessionStore, which croaks session ID is in use while another raider has it open). Returns the journal's path.
reload_mission
Rebuilds the mission (e.g. after the instructions file has been edited) and swaps it into the underlying Langertha::Raider. An explicit "mission" is kept. When "perl_tools_grant" changed since the tool servers were mounted (/pack, /reload), the PerlTools server is mounted or unmounted first, so the engine offers the same tools the new mission describes from the next raid on; a raid resumed from a pending question re-gathers them too.
mission_source
Where the instructions item of "mission" comes from: the "source_label" of mission (-M on the command line) for a mission passed to the constructor; the "label" in Langertha::Raider::Instructions of the instructions file (.raider/instructions.md or .raider.md) when it customizes the default persona (never with "bare"); default otherwise.
source_label
my $label = $app->source_label('pack_names'); # 'pack_names', '--pack' in the CLI
How reports name a setting that was passed to the constructor: in "explain_config", "perl_tools_grant", "detection_state", "mission_source" and the pack sources. The keys are engine, model, api_key, engine_options, engine_options packs, pack_names, no_pack_names, perl, detect, no_detect, bare, mission and cli_skill_sources. A surface names its own way of setting them in "source_labels"; without, the label is the key.
source_labels
The labels of "source_label" by key; none here. The command line (Langertha::Raider::CLI) returns its flags.
explain_config
my $report = $app->explain_config;
Where each effective setting came from, constructor arguments included. The shape of "explain" in Langertha::Raider::Config, with source (and shadowed) naming an argument by its "source_label" (the command line names its flags: -e, -m, -k, -o, --pack, --perl, --claude/--openai/--skills), a layer of the config file by its "label" in Langertha::Raider::Config (.raider.yml, .raider.yml default:, .raider/config.yml openai:), a layer of the home file ~/.raider/config.yml (home, home default:, home openai:), an environment variable (env OPENAI_API_KEY) or default. A value merged from both files (no_detect, detect) names the home layer in merged_with. API key values are never included. Builds no engine. file is the config file in use and ignored_files the config files next to it that are not loaded ("ignored_files" in Langertha::Raider::Config); home_file is the home file, present only when it is loaded.
detection says whether pack detection runs (on, or off with what switched it off) and packs is the "activation_report" in Langertha::Raider::Packs::Collection: each pack with its source (flag, config, default, detected, manual), its origin (project, home, shipped, env) and, for detection rules, the clause that matched or failed. skipped_packs is "skipped_packs" in Langertha::Raider::Packs::Collection. perl_tools is "perl_tools_grant": whether the Perl tools are mounted, and why.
instructions is "mission_source" and bare is "bare". ignored_instructions_files are the instructions files that are present but not used ("ignored_files" in Langertha::Raider::Instructions): a legacy .raider.md next to .raider/instructions.md.
tools lists the mounted tools, each with its name, its source (engine:N) and effects: what it can do, from a static built-in table (read, write, network, code, message; empty when it only steers the run), or undef when the table does not know the tool. Information only -- nothing is enforced by it.
project_tools is present when ~/.raider/config.yml has one (ADR 0011; "project_tools" in Langertha::Raider::Config): the file and label it was read from, selectors with whether each matches "root" and why, and tools, one entry per tool name that a selector names or an active pack requests (the tools: of its pack.yml; a project has no other way to request a tool yet): granted_by, the matching selectors naming it (empty: not granted); requested_by, the requesting packs (pack perl); and known, tool for a built-in or mounted tool, tool group for perl, undef for a name raider does not know (not an error). Information only -- the mounted tools do not follow it yet. A project_tools in the project file, inside a section, or given with -o is not read and is listed in ignored.
SEE ALSO
SUPPORT
Issues
Please report bugs and feature requests on GitHub 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.