Automations & Auto-replies (auto-respond to messages) (part 2)
The second half of the Automations page: the auto-response badge taxonomy that names which automated path produced a message, the activity log's per-step breakdown, and the implementation map of the engines, stores and IPC.
What it is
This is part 2 of the Automations & Auto-replies (auto-respond to messages) page. It carries the auto-response badge taxonomy that names which automated path produced a system message, the per-step breakdown the activity log shows for v2 runs, and the implementation map of the engines, stores, watchers and IPC that execute both kinds of rule.
Where to find it
Automations and Auto-replies both live in the Automations virtual project in the Omniscio sidebar, next to Quick Replies, with each rule edited inline in its list row; Automations & Auto-replies (auto-respond to messages) walks that surface end to end, including the rule editor, the six auto-reply actions, the action chain, backtest and the sender-authentication rules. The badges and internals on this page are read from a session transcript or from the code paths named below.
How it behaves
The auto-response badge — which automated path replied
Every system-injected message — an outgoing operator message Omniscio produced on its own, rather than one you typed — carries a small auto-response badge inline. The badge names which of the eleven automated paths produced the message, so a glance at the transcript tells them apart:
| Badge label | What produced the message |
|---|---|
scheduled wake-up |
A ScheduleWakeup tool call fired after its delay elapsed |
auto-continue |
Omniscio nudged the agent to keep going after it paused mid-task |
rate-limit retry |
A rate-limit window cleared and the session auto-resumed |
auto-resume |
Omniscio recovered the session after a crash or a restart |
auto-reply |
A user-defined Away Mode / Auto-replies rule fired |
auto-approved |
The dev pipeline auto-approved a gate on your behalf |
auto re-check |
The waiting detector re-checked after the agent went quiet mid-wait |
Sent by Inbox Pilot (bot icon) |
Inbox Pilot's router chose and sent the reply |
Every label except Sent by Inbox Pilot uses the amber auto-response palette so user-driven and recovery flows read as one family; Sent by Inbox Pilot keeps its own surface-grey pill with a bot icon. Auto-response messages persisted before this taxonomy existed fall back to a bare amber auto pill. The waiting detector's other auto check-ins are registered kinds too, but render differently: the pre-inbox and inbox auto check-in show the ember ⏱ Checked in tag on the check-in message (see waiting-detector.md) instead of this badge, and the ambiguous-wait confirm probe stays hidden.
Two of these labels render as a visible user-message bubble rather than being hidden by default — auto-reply and auto-approved — because you opted into the automation that sent them, so the transcript reads like you replied yourself. (The mechanical recovery / continue / wake-up kinds stay hidden unless you turn on Show system messages.)
Hover any badge for a tooltip that explains why the system spoke. The tooltip is the kind's description plus an optional per-message reason — for example Auto-injected to continue the agent after a pause — Auto-continue 2/5 — agent stopped mid-task, or Auto-injected after a rate-limit cleared — account-switch trigger. Sent by Inbox Pilot additionally shows the decision id (— decision abc-123) so you can find the matching row in the AI Coaching decisions log. auto-reply keeps the richer rule tooltip — the matching condition and reply source, e.g. Auto-replied — keyword "subagent" — snippet "Standard reply".
Click is wired for the auto-reply badge only: it jumps straight to the rule in the Automations virtual project (under the Auto-replies category) with the editor expanded. If the rule has been deleted since it fired, the click shows an "Auto-run rule no longer exists" toast and stays on the message; if the lookup IPC fails, you get an error toast instead and no navigation happens. Every other badge is informational — a non-interactive label, not a button.
Outgoing channel replies (SMS, Slack, Telegram, Gmail) sent by Automations rules still show the bare "auto" badge with no attribution. That follow-up is tracked in /docs/plans/2026-04-26-automations-channel-reply-attribution-followup.md.
How it works: the eleven-kind taxonomy lives in one shared module — /src/shared/auto-response-metadata.ts — which exports the kind union, a descriptor table (badge label + tooltip text), and the resolveAutoResponseKind() / getAutoResponseReason() accessors. Each producer stamps its metadata as it writes to the session: { systemInjected: true, kind, reason } for the recovery / schedule / continue kinds, { source: 'ai-manager-respond', decisionId, reason } for Inbox Pilot, the richer { autoRule: { ruleId, conditionType, conditionValue (truncated to 80 chars), snippetLabel } } snapshot for auto-reply rules, or { kind: 'pipeline-approval', pipelineAutoAdvance: { gate } } for a dev-pipeline gate auto-approval — threaded through writeToStdin (or sendMessageToSession for non-Claude engines) → addOutput → SESSION_OUTPUT push event → conversation_messages.metadata. Which kinds render as a visible user bubble vs. stay hidden is decided by USER_AUTHORED_AUTO_RESPONSE_KINDS in /src/shared/real-conversation-turns.ts. The renderer's /src/renderer/src/components/ui/AutoResponseBadge.tsx resolves the kind through that module, picks the palette + label, formats the tooltip via /src/renderer/src/components/ui/auto-response-tooltip.ts, and — for auto-rule — calls AUTO_RUN_GET_RULES to verify the rule still exists before deep-linking through /src/renderer/src/features/automations/navigate-to-rule.ts (sets useSessionStore.activeProjectId = AUTOMATIONS_PROJECT_ID, switches the automations-category-store category to auto-replies, and writes pendingExpandRuleId so /src/renderer/src/features/automations/components/AutoRepliesListView.tsx expands the matching row once it mounts and rules finish loading).
What the activity log shows for v2 runs
The Activity Log & Statistics collapsible at the bottom of the Automations settings panel lists every recent automation run. Click a row to expand it. For runs produced by the v2 chain executor (rules whose engineVersion === 2), the expanded panel surfaces a per-step breakdown: each action's label (Archive / Notify / Auto Reply / …), any free-text detail the action returned (e.g. archived 3 threads), a dry-run badge when the step was rendered for backtest only, and a human-readable duration (45ms, 1.2s, 2m05s). Failed steps show a red ✗ glyph and the underlying error text is hosted in a tooltip on the row so it doesn't push the layout around. Legacy v1 runs keep the single-line outcome (Success / Error + actionError paragraph) — only v2 runs grow the per-step block.
For agents
How it works
The Automations virtual project is registered via AUTOMATIONS_PROJECT_ID = '__automations__' in /src/shared/types.ts; the Dashboard routes that sentinel through /src/renderer/src/features/dashboard/Dashboard.tsx into the same regular-project layout used by every filesystem project. /src/renderer/src/features/dashboard/SessionsSidebar.tsx detects the sentinel and renders /src/renderer/src/features/automations/components/AutomationsSidebarSections.tsx above the standard SESSIONS / SNOOZED / ARCHIVED stack — two collapsibles (RULES + AUTO-REPLIES) backed by rulesExpanded / autoRepliesExpanded booleans + a category: 'rules' | 'auto-replies' slot in /src/renderer/src/stores/automations-category-store.ts. The Auto-replies list-view filter is backed by /src/renderer/src/stores/auto-replies-filter-store.ts — a small Zustand store that holds the substring query, plus matchCount / totalCount published by the list view so the sidebar header can render the (M of N) count format without re-running the filter. The main panel is /src/renderer/src/features/automations/AutomationsProjectView.tsx; it watches useSessionStore.activeSessionId and, when that points at a session belonging to the Automations project, mounts the full SessionPanel (the standard close button clears activeSessionId). Otherwise it dispatches by category to /src/renderer/src/features/automations/components/RulesListView.tsx or /src/renderer/src/features/automations/components/AutoRepliesListView.tsx. AI edit sessions are real rows under SESSIONS — no separate AI-sessions category, no bespoke "back" pill. Persistence: Auto-replies rules live in the away_mode_rules SQLite table (intentionally not renamed — the table + IPC channels keep the legacy AUTO_RUN_* / away_mode_rules names so migrations don't break existing installs), backed by CRUD in /src/main/db/queries-auto-run.ts and matched by /src/main/services/automation/legacy/auto-run-engine.ts; Automations live in the separate automation_rules table, backed by /src/main/db/queries-automations.ts and executed by /src/main/services/automation/legacy/automation-service.ts, with renderer state in /src/renderer/src/stores/automation-store.ts. When a message lands, the channel adapter hands it to both engines; Automations runs its ordered rule list first (so its AI condition + action chains take priority), then Auto-replies fill in if nothing matched. Condition evaluation covers literal operators (via /src/main/services/pattern/pattern-matching.ts) and, for AI conditions, a structured-JSON classifier with OAuth-vs-API-key dual-path routing. Cross-engine session output: an Auto-reply rule fires on a session's finished turn for every engine — Claude evaluates it in its NDJSON turn gate (/src/main/process/ndjson-auto-disposition.ts), and non-Claude sessions (Codex / Pi / OpenClaw / Gemini / Cursor / OpenCode / …) evaluate the SAME rules on each clean turn-complete via the engine's runForExternalTurn, delivering a reply through the unified /src/main/services/session/session-send.ts path (the Claude-only stdin path can't reach an external engine); the other five actions are engine-agnostic.
Organize / per-session color: the Organize the session action is the triage arm of the auto-run action union — { type: 'triage', title?, tagId?, color? } (at least one set), applied independently and best-effort by /src/main/services/automation/legacy/auto-run-engine.ts (title via the shared rename helper, tag via applyTagToSessionUnscoped(sid, tagId, 'ai'), color written to the session). It returns false, so organizing a session doesn't suppress the needs_you transition. The color itself lives in a nullable color TEXT column on the sessions table (a #rrggbb hex or NULL = inherit the status color); the manual Set color menu writes it through the SESSION_SET_COLOR (session:set-color) IPC channel, and either path emits a SESSION_COLOR_CHANGED push so every open window re-renders the colored bar live.
Session-state triggers are evaluated by a separate poller — /src/main/services/session/session-state-watcher.ts — that ticks every 30 seconds. On each tick it: (1) lists all enabled on_session_state automations, (2) resolves each rule's scope to the matching live sessions (excluding archived/soft-deleted) via /src/main/services/session/session-scope-evaluator.ts, (3) computes whether every scoped session is in one of the matched statuses, and (4) hands the result to a pure state machine (/src/main/services/session/session-state-machine.ts) that returns one of fire | record | noop. The watcher only fires on a transition from "not all matched" → "all matched"; on the very first tick after enabling a rule it always records a baseline rather than firing, so an automation enabled while the condition is already true won't surprise you with an immediate fire. Action chains for state triggers receive {{trigger.matchedSessionIds}}, {{trigger.matchedSessionCount}}, {{trigger.firedAtIso}}, and {{trigger.scopeDescription}} instead of message vars. The Send to CLI Session action (cli_send_to_session) safely skips (rather than throws) when the target session is archived, deleted, paused, or missing — so a stale rule won't break the chain. Full design + preset catalog: feature-inventory-channels-automations.md. Landing page marketing: "Automations triage your messages. And when one account hits its cap, the next one picks up instantly."
Gmail-scoped rules are fed by a background poll of your connected inbox every 2 minutes — /src/main/services/email/gmail-automation-poller.ts — that reads the newest 20 inbox threads from the last 6 days. Mail that arrived while Omniscio was closed therefore still triggers your rules when it reopens, but nothing older than 6 days does. Each email is handled at most once: a claim remembers it for 7 days (/src/main/db/queries-automation-dedup.ts), and the search deliberately stops a day short of that, so an old email can never trigger the same rule twice.
AI edit sessions launched from the Automations virtual project's "AI sessions" category get a pre-seeded first prompt so the Claude Code agent has full domain context before you type a word. The seeding happens in /src/main/services/session/session-create.ts — when project.folderPath === AUTOMATIONS_PROJECT_ID, it calls /src/main/services/virtual-project-seed.ts to assemble a bundle (this library page + your current rule inventory as JSON + a REST cheatsheet for /automation/* + auth info), mints an in-app session token via /src/main/services/in-app-session-token.ts, formats the combined text as the first CLI prompt, and persists it as a source: 'system' row in conversation_messages so the chat UI renders it as injected context rather than operator input. Rules the AI creates via /automation/* using the in-app token are recognised by /src/main/services/cli/cli-server.ts getAuthContext() as the in-app-session class and skip the normal approval inbox — the trust boundary is "this session was launched by the user from inside Omniscio".
Related
This page is the companion to Automations & Auto-replies (auto-respond to messages), which covers the feature itself: what the two systems are, where to find them, and how to configure a rule. quick-replies.md is the paired virtual project for the snippet library the reply action cites, automation-credentials.md holds the secrets the v2 actions load, dev-pipeline-panel.md can seed a starter set of workflow auto-replies, and waiting-detector.md owns the check-in tags that render instead of a badge.
Last verified 2026-09-28