From 7ea8289c5b95676bdf726568cde6794bda98c721 Mon Sep 17 00:00:00 2001 From: iliya Date: Sun, 29 Mar 2026 01:29:13 +0200 Subject: [PATCH] feat(team): add support for task comment notifications - Introduced 'task_comment_notification' message kind to enhance message handling in the team services. - Updated TeamDataService, TeamInboxReader, and TeamSentMessagesStore to accommodate the new message kind. - Modified filtering logic to exclude task comment notifications from the displayed messages. - Added tests to ensure correct handling and filtering of task comment notifications. --- README.md | 1 + src/main/services/team/TeamDataService.ts | 3 +- src/main/services/team/TeamInboxReader.ts | 6 ++-- .../services/team/TeamSentMessagesStore.ts | 6 ++-- src/renderer/utils/teamMessageFiltering.ts | 2 +- src/shared/types/team.ts | 6 +++- .../services/team/TeamDataService.test.ts | 2 ++ .../services/team/TeamInboxReader.test.ts | 28 +++++++++++++++++++ .../utils/teamMessageFiltering.test.ts | 27 ++++++++++++++++++ 9 files changed, 74 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index daee46d2..d4801cd1 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,7 @@ pnpm dist # macOS + Windows + Linux - [ ] `createTasksBatch` — IPC/service API to create many team tasks in one call (playbooks, markdown checklist import, scripts); complements single `createTask` - [ ] Command palette — extend Cmd/Ctrl+K beyond project/session search to runnable actions (quick commands, navigation shortcuts, team/task operations) in a keyboard-first flow - [ ] Custom kanban columns +- [ ] Run terminal commands --- diff --git a/src/main/services/team/TeamDataService.ts b/src/main/services/team/TeamDataService.ts index 46e4a2f7..bec6cbc8 100644 --- a/src/main/services/team/TeamDataService.ts +++ b/src/main/services/team/TeamDataService.ts @@ -30,6 +30,7 @@ import * as path from 'path'; import { gitIdentityResolver } from '../parsing/GitIdentityResolver'; import { atomicWriteAsync } from './atomicWrite'; +import { extractLeadSessionMessagesFromJsonl } from './leadSessionMessageExtractor'; import { buildTaskChangePresenceDescriptor } from './taskChangePresenceUtils'; import { TeamConfigReader } from './TeamConfigReader'; import { TeamInboxReader } from './TeamInboxReader'; @@ -42,7 +43,6 @@ import { TeamSentMessagesStore } from './TeamSentMessagesStore'; import { TeamTaskCommentNotificationJournal } from './TeamTaskCommentNotificationJournal'; import { TeamTaskReader } from './TeamTaskReader'; import { TeamTaskWriter } from './TeamTaskWriter'; -import { extractLeadSessionMessagesFromJsonl } from './leadSessionMessageExtractor'; import type { PersistedTaskChangePresenceIndex } from './cache/taskChangePresenceCacheTypes'; import type { TaskChangePresenceRepository } from './cache/TaskChangePresenceRepository'; @@ -1806,6 +1806,7 @@ export class TeamDataService { text: notification.text, summary: notification.summary, source: TASK_COMMENT_NOTIFICATION_SOURCE, + messageKind: 'task_comment_notification', leadSessionId: notification.leadSessionId, taskRefs: [notification.taskRef], messageId: notification.messageId, diff --git a/src/main/services/team/TeamInboxReader.ts b/src/main/services/team/TeamInboxReader.ts index fb191740..563a634c 100644 --- a/src/main/services/team/TeamInboxReader.ts +++ b/src/main/services/team/TeamInboxReader.ts @@ -132,7 +132,9 @@ export class TeamInboxReader { })) : undefined, messageKind: - row.messageKind === 'slash_command' || row.messageKind === 'slash_command_result' + row.messageKind === 'slash_command' || + row.messageKind === 'slash_command_result' || + row.messageKind === 'task_comment_notification' ? row.messageKind : row.messageKind === 'default' ? 'default' @@ -144,7 +146,7 @@ export class TeamInboxReader { typeof row.slashCommand.command === 'string' ? { name: row.slashCommand.name, - command: row.slashCommand.command as `/${string}`, + command: row.slashCommand.command, args: typeof row.slashCommand.args === 'string' ? row.slashCommand.args : undefined, knownDescription: typeof row.slashCommand.knownDescription === 'string' diff --git a/src/main/services/team/TeamSentMessagesStore.ts b/src/main/services/team/TeamSentMessagesStore.ts index b7352446..c23c7ccc 100644 --- a/src/main/services/team/TeamSentMessagesStore.ts +++ b/src/main/services/team/TeamSentMessagesStore.ts @@ -99,7 +99,9 @@ export class TeamSentMessagesStore { })) : undefined, messageKind: - row.messageKind === 'slash_command' || row.messageKind === 'slash_command_result' + row.messageKind === 'slash_command' || + row.messageKind === 'slash_command_result' || + row.messageKind === 'task_comment_notification' ? row.messageKind : row.messageKind === 'default' ? 'default' @@ -111,7 +113,7 @@ export class TeamSentMessagesStore { typeof row.slashCommand.command === 'string' ? { name: row.slashCommand.name, - command: row.slashCommand.command as `/${string}`, + command: row.slashCommand.command, args: typeof row.slashCommand.args === 'string' ? row.slashCommand.args : undefined, knownDescription: typeof row.slashCommand.knownDescription === 'string' diff --git a/src/renderer/utils/teamMessageFiltering.ts b/src/renderer/utils/teamMessageFiltering.ts index 4eac6397..c5925605 100644 --- a/src/renderer/utils/teamMessageFiltering.ts +++ b/src/renderer/utils/teamMessageFiltering.ts @@ -18,7 +18,7 @@ export function filterTeamMessages( ): InboxMessage[] { const { timeWindow, filter, searchQuery } = options; - let list = messages; + let list = messages.filter((m) => m.messageKind !== 'task_comment_notification'); if (timeWindow) { list = list.filter((m) => { const ts = new Date(m.timestamp).getTime(); diff --git a/src/shared/types/team.ts b/src/shared/types/team.ts index eaec6531..fd51c499 100644 --- a/src/shared/types/team.ts +++ b/src/shared/types/team.ts @@ -166,7 +166,11 @@ export interface SourceMessageSnapshot { }[]; } -export type InboxMessageKind = 'default' | 'slash_command' | 'slash_command_result'; +export type InboxMessageKind = + | 'default' + | 'slash_command' + | 'slash_command_result' + | 'task_comment_notification'; export interface SlashCommandMeta { name: string; diff --git a/test/main/services/team/TeamDataService.test.ts b/test/main/services/team/TeamDataService.test.ts index 6595d69f..53b7cc36 100644 --- a/test/main/services/team/TeamDataService.test.ts +++ b/test/main/services/team/TeamDataService.test.ts @@ -789,6 +789,7 @@ describe('TeamDataService', () => { from: 'alice', summary: 'Comment on #abcd1234', source: 'system_notification', + messageKind: 'task_comment_notification', leadSessionId: 'lead-1', taskRefs: [{ taskId: 'task-1', displayId: 'abcd1234', teamName: 'my-team' }], messageId: 'task-comment-forward:my-team:task-1:comment-1', @@ -1446,6 +1447,7 @@ describe('TeamDataService', () => { expect.objectContaining({ from: 'bob', summary: 'Comment on #abcd1234', + messageKind: 'task_comment_notification', messageId: 'task-comment-forward:my-team:task-1:comment-2', }) ); diff --git a/test/main/services/team/TeamInboxReader.test.ts b/test/main/services/team/TeamInboxReader.test.ts index eea67418..f2bf7431 100644 --- a/test/main/services/team/TeamInboxReader.test.ts +++ b/test/main/services/team/TeamInboxReader.test.ts @@ -150,4 +150,32 @@ describe('TeamInboxReader', () => { expect(supported).toBeDefined(); expect(supported!.messageId).toBe('m-1'); }); + + it('preserves task comment notification semantic kind', async () => { + hoisted.files.set( + '/mock/teams/my-team/inboxes/alice.json', + JSON.stringify([ + { + from: 'bob', + to: 'team-lead', + text: 'Notification payload', + timestamp: '2026-01-01T02:00:00.000Z', + read: false, + messageId: 'm-task-comment', + source: 'system_notification', + messageKind: 'task_comment_notification', + summary: 'Comment on #abcd1234', + }, + ]) + ); + + const messages = await reader.getMessagesFor('my-team', 'alice'); + expect(messages).toHaveLength(1); + expect(messages[0]).toMatchObject({ + messageId: 'm-task-comment', + source: 'system_notification', + messageKind: 'task_comment_notification', + summary: 'Comment on #abcd1234', + }); + }); }); diff --git a/test/renderer/utils/teamMessageFiltering.test.ts b/test/renderer/utils/teamMessageFiltering.test.ts index 4081031b..d2ee9230 100644 --- a/test/renderer/utils/teamMessageFiltering.test.ts +++ b/test/renderer/utils/teamMessageFiltering.test.ts @@ -105,4 +105,31 @@ describe('filterTeamMessages', () => { expect(result).toHaveLength(1); expect(result[0].messageId).toBe('msg-2'); }); + + it('hides task comment notifications by semantic kind instead of text matching', () => { + const messages = [ + makeMessage({ + messageId: 'task-comment-1', + source: 'system_notification', + messageKind: 'task_comment_notification', + summary: 'Comment on #abcd1234', + text: 'Some future wording that may change completely.', + }), + makeMessage({ + messageId: 'msg-2', + source: 'system_notification', + summary: 'Task #abcd1234 started', + text: 'Visible system notification', + }), + ]; + + const result = filterTeamMessages(messages, { + timeWindow: null, + filter: { from: new Set(), to: new Set(), showNoise: true }, + searchQuery: '', + }); + + expect(result).toHaveLength(1); + expect(result[0].messageId).toBe('msg-2'); + }); });