Skip to content

Web UI (alisio serve) ​

alisio serve starts a local HTTP server that gives a browser access to several workspaces and sessions at once. It drives the same agent core as the terminal: sessions, runs, approvals and the session database are shared with the TUI and alisio run.

Status

The server, its API and the browser interface are available, including image attachments, the files panel, the trajectory view, developer renderers (diff, terminal, JSON, test results), Mermaid diagrams, TeX formulas and the Settings pages for models, plugins, skills, MCP servers and agent presets. See Known limitations.

Start the server ​

sh
alisio serve                      # http://127.0.0.1:4317, opens the browser
alisio serve --no-open --port 0   # any free port, print the URL only
alisio serve --allow-write        # web sessions may write files without asking

The command prints a launch URL with a one-time token, for example http://127.0.0.1:4317/?token=…. Opening it exchanges the token for a session cookie and redirects to /. Keep the URL private: whoever opens it first in a browser can drive the agent with your permissions. Press Ctrl+C (or send SIGTERM) to stop the server; a second Ctrl+C forces exit.

alisio, alisio run and the TUI never load the server: it is imported only by alisio serve.

Flags ​

FlagDefaultDescription
--port <port>4317Port to listen on; 0 picks a free one
--host <address>127.0.0.1Address to bind; anything but loopback requires --allow-remote
--allow-remoteoffAllow a non-loopback --host (no TLS; prefer an SSH tunnel)
--no-openopensDo not open the browser
--max-workspaces <n>4Workspaces with an open application at once
--max-runs <n>4Runs executing at once across all sessions; more wait in a queue

The global flags apply too. The permission flags (--allow-write, --allow-process, --allow-analysis, --allow-external, --allow-mcp, --read-only) are the ceiling of every web session: the browser can narrow them per session but never go beyond them. --trust-project and --config apply to every workspace the server opens, like in the terminal. --db selects the shared session database. Set ALISIO_LOG_LEVEL (debug, info, warn, error, silent) to control the JSON log lines the server writes to stderr.

Using the web UI ​

The page has a sidebar on the left and the open session on the right.

Sidebar. New session starts a session in the project you are looking at. Under Projects (a project is a workspace folder), each folder lists its sessions, pinned first and then the most recently used, with a relative time ("4 min"). A dot before a title shows a session that is running, waiting for you, in use by another process or whose last run failed. As soon as one project is pinned, the list splits into Pinned (pinned projects, ordered by path, because the server stores no pin time) and Recent (every other project, newest activity first: its last opened time or its latest session update); with nothing pinned it is a single list ordered like Recent. Each heading is a button that collapses its group (remembered per browser, like folders). The icons next to the heading collapse or expand every project folder at once (the Pinned / Recent groups keep their own state), search sessions (also Ctrl+K / Cmd+K), show archived sessions and projects, and open another project (see Opening a project). Folders you collapse stay collapsed after a reload (it is remembered per browser; new projects start open, and a search opens folders without changing that). Hover a session for Pin and Archive, or a folder for a new session inside it and its actions menu (Pin, Color, Project settings…, Archive / Unarchive). Color opens a row of colored circles (or Default) that tints the project's folder icon; it is saved on the server with the project and works with the keyboard (arrow keys, Enter). Project settings… opens a dialog for that project (below). Settings sits at the bottom; the panel icon collapses the sidebar to a narrow rail. Below 900 px the sidebar becomes a drawer opened from the header. With many plugin apps, Apps shows the first five (plus the open one) and a Show all (N) button; the full list scrolls inside its own area, so New session, Plugins, Projects and Settings & tools always stay on screen (the choice is remembered per browser). In the rail, the app icons scroll between the Plugins button and the gear. Scrollbars across the interface are thin overlays: they appear when you scroll, move the pointer over a panel or use the keyboard, and fade after a moment; code blocks and tables keep a thin scrollbar of their own.

The sidebar with the Pinned and Recent project groups, a colored project folder and the palette of project colors
Projects in the sidebar: pinned and recent groups, folder colors and the color palette.

Project settings. The Project settings… item of a project's menu opens a dialog with a Name field, the Folder (its name and full path, selectable and with a copy button), the Color palette, and switches for Pinned, Trusted project and Archived. The name is only what Alisio shows in the sidebar: it sets the project's label, never renames the folder on disk, and an empty name goes back to the folder name (the field's placeholder). The name is saved with Save or Enter (at most 200 characters); color, pinned, trust and archived apply as soon as you change them, and trusting a project asks for the same confirmation as the menu item. A project whose folder no longer exists says so and hides the trust switch. The dialog is the same in the browser and in Alisio Desktop. Escape or Close dismisses it and focus returns to the project's menu button.

The Project settings dialog with the name, folder path, color palette and the pinned, trusted and archived switches
The Project settings dialog: name, folder, color and the pinned, trust and archive switches.

Header. Click the title to rename the session (untitled sessions show their first prompt). The badge shows the agent and the permission preset. Session log downloads the session as JSON Lines: every durable event in the alisio run --json format, then one {"type":"message"} line per stored message. The panel icon at the right opens the files panel (below). The Conversation, Trajectory and, while the memory plugin is enabled, Memory tabs switch the main view.

Conversation. Your messages appear on the right with a copy button. The agent's work appears as one-line rows: Think · … for reasoning, Context injection · AGENTS.md, and one row per tool call such as Read · README.md or Shell · npm test. Click a row to see its input and output. The answer streams as Markdown; code blocks have a language label, Wrap lines and Copy, and are highlighted once they scroll into view. Links to workspace paths open the file in the files panel. ```mermaid blocks become diagrams and ```math, $$ … $$ paragraphs and inline \( … \) become formulas (a lone $ stays text, so prices are safe). The page loads Mermaid only when a diagram scrolls into view and KaTeX with the first formula. Diagrams have Source, zoom, Export SVG, full screen and Copy; formulas have a copy button for their TeX. An invalid diagram or formula shows its source and the error instead, without breaking the message. Reasoning is display-only: after a reload, earlier Think rows are gone because reasoning is never stored. When you scroll up, a button jumps back to the latest message; long sessions show the last 30 turns and load older messages on demand.

Run status. While a run is live, one line at the end of the conversation says what it is really doing, taken only from the events and streamed output the page has received: Waiting for the model…, Thinking…, Writing the answer…, Reading your data (sales.csv)…, Running Python (analysis.py)…, Publishing the artifact (dashboard.html)…, Running <tool>…, Compacting context… (also during a manual /compact, when there is no run; sending is blocked until it finishes and the typed draft is kept), The model did not respond; retrying (1/1)… (while a silent request is being sent again, see limits.firstTokenRetries), The response was cut off; retrying in smaller steps (1/2)… (the output-token limit cut the response and the turn is requested again, see limits.truncationRecoveries), or Waiting for your approval / your answer (those two never count as a stall). Next to it are the run's elapsed time, the time in the current step and when something last arrived (last update 3 s ago). If nothing arrives for 15 s the line says how long the run has been quiet and shows Stop; after 60 s it says plainly No response from the model for 60 s (or No output from <tool> … when a tool is the one running) and suggests waiting or stopping. A sub-agent wait is never reported as a stall because its events belong to another session. The line only reads frames, it never invents activity. It announces itself to screen readers only when the step or the quiet level changes (not every second) and its spinner stops with reduce motion. After a reload the elapsed time continues from the server's start time; the last update time restarts at the reload because the page cannot know when the previous frame arrived.

A conversation during a manual /compact: the status line under the last answer reads Compacting context…, the send button is disabled and the draft typed in the message box is kept.
A manual /compact in progress: the status line is shown, sending is blocked and the draft stays in the message box.

If a run is stopped by its time limit, the conversation shows a message that names the model and the provider, the number of seconds of active time and how to proceed (retry, switch model, raise limits.timeoutMs, see configuration). Time you spend deciding on an approval, a question or the plan review is not counted against that limit.

Tool output. Tools that return structured blocks get a native view, each loaded the first time it is needed: file writes and edits show a diff (unified by default, Side by side on demand, foldable hunks, a file list when a patch touches several files); shell commands show their output with ANSI colors, the exit code and the duration, streaming while they run and keeping the last 2 000 lines behind Show earlier lines; JSON shows a foldable tree with copy of values and of their JSONPath; test results show passed, failed and skipped counts with a Only failures filter; progress blocks show their steps. The tool's raw text, which is what the model received, stays under Raw output. A block the page does not know shows its text and the folded JSON. The panel icon on a tool row with a path opens that file. Tools contributed by plugins show their own name followed by the plugin's name as a tag (for example Test report · Smoke tools).

Trajectory. The Trajectory tab lists the session's durable events grouped by run: status, start time, turns and duration per run, and one row per event with its time, turn, type, a short summary and its duration when it has one. It updates as events arrive; older runs sit behind Show earlier runs.

The Trajectory tab listing the durable events of a session (tool_started, tool_completed, turn_completed, artifact_published, memory_save) while an executive sales dashboard is open in the artifact panel on the right.
The Trajectory tab: one row per durable event, with an artifact open in the side panel.

Files panel. The panel icon in the header opens a panel on the right (a bottom sheet below 900 px) with three tabs. Files browses the workspace lazily, 1 000 entries at a time, hiding .git and, in git repositories, what .gitignore excludes. Changes lists the files this session (and its subagents) wrote, most recent first, with their git status code; picking one shows its diff against HEAD and the file. Preview shows text and code with highlighting, Markdown, JSON as a tree and images; files over 2 MB are cut with a Download link, binary files are only downloadable, and HTML or SVG shows as source, never as a page. From the preview you can copy the path, download the file or mention it (@path) in the composer.

Composer. Enter sends, Shift+Enter inserts a new line, and ↑/↓ on the first line walk through the prompts you sent. Typing / opens the command palette (arrows to move, Enter or Tab to pick, Esc to close): commands run on the server and their output appears in the conversation; prompt templates, skills and /ask become a prompt. / anywhere outside a text field focuses the composer.

Side questions (/btw). /btw question asks something about the current session without adding it to the conversation, also while a run is working. A floating panel shows the pending question (with Cancel), then the Markdown answer, the model and the tokens it used, a copy button, and ‹ 2/5 › to browse earlier side answers (←/→ when the focus is not in a text field; Esc closes it and cancels a pending question). /btw alone opens the panel on your most recent side answer, or shows Usage: /btw <question> when there is none. Side questions are never written to the transcript, events, runs or session tokens; the last 20 per session are kept and shared with the TUI.

Below the text box:

ControlWhat it does
+Attach PNG, JPEG, GIF or WebP images, up to 10 MB each and 8 per message (paste and drag and drop work too)
Permission presetRead only, Ask, Workspace write or Full access for this session
Model and effortModel of the workspace's provider and the reasoning effort the model supports
Context ringEstimated context of the next request against the model window
Send / StopStop replaces Send while a run is active and the box is empty

While a run is active you can keep typing: text is queued for the session's next turn. Images upload as soon as you add them and show as thumbnails you can remove; they can only be sent when no run is active.

Stats line. Under the composer, one line sums up the latest run: turns, steps (tool calls), time spent in the model and in tools, the average time to first token, output tokens per second, the share of input served from the provider's prompt cache and the input tokens. Hover it for the totals of the whole session; when decisions happened, the tooltip also shows how many were answered and how many fell back (see Decision Intelligence). Cache shows — when the provider does not report cached input. The /decisions command works in the composer like in the terminal: see /decisions.

Approvals and questions. When a tool needs approval, a panel takes the composer's place and receives focus: it names the tool, the effect and its input. Answer with Deny (D), Allow once (O) or Allow for session (S); keys pressed in the first 300 ms are ignored so a stray Enter cannot approve. Plugin questions (for example ask_user_question) appear the same way.

Settings. The Settings & tools menu at the bottom of the sidebar groups Agents, Skill Builder and Settings; Settings opens this window. Pages that manage agent resources act on the workspace of the open session (or the first workspace when none is open):

The Settings & tools menu at the bottom of the sidebar, open, with Agents, Skill Builder and Settings.
The Settings & tools menu: Agents, Skill Builder and Settings.
PageWhat it does
GeneralUI language (this browser), then the agent settings: compaction, context, limits, plugin hook timeout and web search provider, saved to your user config.json
ModelsProvider profiles with their non-secret values, credentials and Activate in this workspace with a model of that profile; Add a profile creates one
PluginsFour tabs: Plugin configuration (not sandboxed, how plugins are installed, the project file), Installed plugins (search, update pills, switch and Uninstall), Plugin catalog (browse and install) and Install from file (private .tgz plugins); see Plugin catalog, install and updates. The sidebar Plugins menu also opens the Plugin creator
SkillsDiscovered skills with scope, size and a switch; disabled skills leave the / palette. New skill opens the Skill Builder
MCP serversStatus, transport and counts of each configured server, enable switch and Connect; Grant MCP access asks for an explicit confirmation first. The Explore tab browses the MCP catalog saved on this computer (search, category, where it runs, ready-to-install filter, 24 per page, update now); Install or Connect opens one compact form (only what Alisio cannot decide: keys as password fields, the existing MCP consent unchecked by default), picks Only this project or All my projects, shows clear progress with Cancel and ends with Install and test / Connect and test. The Connected list shows where each server runs (On this computer / Remote service) and where it is available (Global / Project) as separate badges and, in a web or headless server, says On the computer running Alisio instead of claiming the browser's computer. Each row offers Check connection (an isolated check that lists what the service announces without calling any tool, with its stages and a way to recover), Configure (a typed form where a saved key shows only as Key saved), Update for installed packages, Sign in / Sign out for services that use OAuth, and a scope-aware Remove / Disconnect that never claims to cancel an account. Details on a catalog card shows who provides it, where it runs, what you need and what it will ask for. PyPI, OCI, MCPB and SSE entries say so and point to manual setup (see Configuration). A step-by-step guide with screenshots is in MCP catalog
Agent presetsbuild, plan and main-capable user or plugin agents, with instructions and suggested model; Use in this session switches the open session
AppearanceTheme (dark, light or system) and whether tool rows start collapsed or expanded, stored in this browser

Signing in to a provider ​

A provider whose plugin declares sign-in methods shows a Sign in block on its profile card, with one button per method (its label and description come from the plugin). While a sign-in runs, the block polls the server every second and shows what the plugin asks for: a device code and its address with Copy buttons, a button that opens the authorization page in a new tab, progress lines, and a field for the answer the plugin asks you to paste (masked when it is a secret), labeled with the plugin's own message. Cancel stops the sign-in. A provider that only signs in shows no API key field; once signed in, the card offers Sign out, and an expired session shows Session expired and Sign in again.

  • A method marked unofficial shows a fixed risk notice (the provider has not officially authorized it, the service may change and the account could be restricted) and starts only after I understand the risk.
  • Links open only with window.open(url, "_blank", "noopener,noreferrer"), and only when they are https: (or http: on localhost/127.0.0.1); any other link is blocked and never opened.
  • Plugin text (labels, descriptions, instructions and prompt messages) is always shown as plain text, never as HTML or Markdown.
  • If the server restarts while a sign-in runs, the flow is gone: the block says Sign-in interrupted, start again and offers to start again. After any sign-in ends, the page fetches the profile again instead of trusting its local state.
  • The browser never receives a token: only the state of the session (signed in, expired, expiry). Sign-in is unavailable under --read-only. See Known limitations for how sessions are stored.
Settings, Models: a profile with an unofficial sign-in method marked unofficial, and a profile whose sign-in is running with a device code, its address with Copy and Open link buttons, a progress line, a field for the confirmation code and a Cancel button.
Sign-in from Settings → Models, with a scripted fake provider: the device code and address, a masked field for the answer the plugin asks for, and Cancel.

Open configuration file (top right) shows the effective configuration file of the workspace, your user settings file and the provider profiles file, each with a copy button. The server never opens an editor. The Alisio version (the server's, the one alisio --version prints) is shown in Settings → About and, discreetly, at the bottom of the sidebar.

Credentials are write-only. A credential field is a password input: after Save it is cleared and the page only shows whether a value is stored, whether it comes from an environment variable, and, for stored values of 16 characters or more, the last three characters (Stored · ends in …71B). No API response contains a secret.

Enabling or disabling a plugin writes a project override (.alisio/config.json) and needs the workspace to be trusted. Plugins are loaded when the workspace application starts, so the server reloads that application as soon as it has no runs: immediately when it is idle, otherwise when its last run finishes (the card shows Restart required meanwhile). Sessions keep their history, and every open command palette refreshes. Skill switches apply at once.

Plugin catalog, install and updates ​

Settings → Plugins installs, updates and removes plugins without a terminal. It has four tabs: Plugin configuration, Installed plugins, Plugin catalog and Install from file. Every install shows the same warning as the CLI first: npm runs with your user's permissions and may run the package's install scripts, and plugins are not sandboxed. Don't ask again this session skips it until you reload the page (a downgrade always asks). Installs and uninstalls are refused under --read-only, and only one runs at a time.

Installed plugins. Each card shows the version; Uninstall (with a confirmation) removes npm plugins installed in your global plugins directory. Built-in plugins and project or local plugins cannot be uninstalled here and say why. When a newer version exists the card shows Update available: vX → vY and an Update button, and the header counts them (2 updates available) with an Update all button that updates them all in one combined npm install (so npm resolves every @alisio/sdk peer range together) and ends with a summary. If updating a single plugin fails because other installed plugins still depend on the older SDK, Alisio retries once as one install of that plugin plus the other installed plugins that have a newer release, and tells you which ones were updated along with it; it never forces or relaxes the peer rules. Check for updates refreshes the catalog for both tabs. Updates are known only for plugins in the catalog; there is no registry lookup for other npm plugins, and a plugin installed from a file has no remote source, so it shows Local package vX and Update from file…, which opens Install from file for that package. An update that needs a newer @alisio/sdk than the one Alisio has is disabled with Update requires a newer Alisio.

Settings, Plugins, Installed plugins: one update available, the Context7 card with the pill Update available v0.4.0 to v0.5.0 and an Update button, and the Brave Search card expanded with its switch and the Uninstall button.
Installed plugins: the update count, Check for updates, the Update button and Uninstall with its package name.

Plugin catalog. The published plugins as a compact list, one row per plugin: the plugin's icon, its name, a short description (cut at the same length for every row; the full text is the tooltip and the accessible description) and one icon button for the action: Install (+), Update, Reinstall when the same version is installed, or Downgrade. An incompatible plugin has the button disabled with a tooltip that says why, and rows carry an Installed, Update or Incompatible badge. The list has one column when narrow, two at the default dialog width and three when the dialog is maximized. Search covers name, description, tags and package; filter by category and tags, optionally Group by category. Ten plugins per page, with loading skeletons and an error state with Try again. The action never opens the detail; click the row (or press Enter or Space on it) to open it.

Settings, Plugins, Plugin catalog: search box, Check for updates, category and tag filters, and a two-column list of plugins, each with its icon, name, short description and an Install or Reinstall icon button.
The Plugin catalog: a compact list with icon, name, short description and the action button.

Plugin detail. Opening a row shows the plugin inside the same tab: the icon, the name, the author and a one-line summary, the main action (Install, Update, Reinstall or Downgrade), Uninstall when it is installed, and links to npm and the website. Below are the cover, the full description, an Information list (latest and installed version, developer, license, category, tags, the @alisio/sdk range it needs and whether this Alisio satisfies it, the npm package, the publication date and the npm, repository and website links; only what the catalog provides), and a note that plugins are not sandboxed. Back to the catalog (or Esc) returns to the list with the same search, filters and page, and the focus on the row you opened.

Settings, Plugins, Plugin detail of OpenAI: icon, name, author, the Update, Uninstall, npm and Website buttons, the large cover, the description, the Information list and the not sandboxed note.
The plugin detail: actions, cover, full description and the Information list.

Plugin icons. An icon appears next to each plugin in the catalog, in Installed plugins, in the install, update and uninstall confirmations and in the Install from file preview. It is the catalog's icon when the package is in the catalog, else the icon the installed package ships (see Plugin icon), else one default icon (a puzzle piece); the default also stands in when an icon fails to load, and for built-in, project and not-yet-installed file plugins. The icons go through the server like the covers, so the browser never contacts the catalog origin.

Catalog cache. The server downloads the catalog (ALISIO_PLUGIN_CATALOG_URL, by default the published alisio-plugins catalog) and keeps it under its state directory. It does not look again for 24 hours unless you press Check for updates; then it sends a conditional request, so an unchanged catalog costs a 304. The tab shows when it was last checked and whether it is cached or offline. Offline, the saved copy is shown with a notice; offline with nothing saved shows an error with Try again. Covers and icons go through the server (the browser never contacts the catalog origin) and are cached on disk. Every field of the catalog is validated: links must be https:, covers and icons must come from the catalog's own origin, and a package with an invalid npm name is dropped.

Install from the catalog. One click, a loading state, and the plugin appears in Installed plugins at once. The catalog version is installed; if npm does not serve it yet, the registry's latest is installed instead.

Install from file. For private plugins that are never published: make a tarball with pnpm pack or npm pack, then choose it or drop it on the tab. The server reads only package/package.json (no tar binary is needed) and shows a validation card: name, version, description, author, license, size, the @alisio/sdk range (a mismatch is a warning, not a block), whether it is a valid Alisio plugin (the alisio-plugin keyword, a valid name and version, an entry point), a prominent warning when it declares preinstall, install or postinstall scripts, and whether it will update, reinstall or downgrade a version already installed. Limits: 50 MB for the file, 1 MB for package.json.

Settings, Plugins, Install from file: the validation card of a private plugin with name, version, author, license, size, the SDK range and a warning that this Alisio has a newer SDK, and the Install button.
Install from file: the validation card with the SDK warning before anything is installed.

After the confirmation a stepper shows the phases: Uploading, Validating, Copying package, Installing dependencies (npm), Registering, Reloading workspace and Cleaning up. On failure the install is rolled back completely and the last lines of npm's output are shown.

Settings, Plugins, Install from file: the progress of an install, with the first three phases checked, Installing dependencies running and the rest waiting.
The install progress: completed phases are checked and the current one spins.

The file is copied to <config home>/plugins/local-packages/<name>-<version>.tgz and installed from that copy, so the file:local-packages/... dependency npm records stays valid even if the original file moves. The upload is staged in <state home>/tmp/plugin-uploads/ and deleted after the install, a failure or a cancel; leftovers older than one hour are swept. Uninstalling removes the stored copy.

Updating in place. Installing a package that is already installed replaces it and never creates a duplicate: Update vX → vY, Reinstall or Downgrade vX → vY (with a warning). The plugins entry of your configuration stays single, and the previous stored tarball is removed once the new one works. A plugin first installed from a file that still points outside local-packages is migrated to the stable store on its next update. If the update fails, the previous version is restored.

Restart after an update. Node keeps the code of a plugin it has already loaded, so replacing a loaded plugin cannot take effect in the running process. The result then asks for a restart: a banner (Plugin updated. Restart Alisio to use the new version.) and a Restart pending pill on the card. In the desktop app the banner has Restart now, which restarts the background process and reloads the window. With alisio serve there is no button: restart the command yourself. A first install of a plugin that was never loaded needs no restart.

Security model ​

The server is designed for one user on one machine:

  • It binds 127.0.0.1 by default. Exposing it on a network requires --host <address> --allow-remote and prints a warning; there is no TLS, so use an SSH tunnel instead.
  • A 256-bit launch token is generated per process and never written to disk. The browser exchanges it once for an HttpOnly; SameSite=Strict cookie that holds a separate secret.
  • Every request must name this server in Host (DNS-rebinding defense), and requests with side effects need a matching Origin and Content-Type: application/json (CSRF defense).
  • Responses carry a strict Content-Security-Policy, X-Frame-Options: DENY, nosniff, Referrer-Policy: no-referrer and same-origin opener/resource policies; API responses are not cached.
  • Approvals are fail-closed: when no browser tab watches the session for 30 seconds, when the run is cancelled or after 10 minutes, the answer is deny.
  • Plugins are not sandboxed: a trusted project's plugins run inside the server process with its operating-system permissions, exactly as in the terminal.
  • The native folder dialog and the in-app folder browser exist only on loopback. The browser lists directory names (never files or contents) that the server's user can read, the same trust as that user's terminal; with --allow-remote both are off and only a typed path is accepted.

Workspaces and trust ​

A workspace is a directory (its git root when inside a repository). The server lists the workspaces of existing sessions plus the directory where alisio serve started, and opens an application for a workspace only when it is used. At most --max-workspaces are open at once: the least recently used idle one is closed to make room, and when all of them are busy the request fails with workspace_limit. Idle workspaces close after 10 minutes.

A workspace whose folder was deleted or moved stays in the sidebar, dimmed and marked "folder not found": its sessions remain readable, but you cannot start new sessions or send prompts there (the server answers 404 workspace_missing). New session uses the current session's workspace, or the most recently used one that still exists; with none, it asks you to open a workspace. Folders with the same name show a short parent path next to it, and the full path on hover.

Archive a workspace from its actions menu to hide it and its sessions from the sidebar; Show archived brings both back (archived workspaces are listed last). Archiving keeps every session, which stays readable, closes the workspace's idle application and is refused with 409 runs_active while a run is active. New sessions in an archived workspace are refused with 409 workspace_archived; Unarchive it, or open the same folder again, to use it. This is also the way to hide a workspace whose folder no longer exists.

Opening a project ​

The folder button next to Projects (and New session when no workspace exists yet) asks for a folder in the best way the server supports:

  1. Native folder dialog. Browsers never give a page the absolute path of a folder, so the server opens the operating system's own dialog on its desktop and uses the folder you pick (a note in the sidebar says so while it is open; only one dialog at a time).
    • Linux (and BSD): zenity, else kdialog, else yad, found on PATH; it needs a desktop session (DISPLAY or WAYLAND_DISPLAY).
    • macOS: osascript with AppleScript choose folder.
    • Windows: Windows PowerShell (or pwsh) with the WinForms folder dialog, and the Shell folder browser as a fallback.
  2. In-app folder browser when there is no dialog tool (headless machine, SSH session, ALISIO_NATIVE_PICKER=0): a window that lists folder names only, starting at your home folder, with breadcrumbs, a parent-folder button and Show hidden folders. On Windows its top level lists the drives.
  3. Type a path is always available as a link, and is the only option when the server is bound for remote access (--allow-remote), because a dialog or folder listing would show the server machine rather than yours.

A directory you have not trusted opens without its project resources (configuration, plugins, agents, skills, prompts) and is marked untrusted. Trust it from its ⋯ menu in the sidebar (Trust this project…) or from the Agents editor, after a confirmation that explains what trust unlocks; or run alisio in that directory to answer the terminal prompt. Both store the same decision, bound to the content of .alisio/config.json: if that file changes, the workspace is untrusted again until you confirm. The workspace reopens so the change applies at once (it is refused while its runs are active, under --read-only, and when --trust-project/--config fix trust for the server). Stop trusting this project… withdraws it.

Sessions, runs and permissions ​

Several sessions can run at the same time, in one or more workspaces. A session runs one prompt at a time: text sent while it is running is queued for its next turn, while attachments must wait until it is idle. Runs beyond --max-runs wait in a first-in, first-out queue. Retrying a prompt with the same request id never starts a second run.

A session that another Alisio process is using (for example the TUI) is reported as locked and is read-only in the web until that process releases it.

Each session has a permission preset, which is also its permission mode (ask, auto or full, see Permission modes). Effects the launch flags do not allow keep asking, whatever the preset:

PresetModeAllowed without askingOther effects
read-onlynonereadsdenied
askaskreadsask for approval
workspace-write (default)autoreads, writesask for approval
full-accessfullreads, writes, processes, networkask for approval

"Allow for session" in an approval widens only that session, never the other sessions of the same workspace.

Agent selector and Shift+Tab ​

The composer toolbar shows the active agent (Agent: build) next to the permissions menu. Open it to pick any main agent; the choice is stored on the session and applies from the next prompt. Shift+Tab with the focus in the message box cycles the same list: build, plan, then your own primary agents by name, wrapping around. The shortcut is ignored while the / palette is open, during an IME composition and with any other modifier, and a screen reader announces the new agent.

Accessibility trade-off: Shift+Tab normally moves the focus backwards, so intercepting it in the message box changes what keyboard users expect there. The agent selector is the accessible path (reach it with Tab) and is always available; the shortcut only acts while the focus is in the message box, and everywhere else Shift+Tab keeps its usual meaning. Cycling is allowed during a turn because it only stores the next agent.

Permission modes ​

/permission (also /permissions) opens the permissions popover from the header: three radio options, Ask, Auto and Full access, the status of the session and Manage saved permissions…, which shows the saved grants (for example Python analysis allowed for this session) with Revoke. Choosing Full access asks for a confirmation that says it is not a sandbox. A mode the server cannot offer (its launch flags are the ceiling, or it runs with --read-only) is listed as unavailable with the reason. /permission ask, /permission auto, /permission full and /permission status run without opening the popover and answer in the conversation; the permissions menu of the composer keeps the four presets.

Plan mode and plan review ​

Switch a chat to the plan agent (the selector, Shift+Tab or /agent:plan) and it investigates with read-only tools, then hands over its plan with exit_plan. The plan opens in the conversation, is saved as a plan.md artifact and the composer gives way to the decision screen: Agree and start implementation, Skip for now (Esc does the same) or Add context. The plan run is read-only whatever the permission mode. The full guide, with screenshots, the decision rules and revisions, is Plan mode and plan review.

Plan diagrams and the plan viewer ​

A plan can carry small Mermaid diagrams. The decision screen offers Open plan viewer and the plan card opens the same viewer in the side panel: sections, diagrams drawn in Alisio's palette, links between diagrams and text, and a ZIP download. See The plan viewer; turn diagrams off or cap them with plan.diagrams and plan.maxDiagrams.

Background tasks ​

Commands the agent starts with bg_run keep running while you chat. The header's panel icon opens the right panel, whose Tasks tab lists them (the tab shows how many are running, and the panel icon gets a dot while any is). Pick one to follow its live output: it is read incrementally once a second, shown in the same terminal view as shell results (colors, copy, the last 2 000 lines) and keeps the last 200 000 characters. Stop ends a running task as you (it becomes Stopped, never Failed). The list also shows the session's subagents as a read-only view of their status; they are managed as before. /tasks in the composer opens the tab, and the panel is loaded on demand (it is not part of the initial bundle).

The Tasks tab of the right panel following a running background command: the task's status, command and elapsed time, a red Stop button and the live output lines in the terminal view, while the chat shows the agent's bg_run call.
The Tasks tab following a running task: live output and the Stop button (Spanish interface).

When a task ends on its own a toast says so (not for a task you or the agent stopped), and one coalesced message wakes the agent: it appears in the chat as Background task finished: … and the agent can read the output with bg_output. The message waits while the session is running, an approval or a question is open, or the session is locked by the TUI. Tasks are not sandboxed, end when alisio serve stops (or the workspace's application is closed) and show as Lost after an abrupt stop; a workspace with running tasks is not evicted when idle, and /reload answers 409 until they end or are stopped. A task is only reachable through the session that owns it.

Session goals ​

/goal <objective> gives the agent one objective for the session and lets it keep working on it, turn after turn, until it is done, blocked, paused or out of budget. The goal bar above the message box shows it, loads on demand (it is not part of the initial bundle) and comes back after a reload (the stream's snapshot carries the goal). The agent marks the goal complete or blocked with update_goal, with evidence; only you pause, resume, edit or clear it or change its budget. The commands, states and limits are the same as in the terminal: see Session goals.

Cost. A goal keeps spending tokens by itself, and each turn sends the whole context again. A goal without a token budget is stopped only by its turn and time limits (goal.maxTurns 50, goal.maxMinutes 120): give long objectives a budget (/goal <objective> budget=50k).

The bar shows the status (Active, Paused, Blocked, Budget reached, Complete), the objective, a progress bar for the token budget (it turns amber at 80 % and red when spent), the turn n/max, the time spent in runs and why the goal waits or stopped (Waiting for your answer, Waiting for permission, Waiting for background tasks, plan mode, or the reason code in words), announced to screen readers. Its buttons follow the state: Pause (while active), Resume (paused or blocked), Edit and Clear (always; Clear asks first). Edit opens a form for the objective and the token budget (50k, 1.5M, clear; Ctrl/Cmd+Enter saves, Esc cancels). A complete or blocked goal shows the agent's report and its evidence under Agent's report.

The goal bar above the message box of a running goal: the Active chip, the objective, a progress bar of the token budget with 600 of 2k tokens, turn 2 of 50, the time spent in runs and the Pause, Edit and Clear buttons, while the chat shows the agent's goal turns.
The goal bar of a running goal: budget progress, turns and its buttons (Spanish interface).

In the composer, /goal and its subcommands work as in the terminal: typing /goal lists /goal pause, /goal resume, /goal edit and /goal clear (the ones the current state allows) in the palette; a new objective over an existing goal asks for confirmation; /goal edit without text opens the form. Every change is a compare-and-set, so two windows cannot fight: an action based on an out-of-date view is refused and the bar reloads the goal.

A goal never continues while an approval, a question or a plan review is open, while background tasks of the session run, in plan mode, while you have a turn running, or after you interrupt it (Stop pauses the goal). If the server stops while a goal is active it comes back paused (reason restart) after the restart and never resumes by itself. Goals use the session's permission preset like any turn and never widen it. alisio run has no /goal in v1.

Proof: acceptance criteria ​

When a session has Proof criteria, a compact card appears above the message box and the right panel gains a Proof tab (also opened by /proof). Both load on demand and read the ledger from the server, so they show the same statuses as the terminal and alisio run --json. The tab lets you add a criterion (with an optional command), edit its wording or required flag, mark it Not applicable with a reason and download proof.md or proof.json. With a goal that has unmet required criteria it also offers Complete the goal anyway (a reason is required; only you can). In a plan review that proposes criteria you can keep, edit or reject each one before approving.

The Proof card above the message box and the Proof tab of the right panel with five acceptance criteria in different statuses.
The Proof card and the Proof tab with synthetic data.

The routes (same authentication and Origin rules as the other session routes):

RoutePurpose
GET /api/sessions/:sid/proofThe ledger: criteria, evidence and the claims without evidence of the last run
POST /api/sessions/:sid/proof/criteriaCreate a criterion, or edit one (criterionId with text and required)
POST /api/sessions/:sid/proof/criteria/:id/not-applicable{"reason"}: mark it not applicable
GET /api/sessions/:sid/proof/export?format=md|jsonDownload proof.md or the proof/v1 JSON
POST /api/sessions/:sid/goal/override{"reason"}: you complete the goal over unmet required criteria

Stream: proof_updated and proof_divergence arrive as ordinary event frames, and the snapshot carries proof: {criteria: n} when the session has criteria.

Context panel ​

With the Context Governor in shadow or on, the right panel gains a Context tab as soon as the open session emits its first context_governed event (/context opens it at any time). It loads on demand and reads the same context report that /context prints in the terminal, so both show the same segments and reasons. The tab shows the mode and policy, the estimated tokens before and after the policy, how many tool results are elided (or would be, in shadow) and why, the share of results the agent acted on later, the invariant counts by kind (never their text) and the segments that need explaining: pinned, low criticality and elided ones, each with its kind, origin, tokens, criticality and reason. A checkbox lists every segment. Nothing in the tab changes the session; it only reads.

The Context tab of the right panel in on mode: the policy summary, two elided tool results with their reason, the invariant counts by kind and the list of segments.
The Context tab with synthetic data.

The route (same authentication and Origin rules as the other session routes; the older GET /api/sessions/:sid/context, the estimate behind the context bar, is unchanged):

RoutePurpose
GET /api/sessions/:sid/context/reportThe ContextReport: segments with their reasons, the elision totals by reason, the invariants and the heuristic usage

Stream: context_governed arrives as an ordinary durable event frame, one per model request.

/reload and /changelog ​

/reload re-reads the configuration, agents, skills, prompt templates and MCP servers of the session's workspace. It is refused (409) while the workspace has runs. The server validates the configuration and builds the new application next to the current one before swapping, so a broken file changes nothing and the error says why. The conversation shows the report (what changed per area and what needs a restart, such as already imported plugin code), a toast confirms it and open palettes and settings pages refresh through catalog_changed. /changelog [version] opens a dialog with the shipped changelog, which works offline. After an upgrade one discreet toast says so; the last seen version is kept in the browser's localStorage and nothing breaks when storage is blocked. Both commands are interactive only: alisio run is unchanged.

Memory tab ​

While the built-in memory plugin is enabled, a Memory tab appears next to Conversation and Trajectory. It shows what the plugin holds for the open chat, read-only, in three sections:

  • Saved in this chat: the memories saved while this chat was open (by the model, by compaction or by the end-of-session summary), pinned first and then newest. Filter by type, search title, content and topic, and press Load more to read 20 at a time. Each entry shows its type and title, a summary of its content (Show more / Show less), when it was updated, where it came from and the raw record under Details.
  • Session summary: the summary the plugin stored for this chat (one per session; compaction and the end-of-session summary overwrite it).
  • Context loaded: the memory context the plugin added when this chat started, which is what the model saw first.
The Memory tab of a chat: the Saved in this chat section shows 20 of 27 memories with a type filter and a search box, a pinned entry first, and each entry with its type, title, content summary, update time and source.
The Memory tab: the memories saved in this chat, pinned first, with filter, search and Load more.

Each section loads, fails and retries on its own. Nothing updates live: Refresh reloads the three sections and keeps your filters, and memories saved while you browse appear after a refresh (the list never repeats or skips an entry while you page). Switching chats reloads the tab and never shows another chat's data. The tab exists only while the plugin is enabled: turning it off in Settings → Plugins removes the tab and returns you to Conversation. An empty chat shows No memory stored for this chat yet.

Memory can hold sensitive project data. The tab is served by the same authenticated session routes as the conversation (GET /api/sessions/:sid/views/memory/…, see plugin data views) and shows it to whoever has this server's session cookie. It is a view: pinning and forgetting stay in the TUI (/memory) and the model's tools. How each section is sourced, and its limits, are in Memory.

Python analysis and artifacts ​

Files published by Python analysis appear as cards under the tool rows of the turn that produced them: kind icon, file name, kind label and an always-visible download button. On hover or keyboard focus a previewable card says Open file instead of its kind; a card that cannot be previewed (DOCX, ZIP, unknown files, files over the preview limit) never says it, and clicking it downloads. Cards survive reloads (they come from the stored tool results) and show Deleted or Expired when the artifact is gone, or Unavailable with Retry when opening it failed.

Opening a card shows the artifact panel on the right of the chat, in the same slot as the files panel (opening one closes the other):

  • Title menu: switches between the session's artifacts (newest first, a filter above eight, arrows, Enter, Esc and type-to-jump; artifacts published meanwhile get a dot and never steal the view), then Open in new tab (dashboards and PDFs), Expand panel, Download analysis sources (for python_run outputs), Copy to workspace…, Details, Run again (for python_run outputs) and Delete. An expired artifact stays in the list marked Expired; opening it offers only Details and Run again.
  • Download, Full screen and Close on the right; Close returns the focus to the card.
  • Resize with the handle between the chat and the panel (shared with the files panel): drag, double click to reset, or focus it and use ←/→ (16 px, 64 px with Shift), Home/End and Enter. The width is remembered per browser; the chat keeps at least 360 px.
  • Previews: Markdown with the chat renderer (relative images resolve inside the artifact), HTML dashboards in the isolated viewer, images with Fit/100 %, PDFs in the browser's own viewer, JSON with the JSON renderer, CSV, TSV and XLSX in the table viewer, and code and text with the code renderer. Anything else, or anything larger than its limit, shows the reason and a Download button.
  • Below 900 px the panel is a full-screen dialog. Esc closes the open menu first, then the panel. Settings and Agents open over the panel, which stays loaded behind them.
  • Copy to workspace… asks for a folder and runs the artifact_export tool in the session: the usual write approval applies, and the copied files appear in Changes. /artifacts [filter] opens the panel with its menu.
An executive sales dashboard generated by Alisio in the artifact panel: seller ranking, sales by channel, customer segments and order status.
A generated executive dashboard in the artifact panel (Spanish interface).

Dashboards run in an iframe with sandbox="allow-scripts allow-downloads" (never allow-same-origin), served from /artifact-view/<token>/… with a signed link that expires after 10 minutes. Its Content-Security-Policy forbids every connection (connect-src 'none') and adds the sandbox directive, so the page has an opaque origin: it cannot read the cookie, the Alisio page, localStorage or the network, also when opened in a new tab. Data must be embedded in the HTML.

A dashboard built with alisio_runtime.charts: headline numbers, a donut of order status, a pie of channel share and a grouped bar chart of sales and profit per seller, scaled to their cards.
A dashboard made with alisio_runtime.charts (Chart.js bundled; see Charts).

Running Python asks in the approval panel with the title Run Python analysis?, the warning that managed Python is not a sandbox and the first 40 lines of the script; S (Allow for this session) saves the permission for the session. The key button in the header opens Session permissions, which lists saved permissions with Revoke; /permissions opens it too. Other tabs refresh when a permission is saved or revoked. alisio serve --allow-analysis allows Python without asking in sessions whose preset allows processes (full-access); elsewhere it asks.

Run again repeats the analysis as a new execution with new artifacts (the old ones stay). It is a python_run { rerunOf } call of the session, so the permission above applies and the approval shows the saved script; Details names the execution a rerun repeats. Installing the optional Python packages asks with Install Python packages?, the package list, the download estimate and that it needs the network, and offers only Allow once and Deny (S does nothing: it is never saved).

Settings → Forja ​

The Forja page of Settings holds what used to be the Data analysis page, now as the Python section. It shows the runtime read only (the three backends with their state and isolation, the analysis.runtime and analysis.isolation policy and the one that would run now, the Python found or how to install it, the optional packages, the container engine and image, and the reminder that managed Python is not a sandbox) and edits the switch (analysis.enabled), the execution timeout and the three retention values. Changes are validated by the server, written to the global configuration file and shown with when the last cleanup ran. runtime, isolation, wasm.* and oci.* are not editable here. The JavaScript and Guides sections hold the forge.javascript.enabled and forge.guides.enabled switches. See Forja: code mode and Configuration.

Tables and attached data ​

The composer's + button (and drag and drop) accepts data files next to images: CSV, TSV, JSON, JSONL and XLSX. A file is uploaded as the raw body of POST /api/sessions/:sid/datasets, kept in the content-addressed blob store and ingested in the background into a SQLite dataset (see Tabular data). The chip says Reading sales.csv… while the server works; the server keeps answering (health, heartbeat) while it does, because ingestion runs in a separate process. When the dataset_ready frame arrives the chip shows the size of the table; a dataset_failed frame (unsupported file, over analysis.data.maxUploadBytes or maxRows, XLSX without Python) turns it into an error with the reason. Sending the message adds a bounded summary (schema, five rows, statistics; at most 4 KB) to what the model reads, while the conversation shows what you typed with the chips; a chip opens the table in the right panel. Data alone sends a default request to look at it.

The table viewer showing a 300-row CSV with its filter box and copy and download buttons, next to the attached-dataset chip in the conversation.
The table viewer with a 300-row CSV and the attached-data chip in the chat (Spanish interface).

The table viewer (SpreadsheetView) also opens spreadsheet artifacts (CSV, TSV, XLSX), which are ingested the first time they are previewed:

  • Fixed 28 px rows and a window of rows and columns, so a million-row sheet scrolls smoothly. Pages of 200 rows come from the keyset-paginated rows API (rowid cursors) through a cache of 20 pages: scrolling or sorting never repeats or skips a row. Jumping to a row works at any position without sorting; while sorted or filtered it reaches the first 100 000 rows, and farther rows load by scrolling.
  • Click a header to sort (ascending, descending, none; aria-sort), type in the filter box (a column, or every column up to 100 000 rows; a substring match), pick a sheet in a workbook, and resize columns by dragging the handle on a header's edge, or with Alt+←/→ (Shift for larger steps) on a focused header. Sorting and filtering are off above analysis.data.maxInteractiveRows (1 000 000) with a notice: the file has no indexes.
  • role="grid" with row and column counts and indexes, one tab stop (arrows, Home/End, PgUp/PgDn, Ctrl+Home/End move between cells; ↑ from the first row reaches the header). Ctrl/Cmd+C copies the cell and Ctrl/Cmd+Shift+C the row (also a toolbar button); Download original is in the toolbar.

API and events ​

The browser talks to JSON routes under /api and to one Server-Sent Events stream per tab:

text
GET  /api/health                       unauthenticated: name, version, protocolVersion, capabilities
GET  /api/workspaces?archived=false|true|all   POST /api/workspaces {path}
PATCH /api/workspaces/:wid {label?, pinned?, color?, archived?}
POST /api/workspaces/pick {start?}     native folder dialog → {path} | {cancelled: true} (loopback)
GET  /api/fs/dirs?path=&hidden=        folder names for the in-app browser (loopback)
GET  /api/sessions                     POST /api/sessions            GET|PATCH /api/sessions/:sid
GET  /api/sessions/:sid/messages       GET /api/sessions/:sid/events GET /api/sessions/:sid/runs
POST /api/sessions/:sid/prompts        POST /api/sessions/:sid/cancel  POST /api/sessions/:sid/compact
GET  /api/sessions/:sid/models         GET /api/sessions/:sid/context GET /api/sessions/:sid/export
GET  /api/sessions/:sid/context/report   the Context Governor's ContextReport (segments, reasons, invariants)
GET  /api/sessions/:sid/views/:plugin/:view?<params>   read-only data view of an enabled plugin
GET  /api/commands?session=<sid>       POST /api/sessions/:sid/commands {requestId, name, args?}
GET  /api/sessions/:sid/btw            POST /api/sessions/:sid/btw {question}  POST /api/sessions/:sid/btw/cancel
GET  /api/approvals                    POST /api/approvals/:aid      POST /api/interactions/:iid
GET  /api/sessions/:sid/artifacts      GET /api/artifacts/:aid       GET /api/artifacts/:aid/download
GET  /api/artifacts/:aid/files/*       POST /api/artifacts/:aid/view  POST /api/artifacts/:aid/export {target, overwrite?}
GET  /api/artifacts/:aid/sources?logs=1   DELETE /api/artifacts/:aid
GET  /artifact-view/:token/*           isolated viewer (signed link, no cookie)
GET  /api/sessions/:sid/capabilities   DELETE /api/sessions/:sid/capabilities/:gid
GET  /api/sessions/:sid/tasks          background tasks of the root session (+ read-only subagent mirror)
GET  /api/sessions/:sid/tasks/:tid/output?offset=&limit=   {text, nextOffset, eof, status}
POST /api/sessions/:sid/tasks/:tid/stop   the user's stop → {task}; 404 task_not_found for another session's task
GET  /api/sessions/:sid/goal           the session's goal (GoalInfo or null)  PUT /api/sessions/:sid/goal {objective, tokenBudget?, replace?, expect?}
PATCH /api/sessions/:sid/goal {objective?, tokenBudget?: number|null, expect?}   DELETE /api/sessions/:sid/goal?goalId=&epoch=
POST /api/sessions/:sid/goal/pause     POST /api/sessions/:sid/goal/resume   {expect?: {goalId, epoch}} → {goal}; 409 goal_conflict
GET  /api/workspaces/:wid/tree?path=&cursor=   GET /api/workspaces/:wid/file?path=&maxBytes=&download=1
GET  /api/workspaces/:wid/diff?path=   GET /api/sessions/:sid/changes
GET  /api/sessions/:sid/scratch        scratchpad of the root session: {available, files:[{name,size,mtime}], usedBytes, quotaBytes}
GET  /api/sessions/:sid/scratch/files/*   download one file (attachment, no symlinks); 404 outside the scratchpad
DELETE /api/sessions/:sid/scratch      empty it → {removed, freedBytes}; 409 runs_active while a Forja script runs
GET  /api/forge?workspace=<wid>        Forja engines (JavaScript + Python), limits, scratch settings, Python isolation policy
POST /api/blobs                        raw image body (not JSON) → BlobRef   GET /api/blobs/:hash
POST /api/sessions/:sid/datasets       raw data file, X-File-Name header → 202 {pending} | 200 {dataset}
GET  /api/sessions/:sid/datasets       GET /api/datasets/:did        GET /api/datasets/:did/download
GET  /api/datasets/:did/rows?sheet=&after=&offset=&limit=&sort=&dir=&filter=&column=
POST /api/artifacts/:aid/dataset       ingest a spreadsheet artifact on first preview
GET  /api/events?session=<sid>         the event stream (snapshot, then live frames)
GET  /api/plugins?workspace=<wid>      PATCH /api/plugins/:id {workspace, enabled}
GET  /api/plugin-catalog?refresh=0|1   catalog, cache metadata and installed versions
GET  /api/plugin-catalog/covers/:slug  a cover through the server cache
GET  /api/plugin-catalog/icons/:slug   an icon through the server cache
GET  /api/plugins/package-icon?package=  the icon an installed package ships (alisio.icon)
GET  /api/plugins/packages?workspace=<wid>   installed npm packages, update and restart flags
POST /api/plugins/install {package, version?, confirmed: true}   a catalog package only
POST /api/plugins/update-all {confirmed: true}   every outdated plugin in one npm install
DELETE /api/plugins/package/:name      uninstall a global npm plugin (URL-encoded name)
POST /api/plugins/local/inspect        raw .tgz body (application/gzip) → validation card
POST /api/plugins/local/install {stagingId, confirmed: true}   202 {jobId}
GET  /api/plugins/jobs/:jobId          {phase, phases, done, error?, result?, logTail?}
DELETE /api/plugins/local/staged/:stagingId   discard an upload
GET|POST /api/server/restart           availability, restart (only when supervised)
GET  /api/skills?workspace=<wid>       PATCH /api/skills/:id {workspace, enabled}
POST /api/workspaces/:wid/skills/draft {idea, ...}   POST /api/workspaces/:wid/skills {scope, draft, overwrite?}
GET  /api/mcp?workspace=<wid>          PATCH /api/mcp/:name {workspace, enabled, connect?}
POST /api/mcp/consent {workspace, confirmed: true, remember?}
GET  /api/mcp/catalog?q&category&execution&ready&status&page&pageSize   GET /api/mcp/catalog/detail?name&version?
POST /api/mcp/catalog/sync   (starts one background update; 403 mcp_not_permitted under --read-only)
GET  /api/agents?workspace=<wid>       GET /api/settings?workspace=<wid>  PATCH /api/settings {workspace, key, value}
GET  /api/changelog?version=&lastSeen= POST /api/workspaces/:wid/reload   409 runs_active while it has runs
GET  /api/providers?workspace=<wid>    PUT /api/providers/:profile {workspace, provider, values, model}
PUT|DELETE /api/providers/:profile/credentials {apiKey?, bearerToken?}   write-only
POST /api/providers/:profile/activate {workspace, model}   GET /api/models?workspace=<wid>

Plugin data views answer 404 for an unknown session, plugin or view or a disabled plugin, 400 for invalid parameters and view_failed (502), view_too_large (502) or view_timeout (504) when the view fails, answers more than 1 MiB or takes longer than 5 seconds.

A background task that appears or changes state sends a tasks_changed frame ({sessionId, task}) to the stream of its root session; the output is pulled with offset.

A goal that is created, changes state or spends tokens sends a goal_changed frame ({sessionId, goal}, goal: null once cleared) to the stream of its session, and the snapshot of a (re)connection carries the goal. 409 goal_conflict answers an action based on an out-of-date expect or one the state does not allow, 404 goal_not_found a session without a goal and 409 goal_disabled a new goal while goal.enabled is false; PUT over an existing goal needs replace: true.

Dataset uploads end with a dataset_ready or dataset_failed frame to the session's stream. Data errors use the codes dataset_unsupported (415), query_rejected (400) and query_timeout (408).

Management changes send a catalog_changed frame (commands, plugins, skills, mcp, models or agents) to every stream, so other tabs refresh. Activating a profile answers 409 runs_active while the workspace has runs, and granting MCP access from the web is recorded with the interactive-web source.

On every (re)connection the stream sends a snapshot of each subscribed session (recent messages, text still streaming, the run's start time, pending approvals) followed by live frames; durable events carry their eventId as the SSE id, and streamed text arrives coalesced about 30 times per second. The protocol version is reported by /api/health and in the stream's first frame.

Plugin apps ​

Plugins can register full-page apps (see Plugin apps). When the open project has at least one, an Apps group appears in the left panel just above Projects; with none, the group is not shown at all. The list refreshes every 15 seconds. Opening an app replaces the whole chat area: the header, messages, composer, stats and side panel are hidden while the projects panel stays. Back to chat returns to the conversation with its draft and pending attachments, and an app you left keeps its unsaved edits until you reload the page. Another project is another instance. On narrow screens the Projects button of the app bar opens the usual drawer. Apps follow the interface language and run in a sandboxed frame without network access: they reach Alisio only through authenticated, permission-checked actions.

The example Hello app open in place of the chat: the left panel shows an Apps group above Projects with the app selected, the app bar has a Back to chat button and the app greets Marta.
The example app in place of the chat, with the Apps group above Projects.

Limitations ​

  • Single host: session locks rely on process ids, and the web does not see live changes a TUI makes to a session until the session is reopened.
  • No TLS; remote access is opt-in and meant for SSH tunnels.
  • The provider is per workspace: Activate in this workspace switches that workspace's application (and saves the profile as the default for new starts); other open workspaces keep their provider until they reopen. Saved credentials apply the next time the profile is activated.
  • MCP access granted from the web lasts until alisio serve stops and covers one workspace, unless you choose to remember it for the user.
  • Background tasks are children of the server process: they end when alisio serve stops, and a task started from the TUI is only visible (not stoppable) from the web while that TUI runs.
  • A goal started from the TUI is read from the shared database when the session opens, but the web does not see its live changes (like any TUI activity), and only the process that drives a goal continues it; a goal whose server stops comes back paused.
  • The standalone binary serves the API only; the web UI assets ship with the npm package.
  • The web UI keeps command output, notices and reasoning only while the page is open; a reload rebuilds the conversation from stored messages.
  • Shift+Tab cycles agents only from the message box (see Agent selector); the changelog is English-only and /reload cannot reload plugin code that was already imported.
  • The files panel shows the workspace only: directories added with --add-dir are not browsable, symbolic links are listed but never followed (even when they point inside the workspace), and .gitignore filtering needs git on PATH. Changes only knows files written through write_file and edit_file; files a shell command changed appear in git status, not there.
  • Artifact previews: Esc pressed inside a dashboard never reaches Alisio (use Close); dashboards cannot use the network, localStorage, module scripts or web fonts from files (fonts must be inline data: URLs); a view link expires after 10 minutes (reopening renews it); PDFs use the browser's viewer without sandbox and fall back to a download where there is none. Downloads of multi-file artifacts are ZIP archives.
  • Tables: sorting and filtering scan the sheet (the dataset file has no indexes), so they are off above analysis.data.maxInteractiveRows; cells longer than 4 096 characters are cut in the grid (the dataset keeps them whole); an XLSX needs Python 3.10+ to be read.
  • Uploaded images are stored once per content hash next to the session database and are not deleted automatically.
  • Rich renderers need JavaScript chunks the page loads on demand: Mermaid is large (a few hundred KB compressed across its chunks) and only loads when a diagram is shown.

Released under the MIT License.