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.
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
Task ID: 09d61d87-311a-414e-b6e2-76376b5eaf21
Agent ID: 2ff01836-b183-478b-89d5-cd2f77118c25
Status: initializing
First run accepted.
| Flag | Description |
|---|---|
--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. |
--wait | Block until the task reaches a terminal status, then exit accordingly. |
--timeout <secs> | Give up waiting after <secs> and exit 1. Requires --wait. |
--json | Emit 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
| Code | Meaning |
|---|---|
0 | The task finished as success. |
1 | The task finished as fail or need_more_info, or --timeout elapsed. |
130 | You interrupted the wait with Ctrl-C. The task keeps running. |
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.
{
"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.
brainbase task list
brainbase task list --agent <agent-id> --limit 20
brainbase task list --json
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
| Flag | Description |
|---|---|
--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. |
--json | Emit 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.
brainbase task get <task-id>
brainbase task get <task-id> --json
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.
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")'
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.
| Flag | Description |
|---|---|
--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, -f | Stay attached and print events as they land, until interrupted with Ctrl-C. |
--json | Emit { "items": [...] } with every raw event and nothing else. With --follow, one raw event per line instead. |
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.
$ 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
| Code | Meaning |
|---|---|
0 | The task reached a terminal state. The follow printed the closing event and ended. |
130 | You stopped the follow with Ctrl-C. The task keeps running. |
143 | Something sent the process SIGTERM — a CI step's timeout, a container shutting down. |
1 | The server refused the stream — an expired login, an unknown task id — or it stayed unreachable for 60 seconds. |
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.
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.
#!/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"
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.