Slack event queues and connector#33
Conversation
There was a problem hiding this comment.
Pull Request Overview
Implements Slack event ingestion, queuing, and processing to convert Slack messages into platform Messages and optionally generate intents via the AI pipeline.
- Adds SlackEvent Prisma model, migrations, and shared Zod schemas
- Stores webhook events, introduces a SlackMessageProcessor to create messages and run analysis, and exposes GraphQL mutations to process events
- Provides a Slack message adapter, test script, QA guide, and intent queries
Reviewed Changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/test-slack-webhook.ts | Script to send a signed Slack webhook event for local testing |
| packages/shared/src/types/prisma/index.ts | Shared Zod schemas/types for new SlackEvent model and selectors/inputs |
| packages/external-services/src/slack/slack-message-adapter.ts | Adapter to convert Slack events to platform MessageCreateInput |
| packages/external-services/src/slack/index.ts | Re-exports Slack message adapter |
| packages/database/src/services/slack-message-processor.ts | Processes queued Slack events into Messages and triggers analysis |
| packages/database/src/services/slack-event.ts | Slack event queue service: store, fetch, mark processed/failed, retry |
| packages/database/src/services/index.ts | Exports SlackEventService and SlackMessageProcessor |
| packages/database/src/services/conversation-analysis.ts | Removes stray console.log |
| packages/database/src/services/tests/slack-event.test.ts | Tests for SlackEventService (skipped unless DATABASE_URL present) |
| packages/database/prisma/schema.prisma | Adds SlackEvent model and indices |
| packages/database/prisma/migrations/.../migration.sql | Migration adding SlackEvent and changing ExternalService enum |
| docs/SLACK_PHASE2_QA_GUIDE.md | End-to-end Slack Phase 2 QA guide |
| apps/api/src/plugins/slack-webhooks.ts | Stores Slack events on webhook receipt |
| apps/api/src/modules/slack/type.ts | GraphQL types for processing Slack events responses |
| apps/api/src/modules/slack/mutation.ts | GraphQL mutations to process events (channel/all) |
| apps/api/src/modules/intent/type.ts | Exposes intent reviewState in GraphQL |
| apps/api/src/modules/intent/query.ts | Adds intent and intents GraphQL queries with filters |
| apps/api/src/modules/enums/type.ts | Adds IntentReviewState enum; ExternalService enum unchanged |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@codex please pr review |
There was a problem hiding this comment.
Pull Request Overview
Copilot reviewed 21 out of 22 changed files in this pull request and generated 6 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review please |
|
Codex Review: Didn't find any major issues. 🚀 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Pull Request Overview
Copilot reviewed 21 out of 22 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
packages/shared/src/types/prisma/index.ts:1
- payload is a JSON field, which Prisma does not support in orderBy clauses. Including payload here will cause invalid queries at runtime. Remove payload from the orderBy schemas to align with Prisma's capabilities.
import { z } from 'zod';
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| const enterpriseId = payload.authorizations?.[0]?.enterprise_id; | ||
| const isEnterpriseInstall = payload.authorizations?.[0]?.is_enterprise_install; | ||
| const workspaceId = (isEnterpriseInstall && enterpriseId) ? enterpriseId : payload.team_id; |
There was a problem hiding this comment.
workspaceId is stored using the raw Slack ID (e.g., T123/enterprise ID), while channel configuration and processors use normalized workspace keys (e.g., team:T123). This mismatch prevents queued events from being picked up and can cause duplicate conversation creation attempts. Normalize the workspace ID here using workspaceKey(...) so SlackEvent.workspaceId matches SlackChannelConfig.workspaceId and message metadata.
| const events = await SlackEventService.getUnprocessedEvents({ | ||
| workspaceId, | ||
| channelId, | ||
| processedAt: null, | ||
| }); |
There was a problem hiding this comment.
This filters SlackEvent by the function parameter workspaceId, which may not match the normalized workspaceId stored in SlackEvent (or the channel config). Use channelConfig.workspaceId for consistency to ensure events are found and processed.
| const messageInput = SlackMessageAdapter.toPlatformMessage( | ||
| payload, | ||
| conversationId, | ||
| event.workspaceId |
There was a problem hiding this comment.
Message metadata slackWorkspaceId is being populated from event.workspaceId, which is inconsistent with the normalized workspaceId used for conversations and channel config. Pass channelConfig.workspaceId here to keep metadata consistent and to avoid failing to find the existing conversation on subsequent runs.
| event.workspaceId | |
| channelConfig.workspaceId |
| }); | ||
|
|
||
| // Filter intents by confidence threshold | ||
| const threshold = channelConfig.autoApplyThreshold || 0.6; |
There was a problem hiding this comment.
Use nullish coalescing (??) instead of || to avoid treating 0 as falsy. Example: const threshold = channelConfig.autoApplyThreshold ?? 0.6;
| const threshold = channelConfig.autoApplyThreshold || 0.6; | |
| const threshold = channelConfig.autoApplyThreshold ?? 0.6; |
| private async processEvent( | ||
| event: any, | ||
| conversationId: string, | ||
| channelConfig: any, | ||
| forceAnalysis?: boolean | ||
| ): Promise<boolean> { |
There was a problem hiding this comment.
channelConfig and forceAnalysis parameters are not used in this method. Consider removing them to reduce confusion, or use them if they are intended for logic here.
| export const SlackEventOrderByWithAggregationInputSchema: z.ZodType<Prisma.SlackEventOrderByWithAggregationInput> = z.object({ | ||
| id: z.lazy(() => SortOrderSchema).optional(), | ||
| eventId: z.lazy(() => SortOrderSchema).optional(), | ||
| eventType: z.lazy(() => SortOrderSchema).optional(), | ||
| workspaceId: z.lazy(() => SortOrderSchema).optional(), | ||
| channelId: z.lazy(() => SortOrderSchema).optional(), | ||
| threadTs: z.union([ z.lazy(() => SortOrderSchema),z.lazy(() => SortOrderInputSchema) ]).optional(), | ||
| messageTs: z.union([ z.lazy(() => SortOrderSchema),z.lazy(() => SortOrderInputSchema) ]).optional(), | ||
| userId: z.union([ z.lazy(() => SortOrderSchema),z.lazy(() => SortOrderInputSchema) ]).optional(), | ||
| payload: z.lazy(() => SortOrderSchema).optional(), | ||
| processedAt: z.union([ z.lazy(() => SortOrderSchema),z.lazy(() => SortOrderInputSchema) ]).optional(), | ||
| processingError: z.union([ z.lazy(() => SortOrderSchema),z.lazy(() => SortOrderInputSchema) ]).optional(), | ||
| retryCount: z.lazy(() => SortOrderSchema).optional(), | ||
| shouldRetry: z.lazy(() => SortOrderSchema).optional(), | ||
| createdAt: z.lazy(() => SortOrderSchema).optional(), | ||
| updatedAt: z.lazy(() => SortOrderSchema).optional(), | ||
| _count: z.lazy(() => SlackEventCountOrderByAggregateInputSchema).optional(), |
There was a problem hiding this comment.
payload should not be present in the orderBy-with-aggregation schema either; JSON fields are not sortable in Prisma. Remove payload from this schema to prevent invalid queries.
No description provided.