App Composition
Three ways to combine Digitorn apps. Each serves a different shape of workflow.
| Pattern | Mechanism | When to use |
|---|---|---|
call_app tool | One agent calls another deployed app as a tool, in-flight. | Ad-hoc composition; agent decides at runtime which apps to invoke. |
flow: graph | In-app workflow (agents, tools, IF, parallel). Prefer this for linear/branching automation. | Background or conversation apps that need deterministic routing. |
| Multi-agent | One app declares a coordinator + specialists in agents:. | Cohesive team behind one app; specialists share workspace + memory with the coordinator. |
runtime.mode: pipeline/runtime.pipeline— schema legacy only. The daemon does not execute pipeline mode. The compiler rejects it; migrate to aflow:graph (orcall_app).
Pattern 1 - call_app (in-agent app invocation)
call_app. An always-available primitive.
The calling agent invokes another deployed app over the daemon's
HTTP API and gets the result back as the action response.
Params
| Field | Type | Required | Description |
|---|---|---|---|
app_id | string | yes | Deployed app_id to call. |
prompt | string | yes | Message sent as the target's user input. |
timeout | float | no (default 120.0) | Seconds before the call times out. |
How it works
call_app runs the installed target app with your prompt,
waits up to timeout, and returns its output (or an error) to the
calling agent as a normal tool result.
Constraints
- Install the target first:
digitorn install target.yaml. - The target must be
runtime.mode: one_shot. - Both apps must be available in the same Digitorn instance.
call_appis a meta-tool - it bypasses the capability gate chain entirely, somax_risk_level/grant/denydon't apply to it (see Security architecture).
Example
agents:
- id: orchestrator
role: coordinator
brain: { ... }
system_prompt: |
For Python files, call_app(app_id='py-analyzer', prompt=path).
For TypeScript files, call_app(app_id='ts-analyzer', prompt=path).
Aggregate the results and present a unified report.
call_app needs no grant - it's always available whenever the
daemon has app-to-app calling wired up, the same as any other
meta-tool.
// LLM-side
{"name": "call_app",
"arguments": {"app_id": "py-analyzer",
"prompt": "src/auth/validate.ts",
"timeout": 60}}
Pattern 2 - sequential chains → use flow: (not pipeline)
runtime.mode: pipeline / runtime.pipeline[] are legacy schema only.
The compiler rejects them; the daemon never executes them.
For a fixed chain of agents/tools (ETL-shaped), declare a flow:
graph with tool / agent nodes and linear routes — or
compose deployed one-shot apps via call_app from a coordinator.
runtime:
mode: background # or conversation / one_shot
entry_agent: coordinator
flow:
entry: extract
nodes:
extract:
type: agent
agent: extractor
routes: { default: classify }
classify:
type: agent
agent: classifier
routes: { default: done }
done:
type: terminal
Migrate any YAML that still has mode: pipeline to the shape above
(or to call_app steps inside an agent loop).
Pattern 3 - Multi-agent inside one app
For tightly-coupled coordination where the specialists share
workspace, memory, and the agent loop, declare them under
agents: and use the Agent(...) tool to spawn from the
coordinator. A module is instantiated once per app, not once per
agent - whatever modules the app declares (memory, filesystem,
and any other), the coordinator and every specialist talk to the
same instance, so they see the same files and the same memory
state. agents[].modules can still restrict which of those a
given specialist can use, but doesn't give it a private copy.
This is documented in detail in Multi-Agent;
the relevant block is agents[].delegate_to, agents[].pool, and the agent_spawn.agent tool with its 7
modes.
Choosing between the three
| Need | Pick |
|---|---|
| Sub-task should run with its own daemon-managed config (different secrets, different sandbox, different deploy lifecycle). | call_app (each app deploys independently). |
| Sub-task needs the coordinator's workspace + memory + shell session. | Multi-agent (sub-agents share the app's module instances). |
| Strictly linear workflow with stable step boundaries. | A flow: graph. |
| Agent decides at runtime which sub-app to call. | call_app. |
| Coordinator should fan-out in parallel to N specialists, each in its own context window. | Multi-agent (pool.max_workers + parallel Agent(...) in the same turn). |
Conditional routing (if X then app_A else app_B), approval gates, decision nodes. | flow: block. |
Cross-references
- App-config block reference for
runtime:: App Configuration → runtime - Built-in tool
call_app: Built-in Tools → always-available primitives - Multi-agent +
Agenttool: Multi-Agent - Declarative orchestration graph: Flows