Memory system — recall redesign (Complementary Learning Systems model).
- Capture is split by "wake/sleep". A per-session, every-N-turns
subagent does encode only (recent exchange → episodic). A new
built-in, visible
dreammission owns consolidate + evict globally on a daily cron plus a turn-seam catch-up (covers a machine that was off overnight). The dream mission is per-run stoppable and deletable as a supported opt-out (degrades automatic curation, never loses data); each run leaves an audit record. - Write-time quality bar — the encoder writes only what a future task benefits from (episodic is recall-visible immediately), instead of liberal capture.
- Write routing by salience — explicit "remember…"/standing
instructions go to the durable store immediately via
Memory_write; incidental signal goes through episodic→consolidate. - Reconcile contract — memory is reconciled against existing memory
on any reactivation (write, recall, dream). Near-duplicates are
deduped mechanically; a genuine contradiction is resolved with the
user (asked, with dates) by appending the corrected row, never
silently overwritten or merged into storage; the no-user dream run
defers contradictions to a later recall. The store stays CRUD-only —
reconciliation is append + read-time + explicit user delete (no
structural "replaces" link). Live-agent guidance ships in the memory
nudge; the full contract is in
doc/memory-spec.md§2.
agent.dream_catchup_hours(default 24) — catch-up threshold for the built-indreamconsolidation mission.
Proxy rooms, user isolation, mission as first-class subsystem, memory system redesign, and LAN access fixes.
Proxy rooms (Phase 6a + 6b) — share AI model access with others through invite-only or public rooms.
- Private/public rooms with shared models, allowed tools, and allowed skills configured in
~/.linggen/room_config.toml. - Two consumer modes — browser-based (linggen.dev/app) and linggen-server (outbound WebRTC client connects via relay signaling).
- Proxy provider —
ProviderClient::Proxyroutes inference over the WebRTC inference data channel and streamsStreamChunks back to the consumer. - Inference data channel — separated from the control channel;
list_models+inferencehandlers run there, filtered byshared_models. - Settings → Sharing tab — room management, shared model checkboxes, allowed tools/skills, member list.
- Persistent token budget —
~/.linggen/token_usage.jsonstore with room-level (token_budget_room_daily) and per-consumer (token_budget_consumer_daily) daily limits. Auto-resets at midnight UTC, flushes every 30s, survives reconnects. - Token usage UI — Room tab reads usage and budgets from local store; bar updates in real time.
- Auto-refresh model list on proxy connect/disconnect via
StateUpdatedevent. - Room chat — bidirectional chat over the inference channel with
sender_idecho prevention. User profile (user_id,user_name,avatar_url) backfilled from relay on startup; falls back toinstance_namewhenuser_nameis missing. - Disconnect on disable — toggling a room off broadcasts
RoomDisabled, kicks consumer peers, syncs status to the linggen.dev DB. UI shows amber "Disabled" badge and hides Connect. - Model selector labels show the room name —
proxy:gpt-5.4 (My Room)instead of generic owner attribution.
User isolation
UserContextreplacesConsumerContext— every peer carriesuser_id+ permission level.room_nameflows through signaling →UserContext→ page_state.- Unified
page_statefiltered per-user (sessions, models, skills, busy_sessions) — no more separate consumer page state. SessionMeta.user_idtracks ownership;ChatRequestacceptsuser_id, injected bypeer.rs.ConsumerFilterdrops events for other users' sessions on outbound delivery.user_idpersisted inremote.toml(returned by linggen.dev registration).- Hidden messages —
[HIDDEN]content is filtered from all session-state APIs (workspace, skill, missions) before sanitization, and fromChatPanelrendering. HTML comments (<!-- ... -->) are stripped from rendered markdown. Newsend_hiddenaction in the skill bridge for system-level prompts.
Mission as first-class subsystem — missions are no longer a "mission skill"; they are a sibling subsystem with skill-shaped markdown.
- Frontmatter matches
SKILL.md— nestedpermission { mode, paths, warning },allowed-tools,allow-skills,requires, optionalentryscript. - Three modes —
agent(default; create session + run agent loop),app(openentryURL in browser),script(runentryas shell command). App/script modes skip the agent loop entirely. - Entry script — runs before the agent loop, captures stdout/stderr to a per-run output dir, passes
MISSION_*env vars.~in cwd is expanded before spawning, fixing the ENOENT that blocked Bash calls. - Tool scope —
allowed-tools+allow-skillsdrivemission_allowed_toolsandconsumer_allowed_skills.*means any skill; an emptyallow-skillsremovesSkillfrom the allowlist; a concrete list gates theSkilltool even whenallowed-toolsis empty. - Mission body injected into the system prompt via
active_mission(mirrorsactive_skill), not as a 3 KB user message. The user turn is a short kickoff.get_system_prompt_apirestoresactive_missionso copy-prompt returns the real prompt for mission sessions. - In-memory mission cache — loaded once at startup, refreshed on create/update/delete and after skill install. Scheduler reads from cache (zero disk I/O per tick).
MissionEditorrewritten — policy (4-way), permission mode + paths + warning, allowed-tools, allow-skills, requires, entry, description.MissionNavsimplified; mission sessions live in the main session list under the Mission tab.- API —
POST/PUT /api/missionsaccepts new fields and legacy aliases (rewritten on next save).GET /api/missions/{id}/runs/{run_id}/outputreturns entry-stage stdout/stderr. - Bundled "dream" mission rewritten in the new format with a real entry script.
Memory system redesign
- Skill
installfield — runs a script on install, wired into all install paths (marketplace, built-in, init). ReplacesSkillMission/create_mission_for_skill— missions are now asset files copied by install scripts, not engine-managed. - Memory frontmatter is fixed — templates are the source of truth; the model only edits the body.
Editadded to the memory tool allowlist (alongsideRead/Write).memory_descriptions_block_emptybootstrap template for first-run when no memory files exist. Oldmemory_block/global_memory_blocktemplates removed.$SKILL_DIRnow resolves correctly in skill content injection (prompt.rs + delegation.rs).
UI architecture
- App entry restructured —
App.tsx/main.tsxreplaced byapps/{Main,Consumer,Embed}App.tsxandentries/main.tsx, one bundle per surface. - Event dispatcher split — per-kind handlers under
eventHandlers/, table-driven dispatcher. CanonicalEVENT_KINDSlist;UiEvent.kindis now an exhaustive union. - Store refactor —
projectStore→sessionStore,agentStore→serverStore; newuserStoreandinteractionStore. - Session list reordered to User / Mission / Skill / All with per-tab counts; defaults to User. Trigger now sends a proper JSON body.
- Chat actions — Copy Chat / Copy System Prompt / Clear Chat moved out of
HeaderBarinto the expandable session bar insideChatPanel, so every chat surface (main, skill, embed, consumer) has them. - System prompt export — frontend sends
session_idso skill-bound sessions export the activeSKILL.mdbody; backend falls back to session cwd whenproject_rootis empty; native tool schemas included. - Skills sorted by usage — localStorage click count + last-used timestamp.
HeaderBarshows the room name (clickable → Settings → Sharing). Leave button navigates to linggen.dev/app.- Subagent return messages render as their own chat bubbles in live-stream sessions, matching the persisted
messages.jsonlreplay path.
LAN / WebRTC
- WebRTC binds to
0.0.0.0and advertises the real LAN IP for ICE — fixes failures when the browser connects via LAN IP instead of localhost. --hostpassed to daemon — was ignored by the daemon child process before. Startup message now shows the actual host.- Auth login uses the real host — was hardcoded to localhost, blocking login from LAN IPs.
- Skip browser open on headless Linux — when neither
DISPLAYnorWAYLAND_DISPLAYis set (SSH sessions). - ChatGPT OAuth tokens read from disk per-request — login/refresh takes effect immediately without restart.
ModelManagerrebuilt after OAuth completes.
- Tool restriction model — single source of truth via
EngineConfig.effective_tool_restrictions()(cascading mission ∩ consumer intersections);EngineConfig.is_tool_allowed()is the unified check used by both prompt building and the execution gate. Two separate gates (mission + consumer) collapsed into one intool_exec.rs.consumer_allowed_toolsnormalized toHashSet. ModelInfo.provided_byfield added so the UI can attribute proxy models.- Increased proxy answer poll timeout — 30s → 60s to tolerate slow relay delivery before the owner picks up the offer.
- Filter messages input dropped — the browser's Cmd+F is sufficient.
AgentTreecomponent (dead code)./api/projectsendpoints andprojectsfrompage_state.MissionPagederives working folders from sessions; project-related store methods deleted.ProjectInfotype and related project store methods.- The "mission" skill — missions depend on skills via
requires:, not on a gated skill. Scheduler and API no longer check for it.
- Permission mode UI not updating after approval —
page_statehandler referenced an undefined_permissionSuppressedUntil, soDate.now() >= undefinedalways evaluated false and blocked all updates. The UI showed stale "read" mode even after the user approved a switch to "edit". - Auto-cleanup proxy connection on disconnect — consumer auto-removes the stale connection, Room tab shows "Connect" instead of stuck "Connected".
- Room chat panel hidden when the owner had room enabled but no
user_nameyet. list_modelsrace — wait for the inference channel to open before sending; double-clickable Connect button now shows a spinner and is guarded.- Skills installed flag stale — Library card kept showing "Install" for freshly-installed built-in skills until the 10-min cache TTL expired. Cache only the GitHub-derived metadata (dir_name/name/description); recompute the
installedflag from the filesystem on every call. - File watcher removed — crashed on permission-denied paths; wasn't used.
Server-pushed PageState, TUI removal, auto-scroll rework, and UX polish.
- Server-pushed PageState — server aggregates projects, sessions, models, skills, agents, missions, and permissions into a single message pushed over the WebRTC control channel at 0.5 Hz with a dirty-flag mechanism. Replaces the HTTP polling storm that fired on every agent run.
set_view_contextmessage — frontend tells the server which session/project is active, scoping PageState pushes to relevant data only.busy_sessionsin PageState — remote clients now see session busy status without needing per-session event channels.- Dismiss button on queued messages banner — manually clear the queue when it gets stuck (e.g.
QueueUpdatedevent missed). - Markdown links open in new tab —
target="_blank"on rendered links so clicking doesn't navigate away and text selection is easier.
- Auto-scroll rework — replaced distance-threshold detection with scroll-direction detection. Added
distanceFromBottom > 150guard so layout reflows during streaming no longer falsely detach auto-scroll. Consolidated duplicate scroll tracking from ChatPanel into the singleuseAutoScrollhook. - Removed HTTP polling — initial load fetches for projects, sessions, models, skills, agents, and config are all replaced by PageState delivery on WebRTC connect. Only Ollama status and session tokens remain as HTTP fetches.
- Removed 5 dead API endpoints —
agent-children,agent-context,missions/:id GET,missions/:id/sessions,builtin-skills/install-all. - SessionModeSelector simplified — reads mode and zone from store (pushed by PageState) instead of fetching
/api/sessions/permissionon every render. - Extracted non-reactive agent tracking —
agentTracker.tssingleton replaces 15+ direct Zustand store mutations ineventDispatcher. - Memoized skill suggestions in ChatInput (was rebuilt twice per render).
- Tokens/sec display wired up —
recordTokenSample+recomputeTokenRatewere never called; now functional.
- TUI — terminal UI (ratatui) and
--tuiflag removed. Linggen is now Web UI only.lingstarts the daemon and opens the browser;ling --webruns the server in foreground. - SSE transport — server-sent events transport removed. All real-time communication uses WebRTC data channels.
- Auto-scroll fighting — removed duplicate scroll tracker in ChatPanel that competed with
useAutoScrollhook. - Session mode selector race — after user switches mode (e.g. admin → read), a 3-second suppress window prevents the next PageState push from overwriting the optimistic UI update.
- Page flash during streaming —
floatingUserMsgeffect was re-subscribing on every token. - Duplicate React key in SubagentTreeView.
- Subagent state leak —
agentTracker.reset()called on session switch. - Plan message overwrite —
mutateLastguard prevents fast-path from overwriting plan messages with streaming tokens. - Skill session chat in remote mode — skill app chat iframe now routes through the relay connect page when accessed via linggen.dev (was loading the landing page instead of the compact chat).
- Skill session restore — reopening an existing skill session with no localStorage cache auto-triggers a fresh scan instead of showing an empty dashboard.
Simplified run system, daemon mode, ChatGPT default model, and bug fixes.
- Background daemon mode — bare
lingnow spawns a background daemon and opens the Web UI in the browser. Terminal returns immediately. Useling --tuifor classic TUI mode. - ChatGPT OAuth default — new installs default to GPT-5.4 via ChatGPT subscription. No API key or local model download needed.
- Unified working folder — all tools (Read, Write, Edit, Glob, Grep) resolve relative paths from the agent's cwd, not just Bash. When the agent
cds into a git repo, the workspace root, CLAUDE.md, and permissions update automatically. - User
! cdtracking —! cd /pathin the Web UI now persists cwd per session, same as agent Bash commands. - UI follows cwd changes —
selectedProjectRootupdates when the agent changes working folder.
- In-memory run store — agent run records are no longer persisted to
{run_id}.jsonfiles on disk. Runs are tracked in memory only (for cancellation and status during execution). - Removed run history UI — run picker dropdowns, context display, timeline, pin/unpin removed from ChatPanel and SubagentDrawer.
- Removed dead code —
AgentsCard.tsx(never imported),timeline.ts, run context types (AgentRunSummary,AgentRunContextResponse, etc.). - Simplified cancel response —
POST /api/agent-cancelreturns{ status: "ok" }instead of{ cancelled_run_ids: [...] }. - Font size +1px — all UI font sizes bumped by 1px for mobile readability.
- Logo — shortened to "Linggen", links to linggen.dev.
- install.sh — removed
--with-memoryflag and ling-mem install block.
- Plan reject buttons not disappearing —
PlanUpdateevents now carrysession_id, so they're delivered via WebRTC data channels (wasNone, events were lost). - Queued messages stuck after cancel —
cancel_agent_run()now drains the queue for cancelled agents. - Queued messages showing in chat — queued messages are no longer persisted to
messages.jsonlat queue time. They're persisted when dequeued, preventing the sync-back from re-adding them. - Session header not showing for user sessions —
fetchSessionswas resettingactiveSessionIdwhen the session wasn't in the project-filtered list. Now checksallSessionsbefore resetting. - Working folder in non-git dirs —
check_working_folder_change()now uses the cwd as workspace root when no git repo is found (was falling back to~). - macOS
/tmpsymlink — cwd is canonicalized before use as workspace root (resolves/tmp→/private/tmp).
Working folder model, per-session engines, WebRTC-first transport, and UX improvements.
- Working folder model — sessions start in HOME mode and auto-detect projects when the agent
cds into a git repo. CLAUDE.md, permissions, and git context load dynamically on project entry. Configurablehome_pathin settings. - Per-session agent engines — each session gets its own engine instance. No more lock contention between sessions — game-table and regular chat run truly in parallel.
- WebRTC-first transport — Web UI always uses WebRTC (local and remote). Per-session data channels provide natural isolation. SSE retained for TUI only.
- WebRTC session_id enrichment — events are tagged with session_id before routing to data channels, preventing cross-session event leaks.
- ChatGPT token expiry UX — inline re-login button when ChatGPT OAuth expires. After re-login, session engines are cleared so the fresh token is used immediately.
- Working folder changed event —
WorkingFolderChangedserver event emitted when the agentcds. UI header updates reactively. home_pathconfig — configurable default working folder for new sessions (defaults to~).- Git root detection —
find_git_root()walks up from cwd looking for.git/. Skips home directory dotfiles repos.
- Flat session storage — all sessions stored in
~/.linggen/sessions/(flat directory). No more per-project/mission/skill session directories. Session metadata trackscwd,project,project_name,mission_id. - Simplified chat creation — clicking
+immediately creates a session. Removed project picker dialog. - Removed project management UI — no more workspace section, project cards, or manual project add/remove in sidebar. Projects are auto-discovered from git repos.
- Skill search ordering — community skills from skills.sh and ClawHub are interleaved by relevance instead of sorted by install count.
- ClawHub ZIP install — handles root-level SKILL.md (no subdirectory) in ClawHub ZIP archives.
- Ollama status polling — only polls when Ollama models are configured, eliminating 404 spam.
- Auto-scroll — any upward scroll stops auto-scroll (was 10% threshold). Resumes within 20px of bottom.
- IME composition — Enter key during Chinese/Japanese input composition no longer triggers send.
- Models card scroll — auto-scrolls to default (starred) model when the model list loads.
- Session list — session rows use
<div>instead of nested<button>(fixes React DOM nesting warning). - Skill reload — installing/uninstalling skills clears session engines so new skills are available on next message.
- install.sh — post-install output now shows
ling initas the first step.
- Session isolation — WebRTC events no longer leak between sessions. Added session_id enrichment in WebRTC peer handler (was missing, only SSE had it).
emit_outcome_event— plan/outcome events now carry session_id (was hardcodedNone).- Compact mode race — skill app iframe now explicitly fetches workspace state after setting
isSkillSession, preventing stale API calls. - Session engine memory leak —
remove_session_enginecalled on all session deletion paths. - TUI session creation —
get_session_metacheck usesOk(Some(_))instead ofis_ok()(was always true). UiEvent.kindtype — added'working_folder'to TypeScript union type.
~/.linggen/projects/session directories — sessions no longer stored per-project.session_rootonEngineConfig— removed; all persistence goes through global sessions.ProjectContext.sessions— removed; all session access throughAgentManager.global_sessions.ProjectStore::session_store()— removed dead code.missions_sessions_dir()/skill_sessions_dir()— removed frompaths.rs.NewChatDialogcomponent — removed project picker dialog from UI.
Remote access, mobile UI, Google login, and infrastructure improvements.
- Remote access — access your linggen from any device. Run
ling loginto link to your linggen.dev account, then connect from any browser atlinggen.dev/app. Peer-to-peer connection — no VPN or port forwarding needed. ling login/ling logout/ling status— CLI commands for managing remote access. Fully automatic browser-based OAuth flow with token exchange; no manual steps needed.ling auth login— ChatGPT subscription auth. Auto-detects headless/SSH environments and falls back to device code flow (removed--deviceflag).- Google login — sign in to linggen.dev with Google or GitHub. Email-based account matching across providers.
- Signaling relay — lightweight relay on linggen.dev handles connection setup. Nonce-based offer/answer exchange via stateless HTTP.
- Mobile UI — responsive layout auto-detected on narrow viewports (or via
?mode=mobile). Full-bleed chat, larger touch targets, iOS safe area support. Right-side drawer for models and skills. - Gzip chunked transfer — large responses (skill files, API data) are gzip-compressed and sent as base64 chunks over data channels. Handles SCTP backpressure correctly.
- Skills open in-app — web launcher skills now open in an in-page iframe panel instead of a new browser tab. Works in both local and remote mode.
- Session project names for missions — mission sessions now show their project name in the session header, matching the behavior of user sessions.
ling loginnon-interactive — uses hostname automatically, no instance name prompt.- Heartbeat interval — increased from 30s to 5 minutes to reduce relay load. Online threshold set to 10 minutes.
- Online status via D1 — instance online/offline status is now determined by
updated_attimestamp in D1 database instead of KV TTL keys. Eliminates KV write quota consumption from heartbeats. - JWT sessions — linggen.dev authentication switched from KV-stored sessions to signed JWT cookies (HMAC-SHA256). Eliminates KV reads on every authenticated request.
- Settings page mobile layout — scrollable tab strip, responsive model card grid, reduced padding on small screens.
- Header compact mode — shorter title ("Linggen" on mobile), status dot without text label, sparkles button for info drawer.
- Session delete on mobile — trash button always visible on touch devices (was hover-only).
- InfoPanel component — extracted models + skills cards into shared component used by desktop sidebar and mobile drawer.
- SSRF bypass — URL-decode path before validation in WebRTC HTTP proxy (blocks
%2e%2etraversal). - JWT algorithm validation — verify
alg: HS256in token header before signature check. - Free-tier instance limit — use
COUNT(*)query instead of single-row check (prevents bypass via new instance IDs). - Token panic — guard
api_tokenlength before slicing inling status(no crash on corrupted config). - Double reconnect — guard
handleDisconnectagainst firing multiple times from concurrent ICE/connection state changes. - Double connect — guard
doConnectagainst concurrent calls (prevents RTCPeerConnection leak). - Session channel leak —
unsubscribeSessionnow called on session change inuseTransporthook. - Token lost on write error — browser response write in
ling logincallback no longer discards the received token if the browser closes early. - Relay poll blocking —
handle_remote_offerspawned in separate task so the offer poll loop stays responsive. - Nonce URL encoding — relay signaling nonce is now URL-encoded in poll requests.
- Relay offer missing Content-Type — added
Content-Type: application/sdpto relay offer POST. - Logout CORS headers — logout response now includes CORS headers for cross-origin requests.
- Dead code — removed unused
split_utf8_safefunction. - Stale comments — updated heartbeat interval comments from "30s" to "5 minutes".
Major release with native tool calling, mission system, TUI, permissions, and extensive UI improvements.
- Native tool calling — models use structured function calling (OpenAI, Ollama) instead of JSON-in-text. Default for all providers; falls back gracefully for legacy models.
- TUI interface — full terminal UI via ratatui. Default mode runs TUI + embedded server;
--webfor web-only. - Mission system — agents self-initiate work on cron schedules when a mission is active. Idle scheduler prompts agents between user messages.
- Plan mode — agents can enter plan mode (
EnterPlanMode) for research and structured planning before making changes. Plans require user approval viaExitPlanMode. - File-scoped permissions —
AcceptEditsmode, deny rules, and per-project permission persistence. - Credential storage — secure API key management via
/api/credentialsendpoint. - Model auto-fallback — health tracking with automatic fallback to next model in the routing chain on errors or rate limits.
- AskUser bridge — agents can ask structured questions mid-run with options and multi-select.
- Web search & fetch —
WebSearch(DuckDuckGo) andWebFetchtools for agents. - Skills marketplace — search, install, and manage community skills from the web UI or CLI (
ling skills add/remove/search). ling initcommand — scaffolds~/.linggen/directory tree, installs default agents, creates config, downloads skills.ling authcommand — ChatGPT OAuth authentication (browser and device code flows).- Session-scoped SSE — events are tagged with session ID; clients filter to their own session.
- Per-session working directory —
cdin one session doesn't affect others. - SSE reconnect handling — automatic state resync on reconnect with UI indicator.
- Context window management — adaptive compaction with importance-based message pruning.
- Prompt caching — stable system prompt prefix cached across iterations.
- Config file renamed from
linggen.tomltolinggen.runtime.toml. - Prompt system refactored from hardcoded strings to TOML templates.
- Tool calls render as individual inline widgets (aligned with Claude Code style).
ChatPanel.tsxrefactored into focused modules underchat/folder.tools.rsandapp.rssplit into module directories for maintainability.- Default
supports_toolschanged totrueeven for unrecognized model IDs (prevents fallback to text-based JSON mode).
- SSE session isolation — events no longer leak across sessions.
- Streamed text-only responses no longer disappear after generation.
- Ollama 500 error — use role
"tool"for tool result messages in native mode. - Agent context loss on long conversations.
- Glob pattern matching edge cases.
- Queued message display order (now chronological).
- Think tag stripping for models that emit
<think>blocks.
Initial patch release.
Initial release — multi-agent engine, web UI, skills system, Ollama and OpenAI providers.