Skip to content

feat(agents): add a Letta Code agent - #345

Open
sarahwooders wants to merge 9 commits into
scaleapi:mainfrom
letta-ai:add-letta-code-agent
Open

sarahwooders wants to merge 9 commits into
scaleapi:mainfrom
letta-ai:add-letta-code-agent

Conversation

@sarahwooders

@sarahwooders sarahwooders commented Oct 27, 2026 •

Copy link
Copy Markdown

What and why

Adds agents/letta-code/, a Letta Code agent built the same way as the other CLI agents, alongside OpenCode. Tasks can name it as letta-code (alias in aliases.toml) or as @local/agentenv-framework/agents/letta-code.

  • How it runs: each turn is one headless letta --backend local -p ... --output-format stream-json run in /app, and its stream-json events are the turn's trajectory.
  • Model endpoint: the local backend calls the task's [model] endpoint, $LITELLM_BASE_URL/v1, as an OpenAI-compatible provider through a per-turn relay that adds [model.headers] and the run's attribution. No Letta account or server is needed.
  • Memory across turns: a context's first task creates a Letta agent and conversation, and later tasks resume the same conversation. The agent's memory therefore persists across a context's turns, and snapshot/v1 save/load carries it to another context.
  • MCP: the task env's MCP servers, including peer_agents, are exposed to the model as direct tools.
  • Image and release: Node and the CLI are pinned by checksum and package-lock.json (Letta Code 0.34.25), and git is included because Letta Code keeps agent memory in git. The image is registered in the release's image build and dependabot lists, and install/v1 covers containers and vm hosts.
  • Smoke task: letta-code-smoke is added.

The agent's README.md has the Extension | Implementation table. agent-config, mcp-config, peer-agents, skill-config, trajectory, snapshot, attribution-probe and install are implemented; staging/v1 is the SDK default.

How it was tested

All model calls used openai/gpt-4.1-mini through an OpenAI-compatible endpoint, with Docker 20.10, compose v2 and buildx.

  • agent-env a2a-agent validate --id letta-code --sandbox local: exits 0. The agent card, message/send, tasks/get, agent-config (including the identity and system_prompt round-trips), mcp-config, peer-agents, skill-config, trajectory, install and the changelog roundtrip pass. As in the same run against opencode (also exit 0), snapshot load/recall_after_load report false only because the recall rubric's default judge, claude-code, isn't in a fresh local store. A separate live run saved a conversation, loaded it into another context, and recalled a phrase from before the save.
  • agent-env run agents --task letta-code-smoke: passed, returning 42.
  • make lint and make unit-test: pass. The new tst/unit/agents/letta_code tests cover the bridge's command building, conversation resume and MCP tool exposure.
  • check_plugin_api.py: reports no break to the plugin surface.

RetriggerView in GreptileConfidence Score: 4/5

The cleanup fix is sound, but changelog cancellation and the remaining unnamed MCP limits must be addressed before merging.

Fix All in CursorFindings

  1. P1 Cancelled restore keeps writing ▶
  2. P2 Named timeout is ignored ▶
Fix with agent prompt
### Issue 1
src/agent_env/agents/agents/letta-code/a2a_server.py:undefined-454
`load_snapshot` awaits `_replace_workspace` through `asyncio.to_thread`, but cancelling that await does not stop the thread. Cancellation releases the workspace lock and deletes the temporary staging directory while the thread can still move files. During shutdown or another cancellation, this can race with a new turn or leave the restore incomplete. Wait for the worker to finish before releasing the lock or removing its staging directory. Threaded changelog writes need the same protection.

