A Headlong-style persistent agency loop powered by one local Letta agent.

Files
README.md

Headlong Letta #

Headlong Letta runs one persistent local Letta agent on an adaptive wake loop. Letta owns the identity, memory, conversation, model, tools, and permission enforcement. The TypeScript controller owns wake timing, local state mapping, backoff, and content-dark receipts.

The repository began from Headlong at commit d8f83042c30ba34931259408077ead20e6bd93c3. The original implementation was removed. Its Apache 2.0 license and Git history remain.

Run it #

Requirements:

  • Node.js 22.19 or newer
  • local Letta Code credentials for xAI
  • xai/grok-4.6 listed as available by the local model catalog

Install and verify the runtime:

npm ci
npm run check
npm start -- models | grep 'xai/grok-4.6'

Create the agent once, then send a message or one autonomous wake:

npm start -- init
npm start -- send 'Inspect this repository and explain what you own.'
npm start -- wake 'Check whether one bounded task is worth doing.'

Run the adaptive loop in the foreground:

npm start -- loop

init persists one agent ID and one dedicated conversation ID under .headlong-letta/state.json. Before creating an agent without state, it searches for one exact tagged identity and recovers it; multiple candidates fail closed. Later commands resume both IDs. The directory is ignored by Git and written with user-only permissions.

What the controller owns #

Concern Owner
Identity, memory, conversation history Letta agent
Model, tools, and runtime permission mode Letta local backend
Wake trigger and adaptive delay Controller
Agent and conversation ID mapping Controller state
Send correlation and ambiguous-failure handling Controller receipts
Event and effect evidence Controller receipts

Each wake asks the agent to choose one bounded function: act, think, or idle. Passing is valid. The final line must use this envelope:

HEADLONG_OUTCOME {"kind":"acted|thought|idle","summary":"one short factual summary"}

Acted work resets backoff. Thought-only and idle wakes descend through a capped exponential schedule. Three outcomes at one level trigger the next level by default.

Permission policy #

The default acceptEdits mode supports repository work while the SDK callback resolves headless approval requests conservatively:

  • local Read, Glob, Grep, and ViewImage approvals are allowed;
  • web reads require HEADLONG_LETTA_ALLOW_NETWORK_READS=1;
  • other approval requests are denied;
  • unrestricted requires both HEADLONG_LETTA_PERMISSION_MODE=unrestricted and HEADLONG_LETTA_ALLOW_UNRESTRICTED=1.

The harness can approve file edits under acceptEdits. Shell commands, outbound communication, and other effects do not inherit permission from the wake loop. Run strict mode to deny every unattended approval.

The SDK approval callback only sees requests the runtime submits. The local runtime can execute tools such as shell commands without ever submitting an approval request, so callback denials do not guarantee prevention. After each turn completes, the controller checks the sanitized tool-call names against the same policy classification that drives the callback. A turn that executed any disallowed tool fails even if the runtime reported success, and its receipt records a policyViolation entry with the tool names. This is detection after execution; it does not prevent or undo the effect.

Backends #

HEADLONG_LETTA_BACKEND=local (default) runs the in-process local Letta Code runtime exactly as before.

HEADLONG_LETTA_BACKEND=cloud targets one existing Letta Cloud agent given by HEADLONG_LETTA_AGENT_ID. Cloud mode never discovers or creates agents; it idempotently creates on first use and later resumes one dedicated conversation on that agent, persisted under the controller home (use a separate HEADLONG_LETTA_HOME, for example .headlong-letta-cloud). Sessions pass HEADLONG_LETTA_MODEL (for example letta/auto) as a conversation-scoped override — Agent SDK 0.7.6 scopes session model options to the conversation, so Loop's global default is never mutated. Init asserts the conversation reports the configured model and fails closed otherwise.

Cloud authentication comes from LETTA_API_KEY or LETTA_CLOUD_API_KEY. Agent SDK 0.7.6 resolves credentials from an explicit key, an Authorization header, or these environment variables only; it does not fall back to Letta CLI login state. Initialization fails with a clear message when neither variable is set. Key values are never printed or written to artifacts.

With no computer selected, the SDK provisions a managed Cloud sandbox scoped to the conversation. The controller's laptop filesystem is not mounted. The session working directory is fixed at /root/workspace/headlong-letta, and each autonomous wake instructs the agent to clone https://tangled.org/cameron.stream/headlong-letta there if absent and to never push. The clone reflects the published remote state only: uncommitted local work never appears in the sandbox, and sandbox changes are not synced back automatically.

