Strict local assembly
@lite-agent/local runs agents against local models (Ollama, vLLM, LM Studio, llama.cpp) with SQLite persistence, mandatory OS sandboxing, deny-by-default permissions, and a tamper-evident audit log — all wired together with safe defaults. Use it when the model and the data must never leave the machine, and you want the safety posture enforced rather than configured.
The guiding principle is fail-closed: every layer refuses to run unless its safety invariant holds. A non-loopback endpoint, a sandbox that cannot initialize, a malformed permission file, or a custom tool without security metadata all abort startup instead of degrading silently.
Requires macOS or Linux, Node ≥ 20, and a running local model server. better-sqlite3 and @anthropic-ai/sandbox-runtime are pulled in as transitive native/runtime dependencies.
Quick start
What createLocalAgent() enforces
createLocalAgent(config) builds on the SDK's createLiteAgent() but locks every safety-critical knob. Knobs that would weaken the posture are omitted from LocalAgentConfig entirely — you cannot pass them.
Additional fixed defaults: bash commands time out at 120 s (30 min for background tasks, at most 4 of them, 5 MiB max output); file mutations reject symlinks escaping the workspace, write atomically, and snapshot up to 1 MiB per change (64 MiB per session) for binary-safe restore; sessions older than 30 days or beyond 1 GiB total are cleaned up.
Runtime data lives under the SDK project directory: sessions.sqlite3 and logs/events.jsonl.
Codec selection
With codec: "auto" (the default), the native codec is used only when the provider declares nativeTools: true; otherwise the JSON codec is used. The ReAct codec must be selected explicitly:
See Tool-call codecs for what each protocol looks like.
Token accounting
Context budgeting needs token counts, and local servers differ wildly here:
vllmandllama.cpppresets use the server's local/tokenizeendpoint (exact).- Any provider may supply
tokenEstimator(exact, as far as the SDK is concerned). - Otherwise a conservative bytes/3 estimate is used and flagged as
"approximate"indiagnostics().tokenizer.
The input budget is derived from the declared contextWindow: contextWindow − maxTokens − 10% reserve. If that leaves nothing, startup fails — declare the real context window.
localOpenAI() presets
localOpenAI(options) returns an OpenAI-compatible provider pre-tagged with local capabilities. Each preset knows its default endpoint; all can be overridden with baseURL.
Options (LocalOpenAIOptions): runtime and contextWindow (required), baseURL, apiKey (defaults to "local"), maxRetries, nativeTools (default false), tokenEstimator, probeTimeoutMs (default 3000).
At startup the provider probes GET {baseURL}/models and fails fast if the server is unreachable. To use a provider you built yourself, tag it with markLocalProvider(provider, capabilities) — the same loopback, contextWindow, and probe checks then apply.
Permissions
Permissions are deny-by-default: only the read-only built-ins (read_file, read_spilled, load_skill, TaskGet, TaskList, BashOutput) and the interactive ask_user/final_answer tools are allowed out of the box. Every mutating tool needs an explicit ask or allow rule.
Discovery order
Rules load from four layers, in order:
- Managed — the file in
LITE_AGENT_MANAGED_PERMISSIONS(orpermissionFiles.managed) - User —
~/.lite-agent/permissions.json(orpermissionFiles.user) - Project —
<workdir>/.lite-agent/permissions.json(orpermissionFiles.project) - Inline —
permissionFiles.inlineRulesin code
Each layer can be disabled by setting it to false. Deny always wins regardless of layer order (deny > ask > allow > default deny), so a managed deny cannot be overridden by a project or inline allow.
Files are hot-reloaded when their mtime/size changes; a malformed update fails closed — the reload throws, the last error is surfaced via diagnostics().permissions.error, and a permission_reload_failed diagnostic event is emitted.
File format
tool— a name or glob (string or array), matched against the tool name; omit to match all tools.when— conditions on dot-paths into the tool call'sinput("command","args.path", …); all keys must match (AND). Operators:regex,glob,equals,in,startsWith,contains,not. A missing field matches nothing — conditions fail closed too.effect—"allow" | "deny" | "ask"(required).asksurfaces a prompt through the permission channel.
Resource limits
Every command the agent runs is wrapped in ulimit caps:
At sandbox initialization the limits are verified with probeResourceLimits() (requires macOS or Linux and /bin/bash); if the host cannot enforce them, startup fails. To apply the same caps to your own sandbox, wrap it with resourceLimitedSandbox(sandbox, limits).
The memory cap uses ulimit -v, which is applied on Linux only. On macOS the CPU and process caps still apply, but there is no hard memory ceiling.
Custom tools
Tools passed via tools must declare Tool.security metadata, and only offline-safe values are accepted — anything else aborts startup:
security.networkmust be"none"or"loopback"—"private"/"unrestricted"are rejected.- Omitting
securityentirely is also an error: silence is treated as unknown risk, not as safe.
Audit and diagnostics
Event sink
By default every event is written — after redaction — to logs/events.jsonl: a 10 MiB rotating file (5 generations) whose entries form a SHA-256 hash chain, so truncation or tampering is detectable. Set the LITE_AGENT_AUDIT_KEY env var (or pass auditKey) to upgrade the chain to HMAC-SHA256. Pass eventSink: false to disable the sink, or your own eventSink/eventRedactor to customize it.
Querying permission decisions
Every permission verdict is persisted as a permission_decision event. Query or export them:
Diagnostics
diagnostics() snapshots the assembly's posture:
Shutdown
close() aborts any in-flight runs, then closes the event sink, checkpointer, and sandbox. If any of these fail, it throws an AggregateError — cleanup failures are never swallowed.
API summary
See also
- Model providers — the
openai()adapterlocalOpenAIis built on, and how to probe compatible endpoints. - Tool-call codecs — the protocols behind
codec: "auto". - Session persistence — the SQLite checkpointer this assembly wires in.
- Permissions — the SDK-level permission model this assembly hardens.