### Issue 2
src/agent_env/agents/agents/letta-code/letta_code_bridge.py:undefined-309
`_command` hardcodes `120` even though `_CLI_TIMEOUT_S` already defines that limit. This violates the repository requirement to give magic numbers descriptive names rather than use them inline. Use `_CLI_TIMEOUT_S` here, and name the relay size limit and the MCP timeout and buffer limits in `mcp_tools.ts`. This requirement must be satisfied before merging.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds a packaged Letta Code agent with local conversations, a model relay, MCP tools, snapshots, and installation support.

  • Letta Code runs tasks with the task’s model and tools.
  • Letta Code snapshots carry a conversation into another context.
  • Letta Code is available by alias and in agent image builds.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Download snapshot] --> B[Stage conversation and workspace]
  B --> C[Take workspace and context locks]
  C --> D[Replace workspace]
  D --> E[Replace conversation]
  E -->|Move fails| F[Restore old conversation and workspace]
  E -->|Move succeeds| G[Try to delete old conversation]
  G --> H[Bind restored conversation]
Loading

Reviews (5) · Last reviewed commit: "fix(agents): keep a restored Letta Code ..." · Reviewed by Greptile

@sarahwooders
sarahwooders requested a review from a team as a code owner October 27, 2026 03:34
@socket-security

socket-security Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​letta-ai/​letta-code@​0.34.258410010099100

View full report

@socket-security

socket-security Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Priority Alert  (click "▶" to expand/collapse) Action
High priority
High CVE: MCP TypeScript SDK: One HTTP request within the default size limit could exhaust server memory during validation in npm @modelcontextprotocol/sdk

CVE: GHSA-46jr-4wx8-v453 MCP TypeScript SDK: One HTTP request within the default size limit could exhaust server memory during validation (HIGH)

Affected versions: >= 1.3.0 < 1.32.0

Patched version: 1.32.0

From: src/agent_env/agents/agents/letta-code/package-lock.json → npm/@letta-ai/letta-code@0.34.25 → npm/@modelcontextprotocol/sdk@1.30.0

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@modelcontextprotocol/sdk@1.30.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn
High priority
High CVE: MCP TypeScript SDK: OAuth client could send credentials to an authorization server chosen by the MCP server in npm @modelcontextprotocol/sdk

CVE: GHSA-6qxp-vccf-f47h MCP TypeScript SDK: OAuth client could send credentials to an authorization server chosen by the MCP server (HIGH)

Affected versions: >= 1.12.0 < 1.31.0

Patched version: 1.31.0

From: src/agent_env/agents/agents/letta-code/package-lock.json → npm/@letta-ai/letta-code@0.34.25 → npm/@modelcontextprotocol/sdk@1.30.0

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@modelcontextprotocol/sdk@1.30.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn
High priority
High CVE: MCP TypeScript SDK: Experimental tasks were not tied to the session that created them in npm @modelcontextprotocol/sdk

CVE: GHSA-22jm-h49p-29qw MCP TypeScript SDK: Experimental tasks were not tied to the session that created them (HIGH)

Affected versions: >= 1.24.0 < 1.32.0

Patched version: 1.32.0

From: src/agent_env/agents/agents/letta-code/package-lock.json → npm/@letta-ai/letta-code@0.34.25 → npm/@modelcontextprotocol/sdk@1.30.0

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@modelcontextprotocol/sdk@1.30.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn
High priority
High CVE: npm sharp: Vulnerabilities in libheif: GHSA-g89c-p67h-r497 and GHSA-2jg2-4ch7-h545

CVE: GHSA-rgj7-g3m4-5g8c sharp: Vulnerabilities in libheif: GHSA-g89c-p67h-r497 and GHSA-2jg2-4ch7-h545 (HIGH)

Affected versions: < 0.35.4

Patched version: 0.35.4

From: src/agent_env/agents/agents/letta-code/package-lock.json → npm/@letta-ai/letta-code@0.34.25 → npm/sharp@0.34.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/sharp@0.34.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn
High priority
High CVE: npm sharp inherited vulnerabilities in libvips: CVE-2026-33327, CVE-2026-33328, CVE-2026-35590, CVE-2026-35591

CVE: GHSA-f88m-g3jw-g9cj sharp inherited vulnerabilities in libvips: CVE-2026-33327, CVE-2026-33328, CVE-2026-35590, CVE-2026-35591 (HIGH)

Affected versions: < 0.35.0

Patched version: 0.35.0

