Skip to content
sonapersonal assistant

Machine-readable mode

Every non-streaming sona command accepts --json and emits a single-line envelope. Streaming commands (chat) emit one envelope per chunk (ndJSON). This is the surface you wire orchestrators, CI pipelines, and parent agents against, with no scraping of human text and no fragile regex.

Envelope shape

One of two shapes, picked by success or failure:

{ "ok": true,  "command": "status",   "result": { "running": true, "pid": 12345 } }
{ "ok": false, "command": "uninstall", "error": { "code": "confirmation_required", "message": "uninstall is destructive; --json requires explicit --yes to skip the prompt." } }

Fields:

  • ok (boolean): true on success, false on error.
  • command (string): the subcommand that wrote the envelope (status, update, uninstall, …). Matches what you typed after sona.
  • result (object, success only): the command-specific payload. See the table below for the per-command shape.
  • error.code (string, error only): a machine-readable identifier. Stable across releases.
  • error.message (string, error only): a human-readable explanation. It may change between releases, so do not match against it.

Exit code reflects the same semantics: 0 for success, non-zero for errors. The exit code is the orchestrator's primary signal, and the envelope is the explanation.

Commands

CommandSuccess result keysNotable error codes
sona --version --jsonversion, jreNone
sona --help --jsonversion, commands[], globalOptions[]None
sona status --jsonrunning, pid, reasonNone (exits 1 when not running)
sona stop --jsonstopped, pid, used_forcesignal_rejected, timeout, interrupted
sona init --jsonconfigExists, configPathinteractive_required (with --add-channel)
sona update --jsonaction (one of installed / already_latest), from, to, currentendpoint_unusable, no_download_url, install_failed
sona update --check --jsonaction: "check", current, latest, url, upToDateendpoint_unusable
sona update --rollback --jsonaction: "rolled_back"no_previous_version, rollback_failed
sona uninstall --dry-run --jsondryRun: true, paths[]None
sona uninstall --yes --jsondryRun: false, removed, failed, skipped, removedPaths[], failedPaths[]confirmation_required, daemon_stop_failed
sona doctor --jsonschemaVersion, overall (one of ok / warn / fail), checks[]None (its own document, not the envelope)
sona logs --count --jsoncountNone
sona chat --jsonper-chunk records (ndJSON, see CLI channel)None
sona search --json "<q>"query, total, hits[]None
sona config show --jsonmasked sona.yml as a config mapNone
sona cost --json [WINDOW]window, totalUsd, byProvider, byWorkspaceNone

doctor --json predates the envelope contract (TNS-466 axis 2). It emits its own document with schemaVersion, overall, and checks. The shape is stable, but do not assume the standard envelope.

Examples

Health check in a CI script

if ! sona doctor --json | jq -e '.overall == "ok"' > /dev/null; then
  echo "Sona not healthy; aborting deploy"
  exit 1
fi

Upgrade only when a newer version exists

LATEST=$(sona update --check --json | jq -r '.result.latest')
CURRENT=$(sona update --check --json | jq -r '.result.current')
if [ "$LATEST" != "$CURRENT" ]; then
  sona update --json | jq .
fi

Audit what uninstall would touch

sona uninstall --dry-run --json | jq '.result.paths[]'
# → "/Users/me/.sona"
# → "/Users/me/.local/bin/sona"
# → "/Users/me/.local/share/sona"

Read config state without triggering the wizard

sona init --json | jq '{exists: .result.configExists, path: .result.configPath}'
# → { "exists": true, "path": "/Users/me/.sona/sona.yml" }

init --json is a query. The interactive wizard is never triggered, and only configExists and configPath are reported. To bootstrap from scratch, fall back to interactive sona init and let the operator walk the prompts. init --add-channel --json is rejected with interactive_required for the same reason: the channel walkthrough is multi-prompt and doesn't translate to a single envelope.

Destructive commands and --json

sona uninstall is destructive. In --json mode, the prompt would be silently skipped, so Sona refuses rather than allow that. --json alone returns confirmation_required (exit 1):

{ "ok": false, "command": "uninstall", "error": { "code": "confirmation_required", "message": "..." } }

Pass --yes explicitly to opt in:

sona uninstall --yes --json

--dry-run --json is always safe (no filesystem touch) and does not need --yes.

Streaming vs. one-shot

This page covers one-shot commands. Streaming commands (sona chat --json) emit ndJSON: one envelope per chunk, terminated by a done: true record. See the CLI channel page for the per-chunk shape and a worked example.

Stability

  • ok, command, result/error keys: stable. Renaming would break every orchestrator. Treated as semver-major.
  • error.code values: stable within a minor release. New codes may appear in minor releases, but existing codes never change spelling.
  • result payload keys per command: additive. New keys may appear, and existing keys keep their meaning.
  • error.message text: unstable. It is written for humans, so never match against it.

See Sona how-it-works for the architecture context, or the TnsAI.Sona release notes for the per-version changelog.