---
title: Team Chat (built-in channels and direct messages)
---

# Team Chat (built-in channels and direct messages)

## What it is

Team Chat is a built-in, real-time team messenger for your company — **channels** and **direct messages** — delivered live to every device (the desktop app and your phone) via the cloud. It is a lightweight Slack replacement that lives inside Omniscio and uses the same **Global Auth** identity you already sign in with, so there is no separate account to create.

It renders as a native built-in **virtual project** in the projects sidebar (gated, so it only appears once enabled). The channel/DM list sits in the sidebar pane; the open conversation (messages + composer) fills the main panel. It is **non-spawnable** — it hosts no Claude coding sessions; it is purely a chat surface.

## Where to find it

**Status:** In-development (ships dark). Reveal via **Settings → Lab → Team Chat**, or launch with `AMC_SHOW_TEAM_CHAT=1`. Until enabled it is completely hidden — no sidebar row, no setting effect.

Team Chat renders as a native built-in **virtual project** in the projects sidebar: its channel and direct-message list is the sidebar pane, and the open conversation — the message list plus the composer — fills the main panel. It hosts no Claude sessions; it is purely a chat surface. Two settings screens belong to it: the **Team Chat** toggle in **Settings → Lab**, and **Settings → Team Chat notifications**, which carries the global default and the per-channel overrides but has no sidebar row of its own — you reach it from a channel's bell menu. On a phone the same chat is reachable two ways: through Omniscio's own mobile web access while your desktop is running, or through a standalone phone web app that works with the desktop off.

### Enabling it

Open **Settings → Lab** and toggle **Team Chat** on (or set the `AMC_SHOW_TEAM_CHAT=1` environment variable). It ships **dark** by default — the workspace must be set up before it does anything useful, so enabling it on a machine with no workspace simply shows the "not in a workspace yet" state.

## How it behaves

### What you can do (v1)

