Skip to main content

Event Stream Reference

Run AdaL with -o stream-json and it writes one JSON object per line (NDJSON) to stdout as the run progresses. Each line is a complete, independently parseable event — there is no envelope or framing beyond the newline.

adal -q "fix the lint errors" -o stream-json
{"type":"tool_call","id":"3f9c...","name":"bash","args":{"command":"npx eslint src/"}}
{"type":"tool_result","id":"3f9c...","name":"bash","status":"completed","output":"src/app.ts\n 12:5 error 'x' is assigned a value but never used\n..."}
{"type":"tool_call","id":"8a12...","name":"replace_by_string","args":{"file_path":"src/app.ts","old_string":"...","new_string":"..."}}
{"type":"tool_result","id":"8a12...","name":"replace_by_string","status":"completed","output":"Edited src/app.ts"}
{"type":"answer","content":"Fixed 3 lint errors in src/app.ts..."}
{"type":"complete","exit_code":0,"session_id":"a1b2c3d4-...","model":"claude-sonnet-4-20250514"}

Event Types​

TypeFieldsDescription
tool_callid, name, argsA tool is being invoked
tool_resultid, name, status, outputA tool completed (completed / failed / cancelled). id matches the tool_call; output is what the tool returned, e.g. a shell command's stdout and stderr (a preview past the size threshold — see the json format)
permission_deniedid, name, args, reasonA tool call needed approval that nobody could give (see Tool Approval). It did not run and the run stops; complete follows with exit_code: 2
answercontentThe agent's final answer
errormessageAn error occurred
completeexit_code, session_id, modelStream finished. Always the last event

Ordering Guarantees​

  • Every tool_result carries the id of the tool_call it answers. Match on id, not on arrival order.
  • The answer event (when present) precedes complete.
  • complete is always the last event. Treat its exit_code as the authoritative outcome — the same codes the CLI process exits with (see Exit Codes).
  • A permission_denied event means the run stopped at that call; complete with exit_code: 2 follows. No further tool events appear after it.

Reading the Stream​

A minimal consumer that reconstructs what happened:

adal -q "review this PR" -o stream-json | while IFS= read -r line; do
type=$(echo "$line" | jq -r '.type')
case "$type" in
tool_call) echo "🔧 $(echo "$line" | jq -r '.name')" ;;
tool_result) echo " $(echo "$line" | jq -r '.status')" ;;
permission_denied) echo "⛔ $(echo "$line" | jq -r '.reason')" ;;
error) echo "❌ $(echo "$line" | jq -r '.message')" >&2 ;;
answer) echo "$line" | jq -r '.content' ;;
complete) echo "✅ exit $(echo "$line" | jq -r '.exit_code')" ;;
esac
done

In Python, group call/result pairs by id and keep the latest state per tool call:

import json, subprocess

proc = subprocess.Popen(
["adal", "-q", "review this PR", "-o", "stream-json"],
stdout=subprocess.PIPE,
)
calls = {}
for line in proc.stdout:
event = json.loads(line)
if event["type"] == "tool_call":
calls[event["id"]] = event | {"status": "running"}
elif event["type"] == "tool_result":
calls[event["id"]]["status"] = event["status"]
calls[event["id"]]["output"] = event["output"]
elif event["type"] == "answer":
print(event["content"])
elif event["type"] == "complete":
print(f"exit={event['exit_code']} session={event['session_id']}")

Success, Failure, and Refusal per Format​

The same outcome is reported differently by each output format. Pick the column that matches your -o flag:

Outcometextjsonstream-json
SuccessFinal answer on stdout"success": true, answer in .answeranswer event, then complete with exit_code: 0
Run errorError on stderr, exit code 1"success": false, message in .error, exit_code: 1error event, then complete
Tool call refusedNothing on stdout; stderr gets Stopped: <reason> then Refused call: <tool> <args>; exit code 2"success": false, reason in .error, the call in tool_calls with "status": "denied", exit_code: 2permission_denied event with the call's id, name, args, reason, then complete with exit_code: 2

Exit Codes​

CodeMeaning
0Success — query completed, answer returned
1Failure — auth error, model error, or agent error
2Stopped — a tool call needed approval that nobody could give

A robust script branches on the exit code first, then parses the stream:

adal -q "$TASK" -o stream-json > events.ndjson
case $? in
0) echo "done" ;;
2) echo "needs approval — re-run with --permission-mode acceptEdits or --yolo" ;;
*) echo "failed — see error event in events.ndjson" ;;
esac
  • Headless Mode — flags, pipe support, and recipes
  • json output format — the same data as one final object instead of a stream
  • SDK — the same event access over a persistent, multi-query connection