ai transports

Lintro's AI review can run on two transports. Their timeouts, cost caps, failure modes, and reported numbers mean different things. Configure them under `ai.transports.*`

AI review transports: API vs CLI

Lintro’s AI review can run on two transports. Their timeouts, cost caps, failure modes, and reported numbers mean different things. Configure them under ai.transports.* (#1923).

Decision table

Dimensionapicli
CredentialANTHROPIC_API_KEY (or provider key)CLAUDE_CODE_OAUTH_TOKEN / claude login
BillingMetered API spendSubscription / OAuth session
Default timeout60s (stream-sized per call)900s (whole-turn)
Cost cap fieldai.transports.api.max_cost_usd (enforced)ai.transports.cli.max_cost_usd_advisory (advisory)
Legacy fallbackai.api_timeout, ai.max_cost_usdai.max_cost_usd for the advisory only
Auth mode recordedapi_keysubscription
Cost basis recordedbilledunpriceable
Typical CI failuresinsufficient_credits, auth_failed:keyauth_failed:oauth_session, cli_version_drift, turn_timeout, killed_externally

The credential rows above are Anthropic-specific (the dogfood default). Other CLI providers authenticate with their own logins and keys — codex login / CODEX_API_KEY for openai, agent login / CURSOR_API_KEY for cursor — see docs/ai-features.md, which also covers Claude’s settings-file apiKeyHelper as a reachable API credential.

Bare-billing exception: under cli with Anthropic, when ai.cli_bare resolves to sending --bare (auto with a reachable ANTHROPIC_API_KEY, or always, #1859), the call bills the API key — the run records auth_mode=api_key and cost_basis=estimated instead of the subscription column above.

Advisory means estimate-based, not unenforced: the CLI advisory cap still stops the run (finalizing a partial review) when locally estimated cost reaches it. It is “advisory” because subscription usage has no billed price — the estimate bounds work done, not spend.

Resolution

Effective settings = transport profile → legacy scalar → built-in default.

lintro review logs the resolved profile at start, for example:

transport=cli auth=subscription timeout=900 cap=advisory:$0.50 cost_basis=unpriceable

Example config

ai:
  enabled: true
  provider: anthropic
  transport: cli
  transports:
    api:
      timeout: 60
      max_cost_usd: 0.50
    cli:
      timeout: 900
      max_cost_usd_advisory: 0.50

When to use which

  • Prefer cli when you have a Claude Code / subscription OAuth session and want dogfood CI without burning a metered API key.
  • Prefer api when you need enforced spend caps, streaming, or non-Claude providers with API keys.

Credentials and LINTRO_CLI_BARE

Variable / settingTransportRole
ANTHROPIC_API_KEYapi (required); cli optionalMetered API / bare-mode auth
CLAUDE_CODE_OAUTH_TOKENcliSubscription OAuth session for claude
ai.cli_bare / LINTRO_CLI_BAREcliauto / always / never — whether to pass --bare

--bare disables OAuth session login and authenticates only against an API key (#1838/#1859). Dogfood CI pins LINTRO_CLI_BARE=never and keeps ANTHROPIC_API_KEY out of scope so the subscription token is actually used.

Reported numbers

Per-run sticky state records transport, auth_mode, and cost_basis (billed / estimated / unpriceable). Under subscription CLI, any ~$ figure is unpriceable — not a bill. Live api runs always record billed (usage is provider-reported); estimated appears only for bare-billed CLI runs and for legacy records whose basis is derived from auth_mode + locally estimated token usage.

See also docs/ai-features.md and the dogfood workflow helpers under scripts/ci/.