Repository navigation
feat(agents): add a Letta Code agent - #345
sarahwooders wants to merge 9 commits into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
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.
|
| 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) |
There was a problem hiding this 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.
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.|
|
||
|
|
||
| async def _command(env: dict[str, str], *args: str) -> bytes: | ||
| code, out, err = await _communicate("letta", "--backend", "local", *args, env=env, timeout_s=120) |
There was a problem hiding this comment.
_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!
…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.
…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.
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 asletta-code(alias inaliases.toml) or as@local/agentenv-framework/agents/letta-code.letta --backend local -p ... --output-format stream-jsonrun in/app, and its stream-json events are the turn's trajectory.[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.snapshot/v1save/load carries it to another context.peer_agents, are exposed to the model as direct tools.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, andinstall/v1covers containers andvmhosts.letta-code-smokeis added.The agent's
README.mdhas the Extension | Implementation table.agent-config,mcp-config,peer-agents,skill-config,trajectory,snapshot,attribution-probeandinstallare implemented;staging/v1is the SDK default.How it was tested
All model calls used
openai/gpt-4.1-minithrough 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 andsystem_promptround-trips), mcp-config, peer-agents, skill-config, trajectory, install and the changelog roundtrip pass. As in the same run againstopencode(also exit 0), snapshotload/recall_after_loadreport 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 lintandmake unit-test: pass. The newtst/unit/agents/letta_codetests cover the bridge's command building, conversation resume and MCP tool exposure.check_plugin_api.py: reports no break to the plugin surface.The cleanup fix is sound, but changelog cancellation and the remaining unnamed MCP limits must be addressed before merging.
Fix with agent prompt
Summary
Adds a packaged Letta Code agent with local conversations, a model relay, MCP tools, snapshots, and installation support.
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]Reviews (5) · Last reviewed commit: "fix(agents): keep a restored Letta Code ..." · Reviewed by Greptile