oh-my-opencode/src/tools/agent-teams/tools.functional.test.ts

1243 lines
33 KiB
TypeScript

/// <reference types="bun-types" />
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
import { existsSync, mkdtempSync, rmSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import type { PluginInput } from "@opencode-ai/plugin"
import type { BackgroundManager } from "../../features/background-agent"
import { createAgentTeamsTools } from "./tools"
import { getTeamDir, getTeamInboxPath, getTeamTaskDir } from "./paths"
interface LaunchCall {
description: string
prompt: string
agent: string
category?: string
parentSessionID: string
parentMessageID: string
parentAgent?: string
model?: {
providerID: string
modelID: string
variant?: string
}
}
interface ResumeCall {
sessionId: string
prompt: string
parentSessionID: string
parentMessageID: string
parentAgent?: string
}
interface CancelCall {
taskId: string
options?: unknown
}
interface MockManagerHandles {
manager: BackgroundManager
launchCalls: LaunchCall[]
resumeCalls: ResumeCall[]
cancelCalls: CancelCall[]
}
interface TestToolContext {
sessionID: string
messageID: string
agent: string
abort: AbortSignal
}
function createMockManager(): MockManagerHandles {
const launchCalls: LaunchCall[] = []
const resumeCalls: ResumeCall[] = []
const cancelCalls: CancelCall[] = []
const launchedTasks = new Map<string, { id: string; sessionID: string }>()
let launchCount = 0
const manager = {
launch: async (args: LaunchCall) => {
launchCount += 1
launchCalls.push(args)
const task = { id: `bg-${launchCount}`, sessionID: `ses-worker-${launchCount}` }
launchedTasks.set(task.id, task)
return task
},
getTask: (taskId: string) => launchedTasks.get(taskId),
resume: async (args: ResumeCall) => {
resumeCalls.push(args)
return { id: `resume-${resumeCalls.length}` }
},
cancelTask: async (taskId: string, options?: unknown) => {
cancelCalls.push({ taskId, options })
return true
},
} as unknown as BackgroundManager
return { manager, launchCalls, resumeCalls, cancelCalls }
}
function createFailingLaunchManager(): { manager: BackgroundManager; cancelCalls: CancelCall[] } {
const cancelCalls: CancelCall[] = []
const manager = {
launch: async () => ({ id: "bg-fail" }),
getTask: () => ({
id: "bg-fail",
parentSessionID: "ses-main",
parentMessageID: "msg-main",
description: "failed launch",
prompt: "prompt",
agent: "sisyphus-junior",
status: "error",
error: "launch failed",
}),
resume: async () => ({ id: "resume-unused" }),
cancelTask: async (taskId: string, options?: unknown) => {
cancelCalls.push({ taskId, options })
return true
},
} as unknown as BackgroundManager
return { manager, cancelCalls }
}
function createCategoryClientMock(): PluginInput["client"] {
return {
config: {
get: async () => ({ data: { model: "openai/gpt-5.3-codex" } }),
},
provider: {
list: async () => ({ data: { connected: ["openai", "anthropic"] } }),
},
model: {
list: async () => ({
data: [
{ provider: "openai", id: "gpt-5.3-codex" },
{ provider: "anthropic", id: "claude-haiku-4-5" },
],
}),
},
} as unknown as PluginInput["client"]
}
function createContext(sessionID = "ses-main"): TestToolContext {
return {
sessionID,
messageID: "msg-main",
agent: "sisyphus",
abort: new AbortController().signal,
}
}
async function executeJsonTool(
tools: ReturnType<typeof createAgentTeamsTools>,
toolName: keyof ReturnType<typeof createAgentTeamsTools>,
args: Record<string, unknown>,
context: TestToolContext,
): Promise<unknown> {
const output = await tools[toolName].execute(args, context)
return JSON.parse(output)
}
describe("agent-teams tools functional", () => {
let originalCwd: string
let tempProjectDir: string
beforeEach(() => {
originalCwd = process.cwd()
tempProjectDir = mkdtempSync(join(tmpdir(), "agent-teams-tools-"))
process.chdir(tempProjectDir)
})
afterEach(() => {
process.chdir(originalCwd)
rmSync(tempProjectDir, { recursive: true, force: true })
})
test("team_create/read_config/delete work with project-local storage", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
//#when
const created = await executeJsonTool(
tools,
"team_create",
{ team_name: "core", description: "Core team" },
context,
) as { team_name: string; team_file_path: string; lead_agent_id: string }
//#then
expect(created.team_name).toBe("core")
expect(created.lead_agent_id).toBe("team-lead@core")
expect(created.team_file_path).toBe(join(tempProjectDir, ".sisyphus", "agent-teams", "teams", "core", "config.json"))
expect(existsSync(created.team_file_path)).toBe(true)
expect(existsSync(getTeamInboxPath("core", "team-lead"))).toBe(true)
//#when
const config = await executeJsonTool(tools, "read_config", { team_name: "core" }, context) as {
name: string
members: Array<{ name: string; model?: string }>
}
//#then
expect(config.name).toBe("core")
expect(config.members.map((member) => member.name)).toEqual(["team-lead"])
expect(config.members[0]?.model).toBe("native/team-lead")
//#when
const deleted = await executeJsonTool(tools, "team_delete", { team_name: "core" }, context) as {
success: boolean
}
//#then
expect(deleted.success).toBe(true)
expect(existsSync(getTeamDir("core"))).toBe(false)
expect(existsSync(getTeamTaskDir("core"))).toBe(false)
})
test("task tools create/update/get/list and emit assignment inbox message", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
context,
)
//#when
const createdTask = await executeJsonTool(
tools,
"team_task_create",
{
team_name: "core",
subject: "Draft release notes",
description: "Prepare release notes for next publish.",
},
context,
) as { id: string; status: string }
//#then
expect(createdTask.id).toMatch(/^T-[a-f0-9-]+$/)
expect(createdTask.status).toBe("pending")
//#when
const updatedTask = await executeJsonTool(
tools,
"team_task_update",
{
team_name: "core",
task_id: createdTask.id,
owner: "worker_1",
status: "in_progress",
},
context,
) as { owner?: string; status: string }
//#then
expect(updatedTask.owner).toBe("worker_1")
expect(updatedTask.status).toBe("in_progress")
//#when
const fetchedTask = await executeJsonTool(
tools,
"team_task_get",
{ team_name: "core", task_id: createdTask.id },
context,
) as { id: string; owner?: string }
const listedTasks = await executeJsonTool(tools, "team_task_list", { team_name: "core" }, context) as Array<{ id: string }>
const inbox = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "worker_1",
unread_only: true,
mark_as_read: false,
},
context,
) as Array<{ summary?: string; text: string }>
//#then
expect(fetchedTask.id).toBe(createdTask.id)
expect(fetchedTask.owner).toBe("worker_1")
expect(listedTasks.some((task) => task.id === createdTask.id)).toBe(true)
expect(inbox.some((message) => message.summary === "task_assignment")).toBe(true)
const assignment = inbox.find((message) => message.summary === "task_assignment")
expect(assignment).toBeDefined()
const payload = JSON.parse(assignment!.text) as { type: string; taskId: string }
expect(payload.type).toBe("task_assignment")
expect(payload.taskId).toBe(createdTask.id)
//#when
const clearedOwnerTask = await executeJsonTool(
tools,
"team_task_update",
{
team_name: "core",
task_id: createdTask.id,
owner: "",
},
context,
) as { owner?: string }
//#then
expect(clearedOwnerTask.owner).toBeUndefined()
})
test("spawns teammate using category resolution like delegate-task", async () => {
//#given
const { manager, launchCalls } = createMockManager()
const tools = createAgentTeamsTools(manager, { client: createCategoryClientMock() })
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const spawned = await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
context,
) as { name?: string; error?: string }
//#then
expect(spawned.error).toBeUndefined()
expect(spawned.name).toBe("worker_1")
expect(launchCalls).toHaveLength(1)
expect(launchCalls[0].agent).toBe("sisyphus-junior")
expect(launchCalls[0].category).toBe("quick")
expect(launchCalls[0].model).toBeDefined()
const resolvedModel = launchCalls[0].model!
expect(launchCalls[0].prompt).toContain("Category guidance:")
//#when
const config = await executeJsonTool(tools, "read_config", { team_name: "core" }, context) as {
members: Array<{ name: string; category?: string; model?: string }>
}
//#then
const teammate = config.members.find((member) => member.name === "worker_1")
expect(teammate).toBeDefined()
expect(teammate?.category).toBe("quick")
expect(teammate?.model).toBe(`${resolvedModel.providerID}/${resolvedModel.modelID}`)
})
test("spawn_teammate requires a category", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const result = await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
},
context,
) as { error?: string }
//#then
expect(result.error).toBeDefined()
expect(result.error).toContain("category")
})
test("rejects category with incompatible subagent_type", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager, { client: createCategoryClientMock() })
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const result = await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
subagent_type: "oracle",
},
context,
) as { error?: string }
//#then
expect(result.error).toBe("category_conflicts_with_subagent_type")
})
test("rejects invalid task id input for task_get", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const result = await executeJsonTool(
tools,
"team_task_get",
{ team_name: "core", task_id: "../../etc/passwd" },
context,
) as { error?: string }
//#then
expect(result.error).toBe("task_id_invalid")
})
test("requires owner to be a team member when setting task owner", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
const createdTask = await executeJsonTool(
tools,
"team_task_create",
{
team_name: "core",
subject: "Investigate bug",
description: "Investigate and report root cause",
},
context,
) as { id: string }
//#when
const result = await executeJsonTool(
tools,
"team_task_update",
{
team_name: "core",
task_id: createdTask.id,
owner: "ghost_user",
},
context,
) as { error?: string }
//#then
expect(result.error).toBe("owner_not_in_team")
})
test("allows assigning team-lead as task owner", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
const createdTask = await executeJsonTool(
tools,
"team_task_create",
{
team_name: "core",
subject: "Prepare checklist",
description: "Prepare release checklist",
},
context,
) as { id: string }
//#when
const updated = await executeJsonTool(
tools,
"team_task_update",
{
team_name: "core",
task_id: createdTask.id,
owner: "team-lead",
},
context,
) as { owner?: string }
const leadInbox = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "team-lead",
unread_only: true,
mark_as_read: false,
},
context,
) as Array<{ summary?: string; text: string }>
//#then
expect(updated.owner).toBe("team-lead")
expect(leadInbox.some((message) => message.summary === "task_assignment")).toBe(true)
})
test("spawn_teammate + send_message + force_kill_teammate execute end-to-end", async () => {
//#given
const { manager, launchCalls, resumeCalls, cancelCalls } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const spawned = await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
context,
) as { name: string; session_id: string; task_id: string }
//#then
expect(spawned.name).toBe("worker_1")
expect(spawned.session_id).toBe("ses-worker-1")
expect(spawned.task_id).toBe("bg-1")
expect(launchCalls).toHaveLength(1)
expect(launchCalls[0]).toMatchObject({
description: "[team:core] worker_1",
agent: "sisyphus-junior",
parentSessionID: "ses-main",
parentMessageID: "msg-main",
parentAgent: "sisyphus",
})
//#when
const sent = await executeJsonTool(
tools,
"send_message",
{
team_name: "core",
type: "message",
recipient: "worker_1",
summary: "sync",
content: "Please update status.",
},
context,
) as { success: boolean }
//#then
expect(sent.success).toBe(true)
expect(resumeCalls).toHaveLength(1)
expect(resumeCalls[0].sessionId).toBe("ses-worker-1")
//#when
const invalidSender = await executeJsonTool(
tools,
"send_message",
{
team_name: "core",
type: "message",
sender: "ghost_user",
recipient: "worker_1",
summary: "sync",
content: "Please update status.",
},
context,
) as { error?: string }
//#then
expect(invalidSender.error).toBe("sender_context_mismatch")
//#given
const createdTask = await executeJsonTool(
tools,
"team_task_create",
{
team_name: "core",
subject: "Follow-up",
description: "Collect teammate update",
},
context,
) as { id: string }
await executeJsonTool(
tools,
"team_task_update",
{
team_name: "core",
task_id: createdTask.id,
owner: "worker_1",
status: "in_progress",
},
context,
)
//#when
const killed = await executeJsonTool(
tools,
"force_kill_teammate",
{
team_name: "core",
agent_name: "worker_1",
},
context,
) as { success: boolean }
//#then
expect(killed.success).toBe(true)
expect(cancelCalls).toHaveLength(1)
expect(cancelCalls[0].taskId).toBe("bg-1")
expect(cancelCalls[0].options).toEqual(
expect.objectContaining({
source: "team_force_kill",
abortSession: true,
skipNotification: true,
}),
)
//#when
const configAfterKill = await executeJsonTool(tools, "read_config", { team_name: "core" }, context) as {
members: Array<{ name: string }>
}
const taskAfterKill = await executeJsonTool(
tools,
"team_task_get",
{
team_name: "core",
task_id: createdTask.id,
},
context,
) as { owner?: string; status: string }
//#then
expect(configAfterKill.members.some((member) => member.name === "worker_1")).toBe(false)
expect(taskAfterKill.owner).toBeUndefined()
expect(taskAfterKill.status).toBe("pending")
})
test("process_shutdown_approved cancels and removes teammate", async () => {
//#given
const { manager, cancelCalls } = createMockManager()
const tools = createAgentTeamsTools(manager)
const leadContext = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, leadContext)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
leadContext,
)
//#when
const shutdownResult = await executeJsonTool(
tools,
"process_shutdown_approved",
{
team_name: "core",
agent_name: "worker_1",
},
leadContext,
) as { success: boolean }
//#then
expect(shutdownResult.success).toBe(true)
expect(cancelCalls).toHaveLength(1)
expect(cancelCalls[0].taskId).toBe("bg-1")
expect(cancelCalls[0].options).toEqual(
expect.objectContaining({
source: "team_force_kill",
abortSession: true,
skipNotification: true,
}),
)
//#when
const configAfterShutdown = await executeJsonTool(tools, "read_config", { team_name: "core" }, leadContext) as {
members: Array<{ name: string }>
}
//#then
expect(configAfterShutdown.members.some((member) => member.name === "worker_1")).toBe(false)
})
test("force_kill_teammate requires lead session authorization", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const leadContext = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, leadContext)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
leadContext,
)
const teammateContext = createContext("ses-worker-1")
//#when
const unauthorized = await executeJsonTool(
tools,
"force_kill_teammate",
{
team_name: "core",
agent_name: "worker_1",
},
teammateContext,
) as { error?: string }
//#then
expect(unauthorized.error).toBe("unauthorized_lead_session")
})
test("process_shutdown_approved requires lead session authorization", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const leadContext = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, leadContext)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
leadContext,
)
const teammateContext = createContext("ses-worker-1")
//#when
const unauthorized = await executeJsonTool(
tools,
"process_shutdown_approved",
{
team_name: "core",
agent_name: "worker_1",
},
teammateContext,
) as { error?: string }
//#then
expect(unauthorized.error).toBe("unauthorized_lead_session")
})
test("rolls back teammate and cancels background task when launch fails", async () => {
//#given
const { manager, cancelCalls } = createFailingLaunchManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const spawnResult = await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
context,
) as { error?: string }
//#then
expect(spawnResult.error).toBe("teammate_launch_failed:launch failed")
//#when
const config = await executeJsonTool(tools, "read_config", { team_name: "core" }, context) as {
members: Array<{ name: string }>
}
//#then
expect(config.members.map((member) => member.name)).toEqual(["team-lead"])
expect(cancelCalls).toHaveLength(1)
expect(cancelCalls[0].taskId).toBe("bg-fail")
expect(cancelCalls[0].options).toEqual(
expect.objectContaining({
source: "team_launch_failed",
abortSession: true,
skipNotification: true,
}),
)
expect(existsSync(getTeamInboxPath("core", "worker_1"))).toBe(false)
})
test("returns explicit error on invalid model override format", async () => {
//#given
const { manager, launchCalls } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const spawnResult = await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
model: "invalid-format",
},
context,
) as { error?: string }
//#then
expect(spawnResult.error).toBe("invalid_model_override_format")
expect(launchCalls).toHaveLength(0)
//#when
const config = await executeJsonTool(tools, "read_config", { team_name: "core" }, context) as {
members: Array<{ name: string }>
}
//#then
expect(config.members.map((member) => member.name)).toEqual(["team-lead"])
})
test("keeps full model id suffix when override contains extra slashes", async () => {
//#given
const { manager, launchCalls } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
//#when
const spawnResult = await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
model: "openai/gpt-5.3-codex/reasoning",
},
context,
) as { name?: string; error?: string }
//#then
expect(spawnResult.error).toBeUndefined()
expect(spawnResult.name).toBe("worker_1")
expect(launchCalls).toHaveLength(1)
expect(launchCalls[0].model).toEqual({
providerID: "openai",
modelID: "gpt-5.3-codex/reasoning",
})
})
test("read_inbox returns team_not_found for unknown team", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
//#when
const result = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "missing_team",
agent_name: "team-lead",
},
context,
) as { error?: string }
//#then
expect(result.error).toBe("team_not_found")
})
test("read_inbox denies cross-member access for non-lead sessions", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const leadContext = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, leadContext)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
leadContext,
)
const teammateContext = createContext("ses-worker-1")
//#when
const unauthorized = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "team-lead",
},
teammateContext,
) as { error?: string }
//#then
expect(unauthorized.error).toBe("unauthorized_reader_session")
//#when
const ownInbox = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "worker_1",
},
teammateContext,
) as unknown[]
//#then
expect(Array.isArray(ownInbox)).toBe(true)
})
test("read_inbox returns messages with read=true when mark_as_read is enabled", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
context,
)
//#when
const unreadBefore = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "worker_1",
unread_only: true,
mark_as_read: false,
},
context,
) as Array<{ read: boolean }>
const markedRead = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "worker_1",
unread_only: true,
mark_as_read: true,
},
context,
) as Array<{ read: boolean }>
const unreadAfter = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "worker_1",
unread_only: true,
mark_as_read: false,
},
context,
) as Array<{ read: boolean }>
//#then
expect(unreadBefore.length).toBeGreaterThan(0)
expect(unreadBefore.every((message) => message.read === false)).toBe(true)
expect(markedRead.length).toBeGreaterThan(0)
expect(markedRead.every((message) => message.read === true)).toBe(true)
expect(unreadAfter).toHaveLength(0)
})
test("rejects unknown session claiming team-lead identity", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const leadContext = createContext("ses-main")
await executeJsonTool(tools, "team_create", { team_name: "core" }, leadContext)
const unknownContext = createContext("ses-unknown")
//#when
const sendResult = await executeJsonTool(
tools,
"send_message",
{
team_name: "core",
type: "message",
sender: "team-lead",
recipient: "team-lead",
summary: "restart",
content: "Lead session migrated",
},
unknownContext,
) as { success?: boolean; error?: string }
//#then
expect(sendResult.success).toBeUndefined()
expect(sendResult.error).toBe("unauthorized_sender_session")
//#when
const config = await executeJsonTool(tools, "read_config", { team_name: "core" }, leadContext) as {
leadSessionId: string
}
//#then
expect(config.leadSessionId).toBe("ses-main")
})
test("rejects unknown session claiming team-lead inbox", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const leadContext = createContext("ses-main")
await executeJsonTool(tools, "team_create", { team_name: "core" }, leadContext)
const unknownContext = createContext("ses-unknown")
//#when
const result = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "team-lead",
},
unknownContext,
) as { error?: string }
//#then
expect(result.error).toBe("unauthorized_reader_session")
})
test("clears old inbox when teammate is removed then re-spawned", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "First run",
category: "quick",
},
context,
)
await executeJsonTool(
tools,
"send_message",
{
team_name: "core",
type: "message",
recipient: "worker_1",
summary: "legacy",
content: "legacy payload",
},
context,
)
await executeJsonTool(
tools,
"force_kill_teammate",
{
team_name: "core",
agent_name: "worker_1",
},
context,
)
//#when
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Second run",
category: "quick",
},
context,
)
const inbox = await executeJsonTool(
tools,
"read_inbox",
{
team_name: "core",
agent_name: "worker_1",
unread_only: true,
mark_as_read: false,
},
context,
) as Array<{ text: string; summary?: string }>
//#then
expect(inbox.some((message) => message.text.includes("legacy payload"))).toBe(false)
expect(inbox.some((message) => message.summary === "initial_prompt" && message.text.includes("Second run"))).toBe(true)
})
test("cannot add pending blockers to already in-progress task without status change", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const context = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, context)
const blocker = await executeJsonTool(
tools,
"team_task_create",
{
team_name: "core",
subject: "Blocker",
description: "Unfinished blocker",
},
context,
) as { id: string }
const mainTask = await executeJsonTool(
tools,
"team_task_create",
{
team_name: "core",
subject: "Main",
description: "Main task",
},
context,
) as { id: string }
await executeJsonTool(
tools,
"team_task_update",
{
team_name: "core",
task_id: mainTask.id,
status: "in_progress",
},
context,
)
//#when
const result = await executeJsonTool(
tools,
"team_task_update",
{
team_name: "core",
task_id: mainTask.id,
add_blocked_by: [blocker.id],
},
context,
) as { error?: string }
//#then
expect(result.error).toBe(`blocked_by_incomplete:${blocker.id}:pending`)
})
test("binds sender to calling context and rejects sender spoofing", async () => {
//#given
const { manager } = createMockManager()
const tools = createAgentTeamsTools(manager)
const leadContext = createContext()
await executeJsonTool(tools, "team_create", { team_name: "core" }, leadContext)
await executeJsonTool(
tools,
"spawn_teammate",
{
team_name: "core",
name: "worker_1",
prompt: "Handle release prep",
category: "quick",
},
leadContext,
)
const teammateContext = createContext("ses-worker-1")
//#when
const spoofed = await executeJsonTool(
tools,
"send_message",
{
team_name: "core",
type: "message",
sender: "team-lead",
recipient: "worker_1",
summary: "spoof",
content: "I am lead",
},
teammateContext,
) as { error?: string }
//#then
expect(spoofed.error).toBe("sender_context_mismatch")
//#when
const validFromContext = await executeJsonTool(
tools,
"send_message",
{
team_name: "core",
type: "message",
recipient: "team-lead",
summary: "update",
content: "status from worker",
},
teammateContext,
) as { success?: boolean }
//#then
expect(validFromContext.success).toBe(true)
})
})