Skip to content

Custom agents

If your agent isn’t one of the built-in providers, the custom provider runs any command you like. OpenLoop spawns it as:

Terminal window
sh -c '<your-command> "$OPENLOOP_PROMPT"'
Terminal window
openloop config project-set-agent api custom \
--command 'my-agent --model x --prompt-file /dev/stdin'

Well, not exactly. The contract is simpler: your command receives the task prompt in the $OPENLOOP_PROMPT environment variable (and the resolved model in $OPENLOOP_MODEL) and should do its work against the current working directory, which is the repo root (or the worktree when useWorktree is on).

Terminal window
openloop config project-set-agent api custom \
--command 'my-agent run --prompt "$OPENLOOP_PROMPT" --model "$OPENLOOP_MODEL"'

The command is stored verbatim in project.json under agent.command:

{
"agent": { "type": "custom", "command": "my-agent run --prompt \"$OPENLOOP_PROMPT\"" }
}
  • Exit code is the signal: 0 means success, anything else fails the attempt (subject to the normal attempt/timeout rules).
  • stdout is parsed best-effort for usage/cost, if your tool can emit a JSON object with usage info as (part of) its output, cost tracking improves; otherwise the per-run estimate is charged.
  • The timeout (runtime.runTimeoutSeconds) applies like any other provider.
  • Scope policy and validations apply identically, a custom agent gets no special treatment in the promotion pipeline.
Terminal window
openloop config project-set-agent api custom \
--command 'bash scripts/agent.sh'
scripts/agent.sh, in the linked repo
#!/usr/bin/env bash
set -euo pipefail
echo "Running task for OPENLOOP_TASK_ID=$OPENLOOP_TASK_ID"
my-agent --task "$OPENLOOP_PROMPT" --json

OPENLOOP_TASK_ID (and OPENLOOP_PROJECT, OPENLOOP_EVENT) are injected into hook environments; in agent commands the reliable variables are OPENLOOP_PROMPT and OPENLOOP_MODEL.