Skip to content
Go To Dashboard

Test locally

A Local Run loads the checked definition and executes its real step bodies on your machine. Every ctx.sapiom.* call is resolved by the local stub client, so the run needs no Sapiom account, makes no Sapiom capability request, and creates no Sapiom capability spend.

Bind the intended project, then choose Local Run in the action bar. This is a direct product action: it does not prompt Claude or use Claude credits.

The button currently sends {} as the entry input. Give every required entry field a schema default when you want the button to run unchanged. For custom input, use the authoring MCP route.

The Hello Agent result includes a run-specific execution ID. The stable fields are:

{
"outcome": "completed",
"output": { "greeting": "Hello, Ada!" },
"steps": [
{
"step": "greet",
"input": { "name": "Ada" },
"status": "succeeded",
"output": { "greeting": "Hello, Ada!" }
}
],
"unusedStubs": [],
"stubWarnings": []
}

An empty input produces { "greeting": "Hello, world!" }, proving the entry schema default was applied before the step ran.

You can start with the scaffolded file:

{
"version": 1,
"steps": {}
}

Calls without a project override use typed defaults from @sapiom/tools. This is enough to trace control flow, but the returned data is test data—not provider evidence.

Stub overrides are scoped by the step that makes the call, then by the exact capability path:

{
"version": 1,
"steps": {
"kickoff": {
"models.coding.launch": {
"status": "failed",
"result": { "success": false },
"error": {
"stage": "run",
"message": "compile failed"
}
}
}
}
}

Save that object in .sapiom-dev/stubs.json, or pass the same object as the stubs argument to sapiom_dev_agents_run_local.

Each configured response is returned verbatim. An array is the response itself for a list-returning method such as repositories.list; it is not a sequence of responses. Namespace calls use their namespace path (repositories.list, models.coding.launch). Methods on an attached handle use the singular handle path (repository.pushFromSandbox, sandbox.exec).

  1. completed reached terminate(...); failed reached fail(...), exhausted a step attempt, or encountered an error. Read output or error rather than treating process exit alone as the assertion.

  2. steps is execution order, including retries. Each record carries the parsed input that author code received, status, output or error, logs, and captured Sapiom calls when present.

  3. unusedStubs identifies supplied keys that no call matched in a step that actually ran. stubWarnings identifies a key that matched but returned an implausible shape. A completed run with either list non-empty is not a clean test.

A stub under an unvisited branch is not reported unused, because that step never ran. Test each meaningful branch deliberately.

  • A dispatched capability such as models.coding.launch records its stub result and automatically resumes the declared resume step with that value.
  • A manual pauseUntilSignal({ ... }) also auto-resumes locally, but with {}. The local runner has no manual-signal payload override; make optional input safe for the happy path and test payload-dependent approval/rejection through the cloud signal path.
  • Local and cloud execution default to three attempts per step. Raising maxAttemptsPerStep in a local call does not raise the cloud ceiling.
  • Local Run stops after 1,000 graph advances as a final guard against a non-terminating graph.

It does not prove provider availability, live credentials, real capability response shapes beyond the stub validators, cloud build success, or production runtime environment. Continue to deploy and production-run testing only after the local outcome and both stub-diagnostic lists are clean.