The managed sandbox is the isolation boundary for Cloud runs. Explicitly unrestricted mode (both environment opt-ins) genuinely authorizes gate decisions and post-hoc execution classification there; local defaults remain acceptEdits. Manifests and receipts record the backend and an observed-working-directory hash so the runtime environment stays auditable.

Receipts #

Each turn creates the following local files under .headlong-letta/runs/{run-id}/:

  • manifest.json: intended backend, model, policy, working-directory hash, and input hash;
  • events.jsonl: event types, IDs, byte counts, and structural key lists;
  • receipt.json: terminal or reconciled completion, conversation readback, outcome, next delay, and structured policyViolation evidence when detected.

Prompts, model output, tool output, environment values, and credentials stay out of these files. Unknown SDK event types are retained by type, key list, and payload byte count.

The controller prefers an SDK terminal result. The current local runtime can return to WAITING_ON_INPUT without emitting that event. After a five-second grace period, the compatibility path requires all of the following evidence before reporting success:

  1. The device reports no active processing.
  2. Conversation readback contains the turn's offline threading ID (OTID).
  3. Persisted assistant text after that user message matches the complete streamed assistant text.
  4. Streamed assistant output is nonempty.
  5. Autonomous output contains a valid HEADLONG_OUTCOME envelope.

The receipt marks this path as reconciled-runtime-idle and records sdkTerminalResult: false. A live Grok 4.6 example is retained at docs/receipts/2026-08-25-live-wake.json.

Failure behavior #

  • A failed send triggers conversation readback with the same OTID. The controller never retries when readback finds it.
  • A stream that ends without result and without the idle reconciliation evidence fails.
  • A turn-wide timeout aborts the session and records retryAuthorized: false.
  • A turn that executed a tool outside the configured policy fails after completion and records the tool names in its receipt. Detection is post hoc and cannot prevent or undo execution.
  • A process lock rejects concurrent turns and reclaims a dead owner's stale lock.
  • Controller recovery never clears conversation state.

See docs/architecture.md for the state machine and evidence contracts.

Configuration #

Variable Default Effect
HEADLONG_LETTA_BACKEND local local or cloud runtime selection
HEADLONG_LETTA_AGENT_ID unset Existing agent ID; required for cloud
HEADLONG_LETTA_MODEL xai/grok-4.6 Exact local model handle
HEADLONG_LETTA_HOME $PWD/.headlong-letta Controller state and receipts
HEADLONG_LETTA_CWD process working directory Agent workspace
HEADLONG_LETTA_PERMISSION_MODE acceptEdits Letta permission mode
HEADLONG_LETTA_ALLOW_NETWORK_READS unset Set to 1 for web-read approvals
HEADLONG_LETTA_ALLOW_UNRESTRICTED unset Second opt-in for unrestricted mode
HEADLONG_LETTA_BACKOFF_BASE_MS 5000 First nonzero idle delay
HEADLONG_LETTA_BACKOFF_FACTOR 2 Delay multiplier
HEADLONG_LETTA_BACKOFF_CAP_MS 300000 Maximum idle delay
HEADLONG_LETTA_BACKOFF_HOLD 3 Outcomes before descending
HEADLONG_LETTA_THOUGHT_CAP_MS 60000 Maximum thought-only delay
HEADLONG_LETTA_TURN_TIMEOUT_MS 600000 Authoritative turn timeout
HEADLONG_LETTA_TERMINAL_GRACE_MS 5000 Idle reconciliation grace period

The SDK App Server request timeout is always longer than the controller's turn deadline plus terminal grace. The controller owns turn cancellation; the transport timeout cannot preempt a valid multi-step tool turn.

Runtime versions #

The package pins Agent SDK 0.7.6 and overrides its Letta Code dependency to 0.30.32. Agent SDK 0.7.6 declares Letta Code 0.30.28, whose local model catalog does not contain xai/grok-4.6. The override supplies the requested model and is covered by the repository tests plus the retained live receipt.

npm audit currently reports three high-severity findings through the Agent SDK dependency tree. The root advisory covers inherited libvips vulnerabilities in sharp <0.35.0; npm reports no available fix. Review the audit before using the loop on an exposed or multi-user host.

Development #

Run the deterministic gates:

npm run check

The suite covers state persistence, controller locking, model and permission configuration, backoff, unknown events, OTID reconciliation, terminal results, missing-result compatibility, ambiguous sends, and receipt redaction.