Bundle structure
A valid job bundle is a directory with two top-level entries:manifest.json— the declarative workflow definition. It specifies the agent nodes, the message-routing edges between them, the entrypoints, initial inputs, and job-level policies.payloads/— a directory containing all scripts, data files, and static assets that executor nodes reference. The runtime resolvessourcepaths inconfig.uploadsrelative to this directory.
The
payloads/ directory must exist even if your workflow has no executor nodes. You can leave it empty with a .gitkeep file.The manifest.json schema
Themanifest.json file is a JSON document that describes the full execution graph. The runtime parses, normalizes, and validates it before starting any agents.
Top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
manifest_version | String | Yes | The manifest format version. Use "1.0". |
graph_id | String | Yes | A unique identifier for the agent graph. |
job_name | String | No | A human-readable name. Defaults to graph_id if omitted. |
long_lived | Boolean | No | Set to true for workflows that run until manually stopped. Defaults to false. |
metadata | Object | No | Arbitrary key-value tags for the job. |
entrypoints | Array | Yes* | A list of node_id strings that receive initial input to start the graph. |
initial_inputs | Object | No | A map of node_id → array of message payloads to seed the job. |
nodes | Array | Yes | The list of agent nodes that make up the workflow. |
edges | Array | Yes | The list of message-routing edges between nodes. |
policies | Object | No | Job-level policies such as recovery_mode. |
*If you omit
entrypoints, the runtime infers them from any node with "role": "root" or "role": "root_coordinator". At least one entrypoint or one root-role node is required.Node fields
Each object in thenodes array defines a supervised agent. The runtime starts one BEAM process per node.
| Field | Type | Required | Description |
|---|---|---|---|
node_id | String | Yes | A unique identifier for this node within the graph. |
agent_type | String | Yes | The runtime primitive: "router", "executor", "aggregator", or "sensor". |
type | String | No | The behavioral template: "generic", "map", "reduce", "stream", "batch", or "accumulator". Defaults to "generic". |
role | String | No | A human-readable tag for the agent’s domain role, such as "researcher" or "root_coordinator". |
config | Object | No | Configuration passed to the agent at startup. Interpreted by the agent_type and type. |
tool_bindings | Array | No | Tool binding declarations for the node. |
retry_policy | Object | No | Per-node retry configuration. |
checkpoint_policy | Object | No | Per-node checkpoint configuration. |
spawn_policy | Object | No | Per-node spawn configuration. |
Edge fields
Each object in theedges array defines how messages flow from one agent to another.
| Field | Type | Required | Description |
|---|---|---|---|
from_node | String | Yes | The node_id of the sending agent. |
to_node | String | Yes | The node_id of the receiving agent. |
message_type | String | Yes | The message type that triggers this edge. |
edge_id | String | No | An optional identifier for the edge. Used in error messages. |
routing_mode | String | No | How the message is delivered. Defaults to "broadcast". |
conditions | Object | No | Optional routing conditions. |
Policies
The top-levelpolicies object supports the following key:
| Key | Type | Description |
|---|---|---|
recovery_mode | String | How the runtime recovers failed agents. Use "local_restart" for local supervised restarts. |
The payloads/ directory
Thepayloads/ directory is where you store any code or data that executor nodes need to run inside the OpenShell sandbox. When the runtime starts an executor node, it stages the files declared in config.uploads by reading them from payloads/ and uploading them into the sandbox at the paths you specify.
Declare uploads on an executor node using config.uploads, an array of objects with source and target keys:
source— the path relative topayloads/on the hosttarget— the absolute path inside the sandbox where the file will be placed
my_job_bundle/payloads/process_data.py from disk and mounts it into the sandbox at /sandbox/process_data.py before running the command.
Complete manifest example
The following manifest is the full working example fromexamples/research_flow. It defines a three-node graph: an ingress router fans the input out, a retriever router passes it along, and a reviewer aggregator collects the result and completes the job.
ingress → retriever → reviewer. The reviewer aggregator completes the job as soon as it receives the first message because complete_on_message is true.
Validating and running a bundle
Validate the bundle
Run the validator before submitting the bundle. It checks the manifest schema, verifies that all node IDs in edges and entrypoints match declared nodes, and confirms that every
agent_type and type combination is supported.Run the bundle
Submit the bundle to the runtime. The runtime loads the manifest, starts a supervised process for each node, and injects the
initial_inputs into the entrypoint nodes.