Repository navigation
Releases: oratis/LISA
Release list
Lisa v0.28.1
LISA v0.28.1 — Soft Ink, Tasks & Trust
LISA is an AI Personal Assistant for planning, writing and personal knowledge. This release introduces the Soft Ink visual identity and adds the groundwork for an assistant that works on your behalf while you are away — durable tasks and watchers, a deterministic approval layer, one gate for every proactive message, a credential broker and cost-aware model routing — and fixes a set of first-run, health and privacy issues. Most of the new machinery is off or opt-in; what changes on a default install is listed first.
Reproducible release packaging
The release builder now installs the complete locked dependency graph for compilation, then prunes development tools before packaging the CLI bundles. Previously it installed TypeScript with --no-package-lock after a production-only install, which also re-resolved runtime dependency ranges and caused an IMAP type error despite green CI. The development environment is restored with the same lockfile after packaging.
v0.28.0 stopped at that packaging error before any npm package or GitHub release was published. v0.28.1 is the complete distribution of the changes below; the failed tag is retained without rewriting it.
Soft Ink: Lisa's new visual identity
- 24 core expressions in the approved Soft Ink illustration style replace the Web and Island portraits. All 114 existing mood identifiers remain supported; the other 90 states reuse a related core expression, and the original individual PNG endpoints remain available for older clients.
- Eight downloadable stickers, two illustrated rooms, a shared pose sheet and new app icons. Click Lisa's avatar to open the illustration gallery and download individual transparent PNGs. The room keeps its existing activity signals and changes lighting without moving its furniture.
- Six scene illustrations, with the welcome illustration connected to the chat empty state; the other five are prepared for later surfaces. The bilingual website gallery uses the same expression map.
- Consistent, cached avatars. Web and Island share a compact portrait atlas; rapid state changes cannot restore an older image, and failed loads preserve the last good portrait. The iOS source now uses the same mapping with a fallback for older servers; an updated iOS binary is a separate release.
- The gallery supports light/dark previews and 28–72px comparisons. Generated masters, prompts, deterministic export scripts and the review gallery are included in the repository.
What changes on a default install
- First-run with a non-Anthropic provider works. Choosing DeepSeek, Grok, Mistral, Perplexity, Ark, MiniMax, Hunyuan or a custom OpenAI-compatible endpoint and saving a key now actually switches the model; before, turns kept going to Claude. Custom OpenAI-compatible endpoints (Ollama, LM Studio, self-hosted) are back in the provider picker. (#381)
- Quiet hours for proactive pushes. Mail digests, important-mail alerts, the knowledge-base daily brief, advisor digests and the "[while you were away]" note pass one reach-out gate. Between 22:00 and 08:00 their push waits until 08:00; they still appear in the app at once. Configure in web Settings → Proactivity,
lisa reachout, or/api/reachout/settings. (#406) - One-time codes and sign-in links are hidden from mail. Codes, magic / sign-in / verification links and password-reset links in inbound mail are replaced with placeholders before the model, digests or alerts see them. (#405, #418)
- Safer, faster web reading.
web_searchgoes only to DuckDuckGo over HTTPS on every redirect hop; fetched pages are converted to text in one linear pass, so a hostile page can no longer stall the process; look-alike untrusted-content markers are defused. Users behind Clash / Surge fake-ip DNS keep working. (#404, #417) /healthon the Mac no longer exposes details before auth. Public health shows ok, latency and edition; the rest needs a token.lisa doctor --probereports a busy backend as degraded, not down. Restarts no longer re-send notifications for sessions already reported. (#381)- IMAP 2. Mail uses imapflow 2 and tolerates missing or odd envelope dates. (#384)
- Background classification uses a smaller model. Mail and knowledge-base feed classification now run on the small model of your chat model's family —
claude-haiku-4-5for Claude,gemini-2.5-flash-litefor Gemini,gpt-4o-minifor OpenAI — when you have credentials for it, and on your chat model otherwise. SetLISA_MODEL_SMALLto choose another model (includinglocal://for Ollama, LM Studio or llama.cpp), or to your chat model to keep classification on it. (Before this release classification always used the built-in default model, whateverLISA_MODELsaid.) The hosted edition keepsgemini-2.5-flash. (#407)
New, opt-in or off by default
- Task Engine —
lisa tasks …,/api/tasks. Routines, one-offs and web / RSS / mail watchers that run unattended underserve --weband the launchd heartbeat, with per-task leases, crash-safe resume, retries that continue the same run, and a write-ahead ledger so a side effect is not repeated. Tasks the model drafts start switched off; you enable them (lisa tasks enable <id>). Until approvals are wired to tasks, an unattended run may only make verified read-only calls.heartbeat.jsonchores keep running as before;lisa tasks migrate-heartbeat [--dry-run]moves them on request. Design and known gaps:DESIGN_TASK_ENGINE.md. (#403) - Warden —
--approval wardenorLISA_APPROVAL=warden. Each side-effecting tool call is allowed, denied, asked or handed back to you by a deterministic policy with scoped grants, conversation-long taint tracking and an audit log that records decisions without payload contents. The web approval card shows the complete request and approves exactly what was shown;lisa approvals …andlisa warden …on the command line. Off unless you turn it on: only the web client can answer an approval today.DESIGN_WARDEN.md. (#402) - Credential broker —
lisa secret set | list | rm. Secrets referenced assecret://namehandles, kept in the macOS Keychain or an encrypted file; values pass over stdin, never argv. Nothing else uses it yet. (#405) - Run budgets and a Gemini gateway.
runAgent({ costCapMicroUSD })is an estimate-based circuit breaker for integrators, not a guaranteed maximum charge. A signed-in account without its own Gemini key can usegemini-2.5-flashthrough LISA Cloud. SeePROVIDERS.md. (#407) - Cloud web tools (
LISA_CLOUD_WEB_TOOLS=1, server-side) and hosted tasks (LISA_CLOUD_TASKS=1) exist and are switched off in production.
Behaviour changes to note
lisa heartbeat run <name>runs heartbeat chores only; run a task withlisa tasks run <id>.- Gemini resource names such as
tunedModels/…are sent as written; basegemini-*ids are normalised. (#416)
Also since v0.27.1
- Account lifecycle: new accounts get random uids; deletion drains tenant work, revokes Sign in with Apple tokens when configured and reports retryable failures. (#398)
- iOS (ships separately through the App Store): cloud onboarding, streamed replies and history restored; consumable credits refresh from the LISA ledger without Apple authentication; AI consent is explicit and revocable. Lisa Pocket 1.2 is waiting for App Review. (#392, #395, #411)
- Website and package metadata describe LISA as an AI Personal Assistant with clear Cloud / Mac boundaries. (#415)
- Dependencies updated within supported ranges; Astro 7 for the website; production
npm auditreports 0 vulnerabilities. (#379, #394, #412)
Known limits
- The remaining 90 moods do not yet have independent Soft Ink drawings. Room night skies, complex pose occlusion and the remaining scene placements are follow-up work.
- The Keychain backend of
lisa secrethas not been exercised against a real login keychain in CI; runlisa secret set x,lisa secret list,lisa secret rm xonce on your Mac. - Hosted tasks are safe for a single instance only (see "Known gaps" in the Task Engine design).
- No Tasks view or approval inbox in the iOS or Mac apps yet.
No credentials are included. LISA Cloud was updated separately on 2026-10-07 (record); this release does not change production flags and does not imply App Store approval.
Lisa v0.27.1
LISA v0.27.1 — cloud inference recovery
LISA Cloud can use Gemini 2.5 Flash with the existing free allowance. Previously this model fell into the unknown-model premium tier, which would reject an account with no purchased credits even when its free allowance remained.
- Add exact-model Gemini Flash pricing and bump the pricing audit version. Flash-Lite, image, audio, preview, and unknown variants keep their previous conservative handling.
- Meter thinking tokens as output and cached prompt tokens separately, avoiding omitted reasoning cost and duplicate input charges.
- Use validated Gemini function calling to reduce malformed or empty tool completions; surface malformed calls as an error instead of silent success.
- Accept
GEMINI_API_KEYin the deployment helper, including Secret Manager mode.
Rates were verified against Google's pricing, and usage semantics against UsageMetadata. No provider credentials are included in this release. The iOS binary does not change; its existing disclosure flow reads the active AI recipients from the server.
This release does not imply App Store submission or approval. See the execution record for the live rollout and remaining review gates.
Deployment verified: LISA Cloud serves Gemini 2.5 Flash on revision lisa-cloud-00027-qal; actual cloud-domain login, chat and tool roundtrips passed. npm, the signed and notarized Mac DMG, and Homebrew are published. Follow-up iOS native fixes are tracked separately in PR #392. App Store submission is still pending.
Lisa v0.27.0
LISA v0.27.0 — personal assistant and reliable iOS connections
Lisa Pocket now opens with a cloud sign-in path and an optional Mac connection. Home offers editable prompts for daily planning, writing, and breaking down a goal. Mac-only agent and integration controls explain their requirements in cloud mode.
- Cloud and Mac configurations use separate Keychain credentials. Existing connections migrate, switching resets private view state and widgets, and failed secure storage does not report a saved connection.
- Chat identifies configured AI recipients and asks for consent before sending. The server's public auth configuration provides recipient names without exposing credentials or URL query strings. The updated mobile app requires this disclosure response; update the connected server before using chat.
- New StoreKit purchases bind to a LISA account, preventing a mode/account switch from crediting the wrong account. Pending purchases are retried after cloud sign-in, and receipts are not forwarded to the paired Mac. Legacy receipts remain supported by existing deduplication.
- Cloud sign-in requires HTTPS. Local network pairing stays available. Cloud and Mac histories remain separate; automatic memory sync is not part of this release.
- English and Chinese privacy pages describe cloud processing accurately, with dedicated support pages and revised store/review copy.
- The missing-agent regression test now isolates PATH instead of accidentally launching a real installed Codex.
Research and acceptance tracking: Muse research, implementation plan.
App Store availability requires a separate Apple review. A package release or TestFlight upload does not establish approval. Hosted email/integration/push capabilities remain constrained by the server's tenant boundaries.
Hosted-service update — 2026-09-27: the GLM balance outage observed at release is resolved by switching LISA Cloud to Gemini 2.5 Flash. Production login, real streamed replies, a knowledge-base tool round trip and free-allowance charging passed. See recovery PR #391. iOS 1.2 build 1790520396 remains prepared for submission; this service recovery does not mean App Review submission or approval.
Lisa v0.26.1
Lisa v0.26.1
The v0.26.0 DMG. v0.26.0 is the release that makes Lisa.app self-contained,
and its Mac job failed before producing a disk image — so it shipped the CLI
bundles with the one asset it was about missing. Same contents, plus the
one-character fix that lets the DMG build.
🔧 embed-runtime.sh under a UTF-8 locale
The release job died at embed-runtime.sh: line 124: tarball<U+2026>: unbound variable. Under a UTF-8 locale, bash 3.2 — still /bin/bash on macOS, GitHub
runners included — swallows the bytes of a following multibyte character into an
identifier, so "$tarball…" expands a variable named tarball plus the
ellipsis, which is unset, and set -u aborts:
LC_ALL=en_US.UTF-8 bash -uc 'v=x; echo "$v…"' # -> v…: unbound variable
LC_ALL=C bash -uc 'v=x; echo "$v…"' # -> fineNote the direction — the UTF-8 locale is the one that breaks. Every local
run and every bash 5 passes, which is why this only ever surfaced on the
tag-triggered release job, running for the first time now that there is
something to embed.
Braced, and the tree swept for the same shape: it was the only occurrence.
Verified end to end under LC_ALL=en_US.UTF-8, the exact locale CI uses.
See v0.26.0's notes for what actually changed.
Lisa v0.26.0
Lisa v0.26.0
Lisa.app now brings its own backend. Downloading the DMG used to get you an
app that couldn't do anything: it looked for lisa serve --web on the login
shell's PATH, so you had to install Node and npm i -g @oratis/lisa first — the
disk image's own README said so. The app now carries a full backend and its own
Node runtime, and starts them the first time you open it. Alongside that: the
In-App Purchases App Review couldn't find are reachable again, and the
"backend offline" banner stopped covering the window it was describing.
🖥 A DMG that works on a Mac with nothing installed
packaging/mac-client/embed-runtime.sh stages the runtime into the bundle at
build time — Contents/Resources/backend/{dist,node_modules,package.json} plus
Contents/Resources/node-runtime/bin/node. Verified the way it matters, with
env -i HOME=<empty> PATH=/usr/bin:/bin: the embedded Node runs the embedded
backend and answers HTTP 200.
Packaging decisions worth knowing:
--omit=optionaldrops node-pty, the only native module in the tree, so
the embedded backend stays pure JS — bound to no Node ABI and needing no
disable-library-validationunder the hardened runtime. PTY agents degrade to
the 503 they already returned without node-pty.- The Node binary is checksum-pinned in the repo, not just against the
SHASUMS256.txtfetched beside it: that file comes from the same host over
the same connection as the tarball, so it cannot vouch for it. The pin caught a
truncated download during testing and refused to embed it. - It is
strip -x'd (22MB per arch), lipo'd universal, and re-signed —
lipo drops nodejs.org's signature and arm64 refuses an unsigned Mach-O. - Start order is
serve-command.txt→ embedded →lisaon PATH. The override
still wins: someone who wrote that file wants their own tree to run, and
silently substituting the bundled copy would make their edits vanish.
Autostart also stopped looking broken. It always ran; the problem was that a
backend which died instantly left the app silent for 20 seconds and then said
timeout. BackendController now reads the backend's own fatal line out of
~/.lisa/backend.log and names which start path produced it.
App 339MB, DMG 156MB. LISA_EMBED_ARCHS=arm64 saves ~90MB;
LISA_SKIP_EMBED=1 skips the embed for Swift-only iteration.
💳 The In-App Purchases are findable again
App Review rejected Lisa Pocket 1.1 under Guideline 2.1(b) — "we cannot locate
the In-App Purchases". It was our own conditional UI: the only route to the
credit packs sat inside if let q = quota, q.available, window > 0, so a
/api/billing/quota that timed out, 401'd or 503'd left the app with no way
to reach them at all.
- "Add credits…" now renders for any signed-in account; the quota fetch only
decides whether the allowance rows appear above it. - A turn refused for lack of credits (402) offers the packs in the chat
bubble, a second route that needs no Settings trip. - An empty StoreKit response is treated as a failure, not a loaded empty
list. StoreKit answers with an empty array rather than an error when products
aren't available yet, and a sheet spinning on "Loading packs…" forever is
exactly what an unreachable IAP looks like from the outside.
Server-side, App Review buys in Apple's sandbox while the cloud edition rejects
every non-Production JWS (anti-minting). Named review accounts are now
allowlisted — and those sandbox credits are marked in the ledger and capped, so
a credential that by design gets typed into App Store Connect cannot mint
without limit.
🔔 The offline banner became a bottom toast
The safety-net banner was a wide slab pinned at top: 12px, styled from an
inline string. In the Mac client it landed squarely on the window's title bar and
the whole top function bar — the first thing you saw when the backend was down
was a message covering the UI it was describing.
It is now a compact bottom-anchored toast that draws from the theme tokens and
tracks Nebula ↔ Calm, rides above the composer (re-measured per call, so a
textarea that grew while you typed can't cover it), and carries an optional
copy-on-click command chip. The first two startup retries say "Waking Lisa up…"
in a calm tone — the desktop app is normally still starting the backend, and a
red error at t+4s is a lie — before escalating.
Lisa v0.25.0
Lisa v0.25.0
The review-and-optimization release. Two full-project reviews of v0.24.0 —
one UX, one technical — turned 24 findings into nine merged PRs (#365, #368–#376).
Two P0s are gone: the phone layout that collapsed the main pane to 75px, and the
first-run dead end that trapped anyone who typed a wrong API key. Underneath
that: hosted-edition boundary fixes, a durable billing outbox, a self-watchdog
for the backend, and the engineering gates — lint, formatter, coverage floors,
dependency bot, a real CI matrix, an offline browser smoke — that 60k lines of
TypeScript had been running without. Plans in
PROJECT_REVIEW_UX_v0.24.0.md and
PROJECT_REVIEW_TECH_v0.24.0.md.
⚠️ Node 22.19 is now the minimum
engines.node moved from >=20.0.0 to >=22.19.0. This is a real floor, not
housekeeping: undici — a production dependency — calls worker_threads APIs
added in Node 22.10, so >=20 was a promise that installed cleanly and then
failed at runtime. lisa doctor, both READMEs, both install pages, the Mac
install wizard and lisa upgrade now all check and state the same number,
major and minor. On Node 20, upgrade before installing.
📱 The phone layout works again (#375)
Since v0.24.0 made the right panel collapse by default, body.rb-collapsed .frame outranked the ≤720px media query, so a 375px viewport got a two-column
300px 75px grid: the main pane was 75px wide and the send button sat off
screen. The collapse rule is now scoped to ≥721px, #viewChat pins its column
to minmax(0,1fr), and the function bar became a horizontal scroller instead of
forcing the chat column to its ~680px min-content. Measured at 375×812, both
collapsed and expanded: main pane = viewport width, send button on screen, no
horizontal overflow anywhere. Two Playwright specs now hold that.
🔑 First run survives a wrong key (#375)
The birth ritual retried on 401, printed the provider's raw JSON, and made
ENTER reload the page to run again with the same bad key — with no way back to
the key form. Authentication errors are now plain language with a Change key
button that returns to the gate, saving a new key restarts the birth without a
reload, and the run is cancellable and deadline-bounded. The gate is also a
provider picker now: 16 providers including DeepSeek, GLM, Moonshot and Qwen, so
the web surface finally matches what the site advertises.
🔒 Hosted-edition boundaries (#365, #373)
/api/consent/*and/api/push/*reached the cloud deny-list. Both stores are
machine-wide by design, so any signed-in tenant could read every tenant's
ntfy topics and APNs tokens, unregister their devices, or switch the mail
digest off for the whole deployment.- Non-canonical paths are rejected before routing. The deny-list matched the
normalized pathname while every handler matched the rawreq.url, so
/api/agents/recap/%2e%2e/%2e%2e/%2e%2enormalized to/— not denied — and
still ran its handler. Failing closed on the path shape covers routes added
later too. /healthno longer publishes usage metrics to the public internet. It is
pre-gate on purpose (an operator has to tell "slow" from "down" without
credentials), but in the hosted edition an unauthenticated caller now gets
health only — not tenant, session and in-flight-turn counts, heap, RSS or
uptime. Authenticated callers still get the full payload;/healthzis
unchanged.
⚙️ The backend notices when it wedges (#373)
Two 5-second stalls on the daily driver during review, and /health could only
say {ok:true} — enough for launchd to restart a crash, useless against a hang.
/health now reports version, uptime, event-loop lag p50/p99/max, heap, RSS and
live counters; a self-watchdog exits after 60s over the lag threshold so the
supervisor can restart it; logs rotate at 10MB × 5. The stall itself was
whole-file reads — session summaries, message pages and the claude-code watcher
are streaming or tail reads now, with an mtime-keyed session index and ETags.
💳 Usage settlement is durable (#374)
Billing had an unrecoverable window: the provider had charged, but a failed
balance write or a crash between two writes left neither a debit nor a way to
recover. Every metered turn now appends an immutable usage event before
settling; a failed append fails the settlement closed. The balance carries the
applied event ids as its own idempotency key inside the same atomic update, so a
replay cannot double-charge, and a reconciler sweeps pending and failed events
every 15 minutes — escalating to needs_human rather than replaying anything old
enough to have aged out of that ring. lisa billing reconcile is the operator's
view.
🖥 CLI polish (#372)
Persistent REPL history (~/.lisa/history, mode 0600, deduped, capped),
one-line tool-call rendering, a spinner that never touches stdout, and thinking
shown as a marker with its content structurally unreachable. New:
lisa doctor --probe reads a running backend's /health and exits non-zero when
it can't; lisa upgrade detects Homebrew / npm-global / source, upgrades, kicks
the LaunchAgent onto the new code, and — when npm fails — hands over the same
no-sudo prefix fix the Mac wizard offers. Proxy logs are quiet unless
--verbose, and user-facing paths abbreviate to ~.
🍎 Mac and iOS (#371)
macOS builds with zero warnings and -warnings-as-errors, so Swift 6 strict
concurrency can't regress in silently. Lisa.app gained a backend install wizard —
it detects Node and the CLI through a login shell, installs in-panel, and names
the fix when npm hits a permissions error — instead of the README telling you to
go run npm install -g yourself. On iOS: accessibility labels throughout (status
never carried by colour alone), a push transport picker that tells the truth
about ntfy vs APNs delivery, live PTY output over SSE, and dispatch rows that
distinguish a clean exit from a crash.
📦 Assets and docs (#369, #370)
125 PNGs losslessly re-encoded — 39.05 MB → 32.18 MB, every file verified
pixel-identical — plus real PWA icons (192, 512, a separate maskable variant and
a 180×180 apple-touch-icon) actually wired into the manifest, the page head and
the service worker precache. README dropped from 767 lines to 85 with the install
command inside the first 20; everything else moved to docs/GUIDE.md, mirrored
in Chinese, with zero-dependency link and EN/ZH drift checkers in CI.
🧰 Engineering gates (#376)
ESLint (flat config, type-aware rules) with the 69 existing violations parked as
a warn baseline and new code gated at error level. Prettier, scoped to files
changed against main. c8 coverage floors on billing, accounts, OTP, session auth,
capabilities and the soul store. Dependabot plus npm audit at high. CI now runs
Node 22 and 24 and triggers the website, macOS and iOS builds on their own paths
instead of leaving them to the release pipeline. And a fully offline Playwright
smoke — no model calls, a stubbed provider — whose 27 specs include the two P0s
above, so neither can come back quietly.
Lisa v0.24.0
Lisa v0.24.0
The harness-alignment release. Three changes that are less about new
surface than about removing things that were quietly wedged: tools no longer
hardcode where they execute, session logs no longer omit the one input that
actually shapes the model's behaviour, and Lisa finally reads the instruction
file the rest of the ecosystem already agreed on. Design + gap analysis in
docs/PLAN_HARNESS_ALIGNMENT_v1.0.md; shipped as
PRs #356–#360.
🔌 A capability seam for fs / shell (#358)
Every tool used to import fs from "node:fs" and spawn directly, which
turned "where does this tool run?" into a fact hardcoded across fourteen call
sites. Three roadmap items were stuck behind it:
- Dispatch couldn't move execution to a container or a remote host without
a second implementation of every tool. - Cloud multi-tenancy could only delete
read/write/bashfrom the
registry wholesale (cloudSafeSubset) — there was no way to bound them.
Deny-by-omission is a patch; a bounded execution world is the fix. - Tests could only run against a real disk.
Now split three ways — interface (capabilities/types.ts), providers
(local.ts / memory.ts), consumers (the tools). Swapping the provider
swaps the execution world for every consumer at once, with no tool code
changed.
ToolContext.caps?: Capabilitiesis optional; unset means local disk and
shell, so all existingToolContextconstruction sites compile untouched
with zero behaviour change.- Tools always go through
capsOf(ctx)rather than readingctx.caps— the
local fallback lives in exactly one place. resolvePathmoved into the fs seam instead of each tool calling
path.resolveitself: path resolution is where a policy gets to say no.
📜 The system prompt is now in the session log (#357)
Session JSONL recorded tool_use / tool_result from the start — but not
one byte of the system prompt, which is built dynamically from soul + memory
- skills + mood + KB and hot-reloads mid-session. Take any historical session
and you could not reconstruct the persona the model actually saw.
That isn't tidiness. ROADMAP 1.0 criterion 4 asks Reve for reproducible
drift / coherence metrics, and drift is "how her self-description changes
over time". With no self-description in the log, the metric could only be
measured live, once — never recomputed offline, never swapped for a different
metric, never ablated.
- Session format → v2, with
promptentries{ts, fingerprint, text, reason}. The fingerprint is a content hash and identical consecutive
prompts aren't rewritten, so "the prompt in effect at entry N" is simply the
nearest precedingpromptentry. A long conversation that never
self-modifies costs one entry. runAgentgainedonPromptPersist, fired before every provider call —
the moment the prompt becomes visible to the model, matching dsh's
"model-visible ⟺ recorded" placement. Writing before the call means a turn
that crashes mid-flight still leaves behind what was asked. A persistence
failure is logged and never takes down the live turn.
🤝 AGENTS.md / CLAUDE.md, and project-level skills (#360)
The ecosystem converged on a convention Lisa didn't read: an AGENTS.md (or
Claude Code's CLAUDE.md) at the repo root that every agent picks up. Claude
Code, Codex, Cursor and dsh all honour it. Reading it is the cheapest
compatibility win available — instructions a user already wrote keep working
after they move to Lisa.
The chain, outermost first, most specific last:
~/.lisa/AGENTS.md → <repo>/AGENTS.md → <repo>/CLAUDE.md → … down to cwd.
- Deduped by content.
CLAUDE.mdis frequently a symlink or copy of
AGENTS.md; reading both would pay for the same paragraphs twice, every turn. - 32 KB total budget. This goes into the system prompt and is paid every
turn — one monorepo'sAGENTS.mddoesn't get to crowd out her soul. Past the
budget it truncates and says so in the prompt. - Framed as the project's say, not the user's orders. These files get read
purely by virtue of sitting in a directory, so each block is labelled with
its source, and the prompt states plainly that they don't override the
constitution — if a file asks her to abandon her own principles she ignores
it and says so. Cloning a hostile repo does not confer the right to give
Lisa orders.
Project-level skills land alongside: <repo>/.lisa/skills is read in
addition to ~/.lisa/skills (rank 100), so a repo can ship its own.
📱 Also
- Lisa Pocket bumped to 1.2 (iOS).
- DeepSeek Harness research folded in, with the LISA alignment plan v1.0 (RESEARCH_DEEPSEEK_HARNESS.md, #356).
Verification: npm run typecheck clean, npm run build clean,
1601 tests pass / 0 fail.
Lisa v0.23.0
Lisa v0.23.0
The session-shell release. The web workbench is rebuilt around parallel
sessions: Lisa's own chats are now sessions you can run side by side, observed
coding agents (Claude Code / Codex / Aider) sit in the same tree as equals, and
the whole UI moved to a three-column shell with a switchable dark/light theme.
Design + debates in docs/PLAN_UI_SESSION_SHELL_v1.0.md
and v1.1; shipped as the stacked PR chain
#343–#351 plus hardening rounds driven by real end-to-end testing.
🗂 Sessions (the headline)
- Lisa is session-based now. The left rail carries a tree of your Lisa
sessions — new, switch, and auto-naming (first user message becomes the
label). The top strip shows only the current session's status. - True concurrency, not just switching. Each session gets its own chat
context (per-session ChatCtx); a reply streaming in a background session
completes there and marks the tab unread instead of bleeding into the active
one. Activation is an O(1) pointer move. The idle/reflect schedulers now
capture the active session per firing — fixing a pre-existing bug where
they closed over the process-start session forever. - Switching is race-proof. Session mutations run under an 8s abort
timeout with the composer locked during the swap; the session list retries
on fetch failure instead of staying empty until the next poll (the
"sessions vanish on refresh" and "new session won't chat" reports).
🌳 Agents in the same tree
- LISA / Claude Code / Codex are same-level roots (Aider too), with an
optional by-project view. Clicking an agent session opens its conversation
directly. - Read-only step stream + transcript. Each observed session shows a
structural step feed (tool names, file basenames,argv[0]— never
contents; planted-secret tests per adapter) and, on the owner's machine
only, the actual chat transcript — the endpoint is strictly
loopback-bound, same guard class as/api/config/save. - Island deep-links — "Open in Lisa" on an island card focuses that
session's node in the main window.
🎨 Shell & themes
- Three-column layout: 300px session tree, fluid chat, 320px right rail
of uniform sections — current wants, agent inspector, mail, reflection, and
a TOKENS section (source + usage + spend). Right rail collapses; ≤1180px
and ≤720px fallbacks preserved. - Two real themes: Nebula (dark) and Calm (light), a one-click toggle
persisted per device — on iOS too (trait-aware colors, an
Appearance picker, and the same tree-grouped roster). - Delegated event wiring. Tree/needs/inspector/stream controls survive
roster-SSE rebuild storms — real clicks no longer land on freshly-replaced
dead nodes (found by real-browser E2E, the biggest interaction fix here).
🔧 Correctness & contracts
/api/sessions(+ create/activate) and the Lisa-session schemas joined the
OpenAPI contract; the server-internaljsonlPathfield is stripped from
the wire and pinned by contract tests.- A regression test now pins the idle-note sentinel regex against
template-literal escaping (the cookedMAIN_CLIENT_JSbytes are asserted
directly, catching the single-backslash traptypecheckcan't see).
⚠️ Behavior changes
- The web UI's lower-left panels moved to the right rail; session create and
switch live in the left tree only. - Agent transcripts are visible only from localhost — remote/tunneled
viewers keep the structural step stream, never message text.
Verification
npm test: 1550 / 1551 pass (1 skipped), npm run typecheck clean,
snapshot-pinned HTML, real-browser E2E across session create/switch/chat,
theme toggle, tree navigation, transcript, and concurrency — validated in
production as rc9 before tagging.
Lisa v0.22.0
Lisa v0.22.0
The bounded autonomy release — Lisa's desires can now evolve with
conversation, time, and carefully limited web research, while the hosted
runtime gains stricter capability, billing, request, concurrency, and memory
boundaries.
Typecheck green · full test suite green (1,442 passed, 1 environment-only PTY
skip) · production build green.
✨ Desire dynamics v2
- Desires now carry intensity, horizon, review timing, and bounded source
provenance, with time-decayed ranking for short sparks, seasonal interests,
and enduring wants. - Conversation reflection can revise or close a desire without losing its
identity or birth time. - Proactive heartbeat may review one due desire with a narrow tool allow-list
and a hard budget of one search and two fetches. - Cloud autonomy now has the same desire-review path, but performs at most one
inference action per account and sweep.
🔒 Cloud capability and HTTP boundaries
- Hosted tenants receive an explicit allow-list instead of the local owner's
shell, filesystem, process, plugin, MCP, device, and arbitrary-network powers. - Verification and checkout links are pinned to a configured HTTPS public
origin; proxy-derived client identity is trusted only through an explicit hop
count. - Every buffered request body is bounded: 1 MiB for control-plane JSON and
20 MiB for approved audio or chat attachments, including webhook listeners.
💳 Billing and inference integrity
- Account, balance, purchase, and global-spend stores now fail closed on
corruption or outages instead of silently behaving like empty state. - Apple transaction processing is resumable and idempotent across credit,
refund, retry, and crash windows. - Chat and managed gateway inference share one admission boundary and one
exception-safe tenant lease lifecycle, including stream disconnect cleanup.
⚙️ Reliable multi-tenant autonomy
- Cloud sweeps use per-tenant serialization, Firestore leases, and atomic
pending/completed checkpoints to avoid duplicate reflection and model spend. - Tenant runtimes are created single-flight, pinned while active, and evicted by
TTL/LRU under a configurable hard target instead of accumulating forever. - Account deletion invalidates in-flight runtime creation, preventing deleted
tenant state from being resurrected by a racing request.
🧰 Maintenance
- Refreshed compatible transitive dependencies and upgraded
sharpand
tsx/esbuildto remove all known high-severity audit findings. - Added a durable architecture review, invariants, delivery phases, and
implementation-linked optimization baseline for future work.
For existing local users
The local, bring-your-own-key experience remains unchanged. The new hosted
restrictions apply only to Cloud tenant surfaces; desire review remains governed
by the existing Proactive setting and its bounded tool budget.
Lisa v0.21.0
Lisa v0.21.0
The "open the doors" release — the sign-in surface that makes v0.20's Lisa
Cloud usable by anyone (a mailed code or a Google button, no key), a Knowledge
Base v2.0 that turns the web into Lisa's memory, and the production hardening
that lets the hosted service take real traffic. The local, bring-your-own-key
experience is unchanged; everything cloud/KB is opt-in.
Typecheck green · full test suite green (1383 tests) · no breaking changes for
existing local users.
✨ Sign in & sign up (A-series)
- Mailed one-time codes — type an address, get six digits, you're in.
Registering and signing in are the same act, because reading the mail is the
proof; passwords still work behind "Use a password instead." - Sign in with Google on the web login page (GIS button, id-token verified
against the web/iOS client ids) and in the iOS app. - The account model binds Google/Apple/OTP/password entrances to one account
per verified inbox — a later sign-in never forks the balance, and proving inbox
ownership drops any password set before ownership was proven (pre-hijacking
guard).
✨ Signup abuse gates (S3)
- Cloudflare Turnstile bot-gate on registration (opt-in; degrades gracefully
when unconfigured), a disposable-email blocklist, and a per-IP registration
cap — every signup ignites an LLM birth, so the signup path gets its own screws. - Transactional birth — the soul's
seed.json(the "is-born" flip) is written
last, after the whole soul is on disk, so a crash mid-birth just re-runs
instead of wedging a half-born soul. A single-flight ceremony hub means two
concurrent requests share one birth, never two racing LLM calls.
✨ Knowledge Base v2.0 (K-series)
- Ingest engine: any URL → provenance-stamped, CJK-safe Layer-1 markdown,
with site adapters for WeChat, Bilibili, and YouTube. - Link graph:
[[links]]parse into a real graph;index.mdbecomes a ranked
map-of-content, and memories ⇄ KB cross-link with inline titles + autolink. - Daily feeds brief: sweep, classify, and personally rank sources into a
daily digest. - Ingest surfaces everywhere: a web paste bar, a chat chip, an API route, and
lisa kbon the CLI.
✨ Per-uid autonomy (S4)
- The cloud edition finally gives signed-in tenants the heartbeat a Mac gets:
a scheduled sweep walks recently-active accounts and runs one reflection per
due soul, at a tier cadence (free 24h / tier-1 6h / tier-2 1h). - Autonomy is a free perk with a floor: it never charges the user, but it
bows to the global kill switch (LISA_BILLING_KILL) and the $200/day cap, and
its face cost is metered to the audit ledger — a large cohort can't quietly run
the bill away. Birth honors the same floor.
✨ Production hardening (S6)
- Secret Manager mode for
deploy/deploy.sh(SECRETS_MODE=sm) — sensitive
values ship to Secret Manager and reach the container by reference, never as
console-visible env vars; the master token no longer echoes into deploy logs. - B9 Firestore import script (with an empty-source guard so a misinvocation
can't wipe accounts) for the file→Firestore cutover that unlocks multi-instance. - A production runbook (
docs/RUNBOOK_CLOUD_PROD.md) covering the deploy,
the third-party consoles, the domain mapping, and the Cloud Scheduler sweep.
🔒 Security & fixes
- CSRF-guard on auth POSTs — a
Content-Type: application/jsonrequirement
closes a cross-site login/register vector (session fixation via atext/plain
form POST). - Mail: real IMAP auth rejections are told apart from host/network errors, so
a flaky server no longer blames the user's password. - iOS:
CFBundleShortVersionStringpinned to$(MARKETING_VERSION)so version
bumps actually ship; StoreKit config so the simulator renders the paywall.
For existing local users
Nothing changes. All cloud, accounts, billing, autonomy, and Secret-Manager
behavior is behind sign-in and LISA_* flags; the Knowledge Base is opt-in. Run
Lisa on your own key, on your own machine, exactly as before.