Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Browser UI

Start the server, then open the URL it prints:

myco
myco --port 8766 --profile research
myco --port 0 --config /path/to/config.toml --resume <session-id>
myco --bind ::1

The HTTP server uses port 8765 by default; --port 0 picks a free port. The server binds to 127.0.0.1 unless --bind names another loopback IP (localhost selects IPv4). Non-loopback addresses, including 0.0.0.0 and ::, are rejected. Open the printed URL directly; there is no login, token, or browser cookie. Assets and Markdown rendering are bundled with myco, with no frontend build step or CDN. Stop the server with Ctrl-C in the launching terminal.

Auto-continue is an opt-in per-session control in the session menu, off by default. When on, the top bar shows an Auto-continue badge and the runner supplies internal follow-up prompts after completed responses or exhausted output caps. The agent disables it with session_meta action=disable_auto_continue when finished or when it needs user input. The control works during a run: turning it off prevents further automatic follow-ups; Stop cancels the current run. The setting persists across compaction and restart, but enabling it or reopening a session does not start work. Generation, persistence, and automatic compaction errors retry after 1, 2, 4, then 5 seconds, remaining at 5 seconds until recovery. The latest error and wait appear in the transcript. Failed saves retain live state and retry before advancing, without repeating input or completed tools. Turning the control off or pressing Stop interrupts the wait. This mode has no retry count, spending, or elapsed-time limit.

For remote access, start Myco on the remote host, then open an SSH tunnel from the computer running your browser:

ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:8766:127.0.0.1:8765 user@remote-host

This forwards local port 8766 to the remote server's default port 8765. Change the printed launch URL's address to http://127.0.0.1:8766, keeping its profile and session path. Keep SSH running while using Myco. If either port differs, adjust the command and browser URL to match. For a remote server bound to ::1, use [::1]:8765 as the forwarding destination.

Myco does not serve HTTPS or manage certificates. SSH provides encryption and host authentication between the two computers; HTTP stays on each loopback connection. Keep the forwarding listener on 127.0.0.1 or ::1. Files, actions, and live event streams work through the tunnel, including when its local and remote ports differ. Requests must address localhost or a loopback IP; LAN names and other hostnames are refused.

Profiles

One server hosts every existing profile under $MYCO_HOME/profiles/, plus the profile selected at launch. Each has its own URL prefix: /profiles/default/, /profiles/research/, and so on. Names contain only letters, digits, hyphens, or underscores. The selector beside myco switches profiles; /profiles/ opens a chooser. Opening an unknown name does not create it.

Each profile has an independent config, session and image store, prelude, and tool runtime. Its instance starts when first opened, in a separate worker process connected over a private Unix socket. The browser shares one live event connection across profiles and tabs. A failed profile does not stop another profile's work; its next request restarts the instance, and open session tabs reconnect. Recorded history survives, but live tools and interrupted runs are not automatically replayed. Ctrl-C stops every instance and saves active sessions.

--profile (or MYCO_PROFILE, default default) selects the initial profile; it does not restrict the available profiles. --config, MYCO_CONFIG, --model, --effort, --resume, and request-debugging overrides apply only to that profile. Other instances load their own config.toml with the normal defaults. The launch directory's .env is loaded once by the server. Configuration changes require a restart. Relative --config and MYCO_CONFIG paths resolve from the server's launch directory. Relative credential-file paths inside a config resolve from that profile's workspace; absolute paths and ~/ retain their meaning.

Each instance uses $MYCO_HOME/profiles/NAME/workspace/ for local tools and served files; MYCO_HOME defaults to ~/.myco. Missing workspace directories are created on first use. Existing workspace contents stay in place. If the directory cannot be opened, that profile reports a startup error while other profiles remain available. Terminal chat and myco -p keep their launch directory. Profiles separate application state; they are not operating-system sandboxes or access-control boundaries.

URLs generated by the browser, including attachments, Markdown files, new tabs, and resumed sessions, stay under their profile prefix. Page titles include the profile name. Existing unprefixed API and file URLs still address the selected launch profile; old session URLs redirect there. Prefer prefixed URLs in bookmarks and automation so their meaning does not depend on the next launch's --profile.

Appearance and session browser

