What a Slack conversation looks like while Apus works, and how commands reach it.

Ack reactions

ackReaction sends an acknowledgement emoji while Apus is processing an inbound message. ackReactionScope decides when that emoji is actually sent.

The acknowledgement stays static during work. With messages.statusReactions.enabled: true, actual failures briefly show an error reaction before restoring the acknowledgement. Tool calls, thinking, compaction, and long-running tools do not cycle or accumulate reactions, and successful completion does not flash a separate success emoji.

Emoji (ackReaction)

Resolution order:

Notes:

Scope (messages.ackReactionScope)

The Slack provider reads scope from messages.ackReactionScope (default "group-mentions"). There is no Slack-account or Slack-channel-level override; the value is global to the gateway.

Values:

The default scope ("group-mentions") does not fire ack reactions in direct messages or ambient room events. To see the configured ackReaction (for example "eyes") on inbound Slack DMs and quiet room events, set messages.ackReactionScope to "all". Scope changes apply to the next message without reconnecting Slack.

{
  messages: {
    ackReaction: "eyes",
    ackReactionScope: "all", // react in DMs and groups
  },
}

Reply threading, history limits, text chunk limits, streaming, typing reactions, and unfurl settings apply to the next admitted message without reconnecting Slack. An active reply retains the settings it started with. The same behavior applies to account overrides.

Text streaming

channels.slack.streaming controls live preview behavior:

Show the tool log while hiding raw command/exec text:

{
  "channels": {
    "slack": {
      "streaming": {
        "mode": "progress",
        "progress": {
          "toolProgress": true,
          "commandText": "status"
        }
      }
    }
  }
}

channels.slack.streaming.nativeTransport controls Slack native text streaming when channels.slack.streaming.mode is partial (default: true).

In progress mode, turns without a reply-thread target post no preview or progress message unless you explicitly select a presentation. This includes channel replies with effective replyToMode: "off" and plain top-level DMs. These turns use a temporary typing reaction and deliver the final answer once; failures still use normal error delivery. Any explicit streaming.progress setting opts top-level turns into a preview, including commentary: true or a custom label. The sole exception is nativeTaskCards: true, which only affects threads. An empty streaming.progress object or setting only streaming.mode: "progress" keeps the quiet top-level default. This rule applies to the effective settings after merging root and account overrides. Modes off, partial, and block retain their existing behavior.

For threaded turns, including Agent View, Assistant View, and Slack-managed threads, Slack's native agent card remains the default: the whole turn is one streamed message that interleaves narration with a live plan/task card and finishes with the assistant's answer in that same message. The card shows authored plan steps when the agent publishes a plan, otherwise one stable work-summary row; approval requests get their own row. Intermediate tool failures and nonzero command exits are hidden by default, so successful answers do not accumulate failure or Recovered: … rows. With progress.toolProgress: true, it also shows per-tool task rows, including tool failures, alongside any authored plan. Routine updates coalesce at one-second intervals; approvals, visible failures, and completion bypass that delay. The card appears only once a turn does real work — tool or plan activity still running after a short delay — so a plain question is answered without one.

Without an authored or explicit headline, native summary rows finish as Completed or Failed.

Set channels.slack.streaming.progress.nativeTaskCards to false to fall back to the Block Kit session card. By default, this separate message shows commentary and text, including italic narration and reasoning, plus actionable approval requests. Only an explicitly configured progress label appears as a plain title. Finished cards keep the Open in Apus or Open work session link when available. The card adds no emoji, bold text, or status headings in either mode, except a plain Failed line when the turn fails. A card is posted only once it has something to show, so a turn with only tool activity and no commentary shows no card by default. A working or successful card whose last visible row goes away, such as a resolved approval, is deleted instead of keeping stale rows. Failed turns keep their card marked Failed, even without an error reply; failed turns with no reply post a plain Failed card even if none appeared while working.

Set progress.toolProgress: true for the detailed Block Kit card: it adds recent tool activity, a plan checklist with a completed-step count, intermediate error and recovery rows, and tool/file/time totals while working. Finished detailed cards retain only file diff totals in their footer.

Set channels.slack.streaming.progress.style to "compact" for one plain-text progress draft instead of either card surface. Explicitly setting progress.toolProgress: false also selects compact style when style is unset. Set style: "card" to keep a card with toolProgress: false, or to select a Block Kit card for top-level turns. Commentary appears as italic text, and authored reasoning and approval requests remain visible. Terminal task errors still use normal error delivery. The final response is posted as a new message, then the temporary preview is deleted after Slack confirms delivery. Older previews displaced by human replies are cleaned up with it; durable messages and videos stay in the conversation.

For streamed preambles, Slack waits for the first complete preamble before creating the message, so its notification contains the full thought rather than a single token. Once that message exists, later preambles can stream as edits without another notification.

{
  channels: {
    slack: {
      streaming: {
        mode: "progress",
        progress: {
          style: "compact",
          label: false,
          commentary: true,
          toolProgress: false,
        },
      },
    },
  },
}

Compact progress always uses normal final delivery, including for media and errors. Other draft modes use normal delivery when the reply cannot safely replace the draft, including oversized text, split block payloads, custom outbound identity, or an edit failure.

Both surfaces link the session with Open in Apus, but only when that link can work: gateway.publicOrigin must be set (the externally reachable Gateway origin) and the Control UI must not be disabled via gateway.controlUi.enabled: false. Installations that leave publicOrigin unset — where there is no way to reach Apus from Slack — get no link rather than a dead one. If the Control UI is served below a path prefix, also set gateway.controlUi.basePath.

When a turn spawns a visible work session, the finished card instead links to that child with Open work session. For multiple visible work sessions, it shows up to five links in acceptance order, using their labels or numbered Open work session links; hidden subagents do not replace the conversation link.

Use draft preview instead of Slack native text streaming:

{
  channels: {
    slack: {
      streaming: {
        mode: "partial",
        nativeTransport: false,
      },
    },
  },
}

Keep Slack native progress task cards enabled for threaded turns:

{
  channels: {
    slack: {
      streaming: {
        mode: "progress",
        progress: {
          nativeTaskCards: true,
        },
      },
    },
  },
}

Legacy keys:

Typing reaction fallback

typingReaction adds a temporary reaction to the inbound Slack message while Apus is processing a reply, then removes it when the run finishes. This is most useful outside of thread replies, which use Slack's processing session status.

Resolution order:

Notes:

Commands and slash behavior

Slash commands appear in Slack as either a single configured command or multiple native commands. Configure channels.slack.slashCommand to change command defaults:

/apus /help

Native commands require additional manifest settings in your Slack app and are enabled with channels.slack.commands.native: true or commands.native: true in global configurations instead.

/help

Native argument menus render as one of the following, in priority order:

/think

Slash sessions use isolated keys like agent:<agentId>:slack:slash:<userId> and still route command executions to the target conversation session using CommandTargetSessionKey.