5. Sessions
A session is one continuous conversation context.
5.1 Persistence
AVL Code appends every message to disk; power loss, shutdown and restart never lose data. Each message has a timestamp and an author tag. Tool calls, plan edits and approval results are recorded as their own cards — the whole session works as a complete engineering audit trail.
5.2 Session operations
- New: session tab bar → New, or
⌘N/Ctrl+N. One workspace keeps at most one empty draft session: hitting+,⌘Nor/newreuses that one draft, and it doesn't appear in the sidebar until you've sent the first message — no more piles of "New session 1 / 2 / 3 …" ghost drafts. - Switch: click a tab, or
/s <short-id>on WeChat, or search session titles in the header command palette (Cmd+K). - Rename: right-click → Rename.
- Smart naming: sessions you haven't manually renamed get an LLM-suggested short title after the first AI reply (default on, toggle in Settings → Context); manually-renamed sessions are permanently exempt from auto-rename. The Rename dialog has a "Smart Name" button that asks the LLM to propose a new title any time.
- Archive: right-click → Archive. Archived sessions leave the main list and can be restored from Session Archive.
- Export / Share: right-click → Share — Save screenshot, Save HTML or Copy as Markdown; "Export session (.zsession)" packs the whole session into a portable archive file. Exported HTML matches what's on screen: it follows the Compact density tier (grey-scale slim rows + same-kind calls grouped with counts, failures still counted), renders sub-agents as cards, and colours user-message bubbles by source.
- Duplicate as new session: branch a new session from this one; the original is unchanged.
Right-click summons the menus directly: session entries and the two title-bar ⋯ menus above them open on a plain right-click — no need to aim at the ⋯ button; a menu close to the bottom of the window is no longer clipped, and right-clicking no longer text-selects the entry.
5.2.1 User-message timestamp toggle
A timestamp label sits to the left of your own messages. Clicking cycles through three formats:
| Format | Example |
|---|---|
| Relative time | Just now, 3 min ago |
| In-session offset | +1m23s (since first message) |
| Absolute time | 14:32 |
Each session remembers its own preference.
5.3 Message anatomy
A conversation is a sequence of "message cards":
- User message
- Assistant reply (streamed; can be interrupted mid-stream)
- Tool call (collapsible card containing parameter summary, elapsed time, result)
- Plan / todo update (structured, separately editable)
5.3.1 Code blocks: syntax highlighting + one-click copy
Fenced code blocks (```) in the conversation render as cards with a header:
- Syntax highlighting: powered by highlight.js (
lib/common), coloured by language; unknown languages fall back to plain-escaped text and still render correctly. - Language label: shown in the top-right of each code block (e.g.
python/go/tsx). - Copy button: top-right one-click copies the whole block to the clipboard — no manual selection; on success the button briefly reads "✓ Copied".
- Line-number grid preserved: cross-line syntax spans are split per source line and paired up, so the line-number column and code column stay aligned — highlighting never breaks the row grid.
5.4 Message-stream control
- While the assistant is running, the send button on the right becomes Stop — pressing it cancels the main assistant and all sub-tasks together.
- If you want to add something mid-run, just press Enter to queue it — it will be merged after the current turn instead of interrupting.
- Each tool-call card can be expanded / collapsed / copied independently.
- Press ↑ / ↓ in the input box to recall input history; executed
/slash commands are now included too (normalized to/<name> <args>), ready to re-run or edit.
5.4.1 Restate intent: agree on the goal before starting
To avoid going off the rails from the first move, AVL Code runs a lightweight confirmation on the session's first message: the AI first restates the goal as it understands it and outlines how it plans to proceed (in multi-agent mode it also gives a phased plan — split into sub-tasks → dispatch sub-agents in parallel → hand back to you for review), and waits for your nod before acting. It's on by default and can be turned off under Settings → Agent → Reflection (toggle: Restate intent).
- Confirmation happens inline in the message stream, not a popup, styled like a normal message.
- It auto-starts after 60 s by default; hovering over the confirmation pauses the countdown so you have time to read.
- The confirmation offers three choices:
- Confirm & start — accept the AI's restatement and go;
- Edit — tweak the restatement before sending (both goal and plan are editable);
- Keep my original — don't adopt the restatement, continue with your original message instead.
- Clicking "Cancel" puts your original message back into the input box, intact — nothing is lost, so you can refine and resend.
- Only asks when it should: it triggers solely on the first message you typed yourself (not auto/program-triggered), and only when that opening message is short (≤30 chars) or simple; a long, clear opening (multi-line, a numbered list, or more than 3 separators like 、 or commas) is taken at face value and runs straight away.
- To manually re-confirm intent on a message, pick "Analyze intent & restart from here" from that message's actions menu (it ignores the conditions above, forces one restatement, and restarts from there); it first hides the messages after the restart point and places the confirmation below them so you can see the scope.
- Choice stats: how often you Confirm / Edit / Keep original / Auto-start (timeout) / Cancel, and the character counts, are recorded and shown as a table under Settings → Agent → Reflection, so you can review whether this confirmation step is actually helping.
5.5 The "generating" card
- Token count interpolates smoothly: the streaming token counter no longer jumps; it interpolates to the target value (~0.4 s) with a brief brand-blue colour change and a tiny upward bump. With "reduce motion" on, it just jumps.
- Elapsed time tiered: under 60 s shows
5s/12s; over a minute upgrades to1m 30s; over an hour,2h 15m 30s. Short tasks see no visual change; only long tasks switch units. - Context-usage ring changes colour by tier: the ring showing the current context-window usage changes colour by tier — yellow at 75%, red at 90% — to flag more prominently that you're approaching the context limit (time to continue / compact).
5.5.0 Message density (Normal / Compact)
Settings → General → Message density offers two tiers (config field message_density, default detailed); the "Density" slider in the header quick menu's "Display" group switches it in one click, without opening Settings:
| Tier | Behaviour |
|---|---|
Normal (detailed, default) |
Full cards + parameter preview / argsSummary; zero regression |
Compact (compact) |
No cards, grey-scale slim rows, minimal chrome; only process rendering is compacted — your prompts and the AI's reply text show in full as usual; row height is no longer compressed |
In Compact mode, process content auto-groups with counts:
- Consecutive tool-call results merge into a single row with a count.
- Multiple thinking + tool-call blocks merge into a "Thinking N · Tools M" row (with a pop animation on count change).
- In-flight tool calls without results yet merge into "N tool-call attempts".
- Any failures in a summary row are tinted light red — visible without being harsh.
Density switching keeps scroll position stable, no jitter; the legacy "collapsed" tier auto-migrates to "compact".
5.6 Scroll and follow
- Auto-follow tracks the bottom during generation.
- As soon as you scroll up — the "Jump to bottom" button appears, meaning you're reading; auto-follow stops.
- Dragging the scrollbar up during generation no longer pins you down: while the AI is streaming, dragging the scrollbar up to re-read earlier content used to get yanked back to the bottom on the same frame (the "won't move" feeling); now an upward drag releases auto-follow synchronously so you can scroll up freely, and you return to the bottom to resume following the newest output.
- Click "Jump to bottom" or scroll back manually and follow resumes.
- The frame-level race between the wheel event and the streaming chunk has been fixed; you won't be interrupted any more.
- List-wide jitter fixed: in some situations the message list would rapidly bounce up and down — root cause was a few messages having their heights re-measured repeatedly. Realized heights are now cached (including a height of 0); the list no longer ping-pongs, and scrolling is steadier.
- "Jump to bottom" lands in one click: previously you had to click several times before it actually stuck at the bottom. Now it continuously re-pins until the content height (
scrollHeight) settles — one click is enough, and first-open of long sessions no longer shows the "the button feels unresponsive" multi-click behaviour. - Rapid duplicate Send clicks no longer fire multiple times: a synchronous in-flight interception ensures rapid clicks send only one message.
5.6.2 Long-session lazy paging
Opening a very long session shows the most-recent segment first, then loads earlier messages page-by-page as you scroll up:
- Scroll to the top and keep scrolling → the previous page loads, with a centred progress indicator when multiple pages are needed.
- Under the hood: a line-offset index makes paging
O(range)instead ofO(scan-from-line-1), a frame is yielded between multi-page loads, and deduplication is built-in — long sessions open and scroll noticeably faster and feel more responsive. - The session-outline jump correctly locates messages that haven't been loaded into the window yet — it loads up to that point first, then scrolls to it.
- Display-fold of oversized single messages is off by default now — previously huge single messages were collapsed into a summary for performance; the new default keeps them fully expanded. Re-enable folding from settings if you want it back.
5.6.0 Session outline + one-click jump
To the left of the session title there's a new session-navigation button (a List icon). Clicking it opens a table-of-contents popover:
- Lists every user prompt in the session, taking the first non-empty line as the entry title; whitespace is collapsed; entries over 120 characters are truncated (with
…). - Click an entry → smooth-scroll to that message and close the popover.
- Current entry highlighted (plain greyscale, restrained professional style) — determined by the user message currently visible in the viewport, refreshed live as you scroll, so the last entry is also selectable correctly (no longer stuck on the first).
- Dismiss:
Esc/ click outside / window resize. - Virtualization-safe: locates by
data-message-id(placeholder messages also carry that attribute with the correct height), so long sessions jump accurately. - The button only appears when there are user prompts to jump to; empty sessions don't show it.
- Rendered via the browser Popover API (
popover=manual+ top layer) — doesn't rely on z-index, so it can't be obscured by dialogs / drawers / etc.
5.5.1 Session strips: the form follows the window width
The File changes / Memory / Todos / Plan / Goal / Guidance / Result draft / Attached materials / Suggested Tasks / Self-check gate strips pick their presentation automatically from the window width:
- Narrow window — a row above the input box: several strips sit side by side above the input box and auto-wrap when there isn't enough width (filling the line adjacent to the input box first, then stacking upward); the gaps tighten and labels on the non-bottom rows revert to full pill form — no clipping, no overlap. Opening one shows a popover that pops upward.
- Wide window — a vertical status stack at the top-right of the message area: when the window is wide enough these strips move to a vertical stack at the top-right of the message area and stop taking up space above the input box. Opening one shows a stacked drawer on the right: no scrim, no squeezing of the body text; it is anchored to the stack as a whole, so the drawer stays put when you switch between strips, and it opens leftwards or rightwards depending on the space available on either side. On very wide windows the drawer slot is permanently reserved, so opening and closing it never makes the body text jump sideways.
With density set to "Compact", the row form above the input box is always used. Switching form only changes position, never the width of the body text.
5.6.1 Session-level path attachments: let the AI temporarily access folders outside the workspace
The input box's "+" menu has two new entries:
| Entry | Permission |
|---|---|
| Attach directory (read-only) | AI may read / list / search; no writes |
| Attach directory (read-write) | Read-only plus write / delete / create |
Typical use: let the AI reference another project's code, write outputs to a designated directory, etc.
A confirmation appears first, clearly stating which permission you're granting. Once attached, an "Attached Paths" strip appears (above the input box on narrow windows, at the top-right of the message area on wide ones — see §5.5.1) showing the current session's attachments and letting you remove them at any time.
Scope and safety:
- Attachments are per-session; other sessions are unaffected.
- Persist with the session; still in effect after restart (re-applied automatically in the background — no need to re-attach).
- Only file I/O (read / write / search / list) is broadened; other safety policies stay in place.
- Search results under attached directories carry absolute paths, so the source is unambiguous.
- The session identity is injected into the call chain by the system, so the AI can't forge its identity to use another session's attached permissions.
Unified "attached materials" entry: session-attached directories / files and the workspace's samples / attachments are now merged into a single "attached materials" strip, with attach progress and status visible in real time. The two keep clear, distinct semantics: removing a session-attached item only unmounts it (the original file isn't deleted), while deleting a workspace sample / attachment goes through an inline double-confirm — so nothing is deleted by accident. The strip auto-refreshes when files change (no manual reload after the AI edits files), presents directories as a multi-level tree, and ignores macOS junk files (e.g.
.DS_Store,__MACOSX/). A local workspace file added to attached materials now shows as "Ready" (no longer the misleading "Upload / Uploaded" for local files) — "Upload" is reserved for genuinely-outbound cases (submitting feedback, upload scanning); the scope badge also uses a more legible indigo colour.
5.7 Clickable links and file paths in messages
URLs and file paths with common extensions in AI replies (including in inline-code backticks) are clickable:
- Workspace-internal file paths → reveal in your system file manager.
- External URLs → open in your default browser.
- Paths inside inline-code backticks (e.g.
`src/main.go`) are also clickable; plain inline code is untouched. - Clickable code references (verifiable grounding): code locations cited in the AI's answer, and
file:linein tool results, are now clickable too — for a workspace file with a line number, "View" opens a source preview scrolled to that line, so you don't have to flip there yourself. This makes thefile:lineevidence fromcode.search/code.askvisible and checkable. - Click pops up a non-modal floating toolbar (primary action "View / Open" + "Copy"); click elsewhere, scroll or
Escto dismiss. - The toolbar uses the browser Popover API and renders at the top layer; even clicking from inside the "About", "Release notes" or "Update" dialogs it isn't obscured.
- Path detection is precise: only strings with a known extension (e.g.
src/main.go,README.md) count as clickable paths; plain directories or unknown-extension fragments (e.g.a/b,/etc/hosts) are no longer mis-flagged.http(s)links are always clickable. - AI replies only: links and paths in your own messages are no longer clickable, to avoid mistapping. Only the assistant's replies surface the inline toolbar.
- Safety: non-
http(s)protocols are blocked; file reveal has workspace-defence checks; it can't escape the workspace.
5.7.1 File preview drawer
Click a file link in the conversation and the inline toolbar has a Preview button next to "View / Open" and "Copy" — read the contents right there without switching to an editor.
- Far more than Markdown: common source code (Go / TypeScript / Python / Rust / Java / C·C++ / Ruby / PHP / Swift / Kotlin, …), config (YAML / TOML / JSON / INI / .env), front-end (HTML / CSS / Svelte / Vue / Astro), scripts (sh / bash / ps1 / bat), text and data (md / txt / csv / tsv / xml / sql / diff), images and common binaries (png / jpg / svg / pdf / zip, …), plus extension-less well-known filenames such as
Dockerfile,Makefile,go.modand.gitignore. - Source code gets syntax highlighting, using the same palette as code blocks in the conversation; very large files (over roughly 256 KB) aren't coloured, but line numbers and navigation still work. An icon matching the format is shown in front of the file path so you can tell the type at a glance.
- Binary files are shown as a hex dump (hex + character column, up to 64 KiB) instead of garbled text. Raw bytes of binary files on remote workspaces can't be fetched, and that is stated plainly rather than faked.
- What can be previewed: the file is looked up inside the workspace first, then matched against the directories / files attached to the current session — attached files preview too. Paths that can't be previewed simply don't get a Preview button, so you never click one that does nothing.
- The form is picked automatically from the window width: with enough room outside the content it stays docked as a right-hand side panel (the body text isn't narrowed); when there isn't enough room it falls back to a floating drawer (
min(820px, 94vw)wide, no scrim), in which case the body text shifts into the left-hand margin to get out of the way — again without being narrowed. - Find in file: search the contents of the file you're previewing right inside the drawer — matches are highlighted, the current position is shown as
index/total, Previous match / Next match step through them one by one, and Match case can be toggled on or off. When there are too many matches only the first few are marked, and you're told so. - Size: preview body is capped at 1 MiB (truncated with a marker beyond) — great for a quick look; for the full file use Save As.
- Save As button (in the drawer header) opens the system Save dialog and writes the source file out using a 64 MiB ceiling, preserving the entire file; cancel = no-op.
- A lone file path inside a code block is previewable too when the file exists: existence is checked asynchronously on render and only then is the path upgraded to clickable, so you never click through to a missing file.
Pressing ↑ to walk back through input history shows a N/N position label at the top-left of the input box (newest = total, oldest = 1/N) so you don't lose track. Draft state, empty history and out-of-range don't show it.
Editing while in history → automatically becomes a new draft: editing a historical entry would previously look like "this history was edited"; this release makes the semantics explicit — edit means new draft; the original history is untouched. Pressing ↑ again still saves your current draft so you don't lose work.
5.9 Cross-device handoff
If you've bound a WeChat channel:
/s <short-id>on the phone resumes an in-progress desktop session.- The desktop can also take over a session that the phone started.
- Only one device "holds" a session at a time; the other side auto-syncs read-only.
See Chapter 19.