The browser uses translucent panels with square corners over a locally rendered sky. Their tint follows the same light: cool blue in daylight, warm rose and amber around dawn and dusk, and deeper blue at night. Overcast weather softens the tint. The conversation stays in a central well, with the sky visible on both sides; its glass background continues below the viewport during native scroll bounce. Behind the well the sky is frosted: its light and soft cloud shapes show through, without stars, airplanes, or rain. A window no wider than the well shows only that frosted sky. Settings → Layout sets the well's width in columns of conversation text: 80, 100 (the default), 120, 150, 200, unbounded, or a custom count from 40 to 1000. The top bar and input bar follow it. The choice is saved in this browser and applies to every page and profile; a window narrower than the chosen width uses its full width. The top bar stays fixed, with its glass extending above the viewport to keep both scroll limits covered. Native conversation scrolling and bounce remain enabled. The input bar aligns with tool cards. Colored state borders stay solid and two pixels wide; matching shadows lift those panels from the glass. Activity animations add light without dimming the base border. Floating controls use background blur. The conversation well is the lightest surface; the input bar and top banner share darker translucent glass, with dialogs darkest in front. Translucent clouds drift slowly in three layers: fine high wisps near the top, soft middle billows below them, and broader low clouds near the horizon. Broken banks vary in shape and height; increasing cover adds thin, broad veils. Their feathered edges and overlapping layers let the sky show through. Gentle directional light travels across the clouds through the day, softening in overcast weather. Colors follow daylight, sunset, and night; stars twinkle behind the clouds. An occasional distant airplane crosses the sky, with a faint contrail or navigation lights after dark. These are decorative flybys, with at most two visible at once. Wet weather adds fine rain streaks at several depths; wind influences their slant, and heavier precipitation increases their density. Overcast skies darken the clouds and obscure stars and airplanes. Reduced-motion settings keep clouds and stars still and disable airplanes and falling rain. Hidden tabs pause animation and airplane arrivals. The bundled renderer is adapted from Horizon, with its MIT license retained in the served source.

Settings in the top bar's menu opens a centered modal. Its Sky section contains the weather controls. Choose a city or explicitly select Use my location to reflect its current cloud cover, rain, showers, and wind from Open-Meteo. The panel names the reported weather condition. The layers roughly represent below 3 km, 3–8 km, and above 8 km. Surface wind influences the deliberately slow drift; the art is an impression of modelled current conditions, not a view of individual real clouds. Rain intensity accounts for the feed's accumulation interval; a drizzle or rain code can still produce light streaks when its amount rounds to zero. Snow-only reports do not produce rain. The approximate day/night cycle and direction of light follow that location's clock and update once a minute; they do not calculate seasonal sunrise, sunset, or the position of the moon.

The location is saved in this browser per profile and shared across that profile's tabs. A previously saved location is retained for default. Coordinates are rounded to two decimal places before Myco forwards them to Open-Meteo. City searches also pass through Myco, with location data from GeoNames. The browser contacts only Myco. Weather refreshes every 15 minutes while visible; the server coalesces requests and keeps bounded caches. The free weather service is for non-commercial use.

With no selected location, Illustrated sky uses decorative clouds and the browser's clock without weather requests or location permission. This is also the fallback when weather is unavailable; settings label any retained conditions as last available, and discard them after two hours. Device location requires browser permission and a loopback browser URL; city search does not need device permission. Weather failures do not affect conversations.

SSH controls remote access. Any process or user able to reach the loopback port can use the sessions, tools, and workspace files; there is no per-user permission model. The server retains Host and browser-origin checks to reject requests from other websites. Restarting does not require signing in again.

The home page lists your visible, unarchived sessions, with search, recent update times, and live activity indicators. A pulsing square beside Running in the session header and browser list means generation or a tool process is running. Shells remain Running between model turns; you can submit a new message while an existing process continues. Ready, Stopped, and Saved are idle; Reconnecting… means the current state is unknown. Reduced-motion preferences disable the pulse. Status changes arrive through the shared event stream; the browser also polls for changes made by other server processes.

Hidden tabs defer live transcript rendering to save browser CPU. Generation, tools, and timers continue on the server. Returning to a tab loads the current transcript and activity state while preserving your unsent draft.

