Repository navigation
feat(agents): add a Letta Code agent - #345
sarahwooders wants to merge 11 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.
0.34.26 ships @modelcontextprotocol/sdk 1.32.2, which resolves GHSA-46jr-4wx8-v453 and GHSA-6qxp-vccf-f47h.
| if staged is not None: | ||
| await shielded_thread(_replace_workspace, staged, commit_session) | ||
| else: | ||
| await shielded_thread(commit_session) | ||
| self.set_session_ref_for_context(target_context_id, imported_session_id) |
There was a problem hiding this comment.
Cancelled restore keeps old reference
Cancelling load_snapshot during the final swap leaves the restored files paired with the old conversation reference. shielded_thread waits for the swap to finish, then raises CancelledError, so set_session_ref_for_context never runs. The next turn resumes the old conversation ID against the restored files, or starts a new conversation if the target was fresh.
Finish updating the reference before allowing cancellation to leave the restore.
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: 466-470
Comment:
**Cancelled restore keeps old reference**
Cancelling `load_snapshot` during the final swap leaves the restored files paired with the old conversation reference. `shielded_thread` waits for the swap to finish, then raises `CancelledError`, so `set_session_ref_for_context` never runs. The next turn resumes the old conversation ID against the restored files, or starts a new conversation if the target was fresh.
Finish updating the reference before allowing cancellation to leave the restore.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.|
Thank you for the Letta Code agent! It's merged to Three commits on top of yours:
With those changes, Thanks again! |
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.Fix restore cancellation and finish the outstanding changelog and named-limit changes before merging.
Fix with agent prompt
Summary
Adds Letta Code as a packaged A2A agent.
Diagram
Reviews (7) · Last reviewed commit: "Merge main into add-letta-code-agent" · Reviewed by Greptile