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:
sh -c '<your-command> "$OPENLOOP_PROMPT"'Configure
Section titled “Configure”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).
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\"" }}Rules of the game
Section titled “Rules of the game”- Exit code is the signal:
0means 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.
Example: wrapping a local script
Section titled “Example: wrapping a local script”openloop config project-set-agent api custom \ --command 'bash scripts/agent.sh'#!/usr/bin/env bashset -euo pipefailecho "Running task for OPENLOOP_TASK_ID=$OPENLOOP_TASK_ID"my-agent --task "$OPENLOOP_PROMPT" --jsonOPENLOOP_TASK_ID (and OPENLOOP_PROJECT, OPENLOOP_EVENT) are injected into hook environments; in agent commands the reliable variables are OPENLOOP_PROMPT and OPENLOOP_MODEL.