Configure an Agent
Configure Plugin Instances locally, publish reviewed changes through Agent Web, and connect a Host-owned remote authority.
An Agent has one visible configuration model: Plugin Instance TOML and resources under its Plugin Root. Local files, the managed SQLite store, and an injected remote authority are different ways to author that same desired state. They are not different runtime models.
Choose an authoring mode
| Mode | What is available now | Use it when |
|---|---|---|
| Direct local Plugin Root | Default, shipped | One operator or local development owns ~/.lenso/agent/plugins/ |
| SQLite managed authority | Opt-in, shipped in Agent Web | One Host needs durable proposals, publication history, recovery, and rollback proposals |
| Injected authority | Supported Host integration port | Your embedding Host already has a remote configuration service or control plane |
Lenso Agent does not ship a first-party distributed configuration service. SQLite is a single-Host store. The injected port makes a remote adapter possible, but its service, authentication, tenancy, and rollout policy belong to the embedding Host.
1. Configure the local Agent Home
The default Agent Home is ~/.lenso/agent:
~/.lenso/agent/
├── .lenso/host-catalog.json
├── plugins/
│ └── lenso.agent.loop/
│ └── agent.toml
└── profiles/
└── code.toml
Create a patch for the default Agent Loop:
model = "gpt-5.6-luna"
max_steps = 9
max_tool_calls = 4
max_user_resumes = 8
max_parallel_tool_calls = 4
max_output_tokens = 1024
max_history_events = 200
max_compaction_summary_characters = 8192
max_memory_items = 8
max_memory_characters = 16384
Apply and inspect it from any directory by passing the Agent Home explicitly:
lenso plugins configure lenso.agent.loop agent \
--file ./agent-loop.toml \
--root "$HOME/.lenso/agent"
lenso plugins list --root "$HOME/.lenso/agent"
lenso app show --root "$HOME/.lenso/agent"
lenso app check --root "$HOME/.lenso/agent"
The final schema is checked after package defaults, Host configuration, and the Instance patch are merged. An unknown field or invalid value fails before the visible Plugin Root is changed.
2. Select Instances with a Profile
A Profile selects exact configured Instances for one Session. It does not hold their configuration:
description = "Read-only review agent"
agent = "lenso.agent.loop/agent"
instances = [
"lenso.agent.loop/agent",
"lenso.agent.session-presentation.model/semantic",
]
Start a Session through that selection:
cargo run -p lenso-agent-tui -- --profile review
cargo run -p lenso-agent-cli -- --profile review "Review this workspace."
Keep large or structured inputs in
plugins/<plugin-id>/<instance>/. Keep secret values in a selected Secret
Provider; only references or mappings belong in TOML. See
Configure a Plugin for the complete file model.
3. Start the managed configuration API
From the Lenso Agent checkout, enable Plugin control and choose the durable SQLite authority:
export LENSO_AGENT_CONTROL_TOKEN="replace-with-a-local-control-token"
cargo run -p lenso-agent-web -- \
--listen 127.0.0.1:8788 \
--plugin-control \
--plugin-configuration-store "$HOME/.lenso/agent/plugin-configuration.sqlite3"
The database path must resolve to an absolute path. Omit
--plugin-configuration-store to use the direct local authority through the
same proposal and publication API; that mode does not provide SQLite history
or rollback proposals.
Set the API base for the following commands:
export LENSO_AGENT_CONTROL_API="http://127.0.0.1:8788/api/console/v1/agent/control"
4. Read the current revision
MANAGEMENT="$(curl -sS \
-H "Authorization: Bearer $LENSO_AGENT_CONTROL_TOKEN" \
"$LENSO_AGENT_CONTROL_API/plugins")"
REVISION="$(printf '%s' "$MANAGEMENT" | jq -r '.revision')"
printf '%s' "$MANAGEMENT" | jq '{revision, configurationAuthority}'
configurationAuthority.kind is local_plugin_root for direct local
publication or sqlite_configuration_store for the durable store. Its
publicationHistory and rollbackProposals fields tell a client which optional
operations are available.
5. Build a read-only proposal
Use the same agent-loop.toml created earlier:
CONFIG_TOML="$(cat ./agent-loop.toml)"
PROPOSAL="$(jq -n \
--arg expectedRevision "$REVISION" \
--arg toml "$CONFIG_TOML" \
'{expectedRevision: $expectedRevision, toml: $toml}' |
curl -sS -X POST \
-H "Authorization: Bearer $LENSO_AGENT_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @- \
"$LENSO_AGENT_CONTROL_API/plugins/lenso.agent.loop/agent/configuration/proposals")"
printf '%s' "$PROPOSAL" | jq '{status, application, baseRevision, candidateRevision, proposalDigest, diagnostics}'
A proposal never changes desired state. Publish only when status is ready.
needs_decision requires an explicit operator decision; rejected includes
diagnostics and cannot be published. application explains whether the change
is a no-op, requires a new App Generation, or is blocked.
6. Publish the exact reviewed proposal
The digest closes the exact TOML bytes, while the expected revision prevents a stale writer from overwriting a newer change:
PROPOSAL_DIGEST="$(printf '%s' "$PROPOSAL" | jq -r '.proposalDigest')"
jq -n \
--arg expectedRevision "$REVISION" \
--arg proposalDigest "$PROPOSAL_DIGEST" \
--arg toml "$CONFIG_TOML" \
'{expectedRevision: $expectedRevision, proposalDigest: $proposalDigest, toml: $toml}' |
curl -sS -X PUT \
-H "Authorization: Bearer $LENSO_AGENT_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @- \
"$LENSO_AGENT_CONTROL_API/plugins/lenso.agent.loop/agent/configuration" |
jq '{status, revision, desired}'
An accepted publication returns HTTP 202 and status: "published". The Host
then snapshots the materialized Plugin Root, resolves it against the immutable
Host Catalog, stages a candidate Generation, and switches only after readiness.
Observe reconciliation separately:
curl -sS "http://127.0.0.1:8788/api/console/v1/agent/plugins" |
jq '{desiredRevision, appliedRevision, configurationStatus}'
7. Inspect history and create a rollback proposal
These routes are available when the selected authority advertises history and rollback support:
PUBLICATIONS_URL="$LENSO_AGENT_CONTROL_API/plugins/lenso.agent.loop/agent/configuration/publications"
HISTORY="$(curl -sS \
-H "Authorization: Bearer $LENSO_AGENT_CONTROL_TOKEN" \
"$PUBLICATIONS_URL")"
printf '%s' "$HISTORY" | jq '.publications'
Choose the proposalDigest of a previous publication, then request a rollback
proposal against the current revision:
CURRENT_REVISION="$(curl -sS \
-H "Authorization: Bearer $LENSO_AGENT_CONTROL_TOKEN" \
"$LENSO_AGENT_CONTROL_API/plugins" | jq -r '.revision')"
TARGET_DIGEST="replace-with-a-previous-proposal-digest"
ROLLBACK="$(jq -n \
--arg expectedRevision "$CURRENT_REVISION" \
--arg publicationProposalDigest "$TARGET_DIGEST" \
'{expectedRevision: $expectedRevision, publicationProposalDigest: $publicationProposalDigest}' |
curl -sS -X POST \
-H "Authorization: Bearer $LENSO_AGENT_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @- \
"$LENSO_AGENT_CONTROL_API/plugins/lenso.agent.loop/agent/configuration/rollback-proposals")"
printf '%s' "$ROLLBACK" | jq '{configurationToml, proposal}'
This is still read-only. Review it, then publish its configurationToml and
proposal.proposalDigest through the same PUT .../configuration route. A
rollback is therefore another revision-fenced publication, not an out-of-band
database rewind.
8. Connect a remote authority
An embedding Host may provide plugin_configuration_authority on
AgentWebConfig, and may pair it with plugin_configuration_history. A valid
adapter must:
- expose stable source provenance;
- inspect one complete desired state and semantic revision;
- build a read-only, revision-fenced proposal;
- publish only the exact reviewed proposal with compare-and-swap; and
- atomically materialize the complete desired state into the managed local Plugin Root before publication returns.
Do not also configure the SQLite store; the two authorities conflict. A remote outage fails the operation instead of silently falling back to local publication. Installation, selection, and removal remain local operations in the current slice. Most importantly, the adapter supplies configuration—not a remote-authored Plan. The local Host remains the only component that resolves, stages, and activates a Generation.