Skip to content

feat(letta-brain): B1 — Letta-backed agent mode (Hermes-front bridge, production)#104

Merged
juniperbevensee merged 3 commits into
mainfrom
dev/juniperbevensee/b1-letta-brain
Jul 22, 2026
Merged

feat(letta-brain): B1 — Letta-backed agent mode (Hermes-front bridge, production)#104
juniperbevensee merged 3 commits into
mainfrom
dev/juniperbevensee/b1-letta-brain

Conversation

@juniperbevensee

Copy link
Copy Markdown
Collaborator

Swarm Map v1 slice 4 — real B1 (spec memory/specs/2026-07-19-swarm-map-v1-definition.md §2B/§3). Supersedes prototype #96 and B1.5 #101: both ride in as the first two commits (rebased onto current main), the third graduates them to production.

What

A Hermes gateway with LETTA_BRAIN_URL + LETTA_BRAIN_AGENT_ID (+ optional LETTA_BRAIN_API_KEY) becomes the messaging door for a Letta brain: surfaces, pairing/group approval, audit, and budget all run as normal, then the turn is forwarded to the bound Letta agent over REST and the reply rides the normal delivery path.

Production hardening over the prototype

  • B3 per-agent serialization — per-(server|agent) asyncio lock; staleness re-checked after queue wait
  • Streaming — Letta SSE → the same GatewayStreamConsumer proxy mode uses (_setup_platform_stream_consumer extracted verbatim, shared); LETTA_BRAIN_STREAMING=off forces blocking
  • Double-turn safety — stream→blocking fallback only on provably pre-delivery failures (LettaBrainError.retryable); partials delivered, never retried
  • Auth — Bearer key for Letta Cloud / secured servers
  • Tool-only turns — quiet empty reply + audit log, not a user-facing error

Tests

21/21 letta (9 new), 170 green across letta/proxy/stream-consumer/streaming-defaults; ruff clean. (9 aiohttp-less proxy failures pre-exist on main in envs without the optional extra.)

Not in this PR (spec-tracked)

B2 (Swarm Map wires door↔brain), B4/C1 verification, interrupt handling, media forwarding.

🤖 Generated with Claude Code

juniperbevensee and others added 3 commits July 22, 2026 14:14
A Hermes gateway with LETTA_BRAIN_URL + LETTA_BRAIN_AGENT_ID (or
gateway.letta_brain.{base_url,agent_id} in config.yaml) forwards each
turn to a bound Letta agent over REST instead of running the native
AIAgent loop. Surfaces, pairing/group approval, budget, and the reply
path are untouched — the delegation slots into the existing
remote-brain seam in _run_agent_inner, right beside proxy mode.

Throwaway prototype for Swarm Map v1 spec §2B/§3 slice 0 — exists to
answer 'what shape does the Hermes-front proxy want to be?'
Verdict: a config-driven mode at the proxy seam. Do not merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Productionizes the #96 prototype's door-context gap (B1.5, ratified: sender-
tagged message text). A shared Letta brain sees only the message text over REST,
so it can't tell who is speaking or which group — multiplayer is broken. We
prepend a compact one-line tag; Letta's server-side history then accumulates
identity turn over turn. Current-turn sender + group label only, NOT transcripts.

- letta_brain.py: build_sender_tag()/apply_sender_tag() + send_message() gains
  sender/group params. Documents the no-trailing-slash /messages requirement.
- run.py: _run_agent_via_letta derives sender (user_name) + group (chat_name,
  else type:id; None for DMs) from the SessionSource and passes them through.

Validated live 2026-07-21 (self-hosted Letta on Primo): given
'[from Alice in #family] ...' the agent replied 'Alice is messaging me from the
group #family.' — the inline tag conveys identity, no memory-sync needed.

12/12 tests (8 new: tag construction, send forwarding, DM vs group vs chat_name
dispatch). ruff clean. Draft — real B1 also needs SSE streaming + per-agent
serialization (spec B3); this lands the door-context bridge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… tool-only turns

Swarm Map v1 slice 4: the slice-0 prototype (#96) graduates to the real
Hermes-front bridge, per the spec verdict ("real B1 = productionize the
prototype + reuse proxy mode's streaming consumer"):

- B3 per-agent serialization: turns queue on a per-(server|agent) asyncio
  lock — Letta processes an agent's messages sequentially, so concurrent
  group messages must never overlap in flight. Staleness is re-checked
  after the wait so superseded turns are dropped, not sent.
- Streaming: letta_brain.stream_message consumes Letta's SSE endpoint
  (/messages/stream, stream_tokens) and feeds the same
  GatewayStreamConsumer proxy mode uses — the setup block is extracted
  verbatim into _setup_platform_stream_consumer, shared by both remote-
  brain modes. Defensive parser (parse_stream_line) skips unknown shapes.
- Double-turn safety: fallback from a failed stream to the live-validated
  blocking client happens ONLY when the failure is provably pre-delivery
  (LettaBrainError.retryable: aiohttp missing, connect-refused, 404/405).
  Ambiguous failures surface as errors — Letta owns server-side history,
  and a blind retry would process the turn twice. Partial text that
  already reached the platform is delivered, never retried.
- Auth: LETTA_BRAIN_API_KEY / gateway.letta_brain.api_key → Bearer header
  (Letta Cloud / secured servers).
- Tool-only turns: a turn ending without assistant_message is a quiet
  empty reply + audit log (message_types), not a user-facing error —
  resolves the prototype's TODO.

21/21 letta tests (9 new), 170 green across letta + proxy + stream-
consumer + platform-streaming suites; ruff clean. The 9 aiohttp-less
proxy failures pre-exist on main (env without the optional extra).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@juniperbevensee
juniperbevensee merged commit 8d14a24 into main Jul 22, 2026
31 of 32 checks passed
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