feat(acp): add Letta as a tier-2 preset harness - #3454
Closed
sarahwooders wants to merge 5 commits into
Closed
Conversation
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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 registration —
PresetHarnessentry, inlinecurrentColormark, catalog copy,CREDITS.mdprovenance row.discovery.rssplit — 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 todiscovery/presets.rs(discovery.rsdrops 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 modelsopens a session on top ofinitialize, but both shared the 10s probe budget. Stateful adapters do real work insession/new(letta-acp: ~9s against Letta Cloud), which does not fit once spawn is counted. Split outMODEL_CATALOG_TIMEOUT(25s); initialize-only probes stay at 10s.id/name(current spec) orconfigId/displayName(older).resolve_model_switch_methodaccepted both; themodelsprinter and Desktop parsers did not, so spec-current adapters rendered as? (configId: ?)with unlabeled models. Both lookups now live incommands/acp_config_options.rs.GPT-5.6 Solreasoning tiers), producing indistinguishable picker rows. Colliding labels are qualified with their id. The CLI'sunknownplaceholder for a missingagentInfono longer reaches user-facing copy.Docs — backend table leads with the constraint that tools must execute where the
buzzCLI is; recommends settingLETTA_ACP_BACKEND=cloud-oauthexplicitly.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-oauthandLETTA_API_KEYin the agent's env vars; the model picker populates and the agent replies in channels and DMs.LETTA_AGENT_IDoptionally pins one agent across restarts.Added: 4 Rust tests in
acp_config_options.rs, 3 inusePersonaModelDiscovery.test.mjs.presetLogos.test.mjsrepointed atpresets.rs.just cipasses locally exceptdesktop-tauri-clippy, which fails on three dead-code errors indesktop/src-tauri/src/linux_media.rs(Linux-only code, dead on macOS) — reproduced on a cleanmaincheckout.mobile-check/mobile-testnot run (no Flutter locally; no mobile code touched).