The input border also shows the session's state. Running and Compacting carry a travelling highlight and soft glow using the sky's current light color. Ready keeps the neutral glass border; Stopped is steady red. Cancelling and Reconnecting… are amber. Live shell processes use the same sky glow between turns. A stopped run keeps its red border while tools remain open. Hidden tabs pause the animation; reduced-motion preferences keep it still. The text status remains available in every state.

New session opens a separate tab, creates a session, and navigates that tab to its /profiles/NAME/sessions/<id> URL. Session links work with bookmarks, middle-click, and browser tab groups. Click the myco name to return home. --resume <id> opens that session directly from the launch URL.

Choose Archive from the session menu to return to that profile's session browser. The browser shows Session Archived. with an Undo button that restores the session to the active list. Undo remains available after a refresh. Archiving from a row in the session browser shows the same confirmation. Choose Archived sessions above the list to find archived sessions and Restore them. Archived sessions have an Archived badge in the list and session header, alongside their activity status. The session menu offers Restore, which restores the session in place and keeps your draft. The badge updates in other open tabs when you archive, restore, or undo. Opening an archived session does not restore it. Archiving preserves history, the session URL, and running tools; an open tab can continue its turn. It does not archive child sessions. A session held by another myco process must be archived from that process or after it closes. The list refreshes while the home page is visible; changes made outside this server can take up to ten seconds to appear.

Use Rename session in the session menu, or Rename on a session-browser row, to edit its name. Enter or Save applies it; Escape or Cancel discards the edit. Names must be non-empty and use the existing 120-character title limit. The title updates in open session tabs and the browser list. Renaming works while tools run and for archived sessions; it preserves the conversation, URL, and archive status. Saved sessions are renamed without starting a runtime. If saving fails, the dialog keeps your proposed name so you can retry.

The launcher options are documented in cli.

Conversation controls

The floating input bar stays pinned while the conversation scrolls. Enter sends; Shift-Enter or Alt-Enter inserts a newline. Use Attach, paste an image from the clipboard, or drop images onto the composer. Preview and remove images before sending; an image-only message is allowed. PNG, JPEG, GIF, and WebP are supported, with up to 20 selected or pasted images per message. Bytes determine the media type. The browser sends the images when you press Send or Queue; rejected sends keep the draft and attachments for retry.

Click an image in a message, tool result, queued message, or attachment preview to open the fullscreen viewer. The image fits the viewport without changing its aspect ratio. Press Escape, use Close image viewer, or click the space around the image to return. Images also open with Enter or Space when focused. Viewing an image does not send a message or interrupt running work.

When the input is empty, Up highlights the newest user message; press Up again for earlier messages and Down for later ones. The input keeps focus and stays empty. Down past the newest message, Escape, or Latest output returns to the live output. Typing clears the highlight, and arrows edit text normally whenever the input contains a draft.

Mention @path/to/image.png to attach a file on the server instead. Each image is limited by the model’s max_image_base64_bytes (default 5 MiB of base64), and all attachments in one message share a 20 MiB budget, including @path images. Bad paths or oversized images fail before the model request. Image paths with spaces are not supported in @path mentions; selected files may have spaces in their names. Accepted uploads use the profile's image store and remain available in saved sessions after restart. Unsent attachments stay in the current tab's draft.

Submitted messages enter one server-owned queue, drained immediately when the session is idle. During a turn, Queue accepts follow-ups in submission order. The composer shows pending messages. They join the next model request after the current tool batch finishes, alongside its recorded results; if no tools are running, they are sent when the current response finishes. Up to 20 messages can wait per session, shared across its tabs and preserved when a tab refreshes or closes.

Edit holds a queued message in its original position and opens its text and images in the composer. Save & send releases the edited message for delivery; Discard edits releases the original. Both restore any draft you had before editing. Messages behind a held edit wait until it is saved, resumed, or removed. Unqueue removes a pending message. A message already claimed for delivery cannot be edited or removed.

Held messages remain visible after a tab closes or reloads; use Edit to continue from the last saved text, or Resume to send it unchanged. Unsaved text and image changes stay in the current tab. Changes from another tab never overwrite your local edits; you can explicitly send those edits as a new message or discard them. If a response is lost, retrying a save does not send it twice.

Cancel & send queued stops the current run, records cancelled tool results, and sends ready messages with a fresh cancellation token. Held edits stay paused. Queues live in the running server and are not restored after a server restart. A rejected submission keeps its draft. Queued image thumbnails are visible across session tabs and after a tab reload.

