Output protocol
stdout carries the result envelope, stderr carries progress only. How to read nextActions and keep a long-running call alive.
Every command writes to two streams with a strict division of labour.
| Stream | Carries |
|---|---|
stdout | The result envelope, or the error envelope. Nothing else. |
stderr | Progress breadcrumbs only — heartbeats and stage events. |
Parse stdout. Never parse stderr, and never treat something on stderr as
a failure.
Exit codes and the envelope disagree on purpose
A non-zero exit means the call failed, and the reason is in the stdout
envelope — not in the exit code. Read the envelope for the cause; use the exit
code only to decide whether to continue.
Heartbeats are not errors
Long operations print a heartbeat to stderr roughly every 30 seconds:
[eximagent] still working (60s)
nextActions
Every successful response carries a nextActions[] array of ready-to-run
commands with valid flags for the logical next step. It is also how paging
works on analytics query and analytics sql — each full page returns the
next-page command with its cursor already bound.
Use them rather than composing the next call by hand. A reasonable policy when an agent is driving:
- Exactly one action, it is free or low cost, and it is inside what the user asked for → run it.
- Otherwise → surface the actions as a pick list and let the user choose.
Reporting what happened
A multi-step run should never be silent. The expected narration is a plan up front, an entry and exit line per step, the reason for any branch taken, and a plain-English summary at the end. See operating doctrine.