Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Letta Code for Yumi Smart Pi One (32-bit ARM)

Letta Code — the coding agent with persistent memory that learns across sessions — running on Allwinner H3 / armv7l (Smart Pi One, Yumi SmartPad), a platform none of its official channels (desktop app, Nix flake) build for.

It runs natively (no emulation): the @letta-ai/letta-code npm package is one plain-JavaScript bundle, so all it needs is a Node ≥ 22.19. Agents, memory and provider credentials stay on the pad (local backend, no Letta Cloud account required). Sign in with an Anthropic subscription (OAuth, works over SSH) or any API key; full interactive agent, full tool use.

╭──────────────────────────────────────────────────────────╮
│  Letta Code                            0.31.12 · armv7l  │
│  › _                                                     │
│                                                          │
│  letta  ·  letta --new  ·  letta -p "…" for one-shot     │
╰──────────────────────────────────────────────────────────╯

Install

curl -fsSL https://raw.githubusercontent.com/Yumi-Lab/letta-code-smartpi/main/install.sh | bash

The installer always fetches the newest version published on npm (re-running it is the update path). To pin or roll back: LETTA_CODE_VERSION=0.31.12 bash install.sh.

Then open a new shell (the installer adds ~/.local/bin to your PATH) and connect a model provider — the first run offers it, or do it from the terminal:

letta                              # setup screen → Anthropic subscription (OAuth) or key
letta connect anthropic <api_key>  # non-interactive

The Anthropic subscription flow prints a URL and asks for the code back — open the URL on any browser, nothing has to run on the pad. (The OpenAI/ChatGPT flow instead listens on localhost:1455: use ssh -L 1455:localhost:1455 pi@pad and a browser on your machine.)

Headless / gateway use

letta -p runs on the local backend with whatever provider is connected, or with a key in the environment — no login step:

ANTHROPIC_API_KEY=sk-ant-… letta -p "summarise this repo"

Usage

Command Purpose
letta Full interactive agent — resumes the last conversation of this project
letta --new New conversation (--new-agent for a fresh agent)
letta -p "question" One-shot, headless
letta --backend cloud Use a Letta Cloud account instead of the local backend
letta connect <provider> … Connect a provider from the terminal (anthropic, openai, codex, ollama, lmstudio, llama-cpp, bedrock…)
letta memory … / letta agents … Memory filesystem and agent subcommands
LETTA_CPUS=0,1 letta … Limit the running agent to 2 cores (default: all 4)
LETTA_DAEMON=1 letta -p … Queue headless jobs, 2 live at a time (see below)
letta-check-update OTA probe — one JSON line
letta-daemon-status Daemon probe — one JSON line

Local backend, cores, warm start, daemon — the board-level knobs

