Add read_messages tool and conversational task prompts
Agents now have a read_messages tool to check their inbox and channel history during runs, enabling them to catch up on conversation before acting. Task prompts are rewritten to encourage checking messages first and discussing work with teammates rather than posting status updates.
This commit is contained in:
1 parent
5bb9aea4f9
commit
65c5bec9a3
2 files changed
+57
-3
No files matched your search
@@ -110,6 +110,19 @@ export const AGENT_TOOLS: ToolDefinition[] = [
|
||||
required: ["channel", "body"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read_messages",
|
||||
description: "Read recent messages from a channel or your direct mentions. Check what your teammates have been saying before you respond or start work.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
channel: { type: "string", description: "Channel name: 'engineering', 'product', 'general'. Omit to read your recent @mentions across all channels." },
|
||||
limit: { type: "number", description: "Number of recent messages (default 10, max 30)." },
|
||||
},
|
||||
required: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create_document",
|
||||
description: "Create a company document (PRD, spec, memo, report, runbook, proposal, policy, guide). Use this for structured deliverables that the company should keep versioned.",
|
||||
@@ -144,6 +157,8 @@ export function dispatchAgentTool(
|
||||
return writeDeliverableTool(deps, ctx, call.arguments);
|
||||
case "send_message":
|
||||
return sendMessageTool(deps, ctx, call.arguments);
|
||||
case "read_messages":
|
||||
return readMessagesTool(deps, ctx, call.arguments);
|
||||
case "create_document":
|
||||
return createDocumentTool(deps, ctx, call.arguments);
|
||||
default:
|
||||
@@ -334,6 +349,45 @@ function joinNames(names: string[]): string {
|
||||
return `${names.slice(0, -1).join(", ")}, and ${names[names.length - 1]}`;
|
||||
}
|
||||
|
||||
function readMessagesTool(deps: AgentToolDeps, ctx: AgentToolContext, args: JsonRecord): string {
|
||||
const channel = optionalString(args.channel);
|
||||
const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 30);
|
||||
if (channel) {
|
||||
const space = deps.db.prepare(
|
||||
"select id from chat_spaces where org_id = ? and lower(name) = lower(?) and space_type <> 'direct' limit 1",
|
||||
).get(ctx.orgId, channel) as { id: string } | undefined;
|
||||
if (!space) throw new Error(`No channel named "${channel}".`);
|
||||
const rows = deps.db.prepare(`
|
||||
select m.body_md,
|
||||
coalesce(a.name, e.display_name, m.sender_type) as sender_name,
|
||||
m.thread_id
|
||||
from chat_messages m
|
||||
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 m.space_id = ? and m.org_id = ? and m.deleted_at is null
|
||||
order by m.created_at desc
|
||||
limit ?
|
||||
`).all(space.id, ctx.orgId, limit) as Array<{ body_md: string; sender_name: string; thread_id: string }>;
|
||||
if (rows.length === 0) return `No recent messages in #${channel}.`;
|
||||
return rows.reverse().map((m) => `[${m.sender_name}] (thread=${m.thread_id}): ${m.body_md}`).join("\n");
|
||||
}
|
||||
const rows = deps.db.prepare(`
|
||||
select m.body_md,
|
||||
coalesce(a.name, e.display_name, m.sender_type) as sender_name,
|
||||
m.thread_id, s.name as channel_name
|
||||
from chat_mentions cm
|
||||
join chat_messages m on m.id = cm.message_id
|
||||
join chat_spaces s on s.id = m.space_id
|
||||
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
|
||||
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 }>;
|
||||
if (rows.length === 0) return "No recent mentions.";
|
||||
return rows.reverse().map((m) => `[${m.sender_name} in #${m.channel_name}] (thread=${m.thread_id}): ${m.body_md}`).join("\n");
|
||||
}
|
||||
|
||||
function createDocumentTool(deps: AgentToolDeps, ctx: AgentToolContext, args: JsonRecord): string {
|
||||
const title = requireString(args.title, "title");
|
||||
const docType = requireString(args.doc_type, "doc_type");
|
||||
|
||||
@@ -92,8 +92,8 @@ export function buildAgentInstructions(input: AgentInstructionContext): string {
|
||||
`You are ${agent.name}, ${agent.title || agent.role} in ${agent.department} at ${input.companyName}. You are a real member of this company with your own judgment and voice — a colleague, not a generic assistant.`,
|
||||
roleVoice(input),
|
||||
"Work like a sharp person: take in what is actually in front of you, decide the smallest real next step, do it, then tell whoever needs to know. Do not narrate busywork.",
|
||||
"Your tools are how you act: hire_agent builds your team, create_task delegates and assigns work by name, update_task_status tracks it, write_deliverable produces real files, create_document writes versioned company documents (PRDs, specs, memos, reports), and send_message is how you talk to everyone.",
|
||||
"Talk like a person on a team, not a status bot: address teammates by @name, say what you genuinely think, ask when you are unsure, and reply when someone asks you something. Post where the work lives — 'engineering', 'product', or otherwise 'general' — and keep a back-and-forth in one thread by passing the thread_id a previous send_message returned. Hand work to a teammate with /handoff @Name and the file_id write_deliverable gives you.",
|
||||
"Your tools are how you act: hire_agent builds your team, create_task delegates and assigns work by name, update_task_status tracks it, write_deliverable produces real files, create_document writes versioned company documents (PRDs, specs, memos, reports), read_messages catches you up on what teammates said, and send_message is how you talk to everyone.",
|
||||
"Talk like a person on a team, not a status bot: address teammates by @name, say what you genuinely think, ask when you are unsure, and reply when someone asks you something. Before starting work, check your messages with read_messages to see if anyone needs you. Post where the work lives — 'engineering', 'product', or otherwise 'general' — and keep a back-and-forth in one thread by passing the thread_id a previous send_message returned. Hand work to a teammate with /handoff @Name and the file_id write_deliverable gives you.",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ export function buildTaskPrompt(input: {
|
||||
const managerReference = input.managerName ? `${manager} (${agentMention(input.managerName)})` : manager;
|
||||
lines.push(
|
||||
"",
|
||||
`Do the work and write real output with write_deliverable, then tell ${managerReference} and the team what you did and what is next — in your own words, the way you would bring a colleague up to speed, not as a form. Hand files over with /handoff and attachment_file_ids, reply in the assignment thread if you were given one, and when the work is genuinely done move the task to done.`,
|
||||
`First, read_messages to check if anyone has said something you should respond to — reply to them like a colleague, not a report. Then do the work: write real output with write_deliverable, discuss decisions and blockers with ${managerReference} and the team in the relevant channel, and when the work is genuinely done move the task to done. Hand files over with /handoff and attachment_file_ids. Keep conversation going — ask questions, share your thinking, push back if something seems wrong.`,
|
||||
);
|
||||
}
|
||||
return lines.join("\n");
|
||||
|
||||
Reference in new issue
Block a user