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:
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 { taskSubtasksDeleteSchema } from "./migrations/041-task-subtasks-delete";
|
||||||
import { approvalNotesMeetingsSprintsSchema } from "./migrations/042-approval-notes-meetings-sprints";
|
import { approvalNotesMeetingsSprintsSchema } from "./migrations/042-approval-notes-meetings-sprints";
|
||||||
import { composioIntegrationSchema } from "./migrations/043-composio-integration";
|
import { composioIntegrationSchema } from "./migrations/043-composio-integration";
|
||||||
|
import { mentionWakeStateSchema } from "./migrations/044-mention-wake-state";
|
||||||
|
|
||||||
interface Migration {
|
interface Migration {
|
||||||
version: number;
|
version: number;
|
||||||
@@ -93,6 +94,7 @@ const MIGRATIONS: Migration[] = [
|
|||||||
{ version: 41, name: "task_subtasks_delete", sql: taskSubtasksDeleteSchema },
|
{ version: 41, name: "task_subtasks_delete", sql: taskSubtasksDeleteSchema },
|
||||||
{ version: 42, name: "approval_notes_meetings_sprints", sql: approvalNotesMeetingsSprintsSchema },
|
{ version: 42, name: "approval_notes_meetings_sprints", sql: approvalNotesMeetingsSprintsSchema },
|
||||||
{ version: 43, name: "composio_integration", sql: composioIntegrationSchema },
|
{ version: 43, name: "composio_integration", sql: composioIntegrationSchema },
|
||||||
|
{ version: 44, name: "mention_wake_state", sql: mentionWakeStateSchema },
|
||||||
];
|
];
|
||||||
|
|
||||||
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
|
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),
|
(m) => m.target.type === "agent" && !(input.senderType === "agent" && input.senderId === m.target.id),
|
||||||
);
|
);
|
||||||
if (agentMentions.length === 0) return;
|
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;
|
if (input.senderType === "agent" && this.autonomousThreadTurnsExhausted(input.orgId, threadId)) return;
|
||||||
for (const mention of agentMentions) {
|
for (const mention of agentMentions) {
|
||||||
try {
|
try {
|
||||||
this.agentWake.wakeFromMention(input, mention.target.id, messageId, threadId, now);
|
this.agentWake.wakeFromMention(input, mention.target.id, messageId, threadId, now);
|
||||||
} catch {
|
this.db.prepare(
|
||||||
// Mentions remain delivery metadata even when the target has no runtime.
|
"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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,10 +145,19 @@ function renderBody(body: string, mentions: ChatMentionRecord[], context: Messag
|
|||||||
if (!token.startsWith("@")) throw new Error("Message mention token is invalid.");
|
if (!token.startsWith("@")) throw new Error("Message mention token is invalid.");
|
||||||
if (mention.offsetStart > lastIndex) parts.push(...formatInline(body.slice(lastIndex, mention.offsetStart), `s${lastIndex}`));
|
if (mention.offsetStart > lastIndex) parts.push(...formatInline(body.slice(lastIndex, mention.offsetStart), `s${lastIndex}`));
|
||||||
const label = mentionLabel(mention, context);
|
const label = mentionLabel(mention, context);
|
||||||
|
const failed = mention.wakeState === "failed";
|
||||||
parts.push(
|
parts.push(
|
||||||
<span
|
<span
|
||||||
key={`m-${mention.messageId}-${mention.offsetStart}`}
|
key={`m-${mention.messageId}-${mention.offsetStart}`}
|
||||||
style={{ background: "var(--accent-soft)", color: "var(--accent)", padding: "1px 5px", borderRadius: 4, fontWeight: 500 }}
|
title={failed ? "Agent could not be reached" : undefined}
|
||||||
|
style={{
|
||||||
|
background: failed ? "rgba(255,100,80,0.1)" : "var(--accent-soft)",
|
||||||
|
color: failed ? "var(--text-3)" : "var(--accent)",
|
||||||
|
padding: "1px 5px",
|
||||||
|
borderRadius: 4,
|
||||||
|
fontWeight: 500,
|
||||||
|
textDecoration: failed ? "line-through" : undefined,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
@{label}
|
@{label}
|
||||||
</span>,
|
</span>,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export interface ChatMentionRecord {
|
|||||||
targetId: string;
|
targetId: string;
|
||||||
offsetStart: number;
|
offsetStart: number;
|
||||||
offsetEnd: number;
|
offsetEnd: number;
|
||||||
|
wakeState: "none" | "woken" | "failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatSlashInvocationRecord {
|
export interface ChatSlashInvocationRecord {
|
||||||
|
|||||||
Reference in new issue
Block a user