Skip to content
Lenso
English
Esc
navigateopen⌘Jpreview
On this page

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:

  1. expose stable source provenance;
  2. inspect one complete desired state and semantic revision;
  3. build a read-only, revision-fenced proposal;
  4. publish only the exact reviewed proposal with compare-and-swap; and
  5. 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.

Last updated on September 6, 2026

Was this page helpful?