Skip to main content

INVOKE BEHAVIORS FROM BEHAVIORS

This guide shows you how to use context.runModel() to invoke one behavior's method from inside another behavior's execute function. runModel is the TypeScript API name; there is no behavior-spelled alias.

Declare the dependency

Your extension's manifest must list the target behavior's extension in dependencies. Without this declaration, runModel rejects the call at runtime.

# manifest.yaml
name: "@mycollective/orchestrator"
version: "2026.07.10.1"
dependencies:
  - "@mycollective/worker-behavior"
behaviors:
  - orchestrator.ts

Invoke by definition name

If the target behavior already has a definition in the repository, pass its name as definition:

const result = await context.runModel!({
  definition: "my-worker",
  method: "process",
  arguments: { input: "some-value" },
});

if (result.ok) {
  context.logger.info(`Worker produced ${result.resources.length} resource(s)`);
} else {
  context.logger.error(`Worker failed: ${result.error.message}`);
}

Invoke by behavior type

If no definition exists, pass the behavior type as modelType and a definition name. Swamp auto-creates the definition in .swamp/auto-definitions/:

const result = await context.runModel!({
  modelType: "@mycollective/worker-behavior",
  name: "us-east-worker",
  method: "execute",
  arguments: { region: "us-east-1" },
});

Handle the result

runModel returns a discriminated union and does not throw. Check result.ok before accessing result.resources:

const result = await context.runModel!({
  definition: "target",
  method: "method",
  arguments: args,
});

if (!result.ok) {
  context.logger.error(result.error.message);
  return { dataHandles: [] };
}

// result.resources is DataHandle[] — references to the target's output
const records = await context.readModelData!("target");
for (const handle of result.resources) {
  const record = records.find((r) => r.name === handle.name);
  // process record?.attributes
}

Data ownership

Data written by the invoked behavior belongs to the target behavior's definition, not the caller's. The DataHandle references in result.resources point to data under the target's namespace, so context.readResource cannot read them. To use that data in the caller's output, read it with context.readModelData and write a derived resource under the caller's own spec.

Limits

A single top-level method execution enforces:

  • Maximum call depth of 10
  • Maximum 100 total runModel invocations
  • Cycle detection — a behavior cannot invoke itself, directly or transitively
  • Vault isolation — the invoked behavior sees only its own extension's vault bindings

Exceeding any limit causes runModel to return { ok: false, error: { message } }.

Remote workers

runModel is not available on remote workers. On a worker, the call returns { ok: false } with an error message instead of running the target. If your behavior's method may run on a worker, move the invocation to a local orchestrator behavior, or chain the behaviors as steps in a workflow.

Refer to the behavior reference for the full runModel signature, and Behaviors, Types, and Methods for the design rationale.