Skip to content

feat(sdk/integrations/slack): add Socket Mode ChatBridge + adapter (first ext dep: gorilla/websocket) #55

@alekspetrov

Description

@alekspetrov

Context

Second half of the Slack chat connector — depends on the client/data issue.
Implement the Socket Mode listener + the core.ChatBridge/ChatCapable adapter.
Source from /tmp/pilot-src/internal/adapters/slack/ (socket_mode.go,
socketmode.go, handler.go). Mirror sdk/integrations/telegram/ (bridge.go +
adapter.go) and the chat contract (.agent/system/chat-bridge-design.md).

This is the first SDK runtime external dependency — Socket Mode needs a
WebSocket client (github.com/gorilla/websocket, as in Pilot). go.mod require

  • go.sum appear in the repo for the first time → run make tidy and commit
    both go.mod and go.sum
    . sdk/{core,log,util,testutil} stay stdlib-only.

Implementation

Add to sdk/integrations/slack/: socketmode.go (WS client), bridge.go
(core.ChatBridge), adapter.go (core.ChatCapable), + tests. Transformations:

  • Remove every github.com/qf-studio/pilot/... import; inject *slog.Logger.
  • Port the Socket Mode WS client (connect to the Slack gateway via apps.connections.open,
    hello/authenticate, heartbeat, read envelopes, ack). Uses gorilla/websocket.
  • adapter.go: Adapter with Name() "slack" + NewChatBridge(core.ChatDeps) core.ChatBridge. Compile-time: var _ core.Adapter = (*Adapter)(nil),
    var _ core.ChatCapable = (*Adapter)(nil), var _ core.ChatBridge = (*bridge)(nil).
  • bridge.go implements core.ChatBridge:
    • Start(ctx) — run the Socket Mode loop; for each inbound envelope map to a
      core.MessageEvent: message/app_mentionAction:"message" (text with
      bot-mention stripped, sanitized in the loop before the handler — CRITICAL,
      the feat(sdk/integrations/github): port client, types, converter, notifier, webhook #28-class rule); leading /Action:"command" (Command+Args, no
      execution); block_actionsAction:"callback" (CallbackID = action_id,
      Data = value). Enforce AllowedChannels/AllowedUsers. ChannelID = channel,
      ThreadID = thread_ts.
    • Send(ctx, OutboundMessage) (MessageRef, error)PostMessage, or
      PostInteractiveMessage rendering OutboundMessage.Buttons as a Block Kit
      actions block. MessageRef = {ChannelID, MessageID: ts, ThreadID}.
    • Edit(ctx, ref, text)UpdateMessage by ts.
    • Ack(ctx, callbackID) — ack the Socket Mode envelope for that interaction.
  • Tests use sdk/testutil fakes; include a guard that inbound Text with
    invisible-Unicode smuggling is sanitized before the handler sees it. Use an
    httptest/fake-WS seam so tests don't hit the network.

⛔ Touch ONLY sdk/integrations/slack/ and the top-level go.mod/go.sum for
the new dep. Do not modify sdk/core/ or other connectors.

Acceptance

  • socketmode.go, bridge.go, adapter.go (+ tests) exist; the 3 compile-time core assertions present; Name() returns "slack".
  • Start maps message/command/callback (block_actions) to core.MessageEvent; inbound Text sanitized in the live loop before HandleMessage (grep shows the call-site).
  • go.mod + go.sum committed (gorilla/websocket); go mod tidy a no-op when re-run; sdk/{core,log,util,testutil} still stdlib-only.
  • grep -rn "qf-studio/pilot" sdk/ nothing; no command EXECUTION; Send renders Buttons as Block Kit.
  • go build ./..., go vet ./..., go test -race ./sdk/integrations/slack/..., gofmt -l sdk/ all clean.

Metadata

Metadata

Assignees

No one assigned

    Labels

    no-decomposeSkip epic detection and decompositionpilotPilot AI will work on thispilot-donePilot completed this

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions