Q2: read_messages lists only mentions the agent has not answered

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 02:25:31 -04:00
1 parent 77505aab8d
commit 9f2adce7b3
3 files changed
+25 -2

No files matched your search

+6 -1
View File
@@ -43,10 +43,15 @@ export function readMessagesTool(deps: AgentToolDeps, ctx: AgentToolContext, arg
left join agents a on a.id = m.sender_id and m.sender_type = 'agent'
left join employees e on e.id = m.sender_id and m.sender_type = 'employee'
where cm.target_type = 'agent' and cm.target_id = ? and m.org_id = ? and m.deleted_at is null
and not exists (
select 1 from chat_messages reply
where reply.thread_id = m.thread_id and reply.sender_type = 'agent' and reply.sender_id = cm.target_id
and reply.created_at > coalesce(m.edited_at, m.created_at) and reply.deleted_at is null
)
order by m.created_at desc
limit ?
`).all(ctx.agent.id, ctx.orgId, limit) as Array<{ body_md: string; sender_name: string; thread_id: string; channel_name: string; space_type: string }>;
if (rows.length === 0) return "No recent mentions.";
if (rows.length === 0) return "No unanswered mentions.";
return rows.reverse().map((m) => {
const where = m.space_type === "direct" ? "in your direct conversation" : `in #${m.channel_name}`;
return `[${m.sender_name} ${where}] (thread=${m.thread_id}): ${m.body_md}`;
+1 -1
View File
@@ -129,7 +129,7 @@ export const AGENT_TOOLS: ToolDefinition[] = [
},
{
name: "read_messages",
description: "Read recent messages from a channel, a direct conversation, or your direct mentions. Check what your teammates have been saying before you respond or start work.",
description: "Read recent messages from a channel, a direct conversation, or (with no arguments) the mentions you have not answered in their thread yet. Check what your teammates have been saying before you respond or start work.",
parameters: {
type: "object",
additionalProperties: false,
+18
View File
@@ -124,5 +124,23 @@ describe("mention resolution", () => {
};
const inbox = dispatchAgentTool(deps, danaCtx, call("read_messages", {}));
expect(inbox).toContain("check the deployment");
// Once Dana answers in that thread, the mention is handled and leaves her inbox.
const threadId = /thread=([^)]+)\)/.exec(inbox)?.[1];
expect(threadId).toBeTruthy();
dispatchAgentTool(deps, danaCtx, call("send_message", { channel: "engineering", body: "Deployment checked.", thread_id: threadId }));
expect(dispatchAgentTool(deps, danaCtx, call("read_messages", {}))).toBe("No unanswered mentions.");
dispatchAgentTool(deps, ceoCtx, call("send_message", { channel: "engineering", body: "@dana one more: ship it", thread_id: threadId }));
const next = dispatchAgentTool(deps, danaCtx, call("read_messages", {}));
expect(next).toContain("ship it");
expect(next).not.toContain("check the deployment");
// A mention added later by editing an old message is unanswered again.
dispatchAgentTool(deps, ceoCtx, call("send_message", { channel: "engineering", body: "status?", thread_id: threadId }));
const plainId = (connection.db.prepare("select id from chat_messages where body_md = 'status?'").get() as { id: string }).id;
dispatchAgentTool(deps, danaCtx, call("send_message", { channel: "engineering", body: "All good.", thread_id: threadId }));
deps.chat.editMessage({ orgId, messageId: plainId, bodyMd: "@dana status on the rollback?", actorType: "agent", actorId: ceoId });
expect(dispatchAgentTool(deps, danaCtx, call("read_messages", {}))).toContain("status on the rollback");
});
});