Integration: Add Slack and Discord ChatOps source types to TaskSpawner for conversational agent triggering
@gjkim42 is already working on this.
Since Apr 16, 2026.
- Dominant language
- Go
- Stars
- 331
- Forks
- 40
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 70
Description
🤖 Kelos Strategist Agent @gjkim42
Problem
Kelos currently supports five trigger sources for TaskSpawner: githubIssues, githubPullRequests, cron, githubWebhook, and linearWebhook. All existing and proposed source types (#687 generic webhook, #894 httpPoll, #914 CloudEvents, #906 GitLab/Bitbucket) share a common limitation: they are either pull-based polling or fire-and-forget webhook receivers. None support bidirectional, threaded communication with the person who triggered the task.
Chat platforms (Slack, Discord, Microsoft Teams) are where development teams already coordinate. Today, if a team wants to trigger Kelos from Slack, they must either:
- Create a GitHub issue manually (context switch, friction)
- Build custom glue code (Slack bot → GitHub issue or
kubectl apply) - Use generic webhooks (#687 if implemented) — but lose threading, user context, and reply-back capability
This gap means Kelos is invisible to the ~80% of an organization that works in chat but doesn't interact with GitHub issues directly: product managers requesting changes, QA engineers reporting bugs, SREs requesting hotfixes, or team leads requesting reports.
Proposal
Add slackBot and discordBot as new source types in the When struct, following the established pattern of githubWebhook and linearWebhook. Each source type would include:
- Connection configuration (bot token, app credentials)
- Trigger filtering (channels, mention patterns, slash commands)
- Authorization (allowed users/channels/roles)
- Bidirectional reporting (reply-in-thread with task status)
API Design
New fields in the When struct (api/v1alpha1/taskspawner_types.go):
type When struct {
// ... existing fields ...
// SlackBot triggers task spawning from Slack messages.
// +optional
SlackBot *SlackBot `json:"slackBot,omitempty"`
// DiscordBot triggers task spawning from Discord messages.
// +optional
DiscordBot *DiscordBot `json:"discordBot,omitempty"`
}
SlackBot Source Type
type SlackBot struct {
// TokenSecretRef references a Secret containing Slack credentials.
// Required keys: SLACK_BOT_TOKEN (xoxb-...), SLACK_APP_TOKEN (xapp-... for Socket Mode).
// Optional key: SLACK_SIGNING_SECRET (for Events API verification).
// +kubebuilder:validation:Required
TokenSecretRef SecretReference `json:"tokenSecretRef"`
// Channels restricts the bot to specific channel IDs.
// When empty, the bot responds in any channel it's invited to.
// +optional
Channels []string `json:"channels,omitempty"`
// Triggers defines what activates task creation.
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinItems=1
Triggers []SlackTrigger `json:"triggers"`
// Authorization restricts who can trigger tasks.
// +optional
Authorization *SlackAuthorization `json:"authorization,omitempty"`
// Reporting configures status replies back to the Slack thread.
// +optional
Reporting *SlackReporting `json:"reporting,omitempty"`
// ConnectionMode selects how to receive Slack events.
// "socket" uses Socket Mode (no public URL needed, recommended).
// "events" uses the Events API (requires public URL).
// +kubebuilder:validation:Enum=socket;events
// +kubebuilder:default=socket
// +optional
ConnectionMode string `json:"connectionMode,omitempty"`
}
type SlackTrigger struct {
// MentionBot triggers when the bot is @mentioned.
// +optional
MentionBot bool `json:"mentionBot,omitempty"`
// SlashCommand triggers on a specific slash command (e.g., "/kelos").
// +optional
SlashCommand string `json:"slashCommand,omitempty"`
// MessagePattern triggers on messages matching this regex pattern.
// +optional
MessagePattern string `json:"messagePattern,omitempty"`
// Reaction triggers when a specific emoji reaction is added to a message.
// The reacted-to message becomes the task prompt.
// +optional
Reaction string `json:"reaction,omitempty"`
}
type SlackAuthorization struct {
// AllowedUsers restricts triggering to specific Slack user IDs.
// +optional
AllowedUsers []string `json:"allowedUsers,omitempty"`
// AllowedChannels restricts triggering to specific channel IDs.
// Distinct from top-level Channels — Channels controls where the bot listens,
// AllowedChannels controls who within those channels can trigger tasks.
// +optional
AllowedChannels []string `json:"allowedChannels,omitempty"`
}
type SlackReporting struct {
// Enabled posts task status updates back to the originating Slack thread.
// +optional
Enabled bool `json:"enabled,omitempty"`
// ThreadReply posts updates as thread replies rather than channel messages.
// +kubebuilder:default=true
// +optional
ThreadReply bool `json:"threadReply,omitempty"`
}
Template Variables
Following the established pattern from ExtractGitHubWorkItem and ExtractLinearWorkItem:
| Variable | Description | Example |
|---|---|---|
{{.ID}} |
Unique message ID | 1234567890.123456 |
{{.Title}} |
First line of message (for task naming) | Fix the login page |
{{.Kind}} |
Always "slack" |
slack |
{{.User}} |
Slack user ID who triggered | U024BE7LH |
{{.UserName}} |
Display name | alice |
{{.Channel}} |
Channel ID | C024BE91L |
{{.ChannelName}} |
Channel name | #dev-requests |
{{.Message}} |
Full message text (bot mention stripped) | Fix the login page CSS... |
{{.ThreadMessages}} |
Full thread context if triggered in-thread | Prior messages |
{{.ThreadTS}} |
Thread timestamp for reply routing | 1234567890.123456 |
Example Configuration
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: slack-code-assistant
spec:
maxConcurrency: 3
when:
slackBot:
tokenSecretRef:
name: slack-bot-credentials
channels:
- "C024BE91L" # #engineering
- "C034BF92M" # #dev-requests
triggers:
- mentionBot: true
- slashCommand: "/kelos"
authorization:
allowedUsers:
- "U024BE7LH" # alice
- "U034CF8MN" # bob
reporting:
enabled: true
threadReply: true
connectionMode: socket
taskTemplate:
type: claude-code
credentials:
type: api-key
secretRef:
name: anthropic-key
workspaceRef:
name: my-repo
branch: "kelos/slack-{{.ID}}"
promptTemplate: |
Request from {{.UserName}} in {{.ChannelName}}:
{{.Message}}
{{- if .ThreadMessages}}
Thread context:
{{.ThreadMessages}}
{{- end}}
Implementation Considerations
Architecture
Following the existing webhook pattern in internal/webhook/:
- New binary:
kelos-slack-bot(similar tokelos-webhook-server) — or extend the existing webhook server with a Slack handler - Socket Mode (recommended default): No public URL required. The bot maintains a WebSocket connection to Slack's servers. This is the lowest-friction option for users since it works behind firewalls without ingress configuration
- Events API (alternative): For high-volume deployments. Requires a public URL, similar to how
githubWebhookrequires a webhook endpoint
Why Not Just Use Generic Webhooks (#687)?
The generic webhook proposal (#687) and Slack's outgoing webhooks could theoretically be combined, but this misses critical capabilities:
| Capability | Generic Webhook | Dedicated SlackBot |
|---|---|---|
| Thread-reply reporting | No | Yes |
| Socket Mode (no public URL) | No | Yes |
| Message context (thread history) | No | Yes |
| Slash command handling | No | Yes |
| Reaction-based triggers | No | Yes |
| User display name resolution | No | Yes |
| Slack challenge verification | No | Yes |
| Channel-level authorization | No | Yes |
Deployment Model
The TaskSpawnerReconciler would create a Deployment (similar to polling sources) running the Slack bot process. In Socket Mode, no Service or Ingress is needed — the bot connects outbound to Slack.
TaskSpawner (slackBot) → TaskSpawnerReconciler creates Deployment
→ kelos-slack-bot connects to Slack via Socket Mode
→ Receives messages, applies filters and authorization
→ Creates Task resources
→ Posts thread replies on task phase transitions
Phased Rollout
- Phase 1: Slack with Socket Mode +
mentionBottrigger + thread reporting - Phase 2: Slash commands, reaction triggers, Events API mode
- Phase 3: Discord bot source type (Discord Gateway WebSocket is architecturally similar to Slack Socket Mode)
- Phase 4: Microsoft Teams (if demand warrants — Teams Bot Framework is more complex)
Why This Matters
ChatOps is a proven DevOps pattern (pioneered by GitHub's Hubot). Adding chat as a first-class source type would:
- Lower the barrier to entry — Non-technical team members can trigger agents without learning GitHub or Kubernetes
- Increase organizational visibility — Team sees agent activity in shared channels, building trust and adoption
- Enable new use cases — SRE requesting "deploy hotfix for CVE-2024-XXXX", PM asking "generate release notes for v2.3", QA reporting "login flow broken on mobile"
- Reduce context switching — Developers stay in Slack instead of switching to GitHub to create issues
- Complement existing sources — A team might use
githubIssuesfor planned work andslackBotfor ad-hoc requests, with the same agent handling both
/kind feature
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.