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.
This commit is contained in:
parent
46355d87df
commit
7ea8289c5b
9 changed files with 74 additions and 7 deletions
|
|
@ -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
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue