Wake agents on @mention in chat messages
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.
This commit is contained in:
1 parent
abe578b1a0
commit
44f31b49fb
2 files changed
+48
No files matched your search
@@ -35,6 +35,27 @@ export class ChatAgentWakeService {
|
|||||||
this.organization = new OrganizationService(db);
|
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 {
|
wakeFromSlash(input: SendMessageInput, mentions: ResolvedMention[], slash: SlashInvocation, messageId: string, threadId: string, now: string): AgentWakeResult {
|
||||||
const targetId = this.requireAgentMention(mentions);
|
const targetId = this.requireAgentMention(mentions);
|
||||||
const agent = this.requireRunnableAgent(input.orgId, targetId);
|
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}`;
|
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 {
|
private taskDescription(input: SendMessageInput, slash: SlashInvocation, messageId: string, threadId: string): string {
|
||||||
return [
|
return [
|
||||||
`Chat command: /agent ${slash.args.trim()}`,
|
`Chat command: /agent ${slash.args.trim()}`,
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ export class ChatService {
|
|||||||
recordChatMentions(this.db, id, mentions);
|
recordChatMentions(this.db, id, mentions);
|
||||||
const slashResultRef = slash ? this.executeSlashCommand(input, space, sender, mentions, attachments, slash, id, threadId, now) : null;
|
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) this.recordSlashInvocation(id, slash, slashResultRef, now);
|
||||||
|
if (!slash || slash.command !== "agent") {
|
||||||
|
this.wakeAgentMentions(input, mentions, id, threadId, now);
|
||||||
|
}
|
||||||
this.handoffs.record({
|
this.handoffs.record({
|
||||||
orgId: input.orgId,
|
orgId: input.orgId,
|
||||||
spaceId: input.spaceId,
|
spaceId: input.spaceId,
|
||||||
@@ -358,6 +361,19 @@ export class ChatService {
|
|||||||
`).run(createId("slash"), messageId, slash.command, JSON.stringify({ raw: slash.args }), resultRef, now);
|
`).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 {
|
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 = ?")
|
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;
|
.get(spaceId, orgId) as SpaceRow | undefined;
|
||||||
|
|||||||
Reference in new issue
Block a user