Record @mention wake failures instead of silently swallowing them

The catch block in wakeAgentMentions now records wake_state on each
mention (woken/failed) instead of silently discarding the error. Failed
mentions render with a strikethrough and muted color so the user sees
which agents could not be reached, with a tooltip on hover. Migration
044 adds the wake_state column to chat_mentions.
This commit is contained in:
ZacharyZhang-NY committed 2026-06-30 21:38:19 -04:00
1 parent f6a2659761
commit 77644121db
5 files changed
+23 -5

No files matched your search

@@ -0,0 +1,3 @@
export const mentionWakeStateSchema = `
alter table chat_mentions add column wake_state text not null default 'none';
`;
@@ -42,6 +42,7 @@ import { documentsKnowledgeAutomationSchema } from "./migrations/040-documents-k
import { taskSubtasksDeleteSchema } from "./migrations/041-task-subtasks-delete";
import { approvalNotesMeetingsSprintsSchema } from "./migrations/042-approval-notes-meetings-sprints";
import { composioIntegrationSchema } from "./migrations/043-composio-integration";
import { mentionWakeStateSchema } from "./migrations/044-mention-wake-state";
interface Migration {
version: number;
@@ -93,6 +94,7 @@ const MIGRATIONS: Migration[] = [
{ version: 41, name: "task_subtasks_delete", sql: taskSubtasksDeleteSchema },
{ version: 42, name: "approval_notes_meetings_sprints", sql: approvalNotesMeetingsSprintsSchema },
{ version: 43, name: "composio_integration", sql: composioIntegrationSchema },
{ version: 44, name: "mention_wake_state", sql: mentionWakeStateSchema },
];
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
@@ -209,14 +209,17 @@ export class ChatService {
(m) => m.target.type === "agent" && !(input.senderType === "agent" && input.senderId === m.target.id),
);
if (agentMentions.length === 0) return;
// An agent's own message must not keep waking teammates once the thread has
// run its autonomous budget; only a human re-entering the thread can.
if (input.senderType === "agent" && this.autonomousThreadTurnsExhausted(input.orgId, threadId)) return;
for (const mention of agentMentions) {
try {
this.agentWake.wakeFromMention(input, mention.target.id, messageId, threadId, now);
} catch {
// Mentions remain delivery metadata even when the target has no runtime.
this.db.prepare(
"update chat_mentions set wake_state = 'woken' where message_id = ? and target_type = 'agent' and target_id = ?",
).run(messageId, mention.target.id);
} catch (error) {
this.db.prepare(
"update chat_mentions set wake_state = 'failed' where message_id = ? and target_type = 'agent' and target_id = ?",
).run(messageId, mention.target.id);
}
}
}