From a9049612c5e7abf5b473d1b37d038e712c3f8159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 26 Jun 2026 17:43:41 -0400 Subject: [PATCH] Frame agent chat runs as a conversation, not a ticket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A woken agent was handed a "Chat reply" ticket whose description was four source ids and "you were @mentioned... reply", then a closing that told it to "report progress and completion" and "update the task status" — so it answered like a status bot. Now the wake reason names who spoke, in which channel, and quotes what they said, and asks the agent to reply to that person in their own voice. When a run carries a thread, buildTaskPrompt ends as a conversation ("reply to the people here... not a status report") and skips the work closing entirely. The ordinary work closing also drops "reporting your progress and completion" for "tell the team what you did and what's next, the way you'd bring a colleague up to speed." The thread_id reply token is preserved. This is the second half of making the company feel human. --- .../main/services/chat-agent-wake-service.ts | 35 ++++++++++++++----- .../src/main/services/task-prompts.ts | 7 ++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/AgentCompany-New/src/main/services/chat-agent-wake-service.ts b/AgentCompany-New/src/main/services/chat-agent-wake-service.ts index 9ad88109..08df6211 100644 --- a/AgentCompany-New/src/main/services/chat-agent-wake-service.ts +++ b/AgentCompany-New/src/main/services/chat-agent-wake-service.ts @@ -38,10 +38,12 @@ export class ChatAgentWakeService { wakeFromMention(input: SendMessageInput, agentId: string, messageId: string, threadId: string, now: string): AgentWakeResult { const agent = this.requireRunnableAgent(input.orgId, agentId); const workspace = this.workspaceForAgent(input.orgId, agent.workspace_id); + const from = this.principalName(input.orgId, input.senderType, input.senderId); + const channel = this.channelName(input.orgId, input.spaceId); const taskId = this.organization.createTask({ orgId: input.orgId, - title: `Chat reply: ${agent.name}`, - description: this.mentionTaskDescription(input, messageId, threadId), + title: `Reply to ${from} in #${channel}`, + description: this.mentionTaskDescription(input, from, channel), status: TASK_STATUS.BACKLOG, assigneeAgentId: agent.id, risk: RISK_LEVEL.LOW, @@ -191,17 +193,34 @@ export class ChatAgentWakeService { return `Chat mention: ${title.length > 64 ? `${title.slice(0, 61)}...` : title}`; } - private mentionTaskDescription(input: SendMessageInput, messageId: string, threadId: string): string { + private mentionTaskDescription(input: SendMessageInput, from: string, channel: string): string { + const said = input.bodyMd.trim(); return [ - `Source message: ${messageId}`, - `Source thread: ${threadId}`, - `Source space: ${input.spaceId}`, - `Sender: ${input.senderType}${input.senderId ? `:${input.senderId}` : ""}`, + `${from} addressed you in #${channel}:`, + `"${said.length > 400 ? `${said.slice(0, 397)}...` : said}"`, "", - "You were @mentioned in a chat message. Read the thread context, then reply in the source thread with send_message and pass the thread_id above.", + `Reply to ${from} in this thread. Answer what they asked, give your honest take, push back if you disagree, or hand work over — speak as yourself, the way a colleague would, not as a status report.`, ].join("\n"); } + private principalName(orgId: string, senderType: string, senderId: string | null): string { + if (senderId && senderType === "agent") { + const row = this.db.prepare("select name from agents where id = ? and org_id = ?").get(senderId, orgId) as { name: string } | undefined; + if (row) return row.name; + } + if (senderId && (senderType === "employee" || senderType === "board")) { + const row = this.db.prepare("select display_name from employees where id = ? and org_id = ?").get(senderId, orgId) as { display_name: string } | undefined; + if (row) return senderType === "board" ? `${row.display_name} (board)` : row.display_name; + } + return senderType === "board" ? "The board" : "A teammate"; + } + + private channelName(orgId: string, spaceId: string): string { + const row = this.db.prepare("select name from chat_spaces where id = ? and org_id = ?").get(spaceId, orgId) as { name: string } | undefined; + if (!row) throw new Error("Chat space was not found."); + return row.name; + } + private taskDescription(input: SendMessageInput, slash: SlashInvocation, messageId: string, threadId: string): string { return [ `Chat command: /agent ${slash.args.trim()}`, diff --git a/AgentCompany-New/src/main/services/task-prompts.ts b/AgentCompany-New/src/main/services/task-prompts.ts index 3b7ad027..96ad27e9 100644 --- a/AgentCompany-New/src/main/services/task-prompts.ts +++ b/AgentCompany-New/src/main/services/task-prompts.ts @@ -162,11 +162,12 @@ export function buildTaskPrompt(input: { if (input.chatContext) { lines.push( "", - "Chat thread you are replying to:", + "The conversation in this thread so far:", input.chatContext.transcript, "", - `Reply in this thread by passing thread_id="${input.chatContext.threadId}" to send_message.`, + `You have been brought into this thread. Reply to the people here in your own voice — answer what they asked, give your honest take, push back if you disagree, or hand work over. Stay in the thread by passing thread_id="${input.chatContext.threadId}" to send_message. This is a conversation between colleagues, not a status report; only change a task's status if the work itself actually moved.`, ); + return lines.join("\n"); } if (input.isManager && !input.hasReports) { lines.push( @@ -178,7 +179,7 @@ export function buildTaskPrompt(input: { const managerReference = input.managerName ? `${manager} (${agentMention(input.managerName)})` : manager; lines.push( "", - `Complete this task: do the work, write your output as real files with write_deliverable, then send_message to your department channel (engineering, product, or general) reporting your progress and completion to ${managerReference}. Use /handoff with attachment_file_ids when passing files. Reply in the assignment thread if you were given a thread_id. Update the task status when done, and summarize.`, + `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.`, ); } return lines.join("\n");