Docs navigation
Command Reference

brainbase task

Dispatch work to an agent and read what happened. create starts a task and its first run; list, get and logs are read-only. Every subcommand takes --json and works without a TTY, so the whole group is usable from a script or a CI step.

brainbase task create

Create a task on an agent and start its first run. Without --agent the agent claimed in brainbase.agent.yaml is used, so a linked folder needs only a message.

bash
brainbase task create --message "triage the open PRs"
brainbase task create --agent <agent-id> --message "triage the open PRs"
brainbase task create --message "run the smoke suite" --wait
brainbase task create --message "run the smoke suite" --wait --timeout 900 --json
text
Task ID: 09d61d87-311a-414e-b6e2-76376b5eaf21
Agent ID: 2ff01836-b183-478b-89d5-cd2f77118c25
Status: initializing
First run accepted.
FlagDescription
--message <text>Required. The first user message.
--agent <id>Override the agent claimed in brainbase.agent.yaml.
--title <text>Optional task title.
--model <id>Model override for this task only.
--waitBlock until the task reaches a terminal status, then exit accordingly.
--timeout <secs>Give up waiting after <secs> and exit 1. Requires --wait.
--jsonEmit one JSON object and nothing else.

Without --wait the command prints the task id and exits immediately — the run continues on the platform. With --wait it polls the task until it settles, printing each status change, and its exit code reports the outcome.

Exit codes with --wait

CodeMeaning
0The task finished as success.
1The task finished as fail or need_more_info, or --timeout elapsed.
130You interrupted the wait with Ctrl-C. The task keeps running.
need_more_info is not a pass
A task ends as need_more_info when the agent stopped mid-work and needs something from you — it also covers a run that hit its token or turn budget. --wait exits 1 on it, so a CI gate does not go green on work that never finished. The same applies to --timeout: giving up waiting is not the same as observing a pass.

Under --json --wait the command prints exactly one line, after the task settles, carrying the final status alongside the exit code it used.

json
{
  "task_id": "09d61d87-311a-414e-b6e2-76376b5eaf21",
  "agent_id": "2ff01836-b183-478b-89d5-cd2f77118c25",
  "status": "success",
  "outcome": "terminal",
  "exit_code": 0
}

outcome distinguishes why the wait ended: terminal (the task settled), timeout (--timeout elapsed) or interrupted (Ctrl-C).

brainbase task list

Recent tasks you can reach, newest first. Metadata only — pass an id to task get for the detail.

bash
brainbase task list
brainbase task list --agent <agent-id> --limit 20
brainbase task list --json
text
success         nightly smoke suite
  09d61d87-311a-414e-b6e2-76376b5eaf21
  agent 2ff01836-b183-478b-89d5-cd2f77118c25 · created 2026-08-31T21:39:53.205552

running         triage the open PRs
  b2d629ae-abbe-4d50-b88d-2f35acb77323
  agent 2ff01836-b183-478b-89d5-cd2f77118c25 · created 2026-08-31T22:04:11.882301
FlagDescription
--agent <id>Only this agent's tasks. Omitted, every task you can reach.
--limit <n>Cap the page. 1–200; the server default is 50.
--jsonEmit the task array and nothing else.

brainbase task get

One task: status, agent, the machine it ran on, any caller metadata, and the eval verdicts recorded against it.

bash
brainbase task get <task-id>
brainbase task get <task-id> --json
text
success         nightly smoke suite

  task     09d61d87-311a-414e-b6e2-76376b5eaf21
  agent    2ff01836-b183-478b-89d5-cd2f77118c25
  created  2026-08-31T21:39:53.205552
  machine  143975a9-9677-4562-9d55-b37e365510c6 (s)
  sandbox  3d86f338-305c-432c-94b6-12fc6c91d446

  EVAL VERDICTS
  pass  answers-the-question

When a task failed, the reason is printed under failure above the metadata.

brainbase task logs

The task's transcript, one line per event: messages, the agent's reasoning, every tool call and its result, and the closing verdict. Output is line-oriented and each event is collapsed onto a single line, so it stays greppable — a tool call that printed twenty lines of output still matches grep tool_call.end.

bash
brainbase task logs <task-id>
brainbase task logs <task-id> --limit 50
brainbase task logs <task-id> --json | jq '.items[] | select(.type == "tool_call.end")'
brainbase task logs <task-id> --follow
brainbase task logs <task-id> --follow --json | jq -r 'select(.type == "idle")'
text
2026-08-31T21:40:15.460110Z  user.message  run the smoke suite
2026-08-31T21:40:31.126370Z  assistant.thinking  Running the suite, then reporting the result.
2026-08-31T21:40:31.126407Z  tool_call.start  run_tests
2026-08-31T21:40:31.126407Z  tool_call.end  run_tests success
2026-08-31T21:40:37.037864Z  idle  success all 128 tests passed
2026-08-31T21:40:37.037864Z  assistant.message  All 128 tests passed.
FlagDescription
--limit <n>Stop after <n> events instead of reading the whole transcript. With --follow, the size of the first page the server sends before it starts tailing.
--follow, -fStay attached and print events as they land, until interrupted with Ctrl-C.
--jsonEmit { "items": [...] } with every raw event and nothing else. With --follow, one raw event per line instead.
The human view is a summary; --json is complete
Plain output drops assistant.message.chunk deltas only where the same turn and subagent already carries the finalized assistant.message they add up to — printing both would show that reply twice. Where no finalized row is present, the chunks are the only copy of the reply and they print: mid-turn while the agent is still writing, after an interrupt, or when --limit cuts the transcript short. --json returns every row unchanged.

