fix(team-mode): gate member wake prompts

This commit is contained in:
YeonGyu-Kim
2026-05-15 11:29:52 +09:00
parent 174cbd0fbd
commit f1a62a9cd1
3 changed files with 70 additions and 14 deletions
@@ -282,6 +282,34 @@ describe("createTeamSendMessageTool", () => {
expect(calls[0]?.directory).toBe(resolveBaseDir(fixture.config))
})
test("#given recipient OpenCode session is busy #when team_send_message attempts live delivery #then it leaves the message unread without starting another reply", async () => {
// given
const fixture = await createTeamFixture()
let promptCalls = 0
const client = {
session: {
status: async () => ({ data: { [fixture.memberTwoSessionId]: { type: "busy" } } }),
promptAsync: async () => {
promptCalls += 1
},
},
}
const liveTool = createTeamSendMessageTool(fixture.config, client)
// when
await liveTool.execute({
teamRunId: fixture.teamRunId,
to: "m2",
body: "ping while busy",
}, fixture.toolContext(fixture.memberOneSessionId))
// then
expect(promptCalls).toBe(0)
const unread = await listUnreadMessages(fixture.teamRunId, "m2", fixture.config)
expect(unread).toHaveLength(1)
expect(unread[0]?.body).toBe("ping while busy")
})
test("live delivery pins the recipient's resolved subagent_type and model on promptAsync", async () => {
// given
const fixture = await createTeamFixture()
+26 -4
View File
@@ -15,6 +15,7 @@ import {
reserveMessageForDelivery,
} from "../team-mailbox/reservation"
import { BroadcastNotPermittedError, sendMessage } from "../team-mailbox/send"
import { promptAsyncAfterSessionIdle } from "../../../hooks/shared/prompt-async-gate"
import type { Message } from "../types"
import { MessageSchema } from "../types"
@@ -33,6 +34,7 @@ export type LiveDeliveryClient = {
}
query?: { directory: string }
}): Promise<unknown>
status?: () => Promise<unknown>
}
}
@@ -180,11 +182,31 @@ async function deliverLive(
applyMemberSessionRouting(recipientSessionId, recipientMember)
try {
await client.session.promptAsync({
path: { id: recipientSessionId },
body: buildMemberPromptBody(recipientMember, envelope),
query: { directory: recipientMember.worktreePath ?? directory },
const promptResult = await promptAsyncAfterSessionIdle({
client,
sessionID: recipientSessionId,
source: "team-live-delivery",
input: {
path: { id: recipientSessionId },
body: buildMemberPromptBody(recipientMember, envelope),
query: { directory: recipientMember.worktreePath ?? directory },
},
})
if (promptResult.status !== "dispatched") {
log("[team-mailbox] live delivery skipped by promptAsync gate, falling back to inbox injection", {
status: promptResult.status,
teamRunId,
recipient: recipientName,
recipientSessionId,
messageId: message.messageId,
})
await releaseReservationSafely(reservation, {
teamRunId,
recipient: recipientName,
messageId: message.messageId,
})
continue
}
await commitDeliveryReservation(reservation)
log("[team-mailbox] live delivery committed", {
teamRunId,