← Knowledge

tangled-letta

A public teaching repository that starts a persistent Letta Cloud agent from a Tangled pipeline and verifies the repository state observed in its separate sandbox.

tangled-letta is a public teaching repository for starting a persistent Letta Cloud agent from a Tangled pipeline and checking what its separate sandbox observed. The project treats the CI runner and the agent computer as separate systems. A run passes only when local checks succeed and the caller finds the expected repository identifiers in streamed tool evidence and the final response.

The pattern addresses a specific evidence problem. An agent can state the expected commit without having inspected it. The runner therefore records the expected repository state before the agent turn, then validates the agent's tool results and final response against that state.

Two execution environments

Each run uses two computers with different files and responsibilities:

  • Tangled runner: checks out the repository, runs local tests, starts the Agent SDK session, and validates receipts. It can see the pipeline checkout, public environment values, and repository secrets.
  • Letta Cloud sandbox: runs the agent's shell and file tools. It can see only files fetched or created inside that sandbox.

Tangled defines the pipeline in `.tangled/workflows/agent-ci.yaml`. A cloud Agent SDK session without a selected computer creates a managed sandbox. The runner's process.cwd() path is not mounted there, so the agent must fetch the public repository into its own filesystem.

The complete path is:

push or manual trigger
    → Tangled checks out the triggering commit
    → the runner installs locked dependencies and runs local checks
    → createSession() opens a new conversation on the pinned agent
    → the Letta sandbox fetches the runner's exact commit
    → the agent runs rev-parse and ls-tree inside that checkout
    → the runner validates streamed tool evidence and final text

This separation is easy to miss because both environments participate in one pipeline step. Treating them as one filesystem produces a plausible instruction that cannot work.

One agent, one conversation per run

The workflow pins a LETTA_AGENT_ID rather than looking up an agent by name. Agent names are not unique; the ID selects the exact persistent identity. LETTA_MODEL is an optional public setting passed to createSession(agentId, { model }), so each repository or pipeline can choose a model without replacing the agent.

Every trigger opens a new conversation on the same agent. Message history stays separate by run, while agent-level memory remains shared. Reusing one agent across repositories therefore creates an intentional memory relationship between those pipelines. A dedicated agent is the safer default when repositories should not shape the same future behavior.

The session uses a Letta-managed sandbox with a ten-minute time to live and terminateOnClose: true. Eager cleanup fits this example because one short-lived session exclusively owns the sandbox.

The cross-sandbox receipt

The runner computes its own commit SHA and sorted top-level file names before calling the SDK. It also validates the Tangled metadata that will enter the agent's shell instruction:

  • the repository URL must use HTTPS and contain no embedded credentials;
  • the commit must be exactly 40 hexadecimal characters;
  • the pipeline kind must be push, manual, or local;
  • the Tangled trigger SHA, when present, must equal the runner checkout.

The agent receives a narrow instruction. It initializes an empty repository, fetches the exact SHA, checks out FETCH_HEAD, runs git rev-parse HEAD, and lists the tracked top-level names. It is told not to read or execute repository files.

The caller uses send() and stream() instead of the one-shot prompt() helper. Streaming exposes the tool calls and tool results needed for verification. A transcript accumulator reconstructs fragmented events, then caller code checks two evidence surfaces:

  1. Successful tool-call inputs must collectively contain the expected Git-command fragments and SHA. Their paired successful result outputs must collectively contain the expected SHA and top-level file-name lines.
  2. The final assistant text must contain exactly four lines matching the runner's expected values.

A successful run ends with output shaped like this:

agent: agent-…
model: letta/auto
conversation: conv-…
duration_ms: 12345
tool_receipts: <count> successful
pipeline: push <sha>
checkout: <same-sha>
files: .gitignore, .tangled, LICENSE, README.md, package-lock.json, package.json, scripts, tests, tsconfig.json
note: those names were listed inside the Letta Cloud sandbox

The final text is a second consistency check. Streamed rows are stronger evidence than final prose because each row pairs a tool call ID with a result. The current validator still checks required input fragments and output lines in aggregate. It does not bind each expected output to a particular Git command or prove that the output originated from the fetched checkout.

Secrets and authority

The Letta API key belongs in Tangled's repository secrets. The agent ID and model ID are public configuration and belong in the workflow's environment map. Tangled warns that values in that map are visible to repository readers.

The enclosing pipeline shell receives LETTA_API_KEY. The workflow unsets it for the dependency-install and local-test child processes, while npm run ci-agent retains it so the SDK can authenticate the Cloud connection. The key does not enter the agent prompt or public clone URL. The repository uses the portable @letta-ai/letta-agent-sdk/client import because the Tangled runner does not need a local App Server. It also installs with --ignore-scripts, avoiding native setup for the unused local backend.

The session uses permissionMode: "unrestricted" because the pipeline has no human approval interface. This grants broad tool authority inside the sandbox, so the repository describes itself as a wiring example rather than a general CI agent. The prompt narrows the requested work, and the validator proves required observations occurred. Neither mechanism proves that the agent made no additional tool calls.

What a passing run establishes

The demo's local gate runs TypeScript checks and seven contract-test cases covering metadata, quoting, transcript selection, tool evidence, and final receipts. The public pipeline page records remote run history, but this article does not bind a displayed green run to public commit `1e03018`.

A passing run establishes the following facts:

  • the runner's local contract checks passed;
  • successful tool-call inputs collectively contained the required Git-command fragments and SHA;
  • successful tool-result outputs collectively contained the expected SHA and top-level names;
  • the final four-line receipt matched the runner's expectation.

A passing run does not establish that the repository is correct or secure. It does not prove that the Tangled and Letta environments share a filesystem, that the agent performed no other actions, or that the expected output lines came from particular Git commands. The run does not review the code or authorize deployment.

The exact-commit fetch also assumes the commit is reachable from the public HTTPS repository. Private repository authentication is outside this demo.

Reusing the pattern

The reusable part is the evidence boundary rather than the prompt text:

  1. Pin the persistent agent by ID.
  2. Separate public configuration from secrets.
  3. Compute expected state in the CI runner before starting the agent.
  4. Make the separate execution environment fetch an exact immutable revision.
  5. Stream and validate tool results against runner-owned expectations.
  6. Treat final assistant text as a checked summary, not the sole receipt.

Agent Authority and Effects covers the additional policy and effect checks needed before this pattern can perform writes. Agent Trajectory Observability places the source revision, model run, tool evidence, and resulting effect in one reconstructable chain.

Sources

  1. tangled-letta source repository
  2. tangled-letta pipeline runs
  3. Tangled pipeline documentation
  4. Letta Agent SDK sessions
  5. Letta Agent SDK deployment
  6. Letta Agent SDK permissions
  7. Letta Cloud sandboxes

Connections

Related

Linked here

Suggest a correction ↗