feat(messages): add sender-side wake recipient override
Senders can now force the recipient agent to wake on receipt regardless of the recipient's `messageResponseMode`. Surfaced as a "Wake recipient immediately" checkbox in MessageComposer and as a `wake_recipient` boolean param on the `fn_send_message` agent tool. Carried as `metadata.wakeRecipient: true` on the message; the heartbeat hook treats forced wakes as `message_received_urgent` in the wake delta so agents can distinguish them from normal `messageResponseMode: immediate` wakes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1149,7 +1149,8 @@ export class HeartbeatMonitor {
|
||||
}
|
||||
|
||||
const runtimeConfig = agent.runtimeConfig as AgentHeartbeatConfig | undefined;
|
||||
if (runtimeConfig?.messageResponseMode !== "immediate") {
|
||||
const senderForcedWake = message.metadata?.wakeRecipient === true;
|
||||
if (!senderForcedWake && runtimeConfig?.messageResponseMode !== "immediate") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1161,7 +1162,7 @@ export class HeartbeatMonitor {
|
||||
void this.executeHeartbeat({
|
||||
agentId: message.toId,
|
||||
source: "on_demand",
|
||||
triggerDetail: "wake-on-message",
|
||||
triggerDetail: senderForcedWake ? "wake-on-message-forced" : "wake-on-message",
|
||||
}).catch((error) => {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
heartbeatLog.warn(`Wake-on-message heartbeat failed for ${message.toId}: ${errorMessage}`);
|
||||
@@ -1774,6 +1775,7 @@ export class HeartbeatMonitor {
|
||||
const deriveWakeReason = (): string => {
|
||||
if (effectiveTriggeringCommentType) return `comment_${effectiveTriggeringCommentType}`;
|
||||
if (triggerDetail === "wake-on-message") return "message_received";
|
||||
if (triggerDetail === "wake-on-message-forced") return "message_received_urgent";
|
||||
if (triggerDetail === "wake-on-comment") return "comment_mention";
|
||||
if (triggerDetail === "task-assigned") return "task_assigned";
|
||||
if (source === "timer") return "timer";
|
||||
|
||||
@@ -113,6 +113,13 @@ export const sendMessageParams = Type.Object({
|
||||
reply_to_message_id: Type.Optional(
|
||||
Type.String({ description: "Optional ID of the message you are replying to (use IDs from fn_read_messages output)" }),
|
||||
),
|
||||
wake_recipient: Type.Optional(
|
||||
Type.Boolean({
|
||||
description:
|
||||
"If true, wake the recipient agent immediately on receipt regardless of their messageResponseMode. " +
|
||||
"Use sparingly for urgent messages. Ignored when the recipient is a user.",
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export const readMessagesParams = Type.Object({
|
||||
@@ -1410,7 +1417,8 @@ export function createSendMessageTool(messageStore: MessageStore, fromAgentId: s
|
||||
label: "Send Message",
|
||||
description:
|
||||
"Send a message to another agent or user. The recipient will be woken if they have " +
|
||||
"`messageResponseMode: 'immediate'` configured. When replying to an existing message, " +
|
||||
"`messageResponseMode: 'immediate'` configured, or if you set `wake_recipient: true` " +
|
||||
"to override their setting for an urgent message. When replying to an existing message, " +
|
||||
"include `reply_to_message_id` to preserve threading.",
|
||||
parameters: sendMessageParams,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -1445,6 +1453,15 @@ export function createSendMessageTool(messageStore: MessageStore, fromAgentId: s
|
||||
};
|
||||
}
|
||||
|
||||
const wakeRecipient = params.wake_recipient === true && recipient.type === "agent";
|
||||
const metadata =
|
||||
replyToMessageId || wakeRecipient
|
||||
? {
|
||||
...(replyToMessageId ? { replyTo: { messageId: replyToMessageId } } : {}),
|
||||
...(wakeRecipient ? { wakeRecipient: true } : {}),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const message = messageStore.sendMessage({
|
||||
fromId: fromAgentId,
|
||||
fromType: "agent",
|
||||
@@ -1452,7 +1469,7 @@ export function createSendMessageTool(messageStore: MessageStore, fromAgentId: s
|
||||
toType: recipient.type,
|
||||
content,
|
||||
type: messageType,
|
||||
...(replyToMessageId ? { metadata: { replyTo: { messageId: replyToMessageId } } } : {}),
|
||||
...(metadata ? { metadata } : {}),
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user