The page heading and browser tab title follow the session title, including the first-message title and agent renames during a running turn. Hover over a truncated heading to read its full title. The top bar aligns with the conversation and gives the title the room beside the status, Activity, and the menu (⋯). The menu holds Rename session, Compact, Auto-continue, Archive, Settings, and Feedback; Escape or a click outside closes it. Feedback opens a new issue for myco on GitHub in another tab; nothing is sent until you submit it. Narrow screens keep the title on its own row above the controls. Activity counts appear only while tools, background sessions, or timers are running.

Each tool appears as soon as execution starts, with a truncated argument preview in its collapsed header. Running calls are white; completed calls turn green, and failures red. Expand a block to inspect its complete recorded input, output, images, and outcome. Input shows each top-level key as a bold label above its value. Strings retain their newlines without JSON quoting; nested objects and arrays retain JSON structure. There is no browser verbose mode.

Thinking traces appear in muted italic text with a vertical bar on the left. Expand Thinking to read a trace. Its text selects and copies like the rest of the conversation. Click the heading or the bar to collapse it; the heading also supports Enter and Space for keyboard control.

Tool calls show elapsed execution time to tenths of a second in their headers and in the Activity drawer. Running timers update every 0.1 seconds and survive page refreshes; completion, failure, or cancellation freezes the final duration. Durations are observed by the running browser server and retained while viewing the same thread. Saved history opened after a server restart has no timing data.

Activity opens a right-hand drawer containing all running tools in one list. Scheduled follow-ups appear in a separate Timers section with their own countdowns and cancellation controls. Every entry opens its transcript card. Shells launched with bash start and commands released with background_after or Background remain running in that card, with an advancing timer and pulsing border, until the process and its output streams finish. The same border appears on Activity entries. Reduced-motion preferences keep the borders steady; hidden or disconnected tabs pause their animation. Calls still blocking the assistant appear first, followed by retained processes, with the same controls and styling throughout the list.

Process status refreshes every second without consuming output. Already connected remote hosts are included; polling never connects a lazy host. Failed observations show state unknown rather than reporting success. A process whose original call is outside the active thread retains a reachable card through compaction. The drawer starts closed; its button shows the activity count. Close it with its close button, Escape, or a click outside it.

The assistant collects additional output with bash read; status polling does not drain it. Visibility covers Myco-owned handles. Launch the target program directly with background_after or bash start to retain one. Recognized nohup invocations in those command fields are rejected with this guidance. Arbitrary daemonized descendants are not independently discovered or adopted.

Running shell calls offer Background in their tool header and in Activity. It releases that call's foreground wait, so the assistant can continue once any other calls in the same batch finish. The process keeps running and its result records a session_id for later bash reads or close on the same host. A backgrounded one-shot exec keeps its original closed stdin; use read, signal, or close, not write. Backgrounding an existing shell-session read or write returns its current output without stopping that session.

Background is separate from Cancel: subsequent turn cancellation does not stop an already backgrounded process. Its original exec timeout no longer applies; the process runs until it exits, is closed, or its owning runtime/host ends. Backgrounding does not start another process, so it may retain an already running exec even when the admission limit for new shell sessions is full. Tabs reconnect to the same background tasks, but server restarts do not restore processes. Saved cards without a current handle show not running. Process identity distinguishes repeated uses of the same session_id and keeps hosts and owning sessions separate. Tool results retain an optional resource reference (id and instance_id); older stored results remain readable. Live status is a browser observation and does not rewrite the original result or consume output.

Session timers and event inbox

Ask the assistant to check back later, for example, “Check the build in two minutes.” In server sessions, the local timer tool supports:

ActionInput
Set a delay{"action":"set","after_seconds":120,"message":"Check the build and report its result."}
Set a time{"action":"set","at":"2026-09-25T09:00:00-07:00","message":"Check the deployment."}
List pending timers{"action":"list"}
Cancel before delivery{"action":"cancel","timer_id":"UUID"}

Use exactly one of after_seconds or at. Absolute timestamps require a timezone. Timers accept delays from 0.1 seconds to 30 days; the worker checks deadlines every second. There can be up to 20 pending or running timers per session, with messages up to 8000 characters. Scheduling returns immediately and does not keep a model request open.

