This guide explains how provider profiles fit into the core runtime.
The focus here is not on any one SDK. The focus is the internal boundary inside
cli_subprocess_core between policy, command construction, and subprocess
execution.
What a Provider Profile Is
A provider profile is the core’s adapter for one external CLI family.
It answers questions like:
- which executable should be launched
- which flags should be emitted
- how input should be shaped for that CLI
- how provider-specific output is normalized back into the shared runtime
The contract is defined by:
lib/cli_subprocess_core/provider_profile.ex
Built-in implementations live in:
lib/cli_subprocess_core/provider_profiles/codex.exlib/cli_subprocess_core/provider_profiles/claude.exlib/cli_subprocess_core/provider_profiles/cursor.exlib/cli_subprocess_core/provider_profiles/amp.exlib/cli_subprocess_core/provider_profiles/antigravity.ex
The Three Internal Stages
Inside the core, it is helpful to think in three stages:
- policy
- command construction
- transport/session execution
Stage 1 is the model registry and related validation.
Stage 2 is the provider profile turning normalized options into command-line arguments and runtime expectations.
Stage 3 is the subprocess transport and session runtime actually starting and managing the external process.
These stages should stay distinct.
Governed launch adds one more boundary rule: if :governed_authority is
present, provider profiles must not accept command, cwd, env, config-root,
auth-root, base-URL, or model env override values from ordinary caller options.
Those values must already be materialized by the authority and must run with
clear_env?: true. The standalone path keeps the normal provider CLI env and
local discovery behavior when no governed authority is supplied.
What Profiles Should Do
Profiles should:
- accept normalized input
- consume the resolved model selection
- emit the correct executable and flags
- define provider-specific environment and framing behavior
- normalize provider output into the shared event model
What Profiles Should Not Do
Profiles should not:
- own model catalogs
- choose fallback models
- silently accept placeholder model input
- invent a second reasoning-effort policy
- override core selection decisions
If a change belongs to policy, it should go into the model registry instead.
Command Construction Flow
At a high level, command construction looks like this:
- caller supplies normalized options
- model registry resolves the final selection
- provider profile reads the resolved selection
- provider profile builds executable, argv, env, and transport expectations
- command/session layers run the process
The important internal rule is simple:
- the profile writes the command
- the registry decides the model
Example Boundary
For a Codex request, the core should conceptually follow this pattern:
{:ok, selection} =
CliSubprocessCore.ModelRegistry.build_arg_payload(
:codex,
requested_model,
reasoning_effort: requested_effort
)
# The provider profile then formats "--model #{selection.resolved_model}"The provider profile may still sanitize transport-level placeholders before emitting flags, but it must not implement fallback policy there.
Cursor is the example for a positional-prompt profile: it renders
agent -p --trust --output-format stream-json --stream-partial-output ...
and appends the prompt as the final argv element instead of inventing a
--prompt flag.
Antigravity is the example for a plain-text --print profile: it renders
agy --print <prompt> ..., treats stdout as UTF-8 text instead of JSONL, maps
non-empty lines to assistant deltas, and closes stdin on start.
Reviewer Checklist
When reviewing a provider-profile change, ask:
- is this truly profile behavior, or is it model policy?
- does the profile read the core selection instead of re-resolving a model?
- are emitted flags consistent with the resolved selection?
- does the profile preserve the shared runtime/event contract?
If the answer depends on provider-wide model rules, the change probably belongs in the registry or catalog, not the profile.