From: src/agent_env/agents/agents/letta-code/package-lock.json → npm/@letta-ai/letta-code@0.34.25 → npm/sharp@0.34.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/sharp@0.34.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn

View full report

Comment thread src/agent_env/agents/agents/letta-code/a2a_server.py Outdated
if not imported_session_id:
raise HTTPException(status_code=500, detail="Letta Code import returned no session id")
if staged is not None:
await asyncio.to_thread(_replace_workspace, staged)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Cancelled restore keeps writing

load_snapshot awaits _replace_workspace through asyncio.to_thread, but cancelling that await does not stop the thread. Cancellation releases the workspace lock and deletes the temporary staging directory while the thread can still move files. During shutdown or another cancellation, this can race with a new turn or leave the restore incomplete. Wait for the worker to finish before releasing the lock or removing its staging directory. Threaded changelog writes need the same protection.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/agents/agents/letta-code/a2a_server.py
Line: 454

Comment:
**Cancelled restore keeps writing**

`load_snapshot` awaits `_replace_workspace` through `asyncio.to_thread`, but cancelling that await does not stop the thread. Cancellation releases the workspace lock and deletes the temporary staging directory while the thread can still move files. During shutdown or another cancellation, this can race with a new turn or leave the restore incomplete. Wait for the worker to finish before releasing the lock or removing its staging directory. Threaded changelog writes need the same protection.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

Comment thread src/agent_env/agents/agents/letta-code/Dockerfile Outdated
Comment thread src/agent_env/agents/agents/letta-code/letta_code_bridge.py Outdated
Comment thread src/agent_env/agents/agents/letta-code/letta_code_bridge.py


async def _command(env: dict[str, str], *args: str) -> bytes:
code, out, err = await _communicate("letta", "--backend", "local", *args, env=env, timeout_s=120)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Named timeout is ignored

_command hardcodes 120 even though _CLI_TIMEOUT_S already defines that limit. This violates the repository requirement to give magic numbers descriptive names rather than use them inline. Use _CLI_TIMEOUT_S here, and name the relay size limit and the MCP timeout and buffer limits in mcp_tools.ts. This requirement must be satisfied before merging.

Rule Used: Store magic numbers as class or instance variables with descriptive names rather than using them inline in the code. (source)

Learned From
scaleapi/scaleapi#126388

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/agents/agents/letta-code/letta_code_bridge.py
Line: 309

Comment:
**Named timeout is ignored**

`_command` hardcodes `120` even though `_CLI_TIMEOUT_S` already defines that limit. This violates the repository requirement to give magic numbers descriptive names rather than use them inline. Use `_CLI_TIMEOUT_S` here, and name the relay size limit and the MCP timeout and buffer limits in `mcp_tools.ts`. This requirement must be satisfied before merging.

**Rule Used:** Store magic numbers as class or instance variables with descriptive names rather than using them inline in the code. ([source](https://app.greptile.com/scale-ai/-/custom-context?memory=002e0051-41ad-46c1-9098-47433c580150))

**Learned From**
[scaleapi/scaleapi#126388](https://github.com/scaleapi/scaleapi/pull/126388)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Cursor Fix in Claude Code Fix in Codex

Comment thread src/agent_env/agents/agents/letta-code/letta_code_bridge.py Outdated
Comment thread src/agent_env/config/describe.py Outdated
…llation-safe

Stage the restored conversation before touching the target context, swap it
in only after the workspace swap succeeds, and put /app back if it fails.
Run snapshot file work in a thread that a cancelled caller still waits for,
so cancellation can no longer release the lock or delete staging mid-move.
Record attribution when the relay forwards a model request, not at CLI
start. Name the CLI timeout and relay size limit, and pin Node 22.23.4,
the minimum the locked CLI supports.
Comment thread src/agent_env/agents/agents/letta-code/letta_code_bridge.py Outdated
…leanup fails

Once the staged conversation is in place, removing the previous tree is
best-effort cleanup, so a non-removable old file can no longer fail the
commit and leave the new conversation paired with the old workspace.

This branch has not been deployed

No deployments
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