When a normal-priority timer becomes due, its message joins the same FIFO as user follow-ups. An idle session wakes automatically; a busy session receives it at the next settled tool/model boundary. A full queue retains the overdue timer until space is available. Held edits keep their queue position. Due timer messages can be edited or unqueued using the normal controls, and appear under SYSTEM · Timer fired when delivered. Their origin remains visible after refresh and in saved history.

Activity → Timers shows local due times, countdowns, and Cancel timer. Countdowns update every 0.1 seconds below one minute. An idle session with a scheduled timer shows Waiting in its page and the session browser. Cancel applies only to that timer; once delivery has started, use the normal run controls.

Timers belong to their session and profile. They survive tab closes, model changes, and compaction. Archiving preserves them; Cancel run stops the current turn but does not cancel scheduled timers. Timers and event triggers are saved before acceptance. Starting the server restores profiles and sessions with pending inbox entries, including archived sessions, without requiring a browser tab. Overdue events run unless their expiry has passed. Ordinary queued user messages are still held in memory. Timers are one-shot; recurring schedules and terminal-only timers are not supported.

The inbox tool supports post, list, and cancel. Posting requires message; at or after_seconds schedules it, otherwise delivery is immediate. event_id is an optional stable UUID for retrying the same post. Scheduled posts with a stable ID require at rather than a relative delay. Cancellation requires that UUID. list includes pending, delivering, running, and recent completed, failed, interrupted, expired, and cancelled events.

Both timer set and inbox post accept priority (normal, urgent, or time_sensitive) and expires_at (RFC 3339 with timezone). Normal delivery backgrounds active bash calls and waits for a settled tool/model boundary. Urgent and time-sensitive delivery takes priority over ordinary queued input, including held edits. It first requests backgrounding for every active bash call, waits for their results, then cancels the current turn and starts a fresh turn with the event. Other active tools receive normal cancellation. Retained background processes remain owned by the same session. Cancellation already requested by the user settles before any event starts.

Expiry applies only before delivery starts; it never kills a running turn or process. The Activity → Event inbox section lists events awaiting delivery with their text, status, priority, due time, expiry, and cancellation, soonest first. Delivered events move under Read; cancelled and expired events move under Not delivered. Both lists start collapsed and show the newest first. A delivered event's full text stays in the conversation. Scheduled timers keep their countdowns in Timers. Event text remains ordinary input; priority does not grant approval or additional tool authority.

Each session accepts up to 100 active events in total and retains its last 100 terminal outcomes. Idempotency lasts while the entry is retained. Reusing an ID with different input is rejected. Pending event edits and held delivery survive restart. A delivery claim is reconciled against saved input across all session threads: input already committed is marked interrupted after a crash and is never replayed; an uncommitted claim becomes pending again. Running effects are not replayed or represented as successfully completed after a restart.

External triggers use the profile API (the same origin checks as other actions):

curl -X POST "$MYCO_SERVER_URL/api/sessions/$SESSION_ID/inbox" \
  -H 'Content-Type: application/json' \
  -d '{"event_id":"4c2f8996-5963-4c56-b495-d2c9c2166411","message":"Build finished; review its result.","priority":"urgent"}'

event_id is required for HTTP posts; generate a fresh UUID for each distinct event and reuse it when retrying. Optional at and expires_at are absolute RFC 3339 timestamps. A successful POST returns HTTP 202 and the accepted entry. GET /api/sessions/{id}/inbox returns {"events":[...]}; POST /api/sessions/{id}/inbox/{event_id}/cancel cancels pending delivery. Session and profile ownership apply to every operation. Inbox files live under the profile's inbox/ directory with their own versioned format; the conversation file format is unchanged.

Compaction activity

Manual and automatic compaction update the activity indicator to Compacting. Compaction cards, summaries, internal resumption instructions, and prelude-change notices are hidden from the conversation, including after reload. Automatic compaction continues the task; manual compaction returns to ready. Failures remain visible so a stopped or unsuccessful operation can be diagnosed.