Local backend. The launcher exports LETTA_LOCAL_BACKEND_EXPERIMENTAL=1: state lives in ~/.letta/lc-local-backend/ (each agent's memory is a git repository), and headless runs do not require a Letta Cloud key — without the switch, letta -p exits with "Missing LETTA_API_KEY". Override per launch with --backend cloud, or globally with LETTA_LOCAL_BACKEND_EXPERIMENTAL=0. Local mode keeps persistent memory, self-improvement, subagents and skills; Slack/Telegram/Discord channels, chat.letta.com remote access and multi-computer need a Letta server.

Cores. LETTA_CPUS pins the running agent (taskset + nice -n 5), the runtime twin of KIMI_CPUS on kimi-code-smartpi and CLAUDE_CPUS on claude-code-smartpi. Default: all 4 cores. LETTA_CODE_BUILD_CPUS does the same for the install step.

Warm start. The launcher exports NODE_COMPILE_CACHE=~/.cache/letta-code/v8, so Node 22 caches the V8 bytecode of the 34 MB bundle: the compile happens once (the installer primes it), every later launch skips it. The cache key includes the Node version and architecture, so a Node upgrade invalidates it automatically.

Daemon (batch work). LETTA_DAEMON=1 routes headless runs (-p) through a small job daemon: a FIFO queue with a semaphore of LETTA_MAX_CONCURRENT (default 2) live runtimes. A queued job costs ~0 RAM until a slot frees, which is what lets a 1 GB board accept a batch of agent jobs without swapping. The daemon lazy-starts on the first job and exits after LETTA_IDLE_MS (default 5 min) of idle — no systemd unit, no boot cost. Each job carries the calling shell's environment (provider keys included). The interactive TUI never goes through it.

LETTA_DAEMON=1 letta -p "review the last commit"   # queued if 2 are already running
letta-daemon-status --json         # {"daemon":true,"running":2,"max":2,"queued":3}
LETTA_MAX_CONCURRENT=3 …           # only after checking `free -m` — see Status

Updating (OTA)

  • Check: letta-check-update prints one JSON line — {"cli":"letta","installed":"0.31.12","latest":"0.32.0","update_available":true}. This is the probe the Yumi AI Gateway console polls for its update badge.
  • Update: re-run install.sh — that IS the updater. It resolves the newest version from the npm registry, exits as a no-op when you are already current, and reinstalls the launcher, shims and probes either way.
  • ⚠️ Never run letta update. Upstream's self-updater runs npm install -g in the background at startup; the launcher disables it (DISABLE_AUTOUPDATER=1) because this board updates through install.sh.
  • Privileges: everything lives under $HOME — no sudo after the first install (which may install Node, git, ripgrep, libatomic1 and earlyoom): the gateway service user updates unprivileged.

How it works

  1. Letta Code's desktop app and Nix flake cover x86_64 and aarch64 only → dead end on armv7l.
  2. But the CLI is published on npm as @letta-ai/letta-code, and that package is plain JavaScript: binletta.js (34 MB ESM bundle with a Node shebang). Its native pieces are sharp (an armv7 prebuilt exists upstream; npm picks it) and node-pty (no Linux prebuild anywhere, lazy-loaded, only needed for interactive PTY shells).
  3. So the installer needs a Node satisfying engines: >=22.19.0 with node:sqlite built in. Debian trixie ships Node 20 → it installs Node 22 armv7l from nodejs.org (into /usr/local when privileged — shared with the other *-smartpi CLIs — otherwise into ~/.local/lib/nodejs).
  4. The package is installed non-globally into ~/.local/lib/letta-code with --ignore-scripts (no compiler, no telemetry ping), then upstream's own post-install patch script (terminal flicker fixes for ink) is run by hand.
  5. A launcher fixes the two switches this board needs — local backend on, self-updater off — plus core pinning and the compile cache; earlyoom completes the safety net.

Details and evidence: docs/METHODOLOGY.md.

Requirements

  • Smart Pi One / SmartPad, Armbian or DietPi (Debian trixie), 1 GB RAM — see the RSS figures below before running more than one agent at a time.
  • ~350 MB free on the SD card: 315 MB of package tree after pruning, ~25 MB Node when not already present, 3 MB of compile cache.
  • Network at install time (npm registry, nodejs.org, apt) and at run time (the model provider only, in local mode).
  • Optional, for interactive PTY shells: LETTA_CODE_WITH_PTY=1 bash install.sh builds node-pty from source (build-essential, python3-dev, ~3 min emulated).

Status

Validated in a real armv7l userland (Docker linux/arm/v7, qemu-arm), not yet on the pad. test/install-armv7-docker.sh runs the real installer and asserts:

Check Result
install.sh end to end on armv7l (apt, Node 22, npm, ink patches, cache) OK — 12 min emulated, 315 MB on disk, sharp armv7 prebuilt selected automatically
letta --version, --help, require("sharp"), node:sqlite OK
headless local turn with an invalid Anthropic key reaches api.anthropic.com, returns the provider's 401 as a structured error — the whole pipeline minus a valid key
same turn through the job daemon, then letta-daemon-status OK
letta-check-update right after install installed = latest, update_available: false
second install.sh run no-op on the npm step
node-pty from source builds and loads (199 s emulated)

Measured under emulation (pad numbers will differ, mostly in elapsed time):

Run (through the installed launcher, compile cache primed) Elapsed (emulated) Max RSS
letta --version 24 s 290 MB
one headless turn 52 s 329 MB

The RSS is what a 1 GB board cares about: about twice a Claude Code or Kimi Code runtime, hence the daemon default of 2 concurrent jobs. Not yet measured on hardware: launch time, TUI behaviour over a real TTY, RSS on the pad, thermals.

Development / tests

shellcheck -x install.sh lib/letta-release.sh bin/letta-check-update test/install-armv7-docker.sh
node --check shim/letta-daemon.mjs shim/letta-client.mjs bin/letta-daemon-status
test/install-armv7-docker.sh            # ~15 min: real install + smoke in linux/arm/v7
LETTA_SMOKE=0 test/install-armv7-docker.sh   # installer + layout only

Related

Licenses

This repository (installer, launcher, daemon, probes): MIT — see LICENSE. Letta Code itself is Apache-2.0, © Letta; it is fetched from npm at install time and is not redistributed here.

About

Letta Code (stateful coding agent with persistent memory) on the Yumi Smart Pi One / 32-bit ARM (armv7l) — native via npm + Node 22, local backend, warm start, bounded-concurrency job daemon. Validated in an armv7 container, pad pending.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages