Skip to content

feat(acp): add Letta as a tier-2 preset harness - #3454

Closed
sarahwooders wants to merge 5 commits into
block:mainfrom
sarahwooders:letta-acp-preset
Closed

feat(acp): add Letta as a tier-2 preset harness#3454
sarahwooders wants to merge 5 commits into
block:mainfrom
sarahwooders:letta-acp-preset

Conversation

@sarahwooders

@sarahwooders sarahwooders commented Jul 29, 2026

Copy link
Copy Markdown

Adds Letta to the tier-2 preset catalog, via the letta-acp adapter (npm install -g @letta-ai/letta-acp). Letta agents are stateful: memory persists across sessions, so the same agent carries context between channels and across restarts.

Changes

Preset registrationPresetHarness entry, inline currentColor mark, catalog copy, CREDITS.md provenance row.

discovery.rs split — the file was 1860 lines against the 1000-line desktop ratchet, which forbids growth on files already over it, so a preset could not be added without splitting first. Tier-2 data moves to discovery/presets.rs (discovery.rs drops to ~1716 lines). Pure code motion.

Model discovery — three fixes, each of which alone leaves the model picker empty for any adapter following the current ACP spec:

  • buzz-acp models opens a session on top of initialize, but both shared the 10s probe budget. Stateful adapters do real work in session/new (letta-acp: ~9s against Letta Cloud), which does not fit once spawn is counted. Split out MODEL_CATALOG_TIMEOUT (25s); initialize-only probes stay at 10s.
  • Adapters spell a config option's identity and label either id/name (current spec) or configId/displayName (older). resolve_model_switch_method accepted both; the models printer and Desktop parsers did not, so spec-current adapters rendered as ? (configId: ?) with unlabeled models. Both lookups now live in commands/acp_config_options.rs.
  • Catalogs reuse one display name across variants (Letta lists six GPT-5.6 Sol reasoning tiers), producing indistinguishable picker rows. Colliding labels are qualified with their id. The CLI's unknown placeholder for a missing agentInfo no longer reaches user-facing copy.

Docs — backend table leads with the constraint that tools must execute where the buzz CLI is; recommends setting LETTA_ACP_BACKEND=cloud-oauth explicitly.

Testing

npm install -g @letta-ai/letta-acp
buzz-acp models --agent-command letta-acp --agent-args ""

Previously exited error: agent timed out (10s); now prints the catalog with real labels.

In Desktop, set LETTA_ACP_BACKEND=cloud-oauth and LETTA_API_KEY in the agent's env vars; the model picker populates and the agent replies in channels and DMs. LETTA_AGENT_ID optionally pins one agent across restarts.

Added: 4 Rust tests in acp_config_options.rs, 3 in usePersonaModelDiscovery.test.mjs. presetLogos.test.mjs repointed at presets.rs.

just ci passes locally except desktop-tauri-clippy, which fails on three dead-code errors in desktop/src-tauri/src/linux_media.rs (Linux-only code, dead on macOS) — reproduced on a clean main checkout. mobile-check/mobile-test not run (no Flutter locally; no mobile code touched).

Letta agents are stateful — memory persists across sessions — which maps
well onto a Buzz agent that lives in a channel over time. They speak ACP
through the letta-acp adapter (npm: @letta-ai/letta-acp), verified
against buzz-acp's `models` probe on the local, remote, and cloud-oauth
Letta backends.

- PRESET_HARNESSES entry (`letta`, command `letta-acp`, no args), which
  also reserves the id against custom harnesses.
- default_agent_args: letta-acp is a zero-arg runtime, so the legacy
  Goose-shaped "acp" default must not reach it as a positional.
- Inline theme-adaptive mark from Letta's own Apache-2.0 letta-evals
  repo, with provenance recorded in CREDITS.md, plus catalog copy.
- README: a "Running with Letta" section and a backend table — the agent
  answers Buzz through the `buzz` CLI, so the CLI has to live wherever
  the chosen backend runs tools.

Signed-off-by: Sarah Wooders <sarahwooders@gmail.com>
@sarahwooders
sarahwooders requested a review from a team as a code owner July 29, 2026 02:55
@sarahwooders
sarahwooders marked this pull request as draft July 29, 2026 02:59
discovery.rs is 1860 lines — over the 1000-line desktop file-size limit,
so the ratchet in `pnpm check` forbids any growth, and adding a preset
entry fails it. Move the tier-2 data (PresetHarness, PRESET_HARNESSES,
and the two helpers projecting it) into a sibling module, which drops
discovery.rs to ~1716 lines and gives presets a small file of their own
to grow in.

Pure code motion: visibility widens to pub(super) for the fields
discovery.rs reads, and the presetLogos guard now parses presets.rs.

Signed-off-by: Sarah Wooders <sarahwooders@gmail.com>
Three independent defects kept Letta's model picker empty in Desktop.
Each one alone is enough to show "reported no models", which reads as an
adapter without model switching rather than a bug.

`buzz-acp models` opens a session on top of `initialize`, but both shared
the 10s probe budget. Stateful adapters do real work in `session/new` —
letta-acp creates or resumes an agent, bootstraps its runtime, and
fetches a live catalog, measured at ~9s against Letta Cloud — which does
not fit once process spawn is counted. Give the catalog probe its own
25s budget (~2.7x the measured worst case) and leave the
initialize-only probes at 10s.

Adapters disagree on how a config option spells its identity and label:
the current ACP spec says `id`/`name`, older ones say
`configId`/`displayName`. `resolve_model_switch_method` already accepted
both, but the `models` printer and the Desktop parsers did not, so a
spec-current adapter rendered as "? (configId: ?)" and its models lost
their labels. Centralize both lookups in commands/acp_config_options.rs
and use them everywhere.

Catalogs also reuse one display name across variants that differ in a
dimension the name omits — Letta lists six `GPT-5.6 Sol` reasoning tiers
— so the picker showed six indistinguishable rows. Qualify only the
labels that collide, and stop leaking the CLI's `unknown` placeholder for
a missing `agentInfo` into user-facing copy.

Signed-off-by: Sarah Wooders <sarahwooders@gmail.com>
Signed-off-by: Sarah Wooders <sarahwooders@gmail.com>

# Conflicts:
#	desktop/src/features/settings/ui/harnessCatalogCopy.ts
The backend table listed four modes without saying which one a
Desktop-spawned agent can actually use. The agent answers Buzz by
shelling out to the `buzz` CLI, so its tools have to execute on the
machine that has that CLI — which rules out any backend that runs tools
elsewhere, and that constraint was left implicit.

Lead with it: recommend `cloud-oauth` (agent and memory in Letta Cloud,
model access included, tools here), and name `LETTA_ACP_BACKEND`
explicitly rather than relying on adapter inference, whose value has
varied across letta-acp releases.

Also record the failure this hides. On `local` the agent resolves its own
model credentials, so an unrelated `OPENAI_API_KEY` in the Desktop
environment is inherited and used; a stale one fails the turn with an
OpenAI 401 that Buzz can only surface as "the agent's harness reported an
internal error", with nothing pointing at the key. The preset install
hint now names the two env vars that avoid it.

Signed-off-by: Sarah Wooders <sarahwooders@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant