Workflows and multi-agent execution
This page documents Osprey source commit 01dabf2fedd1826362162ead9757232e89d9bfa5.
Spawn conversational subagents
Osprey exposes exactly four subagent tools to a lead agent.
| Tool | Required input | Optional input | Output |
|---|---|---|---|
spawn_subagent |
persona, input |
model, name |
Returns agent_id, session_id, and name immediately. |
send_input |
agent_id, input |
None | Confirms that a follow-up user turn was queued. |
wait_agent |
agent_ids |
mode |
Returns settled status and output for requested agents. |
cancel_agent |
agent_id |
None | Confirms cancellation. |
Only a persona with spawnable: true can start as a child.
Persona resolution and tool-policy validation occur before the child session starts.
An optional model value overrides the parent's model through configured provider resolution.
Omit name to receive an automatic agent-N identifier.
A custom name accepts 1 through 64 lowercase letters, digits, underscores, or hyphens.
A custom name cannot use the reserved agent-N pattern.
Names must be unique within the complete spawn tree.
send_input queues the child's next user turn.
The child receives that turn after its current turn finishes.
The child retains its complete conversation history.
wait_agent uses all mode by default.
The all mode preserves the requested identifier order.
The any mode returns the first settled child only.
The identifier list cannot be empty.
Canceling a wait does not cancel any child.
cancel_agent preserves partial child output.
It records a canceled outcome.
Canceling an already settled child succeeds without another action.
Unknown identifiers produce errors for every control tool.
Each child has a persisted session row linked to its parent and root session. Nested children use the same shared spawn tree. Parent shutdown stops current descendants. It prevents late nested spawns.
The default tree-wide limit is eight running children.
Nested descendants count against the same limit.
Set --max-subagents to override the limit for a launch.
Without the option, [agent].max_subagents supplies the configured value.
[agent]
max_subagents = 6
A spawn beyond the limit fails before a child session starts. Settled children no longer consume concurrent capacity.
Discover JavaScript workflows
A workflow is a JavaScript file that coordinates one or more child agents. Osprey resolves workflow names in this order:
- Project
.osprey/workflows/*.jsfiles, from nearest to farthest - User
$OSPREY_HOME/workflows/*.jsfiles - Workflows bundled into the Osprey binary
The default $OSPREY_HOME value is ~/.osprey.
The first script with a given metadata name wins.
An invalid script is skipped without hiding other valid workflows.
Osprey bundles these workflows:
| Name | Behavior |
|---|---|
deep-research |
Runs three research agents, three verification agents, and one synthesizer. Requires web capability. |
review-code |
Runs four review agents and one synthesizer. Incomplete dimensions remain identified. |
test-triage |
Analyzes the first 20 nonempty lines with concurrency three, then runs one synthesizer. |
Each script needs an exported meta object.
The name and description values are required and nonempty.
The phases list is optional.
The requires_web value defaults to false.
export const meta = {
name: 'review-pair',
description: 'Run two focused reviews and combine their results',
phases: ['review', 'combine'],
requires_web: false
}
const reviews = await phase('review', async () => {
return await parallel([
() => agent('Review correctness.', { label: 'correctness', persona: 'coding' }),
() => agent('Review test coverage.', { label: 'tests', persona: 'coding' })
])
})
return await phase('combine', async () => {
return await agent(`Combine these reviews: ${JSON.stringify(reviews)}`, {
label: 'synthesis'
})
})
The embedded engine registers args, agent, phase, and ledger.
The workflow prelude also defines helpers such as parallel, pipeline, and route.
agent accepts a prompt plus optional label, persona, and model values.
Without a label, the runtime creates a wf-N label.
Without a persona value, the child uses the default persona.
Without a model value, the child inherits the workflow host model.
The script receives invocation input through args.
Slash-command input parses as JavaScript Object Notation (JSON) when possible.
Other slash-command input remains a string.
An empty slash invocation supplies null.
The source lint rejects import, require, process, Deno, and common browser globals.
The rejection list also includes fetch, window, document, and XMLHttpRequest.
The source lint is not a security boundary.
The embedded engine exposes no filesystem or network global. Child agents perform authorized filesystem, shell, retrieval, or network work through their tool policies. Each run receives a fresh JavaScript engine. Its global state is isolated from other runs.
Launch and monitor workflows
Discovered workflows become terminal user interface (TUI) slash commands by metadata name.
For example, enter /test-triage followed by newline-separated failure text.
A built-in slash command wins over a workflow name.
A workflow wins over a skill with the same non-built-in name.
Use these built-in commands:
| Command | Result |
|---|---|
/workflows |
Opens the live workflow progress view. |
/run scripts/review.js |
Runs an existing .js file by path. |
/deep-research QUESTION |
Runs the bundled workflow with a web capability check. |
The /run path must exist and end in .js.
The deep-research workflow fails before spawning when no web-capable tool exists.
The progress view displays runs, phases, agents, usage, output, spills, and recent child tools.
Use p and r to pause and resume a selected run.
The selected run pauses at the next agent() boundary.
Use s to stop a selected run or a selected running agent.
Use R to restart a selected noncompleted agent under a new label.
The w and W actions write a JavaScript file named from the workflow metadata.
A save failure leaves the run unchanged and reports the failed path.
Use w to save available script source into the project workflow directory.
Use W to save available script source into the user workflow directory.
The TUI lead has two workflow tools when workflows are enabled.
Headless mode composes the tools before applying persona and toolset filters.
When the headless persona declares a tools allowlist, that list must include run_workflow and propose_workflow to retain both tools.
The bundled default persona omits them.
run_workflow accepts script_path, name, or inline script.
Source selection uses that same order when several fields exist.
The tool also accepts args and resume_from_run_id.
Success returns status, run_id, summary, script path, and script name.
A script failure returns status = "failed" and an error in the structured result.
propose_workflow accepts name, description, phases, script, rationale, and optional estimated_agents.
The supplied metadata must match the script metadata exactly.
Validation happens before an approval request.
An approved proposal starts in the background.
The call returns a run identifier.
A rejected proposal returns status = "rejected" and the reason.
Headless proposal calls are rejected because no interactive approval channel exists.
The proposal itself is not stored after the tool call.
Workflow trust
Bundled workflows are trusted by default. Project and user workflows require an approval for each scope, metadata name, and script hash. Editing the script changes its Secure Hash Algorithm 256-bit (SHA-256) value and invalidates prior trust.
The TUI approval dialog offers four actions.
- Run the script once without storing trust.
- Store trust for the script's scope and run the script.
- View the complete script.
- Reject the launch and place an editable
/runcommand in the composer.
For an inline proposal edit, the TUI first writes the script into the operating system temporary directory.
Canceling the dialog rejects the launch.
Persistent workflow grants use $OSPREY_HOME/workflow_trust.toml.
A missing trust file represents an empty store.
A corrupt trust file also becomes an empty store after a warning.
Project trust applies to the project scope. User trust applies across projects for the same user workflow. Headless runs reject untrusted scripts and report the path, name, and hash.
Configure workflow limits
Workflows are enabled by default.
Set [workflows].enabled = false to remove workflow tools and commands.
The OSPREY_DISABLE_WORKFLOWS variable also disables workflows for 1, true, yes, or on.
The example's positive run_retention_days value enables deletion of older run directories.
Successful deletion can orphan spill paths stored in persistent session transcripts.
[workflows]
enabled = true
max_concurrent = 8
max_total = 200
run_retention_days = 30
Project configuration can override enabled, max_concurrent, and max_total.
Only the global configuration supplies run_retention_days at this commit.
The default concurrent agent() limit is 16 for each workflow run.
The default total agent() limit is 1,000 for each workflow run.
These limits are separate from [agent].max_subagents.
A concurrent or total limit failure raises a JavaScript error. The script can catch that error and use another execution path. An uncaught error fails the workflow run.
Inspect ledgers and oversized results
Each workflow uses an append-only in-memory ledger. The ledger recognizes six entry kinds:
AgentResultClaimDecisionFileWriteStaleReadRiskRestart
Workflow code can append, count, and query ledger records. Sequence numbers start at one. They increase within a run.
Osprey mirrors ledger entries to this JSON Lines (JSONL) file:
$OSPREY_HOME/workflows/runs/RUN_ID/ledger.jsonl
The in-memory ledger remains authoritative during the run. A sidecar write failure produces a warning without failing the workflow.
Each agent result has a default display cap of 32 kibibytes (KiB). The capped result keeps a 12 KiB head and a 12 KiB tail. By default, Osprey writes the complete result to a run spill file when the cap is exceeded.
$OSPREY_HOME/workflows/runs/RUN_ID/AGENT_LABEL.txt
The result and progress view include the spill path. The spill preserves the complete output for later retrieval.
Run directories remain forever by default.
An absent or zero run_retention_days value performs no deletion.
A positive value attempts to remove older immediate run directories.
A removal failure produces a warning and does not stop Osprey.
Successful deletion can orphan spill paths stored in persistent session transcripts.
Resume and recover workflow work
resume_from_run_id works only with a run in the current in-memory session registry.
The run must belong to the same running Osprey process and active session.
A process restart cannot reconstruct resumable state from ledger.jsonl.
Resume reuses only completed agent results. The cache key contains label, prompt, persona, and model. Failed agents run again. Changed inputs also cause another child run.
An unknown or malformed run identifier produces a tool error. The tool does not silently start a cache-free resume.
A child-agent failure becomes a failed agent result that workflow code can inspect.
A failed child does not automatically discard successful sibling results.
Host failures raise JavaScript errors at the calling agent() expression.
Workflow code can catch host or phase errors. An uncaught exception fails the run. Osprey marks unfinished work as failed. Canceling a run interrupts its engine and current child work. Other workflow runs retain their separate engine state.
Configure Mixture of Agents
Mixture of Agents (MoA) sends one advisory view to several reference models. An aggregator model receives the reference outputs as private context. The aggregator remains the acting model. It retains the original tools, schema, and system prompt. Each reference call uses its configured provider and contributes token usage. The aggregator call also uses its configured provider and contributes token usage.
Define named presets in the global configuration.
[moa]
default_preset = "review"
save_traces = false
[moa.presets.review]
reference_temperature = 0.6
aggregator_temperature = 0.4
reference_max_tokens = 600
enabled = true
investigation_max_turns = 4
aggregator = { provider = "anthropic", model = "claude-opus-4-8" }
[[moa.presets.review.references]]
provider = "openrouter"
model = "openai/gpt-5.5"
investigate = true
Every preset needs at least one reference and one aggregator.
Each model string must be nonempty.
An explicit preset name has the highest selection priority.
Next, Osprey uses default_preset.
With exactly one configured preset, Osprey selects that preset automatically.
Use --model moa:PRESET to select a preset for a headless session.
A bare --model moa: resolves the configured default or derives a preset when the configuration allows it.
In the TUI, /model moa:PRESET selects the preset for later turns.
The TUI also accepts /model PRESET for an enabled preset.
A disabled preset requires the explicit moa: prefix.
Without configured presets, Osprey derives a preset from at least two credentialed providers. The active provider and model become the aggregator. Other available providers become references with their default models.
Reference temperature defaults to 0.6.
Aggregator temperature defaults to 0.4.
References have no output-token override by default.
Presets are enabled by default.
The default investigation limit is four model turns.
Osprey executes at most eight reference requests concurrently.
References without investigate = true receive no tools.
An investigate = true slot receives bounded read-only tools when a dispatcher is available.
The investigation allowlist contains read_file, grep, find_files, list_directory, tree, and retrieve.
Budget exhaustion returns available text or an exhaustion marker.
Budget exhaustion does not fail the reference slot.
A failed reference contributes a failure marker. Other references and the aggregator continue. Osprey reuses a reference fan-out while the advisory conversation view remains unchanged.
Set enabled = false on a preset to bypass its reference fan-out.
The aggregator still handles the turn.
Set save_traces = true to persist MoA turn traces in the session database.
Each trace includes reference inputs, outputs, provider, model, usage, and aggregator input.
Trace storage is disabled by default.
Enter /moa PROMPT to run one prompt through the configured default or derived preset.
The TUI restores the prior session model after this one-shot run.
Enter /moa-config to open the preset manager.
An unknown preset or invalid preset prevents MoA construction. Fewer than two credentialed providers prevent automatic preset derivation. An aggregator request failure fails the active turn after reference processing.