From 44f31b49fbb16cbc19fc7b80891bb5e9f16a8a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 7 Jun 2026 17:20:58 -0400 Subject: [PATCH] Wake agents on @mention in chat messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agents were deaf to @mentions — only /agent slash commands triggered a task_run. Now ChatService.sendMessage auto-wakes every mentioned agent (skipping self-mentions and /agent which already handles it) by creating a queued task_run through ChatAgentWakeService.wakeFromMention. --- .../main/services/chat-agent-wake-service.ts | 32 +++++++++++++++++++ .../src/main/services/chat-service.ts | 16 ++++++++++ 2 files changed, 48 insertions(+) diff --git a/AgentCompany-New/src/main/services/chat-agent-wake-service.ts b/AgentCompany-New/src/main/services/chat-agent-wake-service.ts index f6ee0c9e..9ad88109 100644 --- a/AgentCompany-New/src/main/services/chat-agent-wake-service.ts +++ b/AgentCompany-New/src/main/services/chat-agent-wake-service.ts @@ -35,6 +35,27 @@ export class ChatAgentWakeService { this.organization = new OrganizationService(db); } + wakeFromMention(input: SendMessageInput, agentId: string, messageId: string, threadId: string, now: string): AgentWakeResult { + const agent = this.requireRunnableAgent(input.orgId, agentId); + const workspace = this.workspaceForAgent(input.orgId, agent.workspace_id); + const taskId = this.organization.createTask({ + orgId: input.orgId, + title: `Chat reply: ${agent.name}`, + description: this.mentionTaskDescription(input, messageId, threadId), + status: TASK_STATUS.BACKLOG, + assigneeAgentId: agent.id, + risk: RISK_LEVEL.LOW, + projectId: workspace?.project_id ?? null, + goalId: workspace?.goal_id ?? null, + workspaceId: workspace?.id ?? null, + priority: TASK_PRIORITY.HIGH, + requiresFilesystem: false, + }); + const runId = this.createQueuedRun({ orgId: input.orgId, taskId, agent, workspace, messageId, threadId, now }); + this.recordPresence(agent.id, { command: "mention", args: "" }, now); + return { targetId: agent.id, taskId, runId }; + } + wakeFromSlash(input: SendMessageInput, mentions: ResolvedMention[], slash: SlashInvocation, messageId: string, threadId: string, now: string): AgentWakeResult { const targetId = this.requireAgentMention(mentions); const agent = this.requireRunnableAgent(input.orgId, targetId); @@ -170,6 +191,17 @@ export class ChatAgentWakeService { return `Chat mention: ${title.length > 64 ? `${title.slice(0, 61)}...` : title}`; } + private mentionTaskDescription(input: SendMessageInput, messageId: string, threadId: string): string { + return [ + `Source message: ${messageId}`, + `Source thread: ${threadId}`, + `Source space: ${input.spaceId}`, + `Sender: ${input.senderType}${input.senderId ? `:${input.senderId}` : ""}`, + "", + "You were @mentioned in a chat message. Read the thread context, then reply in the source thread with send_message and pass the thread_id above.", + ].join("\n"); + } + private taskDescription(input: SendMessageInput, slash: SlashInvocation, messageId: string, threadId: string): string { return [ `Chat command: /agent ${slash.args.trim()}`, diff --git a/AgentCompany-New/src/main/services/chat-service.ts b/AgentCompany-New/src/main/services/chat-service.ts index 617e73b4..3c578bba 100644 --- a/AgentCompany-New/src/main/services/chat-service.ts +++ b/AgentCompany-New/src/main/services/chat-service.ts @@ -138,6 +138,9 @@ export class ChatService { recordChatMentions(this.db, id, mentions); const slashResultRef = slash ? this.executeSlashCommand(input, space, sender, mentions, attachments, slash, id, threadId, now) : null; if (slash) this.recordSlashInvocation(id, slash, slashResultRef, now); + if (!slash || slash.command !== "agent") { + this.wakeAgentMentions(input, mentions, id, threadId, now); + } this.handoffs.record({ orgId: input.orgId, spaceId: input.spaceId, @@ -358,6 +361,19 @@ export class ChatService { `).run(createId("slash"), messageId, slash.command, JSON.stringify({ raw: slash.args }), resultRef, now); } + private wakeAgentMentions(input: SendMessageInput, mentions: ResolvedMention[], messageId: string, threadId: string, now: string): void { + const agentMentions = mentions.filter( + (m) => m.target.type === "agent" && !(input.senderType === "agent" && input.senderId === m.target.id), + ); + for (const mention of agentMentions) { + try { + this.agentWake.wakeFromMention(input, mention.target.id, messageId, threadId, now); + } catch { + // Agent may lack an enabled provider or service identity; skip silently. + } + } + } + private requireSpace(orgId: string, spaceId: string): SpaceRow { const space = this.db.prepare("select id, org_id, visibility, space_type from chat_spaces where id = ? and org_id = ?") .get(spaceId, orgId) as SpaceRow | undefined;