Assistant responses render Markdown headings, lists, tables, task lists, blockquote text, code blocks, links, footnotes, and images. Loaded transcripts arrive with server-rendered HTML, so opening or reloading a session does not briefly display raw Markdown or request formatting once per message. Streaming updates also render on the server, with requests coalesced per message; the browser keeps existing formatted output while waiting for the next render. Adjacent text parts in an assistant response are rendered together, keeping Markdown intact after streaming and page reloads. Footnotes stay within their message and open in the current tab. Text uses one font size; headings use weight and underlines. Tables size to their contents, wrap long descriptions, and keep Markdown's column alignment. Wide tables scroll within the conversation; focus a table to scroll it with the keyboard. Plain HTTP(S) and www. URLs become clickable in messages, queued messages, and tool output, and open in a new tab. Inline and fenced Markdown code stays literal. Bare <br>, <br/>, and <br /> tags create line breaks, including inside table cells. Other raw HTML is displayed as text. Markdown images can reference HTTP(S) URLs, supported image data URLs, or local files. The server rewrites workspace image and file links to /files/ URLs, including browser formats such as SVG and AVIF. Relative paths resolve from the addressed profile's workspace; absolute paths, ~/, and file:// URLs inside that directory also map to /files/. URL-encode spaces and special characters, or use Markdown's angle-bracket syntax for paths with spaces. Saved images use the profile's image store. Explicit PNG, JPEG, GIF, and WebP paths outside the workspace use the /api/image endpoint.

In server sessions, the assistant can call getlink with a path to obtain a URL for an existing workspace file. Pass a literal absolute or workspace-relative path; the tool encodes spaces and special characters. For example, path: "charts/rain.png" returns /profiles/NAME/files/charts/rain.png. Use that URL in ![Rain](URL) or [Open chart](URL); Markdown uses the same file URL mapping. URLs are relative to the browser's origin, so SSH tunnels can use a different local port. Missing files, paths outside the workspace, and escaping symlinks return a tool error. Copy artifacts from other hosts or directories into the profile workspace before requesting a link. The tool does not upload or copy files; the server serves their current contents.

GET /files/path/to/file and HEAD expose regular files below the addressed profile's workspace, including dotfiles. Anyone able to reach the loopback port can read them. Traversal and symlinks that escape that directory are refused. Files stream without loading the whole document into memory; a single byte range supports media seeking on GET requests. HEAD always describes the complete file. With If-Range, the server returns the full file because it does not issue validators. A directory with index.html displays that file; directories without an index are not listed. Workspace files have no upload or write route; message attachments are stored separately in the profile's image store.

Workspace HTML and SVG have their own restrictive content policy. Static HTML, relative images, and styles render; scripts, forms, and embedded frames are disabled. This keeps generated or checked-out content from executing with the conversation UI's access to the local API. Interactive applications need a separate preview origin instead of weakening this policy on the Myco origin.

USER and ASSISTANT headers show the browser's local date and time on the next line, followed by elapsed whole minutes in parentheses, such as (2 minutes ago). The age updates while the page is open and when returning to a hidden tab; hover over the timestamp to see the full local date, time, and time zone. Saved timestamps remain UTC. A reply that starts with tools gets its ASSISTANT header before those calls, live and on replay. Recorded messages use the turn's saved acceptance time; older turns without one show unknown. The input box has no timestamp.

The model selector in the input bar lists the active configuration's model keys. Choose a model between turns to use it for subsequent requests and compaction. Switching preserves conversation history and live tool sessions, and records the change in the session. An unavailable model leaves the current selection unchanged and shows an error. The selector does not change the configured startup default.

The input bar shows Context used versus the selected model's capacity and the Input, Output, and Cached token counts. Context and Input use the latest provider-reported input count, including cached tokens; Cached is a subset, not an additional cost to the context window. Output accumulates across the current or most recent run's model requests, including tool round trips. These are recorded measurements, not a live estimate of the next prompt: output and newly added messages are not included in Context until the next request reports them. Hover over a count for its exact value and meaning.

Counts refresh during a run after each completed model request is checkpointed, and survive page reloads and server restarts. A dash means no measurement is available. Compaction and model changes invalidate the old counts until another request reports usage. A failed or cancelled request retains the last recorded measurement if its context is unchanged. These counts are not session-wide billing totals.

New opens a fresh session in a new tab; the current tab, draft, and running turn stay in place. Compact creates a successor thread without changing the session URL. These controls also accept /new, /compact, and /resume <id> in the input; /resume navigates only the current tab. Other slash commands are not available.

Automatic compaction is enabled for every model. auto_compact_at is a fraction of its context window in (0, 1], defaulting to 1.0; use 0.8 to compact earlier. Before sending a message, the runner checks the last known prompt size against the selected model's threshold. This sizing estimate survives model changes and restarts, while the usage display waits for a report from the new model. Selecting a model alone does not compact. New input is included when compaction runs. Reopened sessions restore their last recorded model. If it is no longer configured, the server uses its default and shows a notice. When the threshold is reached, the runner settles pending tools, saves a summary in a successor thread, and continues the task automatically. Queued follow-ups join that continued context. Manual Compact finishes after creating the thread and waits for your next message. Transient compaction response failures retry within the worker's existing request budget, with visible, cancellable waits. The source thread and completed tool observations survive. Exhausted retries or terminal errors stop the run; a later message can try again. An ineffective summary disables further automatic compaction until manual compaction or a session change. Cancellation preserves the source thread.

After compaction the conversation opens at the successor thread, which begins with the recent turns it carried over. Scroll to the top, or use Show earlier messages, to load the thread before it above them; keep scrolling for older ones. Each loads once and keeps your place. A Compacted rule marks where the assistant continued from a summary, and each turn appears once, in its original thread or as carried over. Up-arrow message navigation reaches loaded messages. Earlier threads show their recorded results, not live process state. A reload starts from the successor again. A submission removed after a size rejection is kept in its saved thread for session_history but is not shown again.

Running and resuming

One server can run several sessions concurrently. Each has its own runner, model selection, cancellation, tool state, and writer lock. Tabs on different session URLs work independently; tabs on the same URL observe the same run. Changing a model or compacting is disabled during that session's turn. Another browser server cannot write an opened session while this server holds its writer lock. An unavailable or locked session shows an error without interrupting other tabs.

Refreshing, navigating away, or closing a tab does not cancel its active turn. Reopening its session URL reconnects to its output and live tools. Cancel run affects only that session; Ctrl-C in the server terminal cancels all active turns. Opened sessions and their locks stay alive until the server stops. Request retries do not submit the same action or create the same session twice. If submission fails, the input draft remains available. Tabs share a live-output connection through a browser SharedWorker so a tab group does not exhaust HTTP connections. Only tabs viewing a session fetch its full history. Live events carry deltas and small refresh notices; a slow connection resynchronizes subscribed views without replaying every running session's history. Activity bursts coalesce into a session list refresh, and each session view allows one snapshot request at a time. Reload open tabs after upgrading the server to use the updated browser worker.

Restarting the server and resuming a saved session restores conversation history, not old bash processes or editor state. Uncertain tool outcomes from an interrupted process are recorded without rerunning their effects. Compaction within a running session preserves live tools. After a server restart, reopen your saved session URLs directly. Keep the same port to reuse bookmarks (--port 0 chooses a new port each time).

Server API and automation

Automated clients use the same loopback HTTP API as the browser. No credential or login request is needed. Use the local forwarding address when connecting over SSH. POST bodies use Content-Type: application/json.

The paths below are relative to /profiles/NAME. For example, list research sessions with GET /profiles/research/api/sessions. GET /api/profiles lists available names and URLs. The browser's global /api/profile-events stream tags updates with their profile; the scoped /api/events stream retains its session-only protocol for automation.

Local tools receive MYCO_SERVER_URL, the current instance's full URL without a trailing slash. Use $MYCO_SERVER_URL/api/... for nested sessions so they stay in the parent's profile. Read this value from the tool environment instead of saving a port number in conversation history; a restart can change the port.

Browser requests must come from the same origin. A supplied Origin must match the loopback address and port in Host; cross-site and cross-port browser requests are rejected, and no CORS access is granted. Native clients can omit Origin and Fetch Metadata. Access to this API includes session tools and shell execution.

RequestBody / result
POST /api/sessions{"request_id":"UUID"} → {"id":"SESSION_ID"}
GET /api/sessionsVisible sessions with busy and status; add ?archived=true for archives
GET /api/sessions/IDSnapshot at change.snapshot, including archived, busy, status, blocks, queued, timers, usage, and context_window_tokens
GET /api/sessions/ID/threads/THREADA replaced thread from the snapshot's earlier_threads: replaced_at and the blocks its successor did not carry over
POST /api/sessions/ID/action{"request_id":"UUID","session_id":"ID","action":{"kind":"submit","text":"PROMPT"}} → 202 accepted
POST /api/sessions/ID/actionThe same envelope with {"kind":"compact"} or {"kind":"select_model","key":"KEY"}
POST /api/sessions/ID/actionThe same envelope with {"kind":"update_queued","message_id":"UUID","revision":0,"update":{"kind":"edit"}}
POST /api/sessions/ID/actionThe same envelope with {"kind":"cancel_timer","timer_id":"UUID"}
POST /api/sessions/ID/cancel{"session_id":"ID"} → 204
POST /api/sessions/ID/background{"session_id":"ID","call_id":"UUID"} → 202; use the running tool block's background_id
POST /api/sessions/ID/archive{"session_id":"ID","archived":true} → 204; false restores
POST /api/sessions/ID/rename{"session_id":"ID","title":"New name"} → 204; blank titles are rejected
POST /api/sessions/ID/auto-continuePersist the mode with {"session_id":"ID","enabled":true} (or false); allowed during a run
GET /api/eventsServer-sent events with session IDs, revisions, and changes
GET /files/PATH, HEAD /files/PATHProfile workspace files; GET supports a single Range: bytes=START-END

In HTTP snapshots, non-user message blocks include html alongside their original text. The HTML uses the same escaped Markdown and profile workspace URL rewriting as streamed output. It describes that snapshot's text; clients applying later text deltas must discard the cached HTML. Live events remain compact deltas and refresh notices, without rendering unobserved conversations.

Use a fresh UUID per operation and reuse it when retrying that operation. Submit actions may include images, an array of base64 image data URLs or existing image-store references from this profile. text may be omitted when images are present. The server validates image type and size, then keeps image-store references in its queue and history. Snapshots include attachment_limits for the selected model; queued messages include their image references. The action route allows up to 22 MiB of JSON for the image budget and text envelope; other JSON routes retain their 2 MiB limit.

Each queued entry has request_id, revision, and state (ready, editing, or sending). An update_queued action names that entry and its current revision. Its update.kind is edit (hold), save (replace text and images and release), resume (release unchanged), or remove. Each accepted edit, save, or resume increments the entry revision. Saved messages retain their original acceptance time and queue position. Stale revisions, removed entries, and messages already claimed for delivery return 409. Reuse the mutation's request UUID to retry it; the message UUID remains its original submission ID.

Creation uses the UUID as the session ID and survives restart without creating a duplicate. Action deduplication lasts for the running session worker; after restart, inspect the saved snapshot before deciding whether to submit again. A 202 response means accepted, not finished. Wait until the snapshot is idle, then inspect its output and errors and verify the task's artifacts.

To create a hidden child, include "parent_session":"PARENT_ID" in the creation body. The parent must exist in the addressed profile. Add "fork":true to copy its saved context; a fork requires a parent. Children have independent runners and tool state, remain hidden from both normal and archived listings, and can be opened directly by ID. Retrying creation keeps its original context. Model selection is a separate action before submission.

For example, a Python client can create a session using only the standard library. Supply the launch URL through MYCO_LAUNCH_URL; when using a tunnel, first change its address to the local forwarding address:

import json, os, urllib.parse, urllib.request, uuid

launch = os.environ["MYCO_LAUNCH_URL"]
url = urllib.parse.urlsplit(launch)
origin = f"{url.scheme}://{url.netloc}"
parts = url.path.split("/")
assert len(parts) >= 3 and parts[1] == "profiles", "Use the printed profile URL"
base = origin + "/profiles/" + parts[2]
client = urllib.request.build_opener(urllib.request.ProxyHandler({}))

def post(path, payload):
    request = urllib.request.Request(base + path,
        data=json.dumps(payload).encode(),
        headers={"Content-Type": "application/json"})
    with client.open(request) as response:
        return response.read()

session = json.loads(post("/api/sessions", {"request_id": str(uuid.uuid4())}))["id"]
post(f"/api/sessions/{session}/action", {
    "request_id": str(uuid.uuid4()), "session_id": session,
    "action": {"kind": "submit", "text": "Explain this repository"}})

API-created sessions use the addressed profile's workspace and runtime; no new process or model configuration is needed per child.