- **Channels** — create **public** channels (any teammate can read and post) or **private** channels (only invited members).
- **Browse & join channels** — a **"Browse channels"** entry in the sidebar opens a searchable directory of every PUBLIC channel in the workspace, each with a member count. Open any channel straight from the directory (public channels are readable without joining, like a Slack preview), or **Join / Leave** to add/remove yourself from its member list. Joining needs no special setup — it simply toggles you in the channel's member list.
- **Manage channels (right-click)** — **right-click** any channel in the sidebar to open a context menu with **Edit Channel** (rename or change the topic), **Leave Channel** (public channels only), and **Delete Channel** (permanently removes the channel and all its messages — confirms first). Edit and delete are available to the channel's **creator** or a workspace **admin**; leave is available to any member of a public channel. Uses the same `MenuShell` context-menu scaffold as the rest of the app. The edit dialog pre-fills the current name and topic.
- **Channel archiving (admin)** — a workspace **owner/admin** can **archive** a channel (right-click → Archive), which makes it **read-only** (the composer is disabled; an archive banner is shown) and **hides it from the default sidebar and channel browser**. Archived channels appear in a collapsible **"Archived"** section at the bottom of the sidebar. Unread badges are suppressed on archived channels. An admin can **unarchive** to restore normal operation. The archive fields (`archivedAt`, `archivedBy`) are schema extensions (`.merge()`), so the frozen Phase-0 schema is untouched. A CLI endpoint is available at `PUT /team-chat/channels/:channelId/archive`. Invariants A1-A5: [team-chat-workspace-admin-controls-contract.md](../../.claude/memory/contracts/team-chat-workspace-admin-controls-contract.md).
- **Direct messages** — start a 1:1 or small-group DM. The DM is addressed by a **deterministic id** derived from the participants, so "message Alice" always resolves to the same conversation — no duplicate threads.
- **DM inbox (opt-in, cross-workspace)** — toggle `teamChatDmInboxView` on (Settings → Lab or `AMC_SHOW_TEAM_CHAT=1`) and the DM sidebar becomes a triage view with a **"Needs Reply"** section and a **People / Threads** toggle. DMs from **all workspaces** appear regardless of which workspace is active (cross-workspace persistence, Discord-style) — background Firestore listeners keep non-active workspace DMs live in a per-workspace map, and a `useDmInboxChannels` hook merges them with workspace annotations. The **People** view shows **all workspace members** as potential messaging targets — members with existing DM threads appear first (sorted by most-recent message), followed by members with no threads yet (sorted alphabetically) — so every teammate is reachable without using the "add" feature. Clicking a person opens a **detail pane** (matching the thread-pane pattern — it takes over the message pane at every width) showing all their threads with needs-reply/waiting status pills, workspace badges, per-thread mark-done buttons, and **emoji reactions** (reacting to a DM counts as acknowledging it, clearing its needs-reply status). Clicking a thread opens the conversation, with cross-workspace threads triggering a workspace switch + deferred channel open via `pendingDmOpenTarget`. The detail pane and the thread pane are mutually exclusive.
- **Send & receive in real time** — messages appear instantly for everyone in the channel. Your own message shows immediately (optimistic), and if you go briefly offline — or a send has to retry after a network hiccup — it lands **without ever double-posting**: the client-minted id is the message's own id, so a resend updates the same message instead of creating a second copy (identical on desktop and phone).
- **@mentions** — type `@` to open a teammate typeahead (arrow keys + Enter/Tab to pick); selecting inserts the mention so notifications can highlight them. The mention is parsed into the message's `mentions[]` from the inserted token.
- **Needs-reply clears when you look, and never stays cleared forever (default on)** — toggle `teamChatDmAutoClearNeedsReply` (Settings → Notifications → Team Chat, beside **DM thread inbox**) and a DM stops showing as needing a reply as soon as you actually look at it: the conversation is open and the app window is in the foreground, the same rule that already marks a channel read. The conversation **stays in your DM list** — glancing at someone never makes them disappear from the sidebar — and a **new message from that person brings the tag straight back**, so a look can never bury a conversation. A thread row clears only once you open that thread (an unopened reply shows as an "N replies" line, so it has not been looked at). Turn the option off to go back to confirming each DM by hand with **Mark as done**. The inbox preview embedded in a notice never auto-clears, since you never chose to open it.
- **Mark as done hides the conversation; looking at it does not** — `markDmThreadDone` (the hover ✓, the right-click item, the person pane's per-thread button, and reacting to a message) records a **done** acknowledgement, which also drops the row from the DM inbox views. The automatic clear records a **seen** acknowledgement instead, which only stops the "Needs reply" nagging. Both record **which message** they covered, so either one expires the moment a newer message arrives — an old "done" mark can no longer bury a person's later message.
- **Reactions** — react to any message with an emoji: an **"Add reaction"** button in each message's hover toolbar (desktop) — or the quick-reaction row of the mobile long-press action sheet — opens the emoji picker, and your reactions appear as **pills** (the emoji + a count) under the message — shown only once a message actually has reactions — live for everyone in the channel. Your own reactions are highlighted; **click a pill to toggle** it on or off. Your reaction shows instantly and reconciles with the server (a failed action undoes itself); and if it **can't be applied** — you're offline, or chat is still reconnecting — you get a **brief notice** instead of a silent no-op, so a click is never lost without explanation. Reaction-author _names_ in the pill tooltip light up once the workspace member directory lands; until then a pill shows the count. In a **DM**, adding a reaction also **clears the "needs reply" indicator** for that conversation (the same effect as the "Mark as done" checkmark) — and, like every other acknowledgement, it expires if that person sends something newer.
- **Pins** — pin any message to the channel: a **pin button** on each message toggles it pinned/unpinned, and pinned messages collect in a **"Pinned"** bar at the top of the channel (a count that expands to the list, newest first). **Any channel member can pin or unpin** — the bar and each message's pin state are live for everyone in the channel. Your pin shows instantly and reconciles with the server. A pinned message that has scrolled out of the loaded history shows a placeholder until you **"Load older messages"**.
- **Threads** — reply to a specific message to start a thread. Each message has a **"Reply"** action in its hover toolbar (desktop) or the mobile long-press action sheet, and once it has replies it shows an **"N replies"** indicator that reads Slack-style — a small **facepile of the repliers' avatars** sits beside the count, with the count in the **accent colour** so it stands out as a clickable link (making a busy thread easy to spot). Click either to open the **thread pane** (it fills the message pane at every width, phone and desktop alike — a thread is a view of its own, not a strip beside the conversation) — it shows the original message, its replies in order, and a composer to add your own. A reply appears instantly (optimistic) and is live for everyone; if a reply can't be sent it flips to a **failed** row with **Retry / Delete** (the same safety net as the main composer) instead of sticking on "Sending…" forever; the main channel list stays clean (it shows only the original messages, never the replies). **Leave the pane three ways:** the **back arrow** at the left of its header, the **X** at the right, or **Ctrl/Cmd+W**. All three land in the same place — the topic list when you opened the thread from it, otherwise the conversation behind it. The back arrow is the one that matters once the pane is covering the conversation: it is the only control at the leading edge, where a way out is looked for. And typing an ordinary letter (like `e` or `w`) in the reply box never closes it.
- **Topics — separate named conversations, browsed like an inbox** — a chat is no longer one long stream. Opening someone lands you on their **main conversation** exactly as before; from there a **Topics** button in the header steps up to a **list of that conversation's topics**, read like email: one row per topic with its name, who spoke in it last, what they said, and when. Click a row to open that topic — and **closing it returns you to the list**, not to the main thread, so reading several in a row takes a click each rather than a trip back through a menu. A topic **names itself from your first message** (no typing a title) and anyone in the conversation can **rename** it. **Replying to a message makes a topic too**, starting with the message you replied to, and it appears in the same list — so a side conversation from weeks ago is findable by name instead of only from the message it hung off. Works in **DMs and channels** alike, and on a phone (where the button drops its label so it cannot crowd the name beside it). Topic messages stay out of the main conversation, and the chat list still sorts on real activity. Details + invariants: [team-chat-topic-threads-contract.md](../../.claude/memory/contracts/team-chat-topic-threads-contract.md).
- **Rich composer** — a **formatting toolbar** inserts markdown (bold, italic, inline code, fenced code block, block-quote, bulleted/numbered list — plus Ctrl/Cmd+B/I/E) and an **emoji picker** inserts emoji at the cursor. **Pasting rich content** — a formatted outline, a Google-Docs selection — drops in as clean **Markdown** (bold, lists, headings, and links preserved) at the cursor, honoring your _Paste rich text as Markdown_ setting (hold Shift, or Ctrl/Cmd+Shift+V, to paste plain); a plain-text or file paste is left untouched (files still stage as attachments) — the same behavior the main session composer has. **Send key is your choice** (Settings → Sessions → "Team Chat submit key"): **Enter to send** (default — Shift+Enter for a newline) or **Ctrl/Cmd+Enter to send** (Enter for a newline); **Ctrl/Cmd+Enter always sends** in either mode. **List auto-continuation:** pressing the _newline_ key (Shift+Enter in Enter-to-send mode, plain Enter in Ctrl+Enter mode) on a bulleted (`- `) or numbered (`1. `) list line auto-inserts the next marker; on an empty marker it strips it to exit the list. The box grows as you type, and when the cap is not enough a **drag handle** above the box lets you pull it as tall as you want (desktop only — drag it back down to the resting size and the box goes back to growing with your text), and the composer **rests as a single row** — the **formatting toolbar is revealed on focus** on desktop (so an idle composer never parks a row of buttons above it) and stays **behind a small toggle on a phone** (where the soft keyboard already crowds the screen); every button and the keyboard shortcuts still work whether the strip is showing or not. **Reply focus matches a session's** — opening a channel, DM, or thread drops your cursor in the box from the start (when you keep the default auto-focus on), and pressing **R** focuses it when auto-focus is off — the same reply behavior you get on a session (skipped on mobile so the keyboard doesn't pop up).
- **Voice dictation & FlowVoice** — the Team Chat composer supports the same voice input as sessions. **Built-in dictation** (wake word, mic button, Alt+V, push-to-talk) routes the transcript to the Team Chat composer when Team Chat is the active panel, honoring the shared **Dictation delivery** setting (send immediately or review before sending). **FlowVoice** auto-submit also routes to Team Chat when active, sending the pasted transcript through the real send path. A **FlowVoice mic button** (`FlowVoiceMicButton`) is rendered in the composer's action bar (between the emoji picker and the preview toggle) so users can start/stop dictation with a click without needing the hotkey; it self-hides when FlowVoice is disabled or on mobile (desktop-only). No new settings are needed; the existing voice settings apply to both sessions and Team Chat.
- **Formatted messages** — what you format actually renders: **bold**, _italic_, `inline code`, fenced (syntax-highlighted) code blocks with a **hover-revealed copy button** (top-right overlay; copies clean text via `textContent`), quotes, bulleted/numbered lists, and clickable links all display formatted in the message — through the **same renderer the rest of the app uses** for messages (`ChatProseBlock` inherits the shared `markdownComponents` pipeline, including the `CodeBlock` + `CopyButton` overlay; design modeled after an earlier internal prototype's design patterns). @mentions still show as highlighted "@Name" chips, and an `@name` typed inside code stays literal.
- **Message layout — pick your style** — choose how the message list looks in **Settings → Appearance → "Team Chat message style"**: **Bubbles** (the default) shows rounded **3D bubbles** exactly like the regular Omniscio chat — **your own** messages sit on the **right** in an accent bubble (just the time — your name/avatar are dropped, since it's you), **everyone else's** on the **left** with their name + avatar, each bubble only as wide as it needs (~80%); **Clean** is one calm borderless single stream; **Compact** is dense. Messages are **grouped by sender** — a run of messages from one person shows the avatar/name/time **once**, not on every line. The look stays calm: each message's actions — **react, reply, pin, save, copy, and edit/delete your own** — live in **one small toolbar that appears on hover** on desktop; on a **phone** the toolbar is gone, and instead a small **⋯** sits beside each message — tap it (or **long-press** the message) to open a **bottom action sheet** with those same actions. Reactions + the "N replies" indicator show **only when they actually exist**. The bubble's 3D depth follows your **Chat Depth** setting and its colour follows your theme (nothing new to configure). The same layout carries into the thread/reply view.
- **Real names & avatars** — every message **from a teammate** shows the author's real **display name and photo** (with an automatic initials fallback), resolved live from the workspace member list — never a raw account id. (Your own messages drop the name + avatar — see **Chat bubbles** above.)
- **Agent messages are clearly labeled — everywhere, and impossible to hide** — when a message is posted by a teammate's **agent** (an AI automation acting on their behalf — e.g. via the command-line relay route `POST /team-chat/channels/:channelId/messages`, or a channel's **Session Starter Bot** acknowledgment) rather than the person typing in the app, it is unmistakably marked as **"<Name>'s agent"** with a small **labeled 🤖 "Agent" badge** — so it's always obvious a teammate's _agent_ wrote it, not the teammate themselves. Because an agent posts under the person's **own** signed-in identity (`authorUid`), _whose_ agent it is follows automatically — and the label appears on **every surface the message is read**: the **desktop** message row (including the viewer's OWN agent's messages, and inside the inbox conversation view — the two places a normal own/embedded message hides the name), the **mobile app** (which previously rendered an agent message identically to a human one), and the **notification + inbox preview line** ("Alice's agent: …", for channels, 1:1 DMs, _and_ cross-org connection DMs). An agent reply also **breaks the sender grouping**, so it can never tuck in silently under the person's own messages as an unlabeled continuation (grouping by author alone couldn't tell an agent reply from the person's own, since they share one identity). It is **basically impossible for an agent to post an un-labeled message**: **all three** agent-only send relays — a **channel** post, a **1:1 DM**, AND a **cross-org connection DM** — stamp the agent-authorship flag **unconditionally** (they are the agent-only send paths; humans write directly to Firestore and never touch them), so no code path can omit it (the 1:1 DM path reached parity 2026-08-07 and the cross-org **connection DM** path 2026-08-11 — each closing a gap where an agent DM was authored as the bare human). An agent message also carries a **distinct green "agent" bubble** (not your accent bubble — the `status-agent` tone), so an AI reply is set apart at a glance as well as by the label, on both desktop and the mobile web app. A normal, human-typed message is unchanged. Backed by the additive `authoredByAgent` message field; the "<Name>'s agent" labeling is one shared helper across all surfaces. Invariants: **D22** in [team-chat-desktop-contract.md](../../.claude/memory/contracts/team-chat-desktop-contract.md) · **`an-ai-agent-reply-is-identified-on-mobile`** in [team-chat-pwa-contract.md](../../.claude/memory/contracts/team-chat-pwa-contract.md) · field in [team-chat-schema-foundation-contract.md](../../.claude/memory/contracts/team-chat-schema-foundation-contract.md).
- **The Agent badge is a link back to the session that sent the message** — the green "**Agent**" badge does more than label: clicking it opens the Omniscio **session** that posted that message, so reading an agent's update in chat is one click from talking to the agent itself. It navigates exactly like every other "go to session" link (an archived session, or one in a project you aren't currently in, opens the same way). It works because the machine that sent the message already recorded which of **its own** sessions authored it, in a **local** ledger keyed by message id — and that fact is deliberately **never** written onto the shared message, because a session id only resolves on the computer that owns the session. So a teammate reading your agent's message gets the same badge with no link, rather than a dead one. The badge looks and reads exactly as it always has whenever no sending session is known: another machine's agent, a bot, a scheduled send, or a message older than the ledger. On a message where it IS a link, the badge makes that obvious — it takes a pointer cursor and the standard focus ring, so it never reads as the static label beside it — and **hovering it names where it goes**, showing the sending session's own title (**"Open session Fix the login redirect"**) rather than a generic hint, so you know which session you're about to land on before you click. Invariant: **`an-agent-message-points-back-at-its-session`** in [team-chat-desktop-agent-invariants-contract.md](../../.claude/memory/contracts/team-chat-desktop-agent-invariants-contract.md).
- **Agents can be blocked from Team Chat writes entirely (owner opt-in).** Attribution above makes an agent send _visible_; a separate second layer can _refuse_ it. The **`allowAgentTokenTeamChat`** setting (Settings → CLI Control, **off by default**) governs whether an agent's scoped `$AMC_CLI_TOKEN` may write to Team Chat at all — send a DM or channel message, react, edit, delete, pin, schedule a DM, invite a guest. Off ⇒ every such write over the CLI from a scoped agent token is refused (403), so a compromised MCP server or npm dependency that reads an agent's env can't message your teammates as you — mirroring `allowAgentTokenSpawn`. The global CLI token and your own in-app session are always allowed; reading Team Chat is never gated. This is the layer that covers the react/edit/delete/pin vectors a label can't mark. Invariant: **D22 (e)** in [team-chat-desktop-contract.md](../../.claude/memory/contracts/team-chat-desktop-contract.md); gating in [cli-server-gating.md](../../.claude/memory/cli-server-gating.md).
- **@mentions** — type `@name` to mention a teammate; the mention is parsed into the message so notifications can highlight you, and it renders **inline as a highlighted "@Name" chip** in the message.
- **Edit & delete your own messages** — on desktop, right-click any message for the full action menu (Reply, Pin, Save, Copy text, Copy link); your OWN messages also show **Edit** (inline, shows an "(edited)" marker) and **Delete** (confirms, then offers an **Undo** that re-posts it) below a separator. On a **phone**, tap the **⋯** beside any message (or long-press it) → the action sheet with the same actions. You can only edit/delete your own messages.
- **Copy message link** — every message has a **share icon** (desktop hover toolbar) or a **"Copy link"** row (mobile long-press action sheet) that copies an `omniscio://chat/…` deep link to the clipboard. Paste it anywhere — clicking it opens Omniscio and navigates straight to that channel and message, Discord-style. The link carries the workspace kind + id, channel id, and message id, so it works across both company and self-serve workspaces.
- **Quote a message — or just the part that matters** — **highlight any text inside a message** and a small **Quote** button appears right at your selection; click it and **just that passage** drops into the composer as a markdown blockquote, attributed to whoever wrote it (`> Alex wrote:` above the quoted lines), with the cursor on a fresh line below ready for your reply. To quote a **whole** message instead, use **Quote** in the message's ⋯ menu (desktop) or the **long-press action sheet** (phone). Both routes build the identical block from one shared helper, so they can never drift apart. Two things it deliberately does NOT do: the attribution is **plain text, never an `@mention`**, so quoting someone never _notifies_ them; and the quote is built from the message **as you see it**, so a mention inside the quoted text comes through as `@Bob` rather than the raw account id the message actually stores. A quote is ordinary text you can edit before sending — nothing new is stored on the message, so there is no reply-link to break. Quoting is hidden on an attachment-only message (an empty quote is impossible), it lands in the composer for **the surface you're in** (a thread quote never leaks into the channel box), and it never clears a draft you'd already started. The **highlight button is desktop-only** — phones deliberately disable text selection in the message list so long-press can open the action sheet — and it has an off switch: **Settings → Sessions → "Quote button when you highlight text"** (on by default, `teamChatQuoteOnSelect`) hides the floating button while leaving the ⋯ menu's Quote untouched. Invariants: [team-chat-quote-contract.md](../../.claude/memory/contracts/team-chat-quote-contract.md) (`the-block-shape-is-fixed` … `mobile-quotes-from-the-action-sheet`); the composer's insert handle is `insert-quote-and-insert-text-are-ref-scoped` in [team-chat-composer-contract.md](../../.claude/memory/contracts/team-chat-composer-contract.md).
- **Profile card** — click any author's name or avatar to see a small card with their name, photo, role, and online status. A **Message** button (hidden on your own card) opens or creates a DM with that person.
- **Member directory** — a **Members** button in the channel header opens a directory of everyone in the workspace (avatar, name, role, presence). Each member row has a **Message** button (hidden for yourself) that opens or creates a DM with them. Invariant: `member-surfaces-agree` in [team-chat-desktop-invariants-contract.md](../../.claude/memory/contracts/team-chat-desktop-invariants-contract.md).
- **Invite teammates by email (admins only)** — a workspace **owner/admin** can add new people without leaving chat: an **Invite** button in the sidebar header and a **"Don't see someone? Invite by email"** link at the bottom of the New-DM picker open a dialog to paste one or more email addresses. Invitees are added to your workspace and **appear in chat once they accept and sign in** (an invite is not an instant DM — their chat profile is created on first sign-in). Regular members never see the invite controls. It reuses the same workspace-invite system as **Settings → User Management**, so there's no separate setup.
- **Add team members to Chat (admins only)** — for people who are **already in your workspace** but don't have a chat member doc yet (e.g. they were assigned before Team Chat existed, or the backfill didn't cover them): a **Users** icon button in the sidebar header (next to Invite) opens a picker listing every org member who isn't already in chat. Select one or more, confirm, and their member docs are **created immediately** — no sign-out/sign-in required. Each added member gets a **best-effort email** telling them to enable Team Chat in Settings → Lab. Admin-only (same gate as Invite). Invariants: `adding-org-members-creates-their-member-docs` in [team-chat-identity-contract.md](../../.claude/memory/contracts/team-chat-identity-contract.md), `admins-add-members-directly` in [team-chat-desktop-invariants-contract.md](../../.claude/memory/contracts/team-chat-desktop-invariants-contract.md).
- **Auto-heal (background safety net)** — when you open Team Chat, a **one-shot background check** silently verifies your member doc exists. If it's missing (you were assigned to the org before Lane A existed, or the backfill was incomplete), the Cloud Function **creates it on the spot** — no admin action needed, no sign-out/sign-in. **A second background call (`syncOrgChatMembers`) runs alongside it and heals ALL org users' missing member docs** — so every workspace member appears in the DM picker and member directory even if they never opened Team Chat themselves. Both run once per session, fire-and-forget, silent on failure. This softens the hard ordering prerequisite (backfill before gate flip) — a missed user no longer hits a dead end. Invariants: `ensure-chat-member-auto-heals-on-open` + `sync-org-chat-members-batch-heals-on-open` in [team-chat-identity-contract.md](../../.claude/memory/contracts/team-chat-identity-contract.md), `opening-heals-membership` in [team-chat-desktop-invariants-contract.md](../../.claude/memory/contracts/team-chat-desktop-invariants-contract.md).
- **Presence & typing** — see who is online / away and who is currently typing (a typing signal clears automatically if a client drops, via a short TTL).
- **Profile photos in the DM list** — the sidebar Direct Messages list can show each teammate's **profile photo** next to their name (with an initials-circle fallback for anyone without one), keeping their online/away **presence dot as a small corner badge** so turning photos on never hides who's online. A per-user display option, **on by default**; toggle it via **"Show profile photos"** in the sidebar header's ⋯ overflow menu. Applies to 1:1 DMs and cross-org connection DMs (channels keep their `#` glyph). Setting: `teamChatShowDmPhotos`. Invariant `dm-photos-are-on-by-default` in [team-chat-desktop-invariants-contract.md](../../.claude/memory/contracts/team-chat-desktop-invariants-contract.md).
- **Self-profile & manual availability** — a self-profile strip at the top of the sidebar shows your avatar, presence dot, display name, and custom status. Click it to open a popover with an **availability selector** (Online / Away / Do Not Disturb / Out of Office / Offline) that manually overrides your automatic presence — useful because Omniscio runs 24/7 and you may want to pin "away" or "DND" even though the app is focused. Setting availability to `null` (clearing the override) returns to automatic detection (online when focused, away when blurred, offline on quit). Manual "online" degrades to "away" if the heartbeat goes stale (past `PRESENCE_STALE_MS`, 90 s in [presence-dot.ts](/src/shared/team-chat/presence-dot.ts) — prevents ghost-online on quit; the publisher heartbeats every `PRESENCE_HEARTBEAT_MS`, 30 s). Away/DND/OOO/Offline persist intentionally across app restarts. The popover also has an **Edit Profile** button (now available to all users, no longer dev-gated) that opens the full profile editor (display name + avatar upload). The availability override writes a `manualAvailability` field on the presence doc (a field-scoped merge that can't clobber the heartbeat, and vice-versa). `effectivePresence()` reads `manualAvailability` first; when set, it wins (except manual 'online' degrades on stale heartbeat).
- **Unread badges** — each channel shows how many messages you haven't read; **opening a channel clears its badge** (marked read instantly, then reconciled with the server). You can also **right-click a DM** in the sidebar and choose **"Mark as read"** to clear its unread badge without opening it (with **undo** support that restores the previous read marker and unread count). A channel also lights a badge when it has a **durable inbox notice** waiting (even if the server read-state was reset on another device). The **Team Chat hub badge counts only notices you can actually act on** — a notice for a channel you can no longer open (you left it, were removed, or it was deleted) isn't counted, and is auto-cleared from the Inbox too, so the hub badge always matches what shows inside Team Chat — see [team-chat-v2-features-contract.md](../../.claude/memory/contracts/team-chat-v2-features-contract.md) `the-unread-badge-counts-inbox-rows` / `the-hub-badge-counts-only-surfaceable-notices`.
- **History** — the live view shows the latest messages; **"Load older messages"** pages further back on demand.
- **Jump to date** — a **calendar icon** in the channel header (or **Ctrl+Shift+D**) opens a date picker; selecting a date **detaches the live listener** and loads messages from that day in **replace mode** (the live tail is paused, not destroyed). In jumped mode you paginate **bidirectionally** (older + newer), a sticky **"Viewing messages from [date]"** banner shows where you are, and **"Return to latest"** re-attaches the live listener and scrolls to the bottom. Selecting **today** shortcuts straight to return-to-live (no query). On mobile the date picker renders in a `DialogShell` modal instead of a portal popover. Search results also gain a **"Jump to context"** action that opens the result's surrounding conversation via the same mechanism.
- **Quick switcher (Cmd/Ctrl+K)** — while Team Chat is open, press **Cmd/Ctrl+K** to fuzzy-search your channels and DMs by name and **jump** to one: type to filter, arrow-keys to move, **Enter** to open, **Esc** to close. It only takes over Cmd/Ctrl+K while Team Chat is the active surface — everywhere else that key still opens global search. Desktop only.
- **Pop it out into its own window** — right-click the Team Chat sidebar row → **Open in new window**, or press the **Team Chat window** global hotkey (default **Ctrl+Alt+T**) to open Team Chat in a separate desktop window from anywhere, even while Omniscio is in the background. Pressing it again focuses the window you already have rather than opening a second one — the hotkey and the right-click item resolve to the same window. The hotkey registers only while Team Chat is enabled and is rebindable (or disableable) in **Settings → Keyboard Shortcuts** (System-wide), taking effect the moment you change it, with no restart.

## Related

This page is split across four files. The parent page you are reading carries the description of the feature and everything the first version of Team Chat can do; [part 2](team-chat-part-2.md) covers the second wave of features and the workspace story; [part 3](team-chat-part-3.md) covers the phone client, notifications and the smaller surfaces around the chat; and [part 4](team-chat-part-4.md) covers what the feature does not do and the engineering behind it.
