---
title: Send a message
---

# Send a message

## What it is

The **composer** is the textarea pinned at the bottom of an open session — type a message, press Enter (or Ctrl+Enter, depending on your **Submit key** setting), and Omniscio delivers the text to the running Claude CLI process for that session. This is the core back-and-forth with the agent: each send is one operator turn, and the agent replies stream into the chat above the composer.

The composer also accepts file attachments (paperclip, drag-drop, or paste — see [attach-a-file.md](attach-a-file.md)), AI-suggested replies, snippets, and a "Send Later" scheduler. **Ctrl+V (Cmd+V on macOS) works from anywhere in the open session** — sidebar, toolbar, body — and the paste lands in this composer; you don't have to click into the textarea first. The redirect is skipped when a modal owns input, when your focus is already in some other text field, or when the session's textarea is disabled (ended / archived / paused sessions). While Claude is mid-stream, you can still type — your draft is preserved across session switches and app restarts. Slash commands like `/clear` or `/init` are passed through as plain text to the CLI; Omniscio does not parse them itself.

A session has to exist before you can send. If you don't have one open yet, see [start-a-new-session.md](start-a-new-session.md).

## Where to find it

The composer is the rounded textarea pinned to the bottom of an open session’s panel, with the paper-airplane **Send** button at its right edge. The preference that decides whether Enter or Ctrl+Enter sends lives in **Settings → Sessions**, next to the list auto-continuation toggle.

## How it behaves

### How to use it

1. **Click anywhere in the composer** at the bottom of the session panel (the rounded textarea labeled "Send a message..." or one of its status-aware variants like "Type your response..." for `needs_you`, "Continue the conversation..." for `ended`, or "Send your first message to begin..." for a fresh session — on a phone the long hints show shorter variants, e.g. "Your first message...", so the empty box stays a single line).
2. **Type your message.** Drafts auto-save per session — switching to another session and back keeps your text. Drafts also survive app quit. The textarea grows vertically as you type up to a soft limit; you can drag the small grip handle above the composer to resize it taller.
3. **Press Enter to send.** This is the default. The textarea clears, your message appears in the chat as an operator turn (right-aligned bubble), the agent's status flips to "Running", and Claude's reply streams in from the top.
   - **Want a newline instead?** Hold **Shift** and press Enter — that inserts a line break and keeps the draft.
   - **Prefer Ctrl+Enter?** Open Settings → Sessions → **Submit key** and pick "Ctrl+Enter". Plain Enter then becomes a newline and only Ctrl+Enter sends. Useful if you write long multi-paragraph prompts and don't want to accidentally fire half-typed messages.
   - **One setting, everywhere you launch an agent.** Your Submit key choice applies consistently wherever you send an agent message — this composer AND the **Start a session** dialogs (from an alert, the inbox, Team Chat, or Supermail) — so Enter vs Ctrl+Enter behaves the same across all of them. (Human-to-human chats — Team Chat, support chat, and channel messages — follow their own separate **Team Chat submit key** under the same Settings section.) A mid-composition key (typing an emoji or another language via an IME) never fires a send.
   - **List auto-continuation.** When you're typing a bulleted (`- `, `* `, `+ `) or numbered (`1.`, `2)`) list and press the newline key, the next line is pre-seeded with the next marker so the list keeps going — numbered lists increment automatically (`1.` → `2.` → `3.`), bullets repeat, leading indentation is preserved. Press the newline key on an _empty_ list item (marker present, no content after it) to exit the list — the marker is stripped and you get a plain blank line. With the default submit key (Enter), the trigger is **Shift+Enter**; with Ctrl+Enter mode, it's plain **Enter**. Toggle at Settings → Sessions → **List auto-continuation** (default on). Mobile soft keyboards don't have a Shift key, so on mobile with default-Enter mode this is effectively desktop-only; switch to Ctrl+Enter submit mode to use it on mobile.
