feat(slack): Level 3 — ambient cross-agent context from Slack channels

When a cron fires, the scheduler now fetches the last 10 messages from
the workspace's Slack channel via conversations.history and prepends them
to the cron prompt as '[Slack channel context — recent team messages]'.

This gives each agent ambient awareness of what peers are doing:
- Backend sees Frontend posted 'PR #840 ready for review' → can check
- Security Auditor sees Backend posted 'new endpoint added' → plans review
- PM sees all engineering activity → better synthesis in rollup

Implementation:
- slack.go: FetchChannelHistory() calls conversations.history, filters
  bot's own messages, returns last N as SlackHistoryMessage structs
- manager.go: FetchWorkspaceChannelContext() looks up the workspace's
  Slack config, fetches history, formats as readable context block
- scheduler.go: ChannelBroadcaster interface extended with
  FetchWorkspaceChannelContext; fireSchedule injects context before
  the cron prompt (prepended, not appended, so the agent sees team
  context BEFORE its task instructions)

Best-effort: if Slack API fails or workspace has no channels, the
prompt is unchanged. Truncated to 200 chars per message, 10 messages
max to keep prompt overhead bounded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
rabbitblood 2026-04-17 13:59:26 -07:00
parent 65a3496522
commit 8f89ba0b0a

View File

@ -43,9 +43,10 @@ type scheduleRow struct {
Prompt string
}
// ChannelBroadcaster posts messages to a workspace's configured social channels.
// ChannelBroadcaster posts messages to and reads context from workspace channels.
type ChannelBroadcaster interface {
BroadcastToWorkspaceChannels(ctx context.Context, workspaceID, text string)
FetchWorkspaceChannelContext(ctx context.Context, workspaceID string) string
}
// Scheduler polls the workspace_schedules table and fires A2A messages
@ -260,6 +261,17 @@ func (s *Scheduler) fireSchedule(ctx context.Context, sched scheduleRow) {
fireCtx, cancel := context.WithTimeout(ctx, fireTimeout)
defer cancel()
// Level 3: inject ambient Slack channel context into the cron prompt.
// The agent sees recent peer messages before acting, enabling cross-agent
// awareness without explicit A2A delegation. Best-effort — if the fetch
// fails or the workspace has no Slack channels, the prompt is unchanged.
prompt := sched.Prompt
if s.channels != nil {
if channelCtx := s.channels.FetchWorkspaceChannelContext(fireCtx, sched.WorkspaceID); channelCtx != "" {
prompt = channelCtx + "\n" + prompt
}
}
msgID := fmt.Sprintf("cron-%s-%s", short(sched.ID, 8), uuid.New().String()[:8])
a2aBody, _ := json.Marshal(map[string]interface{}{
@ -268,7 +280,7 @@ func (s *Scheduler) fireSchedule(ctx context.Context, sched scheduleRow) {
"message": map[string]interface{}{
"role": "user",
"messageId": msgID,
"parts": []map[string]interface{}{{"kind": "text", "text": sched.Prompt}},
"parts": []map[string]interface{}{{"kind": "text", "text": prompt}},
},
},
})