Skip to main content

App Composition

Three ways to combine Digitorn apps. Each serves a different shape of workflow.

PatternMechanismWhen to use
call_app toolOne agent calls another deployed app as a tool, in-flight.Ad-hoc composition; agent decides at runtime which apps to invoke.
flow: graphIn-app workflow (agents, tools, IF, parallel). Prefer this for linear/branching automation.Background or conversation apps that need deterministic routing.
Multi-agentOne 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 a flow: graph (or call_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​

FieldTypeRequiredDescription
app_idstringyesDeployed app_id to call.
promptstringyesMessage sent as the target's user input.
timeoutfloatno (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_app is a meta-tool - it bypasses the capability gate chain entirely, so max_risk_level/grant/deny don't apply to it (see Security architecture).

Example​

yaml
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.

json
// 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.

app.yaml (preferred)
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​

NeedPick
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​