4. **Or click the Send button** (paper-airplane icon in the accent color, on the right side of the composer). It's disabled while the textarea is empty AND no attachments are queued. While the session is `running`, the same button stays available — its placeholder reads "Send now (interrupts the current step)...", because a plain click is what it does (see step 6). That running-state Send is the SAME **split button** as the idle one: with text in the composer a **▾ caret** sits beside the plane (and press-and-hold works too), so **Send Later**, the "After the agent finishes" queue, and **Send as aside** stay reachable in the composer _during_ a run — not only when idle (see step 9 and [queue-a-message.md](queue-a-message.md)). The caret opens those options WITHOUT interrupting; only a plain click on the plane interrupts.
5. **Send while paused, ended, error, or archived.** All four are recoverable: typing and sending **auto-resumes** the session by re-spawning the CLI with `--resume <cliSessionId>`, so multi-turn context is preserved. An archived session is silently un-archived on send and re-appears in the live list; paused / ended / error sessions just respawn the CLI for the new turn and the row flips back to `running`. **Sending during `starting` also works** — the placeholder says "Type now — will send when session is ready..." and both Enter and the Send button submit: the text is folded into the launching turn (or expedites a recovery-queued session); if the launch handshake isn't far enough along yet, the send **briefly waits for the session to finish coming up and then delivers automatically** — so a message typed a beat too early heals itself instead of erroring, honoring that "will send when session is ready" promise. You only ever see a "still starting" toast (with your draft restored) in the rare case where a session never finishes starting within a short bounded window — a genuinely stuck spawn. There is no "explicit unpause required" gate today.
6. **While Claude is streaming a reply.** Sending a new message _during_ a stream **interrupts the current turn**: Omniscio stops the in-flight response, records "Your message interrupted the previous turn" in the transcript, and restarts the agent with your message (merged with the original prompt when the session hasn't established its CLI id yet). The partial answer streamed so far is kept in the chat. **Want it to wait instead of interrupt?** Queue the message to send when the current step finishes — hold Send → Send Later → "After the agent finishes" (see [queue-a-message.md](queue-a-message.md)); you can line up several. To stop the agent _without_ sending anything, press **Esc twice**: the first press shows a "Press Esc again to interrupt" hint and the second (within ~1.5s) actually stops the running CLI process, so one stray — or held — Esc can never kill a turn. Esc clears anything else open first: it leaves a focused text box, closes an open file preview, then deselects a non-running session, so interrupt is its **last** rung. Full ladder: [keyboard-shortcuts.md](keyboard-shortcuts.md). With no agent turn in progress, Esc just closes the panel. The red ■ Stop button needs no confirm — a click is already deliberate.
   - **Codex sessions interrupt-and-redirect the same way.** Sending mid-turn to a running **Codex** session now stops its current step and runs your message next, with the conversation context preserved (the partial output so far is kept, and there's no "stopped" line wedged before your message). Previously a mid-turn send to Codex was refused with "still working on your previous message" — now it does what the button promises. The other alternative engines (Gemini, Pi, Cursor, OpenCode, OpenClaw) still ask you to wait for the current turn to finish before sending; press **Esc** twice (the double-tap confirm above) or click the red ■ Stop button once, to stop them first. Automated sends (away-mode auto-replies, agent-to-agent nudges) never interrupt a live turn — only a message _you_ send does.
7. **Slash commands & the skill menu.** Typing `/clear`, `/compact`, `/help`, `/init`, etc. and sending works — Omniscio just forwards the text to the CLI which interprets it natively. Note: stream-JSON mode does not give every slash command rich UI, so some commands print plain output instead of opening a panel. **When you type `/` at the _start_ of a message, an autocomplete menu of your installed skills opens** — each row shows the skill's `/command` and a one-line description, and it filters as you keep typing. Use **↑/↓** to move the highlight, **Enter** or **Tab** (or a mouse click) to insert the chosen `/<skill>`, and **Esc** to dismiss. It triggers only on a _leading_ slash — a slash mid-sentence (`and/or`, a `/path/to/file`) is left alone — and it lists your `~/.claude/skills`, installed plugin skills, **and the project-local skills that live in the session's own working directory (`<workdir>/.claude/skills`)** — exactly the skills the CLI can actually run in that session. Project-local rows sort first and carry a small **Project** badge; if one shares a name with a global skill, the project one wins (the CLI resolves it that way too). A plugin skill inserts as `/<plugin>:<skill>`, e.g. `/mattpocock-skills:tdd`. When a project spans multiple repos, the menu scopes to the specific repo/folder that session is running in, so an inserted `/skill` always resolves. Toggle it at Settings → Sessions → **Slash-command menu for skills** (default on). Built-in commands like `/clear` and your own custom `.claude/commands` aren't in the menu yet — type those yourself.
8. **Quick-action affordances next to the composer.** The accent-colored bolts and chips (Alt+1/2/3 AI suggestions, Alt+S snippet picker, Alt+Z Zap button) drop pre-written text into the composer or send it directly. See [use-quick-responses.md](use-quick-responses.md) and [quick-replies.md](quick-replies.md). **On a phone**, those left-side action buttons (attach, quick replies, etc.) show **inline** — the same enabled buttons as on desktop, no **+** menu. Choose which appear — on phone and desktop — at **Settings → Sessions → Composer buttons** (switch off the ones you don't use to free up room; the Clipboard-history, voice, and screenshot buttons are always hidden on mobile).
9. **Send later, or as an aside.** With text in the composer, **press and hold the Send button** (~½ s — a small corner fold in its lower-right hints this) to open a small **send-options menu**: pick **Send Later** to schedule it (a time picker opens, identical to the snooze picker — see [send-later.md](send-later.md)), or **Send as aside** to fire it as a one-shot side question that stays out of the main thread (see [asides.md](asides.md)). Send Later is also on the `sendLater` shortcut (default Ctrl+Shift+L) and the session's ⋯ overflow menu.

What happens after a successful send: the textarea clears, the draft for this session is dropped, the session row pins to the top of the sidebar (lastActiveAt updates), and the textarea may auto-focus a different session per your **Post-send navigation** preferences (Settings → Sessions → "After I send a message..."). What happens on failure: the draft is restored, the attachment chips come back, and a toast names the reason (ended session, network error, etc.). That failure toast also carries a **View session** button that jumps you back to the session that failed — useful because the error often surfaces _after_ post-send navigation has already moved you elsewhere (the "took too long" timeout especially fires long after the send). Empty sends — no text and no attachments — silently no-op.

**Replying to a stopped Claude session no longer waits for it to restart (2026-09-26).** When your message has to bring a paused, ended, errored or archived Claude session back, Send finishes as soon as the message is saved, and the agent comes back in the background. Before this, a slow restart on a busy machine could hold the send open for three minutes and end in a "took too long" failure — on the phone especially — even though the message went through seconds later. If the restart itself fails, your message stays in the conversation (no draft is restored, because nothing was lost), a note in that session says why it couldn't resume, and the session shows as errored; sending again retries.

**Your message never waits in the background restart line (2026-09-29).** When many sessions come back together — after a usage-limit reset, or after Omniscio restarts — they start one at a time, spaced by **Session restart spacing** (Settings → Sessions). If you message a session that is still waiting its turn in that line, your message starts it right away instead of waiting for its place, and a "background" tag an automatic retry left on the session can no longer slow a message you send. Messages from other agents keep the spacing, so the line still protects your computer from a burst.

## For agents

### How it works

The composer textarea lives in [SessionPanel.tsx](../../src/renderer/src/features/sessions/SessionPanel.tsx) (look for the `<textarea>` with `aria-label="Response message"` near the bottom of the file). Its `onKeyDown` handler is `handleKeyDownOverride` which delegates to [useSessionPanel.ts](../../src/renderer/src/features/sessions/useSessionPanel.ts) — the relevant block computes `shouldSubmit` from `submitKeyMode` (`'enter'` default, `'ctrl_enter'` opt-in) and calls `handleSendResponse()`. `handleSendResponse(overrideText?)` is the canonical send entry point — every programmatic-send caller (snippet picker, AI suggestion chips, Zap button, away-mode reply) reuses it via the override argument so draft handling and post-send navigation stay consistent. CLAUDE.md "`handleSendResponse(overrideText?)`" rule.

The send pipeline goes: composer → `handleSendResponse` → `executeSend` → `useSessionStore.sendResponseAndNavigate` → `SESSION_SEND_RESPONSE` IPC → [session-service.ts](../../src/main/services/session/session-service.ts) `sendResponse()` → `processManager.writeToStdin()`. `sendResponse()` does the heavy lifting: auto-unsnooze if `snoozedUntil` was set, auto-unarchive if status was `archived`, partition attachments via [attachment-partition.ts](../../src/main/services/attachment-partition.ts) (images vs PDFs vs text docs — see [chat-attachments.md](chat-attachments.md)), persist the operator message row, and hand the prompt to the process manager.

The `/`-skill autocomplete menu is a separate, self-contained layer under [skill-slash/](../../src/renderer/src/features/sessions/skill-slash/): a framework-free leading-slash detector + suggestion filter (`skill-slash-detector.ts`, unit-tested without a DOM — it also owns `mergeProjectSkills`, which dedups project rows over the global list by slug), a hook that reads the installed-skills store (`useSkillSlashAutocomplete.ts`, lazy-loading the list the first time a `/` appears), and the popover (`SkillSlashAutocomplete.tsx`, anchored above the composer). It's wired into [ComposerInput.tsx](../../src/renderer/src/features/sessions/ComposerInput.tsx)'s `handleKeyDown` so an open menu consumes ↑/↓/Enter/Tab/Esc _before_ the send / undo / list-continuation handlers, and inserts through the same `applyValue` chokepoint. The global skill list comes from the existing skills service (`useSkillsStore().installed` — see [use-skills.md](use-skills.md)); the session's **project-local** skills are fetched separately by session id via the `skills:list-for-session` IPC, which resolves that session's working directory main-side (`resolveSessionWorkDir`, the same resolver the spawn uses) and scans `<workdir>/.claude/skills` — so the renderer never hands the backend a filesystem path. This feature only reads both lists.

`writeToStdin()` in [process-manager.ts](../../src/main/process/process-manager.ts) kills the existing child process, bumps `spawnEpoch` (race guard), and re-spawns the CLI with `--resume <cliSessionId>` so the conversation history is preserved. The stale-account invariant kicks in here: if the persistent process was spawned under a different Claude account than the currently-active one, `writeToStdin` forces a fresh spawn so the new account's credentials get injected. A PERSON's send — `isAutomatedWakeInbound` says it is not the app's own send or another agent's message — takes the immediate spawn lane at both doors that start a session: `UserMessageRouter.writeToStdin` marks the next spawn `'interactive'` before its final spawn, and `runAutoResumeLaunch` ([session-auto-resume.ts](../../src/main/services/session/session-auto-resume.ts)) does the same before `processManager.launch`, so a leftover `'recovery'` tag cannot decide the lane. A person's send that lands while a respawn is in flight rides that respawn (the respawn-window hold) and raises the spawn claim's one-shot wake (`ProcessManager.wakeSpawnInFlightForPerson`), which ends that spawn's recovery start-gap wait in [spawn-concurrency-gate.ts](../../src/main/process/spawn-concurrency-gate.ts) (`acquireSpawnSlot`'s `personWaiting`). Drafts are persisted in `sessionDrafts` (an in-memory Map in `session-draft-store.ts`) plus the SQLite `sessions.draft_text` column. The `submitKeyMode` setting flows from `AppSettings` in [types.ts](../../src/shared/types.ts) (default `'enter'`) and the keyboard-shortcut bindings live in [keybindings.ts](../../src/shared/keybindings.ts). For the full process lifecycle, see [process-management.md](../../.claude/memory/process-management.md).

## Related

- [start-a-new-session.md](start-a-new-session.md) — needs to exist before you can send to it
- [attach-a-file.md](attach-a-file.md) — paperclip, drag-drop, paste-image flow into the same Send
- [chat-attachments.md](chat-attachments.md) — deep dive on what Claude actually sees for each attachment type
- [use-quick-responses.md](use-quick-responses.md) — Alt+1/2/3 AI suggestions, Alt+S snippets, Alt+Z Zap
- [quick-replies.md](quick-replies.md) — managing the snippet library that Alt+S pulls from
- [send-later.md](send-later.md) — schedule a message for future delivery instead of sending now
- [session-stuck-in-needs-you.md](session-stuck-in-needs-you.md) — what to do when send appears to succeed but the agent never responds
- [notifications-and-silence.md](notifications-and-silence.md) — Post-send navigation lives here, plus per-project mute