Following a running task

Without --follow the command reads the transcript and exits, which is the dump you want after task create --wait or in a CI step. With --follow it stays attached and prints events as they land, until the task finishes — so it is something a CI step can block on rather than something that has to be killed.

text
$ brainbase task logs 67e4806e-fd61-4124-89ec-9f5db12ca3be --follow
Following task 67e4806e-fd61-4124-89ec-9f5db12ca3be Ctrl-C to stop.
2026-09-01T21:06:46.229288Z  assistant.message  first line, landed while following
2026-09-01T21:06:50.383054Z  tool_call.end ok
2026-09-01T21:06:54.525705Z  idle  success

The server sends a page of recent history first and then keeps the connection open. --limit sizes that first page only — the feed then catches up from where it left off and tails live, so --limit is not a cap on how much history you see. Leave it off to take the server's own default.

A dropped connection reconnects on its own and resumes from the last event it received rather than replaying, so a long follow over a flaky link does not print the same events twice. Resuming needs a control plane new enough to stamp each frame with its cursor; against an older one a reconnect re-sends the opening page, so you may see a few events a second time. Reconnection notices go to stderr, which keeps a --json pipeline parseable. Stop it with Ctrl-C.

Exit codes with --follow

CodeMeaning
0The task reached a terminal state. The follow printed the closing event and ended.
130You stopped the follow with Ctrl-C. The task keeps running.
143Something sent the process SIGTERM — a CI step's timeout, a container shutting down.
1The server refused the stream — an expired login, an unknown task id — or it stayed unreachable for 60 seconds.
0 means the follow finished, not that the task passed
A follow ends when the task settles, whatever it settled on: a task that ends in failure still exits 0 here, because what failed is the work and not the tail. Use task create --wait, whose exit code reports the task's own status, when you need the job to go red on a failed task.

Reconnecting is not failing. A follow rides out dropped connections for as long as they keep coming back, and only gives up once the stream has been continuously unreachable for 60 seconds — long enough to cover a network change or a control-plane restart, short enough that a broken link answers you rather than hanging. Any successful reconnect resets that clock, so a flaky hour of one-off drops never ends the follow.

That 60-second budget cannot cost you a task that finished while the link was down. The clock only runs while the stream is disconnected, and a reconnect resets it before any event is delivered — so a task that settles during an outage still exits 0 once the stream comes back and the closing event lands. The 1 is reachable only when the stream never returns inside the budget, which is the one case where the command genuinely does not know how the task ended.

Two things read differently while following
A live tail cannot see the future, so it cannot do what the finished read does. Streaming assistant.message.chunk deltas print as they arrive instead of being folded into the finalized message that later replaces them — waiting for that message would mean showing nothing at all while the agent is mid-answer. --json emits one record per line rather than a single { "items": [...] } document, because an open stream has no last element to close one on. And rows come out in the order they arrive rather than sorted by timestamp, which the finished read can do because it holds the whole transcript — so following a task and then re-reading it can show the same events in a different order.

Dispatch and assert in CI

The read side plus --wait is what makes a task a build step. Dispatch, block on the result, and let the exit code fail the job — then print the transcript so a failure is diagnosable from the build log alone.

bash
#!/usr/bin/env bash
set -euo pipefail

# A user-scoped PAT must be exported, not just stored by `token create`.
export BRAINBASE_TOKEN="$BRAINBASE_PAT"

status=0
result=$(brainbase task create \
  --agent "$AGENT_ID" \
  --message "run the release checklist for $GIT_SHA" \
  --wait --timeout 1800 --json) || status=$?

[ "$status" -eq 0 ] && exit 0

# `create` can fail before a task exists at all — an expired PAT, an
# unreachable control plane — so there may be no id to read. Print
# whatever came back instead of running `logs` on an empty id, and keep
# the transcript from replacing the exit code that says what went wrong.
task_id=$(jq -r '.task_id // empty' <<<"$result" 2>/dev/null || true)

if [ -n "$task_id" ]; then
  echo "Task $task_id did not pass:"
  brainbase task logs "$task_id" || true
else
  echo "Could not start the task: $result"
fi

exit "$status"
Export the PAT; don't rely on the stored one
Saving a PAT with token create is not enough for CI. The stored token is consulted only when no login session exists at all, and it cannot read the eval verdicts on task get — those come from the control plane, which never reads it. Export the PAT as BRAINBASE_TOKEN and every task command uses it; see Environment variables.