One box on a laptop: the personal assistant surface¶
Date: 2026-10-17. Status: research and design, nothing built. Everything about the
platform below was read from this repository on that date (main at 2f25a7d2f, plus the
integration/personal-agent branch at 282f89736); file paths are relative to the repo root.
Everything about other products comes from their public docs and press, listed at the end.
Nothing was measured on real users.
The brief¶
Someone installs VegaDūta on a laptop with the new wizard (installer/) and the personal
tier that the personal-agent branch adds. They do not want to tune agents, pick tools or build
workflows. They want one box, like Claude Desktop, ChatGPT, Hermes or OpenClaw: type what you
want, the assistant works out what kind of request it is, does it, keeps going until it is
finished, asks only when it must, and remembers you next time.
Constraints set for this document:
- No change to existing behaviour. The surface is a new route in the console that calls APIs that already exist. Where a capability exists behind a flag, the personal profile turns the flag on. Nothing is renamed, moved or re-implemented.
- Every claim about the platform points at code. Where something is missing, it is listed as missing, with the workaround that stays inside constraint 1, or marked as backend work for later.
What the others do, and what it teaches¶
| Product | Shape | What makes it feel like "one intelligent piece" |
|---|---|---|
| Claude Desktop / Cowork | Chat, Code and Cowork tabs; Cowork is merging into chat so Claude decides whether a request is a quick answer or a task. Tasks run in the cloud and keep running when the laptop is closed; phone notification when done or when input is needed; folder access only while the app is open; connectors, skills, plugins; computer use and a built-in browser; scheduled tasks. | The decision is hidden: one composer, the system picks answer vs task. Progress, questions and results are visible from any surface. |
| ChatGPT (agent mode, Projects, Tasks) | Agent mode spins up a sandbox with browser, terminal and files, pauses before consequential actions; Projects scope files, instructions and memory; Tasks are scheduled runs; memory draws on past chats, files and connected mail. | Memory that spans sources and a per-project scope; approval before real-world effects. |
| Hermes Agent (Nous) | CLI/TUI, Hermes Desktop, one gateway for 20+ messaging platforms, cron in natural language, subagents, 40+ tools, seven terminal backends. The learning loop: MEMORY.md (2,200 chars) and USER.md (1,375 chars) the agent curates itself, a background review after turns that saves memory and creates or patches skills (SKILL.md), /journey shows what it learned. Approvals: smart mode (auxiliary model rates risk, auto-approves low risk), 300 s timeout then deny, allowlist, headless contexts default to deny, hard blocklist even in yolo. |
The assistant visibly gets better at your work, and safety is a ladder (smart, manual, off) with a floor that cannot be lowered. |
| OpenClaw | A local Gateway (WebSocket, port 18789) owns sessions, routing and channels; Control UI with chat, session rail, agents roster, automations, usage, memory, skills, nodes (phone camera, screen, location); ClawHub skills marketplace. Agent loop: runs serialised per session, lifecycle start / finishing / end / error, queue modes for messages arriving mid-run (steer, followup, collect, interrupt), stuck-session diagnostics after two minutes without progress, 48 h per-attempt timeout. Transcript UX: consecutive tool calls share one expandable log, finished turns collapse under "Worked for …", skipped calls shown as "Skipped", separate Subagents and Processes panels. |
The best progress language: a working indicator distinct from messages, collapsed "Worked for 4 m 12 s, 1 failed call", and a clear answer to "what happens if I type while it is working". |
| Grok | Standalone app, voice with live camera, Imagine, DeepSearch, scheduled automations, companions. | Voice and camera as first-class entry points, not settings. |
| Open WebUI / LibreChat / AnythingLLM / Jan | Self-hosted chat front-ends over Ollama and APIs; RAG, web search, voice, MCP agents (LibreChat), desktop app (Jan, AnythingLLM). None runs an autonomous task to completion or learns skills. | This is the bar a local install is measured against today, and it is a chat bar, not an agent bar. |
Five things to copy, one to avoid:
- One composer, hidden dispatch. The user never chooses "chat" or "task" or "schedule".
- A visible work ledger. Running, waiting for you, done; from the same screen and from the phone (channels) later.
- A learning loop the user can see. Memory and skills the assistant wrote, in a timeline, with approve, edit and delete.
- A safety ladder with a floor. Auto-approve the harmless, ask for the consequential, never allow the destructive, and tell the user which rung they are on.
- Progress language. "Working for 2 m", collapsed tool logs, "Skipped", "needs you".
- Avoid the OpenClaw-style open skill marketplace as a default on a laptop: security researchers reported hundreds of malicious skills on ClawHub in February 2026. The platform's skill security scanning and the pending/approve skill library on the personal-agent branch are the right default.
What the platform already has¶
The surface is a composition of existing endpoints. The full list is docs/api/openapi.json
(1,064 paths; streaming operations carry x-sse: true) and docs/api/asyncapi.json.
| Need | Existing API | Where |
|---|---|---|
| Send a message, stream the answer with tool steps | POST /api/agents/{id}/chat (SSE, stepEvents:true adds tool_call, tool_result, plan, guardrail, handoff, swarm, memory, context_compacted, heartbeat, run_started, run_finished) |
core/agents-turn/.../turn/AgentChatStreamController.java, payloads in core/agents/.../tools/AgentStepEventPayloads.java |
| Same, as a typed protocol | AG-UI: POST /api/agents/{id}/agui/run, resume at GET /runs/{runId}/events |
core/agents-interop/.../agui/AgUiRunController.java |
| Survive a tab close or reload mid-answer | Resumable turns GET /api/chat/turns/{turnId}/stream?after= (flag app.chat.resumable-streams.enabled) |
core/agents-turn/.../resumable/ResumableChatTurnController.java |
| Cancel, rewind, attach files | POST .../chat/sessions/{sid}/cancel, /rewind, POST .../chat/attachments |
AgentChatSessionController.java |
| Read a conversation back | GET /api/agents/{id}/sessions/{sid}/transcript, /export |
core/agents-chatport/.../ChatTranscriptController.java |
| Edit and regenerate | Branching (flag app.chat.branching.enabled), which also persists messages in Postgres instead of the 24 h Redis TTL |
core/agents-turn/.../branching/ChatBranchController.java, application.yml:1218 |
| Pick the right agent for free text, without an extra LLM call | AgentRelevanceSelector (lexical, Jev-upgraded when configured) and ToolRelevanceSelector |
core/agents-turn/.../decision/AgentRelevanceSelector.java |
| "Type it and go" with a preview and a delivery channel | Quick Task: POST /api/quick-task/preview, /stream, /recent, /{id}/cancel |
core/app/.../quicktask/QuickTaskController.java, UI web/app/components/SimpleTaskRunner.tsx |
| Long work that outlives the request | Agent jobs: POST /api/agents/{id}/jobs {goal, timeoutSeconds, maxToolCalls} (202), GET .../progress, .../trace, .../trace/stream (SSE), states QUEUED, RUNNING, AWAITING_APPROVAL, SUCCEEDED, FAILED, CANCELLED; runs in-process, no Temporal needed |
core/agents-jobs/.../job/AgentJobController.java, AgentJobObservabilityController.java |
| Routines | POST /api/scheduled-tasks {agentId, name, prompt, frequency, time, day, cron, delivery, deliveryTo}, /preview (next runs), pause, resume, run-now, runs (flag SCHEDULED_TASKS_ENABLED); also the agent-side tool schedule_task |
core/agents-jobs/.../scheduledtask/ScheduledTaskController.java |
| Deep research with citations | POST /api/research/runs {question, depth, seedUrls, language} (flag app.research.runs.enabled); creates a per-user "Deep Research" agent on demand, a precedent for provisioning a personal agent through existing services |
core/agents-jobs/.../research/ResearchRunService.java |
| Sub-agents and parallel work | tools delegate_to_specialist, delegate_parallel; PATCH /api/agents/{id}/collaborators; swarm and handoff step events |
core/agents-tools |
| Ask the human | tool request_human; approvals GET /api/approvals?status=PENDING, POST /{id}/approve, /reject; the request's requestedBy is agent:<agentId>:session:<sessionId>, so a chat can claim its own approvals |
core/approval/.../ApprovalController.java, core/guardrails/.../GuardedToolCallback.java:566 |
| Tell the user later | GET /api/notifications, /unread-count; NotificationBell polls every 30 s |
web/app/components/NotificationBell.tsx |
| Memory the user owns | GET/PUT /api/memory/blocks/{label}, /facts, /export, /import; per-agent memories; the memory step event says what was used in a turn |
core/agents-turn/.../usermemory/UserMemoryController.java, UI web/app/memory |
| Skills the assistant learns | personal-agent branch: app.skills.learning.enabled, SkillLifecycleController, library UI with Active, Pending, Archived tabs, import dropzone, diff view |
web/app/components/skillLibrary/* (branch) |
| Files and knowledge | POST /api/knowledge/upload, /search, collections; /files page aggregates generated media and documents |
core/knowledge, web/app/files |
| Web, browser, code, documents, Google | tools web_search, fetch_web_page, deep_research, verify_citations, browser_*, web_task, run_code, coding_task, generate_document, google_calendar/gmail/drive/sheets, http_request, data_table_*, channel_send_message |
core/agents-tools, core/agents-browser, core/google |
| Bring your own MCP server | POST /api/mcp/registry {name, type: STDIO or SSE, command, args, env, url}, /catalog, /search, OAuth; STDIO under an OS sandbox on the personal profile |
core/mcp/.../McpRegistryController.java |
| A model with no key | host Ollama through the hosted pool (zero-cost rows), and on the branch the local-model wizard at /providers/local-model with a probe for local OpenAI-compatible servers |
docs/local-models.md, web/app/components/localModel/* (branch) |
| The same assistant on your phone | PUT /api/channels/{telegram, whatsapp, sms, signal, imessage, matrix...}/default-agent; Telegram long-polls on the personal profile, so no public URL is needed |
core/omnichannel, application-personal.yml (branch) |
| Sign in once on a laptop | personal-agent branch: app.auth.mode=local, LocalAuthController, LocalLogin.tsx; API keys vdak_ as personal tokens |
core/admin/.../localauth (branch), core/security/.../apikey |
| Guardrails that fail closed, approvals that cannot double-run | APP_GUARDRAILS_FAIL_MODE, TOOL_UNCLASSIFIED_ACTION, row-locked approval decisions, tool fingerprint on replay |
CHANGELOG.md Unreleased |
| Console pieces to reuse | api.streamChat and pumpChatSse, AgentActivityTrace, ToolCallCard, PendingToolApprovals, RichText, MediaCard, GenerativeUiRenderer, ArtifactPane, SourcesPanel, PlanStream, chatStreamMachine, useTranscript, useRequestLifecycle, ChatAttachments, AppShell fillViewport, CommandPalette, ThemeProvider |
web/app/lib/api/chat.ts, chatSse.ts, web/app/components/{agents,chat,research,brief,artifacts} |
Nothing comparable in the open-source field ships channels, approvals, guardrails, workflows, knowledge, memory, skills and on-device models in one process. What is missing is the one screen that makes a laptop user feel all of that as a single assistant.
Gaps, and how each is handled without touching the backend¶
| # | Gap | Evidence | Handling |
|---|---|---|---|
| G1 | ~~No endpoint lists a user's conversations~~ Done: GET /api/chat/threads lists the signed-in user's sessions (title = first user line, agent, last activity; keyset paging on updatedAt) and DELETE /api/chat/threads/{agentId}/{sessionId} removes one with its live history, both over the branching tables (ChatThreadController, 404 while APP_CHAT_BRANCHING_ENABLED is off). The browser index is now a cache of that list plus local-only state (rename, pin, sessions of a core that stores nothing); the Assistant reads the server tree after each turn so its sessions are recorded, and the transcript route falls back to the stored tree after the 24 h Redis window. |
no GET .../sessions in any chat controller; application.yml:1090 |
Client-side conversation index in IndexedDB (same layer as lib/api/persistentCache.ts and the playground's persistence.ts), one row per session: id, title, agent, last message, pinned. Transcript re-fetched from /sessions/{sid}/transcript when opened. The personal profile sets APP_CHAT_BRANCHING_ENABLED=true so messages persist in Postgres and survive longer than 24 h. Titles: first user line, truncated; optional rename stored locally. Export via /export. A server-side list is the first backend follow-up (one controller over the branching tables). |
| G2 | No agent-agnostic chat entry. Every turn needs an agentId. |
AgentChatStreamController |
One personal Assistant agent per user, created on first run through POST /api/agents (or POST /api/agents/generate from a one-paragraph description), marked with a name and a memory block so it can be found again, like Deep Research does with its capability row. Quick Task's auto-selection stays available as "ask a specialist" when the tenant has more agents. |
| G3 | Approvals are not in the stream. The request id reaches the model as text in a tool_result; the decision outcome arrives through notifications. |
GuardedToolCallback.java:581, ApprovalController |
While a turn or job is live, poll GET /api/approvals?status=PENDING every 3 s (what PendingToolApprovals already does) and claim rows whose requestedBy names this session. Render as an inline card; approve and reject call the existing endpoints. After the decision the agent job resumes on its own (AWAITING_APPROVAL to RUNNING); for a chat turn the platform now starts the continuation turn itself (app.approvals.continuation.enabled, default true: same agent and session, it restates the user's goal and hands the agent the tool's result or the denial), so the surface does not send "continue"; it follows the stored conversation for the agent's reply (useApprovalContinuation). |
| G4 | No thinking or citation events in the chat stream. | AgentStepEventPayloads |
Show plan{text} as the "thinking" line. Citations: use SourcesPanel on research-run results (which do carry sources) and on fetch_web_page and web_search tool results in the trace. |
| G5 | No local shell or local file-system tool. Code runs in E2B or the tool-sandbox; files reach the assistant only as uploads or knowledge documents. | core/agents-tools |
In scope for v1: drag-and-drop to /chat/attachments and /knowledge/upload; run_code when a sandbox is configured. A folder-scoped local file tool (the Cowork model) is backend work and listed under follow-ups. |
| G6 | Job-vs-turn decision is the client's. The chat turn has a per-turn tool-step cap; a job has a timeout and maxToolCalls. |
ToolStepLimitExceededException, AgentJobStartRequest |
The dispatch ladder below. The model also escalates itself: the Assistant agent gets schedule_task, delegate_parallel, deep_research and request_human, so "every Monday" or "research all three" is handled inside the turn without the UI guessing. |
| G7 | Several needed features are off by default: resumable streams, branching, scheduled tasks, research runs, turn journal, artifacts directive. | application.yml |
Branching and scheduled tasks are turned on in the personal profile; resumable streams, research runs and the artifacts directive are not (see the table below); the turn journal is not either: application-personal.yml and deploy/compose/personal.yml leave app.turn-journal.enabled at its code default false, and, once switched on, it records every reasoning depth and delegated turns (G10). |
| G8 | Ollama registration is platform-admin only on main. | /api/platform/hosted-llm-providers |
The personal-agent branch adds the local-model wizard and the local OpenAI-compatible probe; on the personal profile the single user is the admin. |
| G9 | No desktop wrapper, no offline shell. | web/public/manifest.webmanifest only |
The installer already opens the console; it should open /assistant and offer "Install as app" (PWA, standalone, already declared in the manifest). A Tauri shell is a follow-up, not a prerequisite. |
| G10 | The turn-journal turnId was never returned to the client. |
AgentChatStreamResponder.journalTurnHeader, TurnJournalGateway.TURN_ID_HEADER |
Fixed: while the journal is on, the stream response carries X-Journal-Turn-Id and GET /api/agents/{agentId}/turn-journal?sessionId=... lists a session's turns. The journal stays off in the personal profile, so for v1 the "what did it do" view is still the step trace plus GET .../tool-calls and /sessions/{sid}/llm-requests. |
The design¶
One screen¶
Route web/app/assistant/page.tsx, inside AppShell with fillViewport, no new framework,
existing tokens. The rail collapses to icons; on the personal profile this route is the landing
page after sign-in (today / sends members to /build; the personal profile redirects to
/assistant instead, a one-line, profile-gated change in web/app/page.tsx).
┌───────────┬──────────────────────────────────────────────┬──────────────────┐
│ Threads │ Assistant │ Work │
│ (local │ │ ● Running (1) │
│ index) │ user: book me a dentist next week and put │ dentist search │
│ │ it in my calendar │ 2 m 10 s │
│ Today │ │ ◐ Needs you (1) │
│ dentist… │ ▸ Working for 2 m 10 s · 6 tool calls │ send email? │
│ trip… │ web_search "dentists near …" │ ○ Scheduled (2) │
│ Yesterday │ browser_navigate … │ Mon brief 8:00 │
│ … │ google_calendar create ✓ │ ✓ Done today (3) │
│ │ │ │
│ │ assistant: Booked Tue 10:30 at … Added to │ Learned │
│ │ your calendar. Want a reminder the day │ + skill: book │
│ │ before? │ appointments │
│ │ │ + fact: prefers │
│ │ [ Yes, 9:00 ] [ No ] │ mornings │
│ │ │ │
│ + New │ ┌────────────────────────────────────────┐ │ │
│ │ │ Ask or tell me what to do… 📎 🎤 ↵ │ │ │
│ │ └────────────────────────────────────────┘ │ │
└───────────┴──────────────────────────────────────────────┴──────────────────┘
- Threads (left): the IndexedDB index (G1), a cache of
GET /api/chat/threads. Search is local full-text over cached transcripts, the Hermessession_searchidea without a server. - Transcript (centre):
RichText/MediaCardfor answers,GenerativeUiRendererfor model-emitted forms and buttons (the "[Yes, 9:00] [No]" chips are that),ArtifactPanewhen an artifact directive arrives,SourcesPanelfor research. Tool steps collapse under one "Working for …" row per turn, OpenClaw style; failed calls counted in the heading;guardrailevents shown as "Skipped: reason". - Work (right, collapsible): everything that outlives a turn. Running agent jobs with their
trace/stream, approvals that need the user (G3), scheduled tasks with next run from/preview, today's finished jobs and quick tasks. Clicking any item opens its thread. "Learned" lists memory facts and skills written since the user last looked (thememorystep event and the skill library's Pending tab), with approve, edit and forget inline. - Composer: text, paste or drop files (attachments or knowledge, by size and type), microphone
(existing voice turn
POST /api/voice/agents/{id}/turnand edge speech), slash commands that map to existing features:/research,/schedule,/remember,/forget,/agent <name>,/export. Typing while a run is live offers "Steer" (sends into the same session; the server serialises turns per session) or "New thread".
The Assistant agent (first run)¶
On the first visit, if no agent named Assistant with the marker memory block exists for this
user, the surface creates it with calls that exist today:
POST /api/agentswithname: "Assistant",experienceMode: SIMPLE, the provider and model the local-model wizard or BYOK step produced (GET /api/providers/default-model), a system prompt written for autonomous completion (plan briefly, act, verify, report; prefer tools over asking; escalate toschedule_task,delegate_parallel,deep_research; callrequest_humanfor anything consequential; save durable facts withremember_fact).PATCH /tool-accesswith every name fromGET /available-toolsminus payments andhttp_request_write;PATCH /knowledge-base {enabled:true};PATCH /share-memory;PATCH /autonomy-tierto the highest tier the install allows;PATCH /run-budgetfrom the personal defaults.PUT /api/memory/blocks/assistantwith the marker and the user's name, locale and time zone (the Hermes USER.md idea, on the existing block API).- If the user connected Telegram or WhatsApp in the wizard,
PUT /api/channels/<ch>/default-agent.
Everything is reversible from the existing Agents page; the surface never hides that page, it just
does not require it. The 77 templates remain reachable as "specialists": /agent <name> or the
Quick Task auto-selector routes a message to one of them when the user has imported some.
The dispatch ladder (how it "understands what it is")¶
The surface does not run its own intent model. It makes one cheap structural choice, then lets the agent do the understanding with its tools. Rungs, top to bottom:
| Rung | Trigger (client, deterministic) | Call | What the user sees |
|---|---|---|---|
| 0 Slash | message starts with a known slash | the mapped endpoint | the matching card |
| 1 Turn (default) | everything else | POST /api/agents/{assistant}/chat with stepEvents:true, X-Resumable-Stream: 1 |
streaming answer; "Working for …" when tools run |
| 2 Job | the turn ends with a guardrail or error step naming the tool-step cap, or the assistant replies with a structured "this needs more than one sitting" marker (prompted), or the user presses "Keep going in the background" on a running turn |
POST /api/agents/{assistant}/jobs {goal: <thread so far + request>, timeoutSeconds: 3600} then trace/stream |
the thread shows "Continuing in the background", the Work rail shows the job; result posted back into the thread when SUCCEEDED |
| 3 Routine | the assistant called schedule_task (visible as a tool_call), or the user used /schedule, or the preview card's "Make this a routine" |
POST /api/scheduled-tasks with /preview first for the next three runs |
a routine card with next run and delivery (chat, Telegram, WhatsApp, email) |
| 4 Research | /research, or the assistant called deep_research, or the message matches the research-run depth heuristics already in ResearchRunPlan |
POST /api/research/runs |
a report card with sources (SourcesPanel) and "verify citations" |
| 5 Specialist | /agent <name>, or the tenant has more than one agent and POST /api/quick-task/preview (no agentId) names a different agent with a non-zero score |
the chosen agent's /chat |
a chip "Answering as |
The ladder is a pure function (dispatch.ts) with unit tests, the same pattern as
web/app/start/flow.ts and chatStreamMachine.ts. Because rungs 2 to 4 can also be entered by
the model's own tool calls, the UI only has to notice them in the step stream and render the right
card; it never has to be smarter than the agent.
Keeping going until it is done¶
- Within a turn, the server's tool loop already iterates until the model stops or the step cap
is hit;
heartbeatevents keep the connection warm;context_compactedis shown as a quiet "condensed earlier context" line. - Resumable streams mean a reload or a closed lid mid-answer resumes from
GET /api/chat/turns/{turnId}/stream?after=; thelib/resumableStream.tshelper already does this for the playground. - Agent jobs continue when the browser is gone. On return, the Work rail reconciles from
GET /api/agent-jobs?status=liveand posts results into the right thread (the job'ssessionIdis derived from the job id, so the thread index stores job id to thread id). - Approvals park a job in
AWAITING_APPROVAL; the notification bell and, when a channel is connected, the channel's approval message reach the user away from the laptop; deciding from any of them resumes the job. - Stuck detection, the OpenClaw two-minute rule: no step event and no heartbeat for 120 s shows "Still working, nothing new for 2 minutes" with Cancel and "Move to background" (rung 2).
Safety ladder on a laptop¶
The platform already has the rungs; the surface names them in one setting, "How much should I check with you?":
| Rung | Platform settings behind it |
|---|---|
| Ask before anything that leaves this machine | TOOL_UNCLASSIFIED_ACTION=require-approval, approval policy rows for send, write and payment action types |
| Ask only for consequential things (default) | TOOL_UNCLASSIFIED_ACTION=allow, policy rows for channel_send_message, google_gmail send, http_request_write, payments; app.agents.taint.mode=enforce (branch) so untrusted web content cannot drive a consequential tool without a check |
| Just do it | same, with the per-agent autonomy tier raised; the fail-closed guardrails and the hard refusals (payments, taint) remain, matching Hermes' floor that even yolo cannot lower |
The approval card always shows the tool, the summarised input and the fingerprint-changed warning the backend now produces. Decisions time out on the server; the card says so.
Memory and the learning loop, visibly¶
- The
memorystep event lists which blocks and facts a turn used; the transcript shows a small "Used 3 memories" link that opens them. - New facts (
remember_fact) and new skills (branch: Pending tab) appear in "Learned" on the Work rail with approve, edit and forget. This is Hermes'/journeyandwrite_approvalon the existing memory and skill APIs. - The user's profile block (name, locale, time zone, preferences) is editable as plain text, the
USER.md idea on
PUT /api/memory/blocks/{label}. - Import from ChatGPT or Claude exports uses the existing
/api/memory/importand/api/chat-importpaths, surfaced once at first run.
Same assistant, other doors¶
The Assistant agent is the default agent of every channel the user connects, so a WhatsApp
message, a Telegram voice note or an SMS lands in the same memory and the same approval queue.
The Work rail shows channel conversations from GET /api/handoff/conversations. This is what
Hermes and OpenClaw lead with, and the platform has more channels than either.
Local model, no key¶
On the personal-agent branch the Providers page opens with the local-model first-run and the
wizard. The surface defers to it: if GET /api/providers/availability reports nothing usable,
the composer shows one card, "Pick a model to start", linking to /providers/local-model, and
Quick Task's existing no-provider banner logic (providerGate.ts) is reused rather than copied.
Profile settings the surface relies on¶
All of these settings exist. Only some are set by the personal profile today (checked against
application-personal.yml and deploy/compose/personal.yml); the rest are what the surface needs and
must be turned on by the operator or by a later change to the profile.
| Setting | Value on personal | Set by the personal profile today? | Why |
|---|---|---|---|
APP_CHAT_BRANCHING_ENABLED |
true |
yes (application-personal.yml) |
messages persist in Postgres (G1); edit and regenerate |
app.chat.resumable-streams.enabled |
true |
no (default false; lite.yml sets it) |
reload-safe answers |
SCHEDULED_TASKS_ENABLED |
true |
yes | routines |
app.research.runs.enabled |
true |
no (default false) |
research with citations |
NEXT_PUBLIC_ARTIFACTS |
true |
no (empty in deploy/compose/.env.example, WEB_ARTIFACTS) |
artifact pane |
app.turn-journal.enabled |
true if the inspector is wanted |
no (default false; records every depth when on) |
"what did it do" view (G10) |
TOOL_UNCLASSIFIED_ACTION |
allow (default rung) |
yes (the shared default; nothing to set) | safety ladder |
app.agents.taint.mode |
enforce (branch default) |
yes (application-personal.yml) |
untrusted content cannot drive consequential tools |
app.skills.learning.enabled |
true (branch default) |
yes (application-personal.yml) |
learned skills, with the Pending gate |
app.chat.session-ttl-hours |
leave at 24 | not set (shared default) | branching retention governs persistence |
Each needs a line in docs/configuration.md where it is not already documented.
Build plan¶
Sized so each phase is one person, one or two weeks, each independently shippable. Gates: file
size cap (665 lines per .ts/.tsx/.css), ESLint (no raw process.env, no native select, a11y),
node scripts/api-contract.mjs --write for every new apiFetch call site, i18n keys in
messages/en.json, co-located vitest tests, navSections.test.ts for the rail entry, a
CHANGELOG.md line.
Phase 1, the box (new files only):
web/app/assistant/page.tsx,web/app/components/assistant/{AssistantPane,AssistantComposer,AssistantTranscript,WorkedForBlock,WorkRail,ThreadList}.tsxweb/app/components/assistant/{dispatch.ts,threadIndex.ts,assistantAgent.ts}with tests (dispatch.test.ts,threadIndex.test.ts,assistantAgent.test.tsagainst a mockedapi)web/app/lib/api/assistant.ts: thin wrappers over existing modules (streamChat, approvals, jobs, scheduled tasks, research, memory), no new endpoints- rail entry in
navSectionsAuthor.ts;/assistantas the personal-profile landing inweb/app/page.tsxbehindNEXT_PUBLIC_SITE_MODEor an equivalent public env read throughgetPublicEnv - preview harness entry in
web/app/ui-kit/console-previewso the screen renders without a core
Phase 2, work that outlives a turn: rungs 2 to 4, the Work rail's live sections over
/api/agent-jobs, /api/approvals, /api/scheduled-tasks, /api/research/runs; result
posting back into threads; stuck detection; notifications merged into the rail.
Phase 3, the learning loop: "Learned" section, memory event rendering, profile block
editor, skill Pending approvals (needs the personal-agent branch merged), first-run import.
Phase 4, the doors: channel default-agent wiring from the first-run flow, channel
conversations in the rail, voice composer, PWA install prompt and the installer opening
/assistant.
Follow-ups that need backend work (out of scope here, listed so nobody re-discovers them):
GET /api/agents/{id}/sessionsover the branching tables, with title and last activity, replacing the IndexedDB index as the source of truth.- An approval event in the chat stream (
approval{id, tool, summary}) and an outcome event, so the client stops polling. - A
thinkingstep event when the provider streams reasoning. - A folder-scoped local file tool for the personal profile (read, write under an allowlisted
root, no shell), the Cowork and Hermes
HERMES_WRITE_SAFE_ROOTidea. - (Done:
X-Journal-Turn-Idis returned while the journal is on.) Switching the journal on for the personal profile, which needs a retention decision first. - A Tauri shell with a tray icon and OS notifications, after the PWA has been measured.
Open decisions¶
- Name and route.
/assistantis used above. The product may want a name that is not the platform's ("Dūta"?). Cheap to change before Phase 1 ships. - One Assistant agent per user, or per tenant. Per user matches the personal profile and memory ownership; per tenant is what the lite tier with a family on one install would want. The design above is per user, found by name plus marker block.
- Default safety rung. "Ask only for consequential things" is proposed. The alternative, ask for everything that leaves the machine, is what ChatGPT agent mode does and is safer for a first run.
- Branching on by default doubles the persistence of every chat on the personal profile.
The retention purge exists (
ChatBranchRetentionPurge), so this is a disk question, not a correctness one. - Whether
/assistantreplaces/buildas the landing page for lite as well as personal. The build studio is a maker's front door; a laptop user wants the box. Proposed: personal only. - Ship behind the personal-agent branch, or before it. Phases 1 and 2 need nothing from the branch except the local login; they can land on main behind the rail entry and work on lite today, with Keycloak.
Sources¶
Platform: files cited inline; docs/api/openapi.json; docs/architecture/AI-FOR-EVERYONE-2026-10-14.md;
installer/README.md; branch integration/personal-agent.
Other products (public pages, read 2026-10-17):
- Anthropic, Claude Cowork support article: https://support.claude.com/en/articles/15520349
- Nous Research, Hermes Agent repository and docs: https://github.com/NousResearch/hermes-agent, https://hermes-agent.nousresearch.com/docs/ (memory, skills, security pages)
- OpenClaw docs: https://docs.openclaw.ai/ (architecture, agent loop, Control UI); Acronis on ClawHub malicious skills: https://www.acronis.com/en/tru/posts/openclaw-agentic-ai-in-the-wild-architecture-adoption-and-emerging-security-risks/
- ChatGPT agent mode and desktop: https://getcoai.com/news/openai-adds-agent-mode-to-chatgpt-mac-app-for-plus-users/, https://www.promptspace.in/blog/chatgpt-agent-mode-complete-guide-2026
- Grok app features: https://releases.sh/xai/grok.md
- Self-hosted chat front-ends compared: https://toolhalla.ai/blog/open-webui-vs-anythingllm-vs-librechat-2026, https://www.droptica.ai/blog/open-source-llm-ui-best-interfaces-local-ai-deployment-2026/