Skip to content

Letta Chat: bind a pane from a fact Letta or herdr reports, not from per-folder state #2314

Open

Description

@devswha

Follow-up to #2312 (first deliverable of #1730). Chat reads a Letta Code pane's transcript only when Ovis can tie the pane to one conversation, and otherwise shows the terminal's text. The tie is still circumstantial: three known edge cases can make Chat show a conversation the pane is no longer on. The owner chose to merge #2312 as is and test it himself.

Root cause

No fact Ovis can read says which conversation a running letta process is on now:

  • herdr 0.9.5 keeps the first session report of a letta process. Its Letta integration (herdr integration install letta) reports the conversation from Letta's SessionStart hook. After /new or /resume, herdr still names the conversation the pane left: a later pane report-agent-session with a higher --seq, even one sent from inside the pane, was ignored on a real run. herdr's own docs say an integration should "report it again whenever your agent switches sessions".
  • Letta Code 0.34.27 records its conversation per folder, not per process. It writes .letta/settings.local.json (sessionsByServer) in its current folder on start and on every switch. It skips the write when the conversation is unchanged, and it carries on after a failed write. It keeps no file open and no lock that names its conversation.

So #2312 binds only from circumstantial facts:

  • herdr's first report;
  • a project record older than the letta process;
  • the process still being in the folder it started in, judged by the PWD its environment keeps;
  • no other letta pane in that folder;
  • the local store naming the agent.

Known wrong-conversation cases (in #2312's PR body)

  1. Stale PWD. A letta started with env -C <dir> (or by any parent that changes the child's folder without updating PWD) has a PWD naming another folder. If it later moves into exactly that folder (SetWorkingDirectory, EnterWorktree) after /new, and that folder's older record names the conversation herdr still reports, Chat shows that old conversation.
  2. An excursion Ovis did not see. A letta leaves its start folder (SetWorkingDirectory), runs /new there (recording the new conversation in the other folder's record), and comes back before Ovis looks at the pane again. Its start folder's older record and herdr's stale report still agree on the old conversation, so Chat shows it.
  3. "Seen elsewhere" is forgotten on restart. The memory of letta processes seen outside their start folder lives only in the Ovis server process, so a server restart loses it. Combined with case 2, a process that moved and came back binds again.

Each needs a folder move by Letta's own tools plus a conversation switch Ovis cannot see.

Real fix

Bind from a fact Letta or herdr reports about the running process, not from shared per-folder state:

  • herdr: accept a later herdr:letta session report from the same process. Letta's hook already fires on every SessionStart, including after /new and /resume. Then the herdr report alone names the current conversation, and the record/folder cross-checks can go.
  • or Letta: a per-process record of the open conversation (for example a session lock naming the pid, as Muse writes).

Until one of those exists, the three cases above stay documented limits. Refs #1730, #2312.

Activity

  1. devswha commented on Oct 29, 2026

    @devswha
    OwnerAuthor

    Also from CodeRabbit on #2312 (server/letta-store.ts, lettaInStartFolder): the 'seen elsewhere' memory is keyed on processStartLowerBound, which can shift between lookups after a backward wall-clock step on Linux; key it on processStartedAt (the stable process identity) instead.

  2. devswha commented on Oct 29, 2026

    @devswha
    OwnerAuthor

    Upstream: herdr's Letta support landed in herdrdev/herdr#3107 (merged 2026-09-13; detection, restore and the opt-in herdr integration install letta session hook). In that thread Letta users report this root cause: after /new, Letta's hook emits the new conversation id, but herdr keeps the previous one. leonardschneider has two fixes:

    • herdr (commit d6ab6f7e): herdr:letta reports for new/resume update the same process's stored conversation id.
    • Letta: a same-agent /resume emits SessionStart.

    herdr's maintainers plan to take the herdr fix after their agent-registry PR.

    Plan: once a herdr release ships it, Ovis binds Letta Chat from herdr's report alone and drops the folder/record heuristics from #2312 (lettaInStartFolder, the record-age and peer checks), which also closes the edge cases above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions