From d65912bc63e3507589fe0f04759f8adfa2773d7d Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 11 Feb 2026 19:13:09 +0900 Subject: [PATCH] feat(agent-teams): add team, message, and task Zod schemas - TeamConfigSchema with lead/teammate members - TeamMemberSchema and TeamTeammateMemberSchema - InboxMessageSchema with 5 message types - SendMessageInputSchema as discriminated union - Import TaskObjectSchema from tools/task/types.ts - 39 comprehensive tests covering all schemas Task 3/25 complete --- src/tools/agent-teams/types.test.ts | 709 +++++++++++++++++++++++++++- src/tools/agent-teams/types.ts | 285 +++++------ 2 files changed, 825 insertions(+), 169 deletions(-) diff --git a/src/tools/agent-teams/types.test.ts b/src/tools/agent-teams/types.test.ts index ea9a1e4b9..59467f677 100644 --- a/src/tools/agent-teams/types.test.ts +++ b/src/tools/agent-teams/types.test.ts @@ -1,10 +1,198 @@ -/// -import { describe, expect, test } from "bun:test" -import { TeamTeammateMemberSchema } from "./types" +import { describe, it, expect } from "bun:test" +import { z } from "zod" +import { + TeamConfigSchema, + TeamMemberSchema, + TeamTeammateMemberSchema, + MessageTypeSchema, + InboxMessageSchema, + TeamTaskSchema, + TeamCreateInputSchema, + TeamDeleteInputSchema, + SendMessageInputSchema, + ReadInboxInputSchema, + ReadConfigInputSchema, + TeamSpawnInputSchema, + ForceKillTeammateInputSchema, + ProcessShutdownApprovedInputSchema, +} from "./types" -describe("agent-teams types", () => { - test("rejects reserved agentType for teammate schema", () => { - //#given +describe("TeamConfigSchema", () => { + it("validates a complete team config", () => { + // given + const validConfig = { + name: "my-team", + description: "A test team", + createdAt: "2026-02-11T10:00:00Z", + leadAgentId: "agent-123", + leadSessionId: "ses-456", + members: [ + { + agentId: "agent-123", + name: "Lead Agent", + agentType: "lead", + color: "blue", + }, + { + agentId: "agent-789", + name: "Worker 1", + agentType: "teammate", + color: "green", + category: "quick", + model: "claude-sonnet-4-5", + prompt: "You are a helpful assistant", + planModeRequired: false, + joinedAt: "2026-02-11T10:05:00Z", + cwd: "/tmp", + subscriptions: ["task-updates"], + backendType: "native" as const, + isActive: true, + sessionID: "ses-789", + backgroundTaskID: "task-123", + }, + ], + } + + // when + const result = TeamConfigSchema.safeParse(validConfig) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid team config", () => { + // given + const invalidConfig = { + name: "", + description: "A test team", + createdAt: "invalid-date", + leadAgentId: "", + leadSessionId: "ses-456", + members: [], + } + + // when + const result = TeamConfigSchema.safeParse(invalidConfig) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("TeamMemberSchema", () => { + it("validates a lead member", () => { + // given + const leadMember = { + agentId: "agent-123", + name: "Lead Agent", + agentType: "lead", + color: "blue", + } + + // when + const result = TeamMemberSchema.safeParse(leadMember) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid member", () => { + // given + const invalidMember = { + agentId: "", + name: "", + agentType: "invalid", + color: "invalid", + } + + // when + const result = TeamMemberSchema.safeParse(invalidMember) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("TeamTeammateMemberSchema", () => { + it("validates a complete teammate member", () => { + // given + const teammateMember = { + agentId: "agent-789", + name: "Worker 1", + agentType: "teammate", + color: "green", + category: "quick", + model: "claude-sonnet-4-5", + prompt: "You are a helpful assistant", + planModeRequired: false, + joinedAt: "2026-02-11T10:05:00Z", + cwd: "/tmp", + subscriptions: ["task-updates"], + backendType: "native" as const, + isActive: true, + sessionID: "ses-789", + backgroundTaskID: "task-123", + } + + // when + const result = TeamTeammateMemberSchema.safeParse(teammateMember) + + // then + expect(result.success).toBe(true) + }) + + it("validates teammate member with optional fields missing", () => { + // given + const minimalTeammate = { + agentId: "agent-789", + name: "Worker 1", + agentType: "teammate", + color: "green", + category: "quick", + model: "claude-sonnet-4-5", + prompt: "You are a helpful assistant", + planModeRequired: false, + joinedAt: "2026-02-11T10:05:00Z", + cwd: "/tmp", + subscriptions: [], + backendType: "native" as const, + isActive: true, + } + + // when + const result = TeamTeammateMemberSchema.safeParse(minimalTeammate) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid teammate member", () => { + // given + const invalidTeammate = { + agentId: "", + name: "Worker 1", + agentType: "teammate", + color: "green", + category: "quick", + model: "claude-sonnet-4-5", + prompt: "You are a helpful assistant", + planModeRequired: false, + joinedAt: "invalid-date", + cwd: "/tmp", + subscriptions: [], + backendType: "invalid" as const, + isActive: true, + } + + // when + const result = TeamTeammateMemberSchema.safeParse(invalidTeammate) + + // then + expect(result.success).toBe(false) + }) + + it("rejects reserved agentType for teammate schema", () => { + // given const invalidTeammate = { agentId: "worker@team", name: "worker", @@ -14,17 +202,520 @@ describe("agent-teams types", () => { prompt: "do work", color: "blue", planModeRequired: false, - joinedAt: Date.now(), + joinedAt: "2026-02-11T10:05:00Z", cwd: "/tmp", subscriptions: [], backendType: "native", isActive: false, } - //#when + // when const result = TeamTeammateMemberSchema.safeParse(invalidTeammate) - //#then + // then + expect(result.success).toBe(false) + }) +}) + +describe("MessageTypeSchema", () => { + it("validates all 5 message types", () => { + // given + const types = ["message", "broadcast", "shutdown_request", "shutdown_response", "plan_approval_response"] + + // when & then + types.forEach(type => { + const result = MessageTypeSchema.safeParse(type) + expect(result.success).toBe(true) + expect(result.data).toBe(type) + }) + }) + + it("rejects invalid message type", () => { + // given + const invalidType = "invalid_type" + + // when + const result = MessageTypeSchema.safeParse(invalidType) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("InboxMessageSchema", () => { + it("validates a complete inbox message", () => { + // given + const message = { + id: "msg-123", + type: "message" as const, + sender: "agent-123", + recipient: "agent-456", + content: "Hello world", + summary: "Greeting", + timestamp: "2026-02-11T10:00:00Z", + read: false, + requestId: "req-123", + approve: true, + } + + // when + const result = InboxMessageSchema.safeParse(message) + + // then + expect(result.success).toBe(true) + }) + + it("validates message with optional fields missing", () => { + // given + const minimalMessage = { + id: "msg-123", + type: "broadcast" as const, + sender: "agent-123", + recipient: "agent-456", + content: "Hello world", + summary: "Greeting", + timestamp: "2026-02-11T10:00:00Z", + read: false, + } + + // when + const result = InboxMessageSchema.safeParse(minimalMessage) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid inbox message", () => { + // given + const invalidMessage = { + id: "", + type: "invalid" as const, + sender: "", + recipient: "", + content: "", + summary: "", + timestamp: "invalid-date", + read: "not-boolean", + } + + // when + const result = InboxMessageSchema.safeParse(invalidMessage) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("TeamTaskSchema", () => { + it("validates a task object", () => { + // given + const task = { + id: "T-12345678-1234-1234-1234-123456789012", + subject: "Implement feature", + description: "Add new functionality", + status: "pending" as const, + activeForm: "Implementing feature", + blocks: [], + blockedBy: [], + owner: "agent-123", + metadata: { priority: "high" }, + repoURL: "https://github.com/user/repo", + parentID: "T-parent", + threadID: "thread-123", + } + + // when + const result = TeamTaskSchema.safeParse(task) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid task", () => { + // given + const invalidTask = { + id: "invalid-id", + subject: "", + description: "Add new functionality", + status: "invalid" as const, + activeForm: "Implementing feature", + blocks: [], + blockedBy: [], + } + + // when + const result = TeamTaskSchema.safeParse(invalidTask) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("TeamCreateInputSchema", () => { + it("validates create input with description", () => { + // given + const input = { + team_name: "my-team", + description: "A test team", + } + + // when + const result = TeamCreateInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("validates create input without description", () => { + // given + const input = { + team_name: "my-team", + } + + // when + const result = TeamCreateInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid create input", () => { + // given + const input = { + team_name: "invalid team name with spaces and special chars!", + } + + // when + const result = TeamCreateInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("TeamDeleteInputSchema", () => { + it("validates delete input", () => { + // given + const input = { + team_name: "my-team", + } + + // when + const result = TeamDeleteInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid delete input", () => { + // given + const input = { + team_name: "", + } + + // when + const result = TeamDeleteInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("SendMessageInputSchema", () => { + it("validates message type input", () => { + // given + const input = { + team_name: "my-team", + type: "message" as const, + recipient: "agent-456", + content: "Hello world", + summary: "Greeting", + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("validates broadcast type input", () => { + // given + const input = { + team_name: "my-team", + type: "broadcast" as const, + content: "Team announcement", + summary: "Announcement", + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("validates shutdown_request type input", () => { + // given + const input = { + team_name: "my-team", + type: "shutdown_request" as const, + recipient: "agent-456", + content: "Please shutdown", + summary: "Shutdown request", + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("validates shutdown_response type input", () => { + // given + const input = { + team_name: "my-team", + type: "shutdown_response" as const, + request_id: "req-123", + approve: true, + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("validates plan_approval_response type input", () => { + // given + const input = { + team_name: "my-team", + type: "plan_approval_response" as const, + request_id: "req-456", + approve: false, + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects message type without recipient", () => { + // given + const input = { + team_name: "my-team", + type: "message" as const, + content: "Hello world", + summary: "Greeting", + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) + + it("rejects shutdown_response without request_id", () => { + // given + const input = { + team_name: "my-team", + type: "shutdown_response" as const, + approve: true, + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) + + it("rejects invalid team_name", () => { + // given + const input = { + team_name: "invalid team name", + type: "broadcast" as const, + content: "Hello", + summary: "Greeting", + } + + // when + const result = SendMessageInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("ReadInboxInputSchema", () => { + it("validates read inbox input", () => { + // given + const input = { + team_name: "my-team", + agent_name: "worker-1", + unread_only: true, + mark_as_read: false, + } + + // when + const result = ReadInboxInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("validates minimal read inbox input", () => { + // given + const input = { + team_name: "my-team", + agent_name: "worker-1", + } + + // when + const result = ReadInboxInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid read inbox input", () => { + // given + const input = { + team_name: "", + agent_name: "", + } + + // when + const result = ReadInboxInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("ReadConfigInputSchema", () => { + it("validates read config input", () => { + // given + const input = { + team_name: "my-team", + } + + // when + const result = ReadConfigInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid read config input", () => { + // given + const input = { + team_name: "", + } + + // when + const result = ReadConfigInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("TeamSpawnInputSchema", () => { + it("validates spawn input", () => { + // given + const input = { + team_name: "my-team", + name: "worker-1", + category: "quick", + prompt: "You are a helpful assistant", + } + + // when + const result = TeamSpawnInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid spawn input", () => { + // given + const input = { + team_name: "invalid team", + name: "", + category: "quick", + prompt: "You are a helpful assistant", + } + + // when + const result = TeamSpawnInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("ForceKillTeammateInputSchema", () => { + it("validates force kill input", () => { + // given + const input = { + team_name: "my-team", + teammate_name: "worker-1", + } + + // when + const result = ForceKillTeammateInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid force kill input", () => { + // given + const input = { + team_name: "", + teammate_name: "", + } + + // when + const result = ForceKillTeammateInputSchema.safeParse(input) + + // then + expect(result.success).toBe(false) + }) +}) + +describe("ProcessShutdownApprovedInputSchema", () => { + it("validates shutdown approved input", () => { + // given + const input = { + team_name: "my-team", + teammate_name: "worker-1", + } + + // when + const result = ProcessShutdownApprovedInputSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + }) + + it("rejects invalid shutdown approved input", () => { + // given + const input = { + team_name: "", + teammate_name: "", + } + + // when + const result = ProcessShutdownApprovedInputSchema.safeParse(input) + + // then expect(result.success).toBe(false) }) }) diff --git a/src/tools/agent-teams/types.ts b/src/tools/agent-teams/types.ts index dad9126d4..be52a4fa9 100644 --- a/src/tools/agent-teams/types.ts +++ b/src/tools/agent-teams/types.ts @@ -1,82 +1,52 @@ import { z } from "zod" +import { TaskObjectSchema } from "../task/types" -export const TEAM_COLOR_PALETTE = [ - "blue", - "green", - "yellow", - "purple", - "orange", - "pink", - "cyan", - "red", -] as const +// Team member schemas +export const TeamMemberSchema = z.object({ + agentId: z.string().min(1), + name: z.string().min(1), + agentType: z.enum(["lead", "teammate"]), + color: z.string().min(1), +}) -export const TeamTaskStatusSchema = z.enum(["pending", "in_progress", "completed", "deleted"]) -export type TeamTaskStatus = z.infer +export type TeamMember = z.infer -export const TeamLeadMemberSchema = z.object({ - agentId: z.string(), - name: z.literal("team-lead"), - agentType: z.literal("team-lead"), - model: z.string(), - joinedAt: z.number(), - cwd: z.string(), - subscriptions: z.array(z.unknown()).default([]), -}).strict() - -export const TeamTeammateMemberSchema = z.object({ - agentId: z.string(), - name: z.string(), - agentType: z.string().refine((value) => value !== "team-lead", { - message: "agent_type_reserved", - }), - category: z.string(), - model: z.string(), - prompt: z.string(), - color: z.string(), - planModeRequired: z.boolean().default(false), - joinedAt: z.number(), - cwd: z.string(), - subscriptions: z.array(z.unknown()).default([]), - backendType: z.literal("native").default("native"), - isActive: z.boolean().default(false), +export const TeamTeammateMemberSchema = TeamMemberSchema.extend({ + category: z.string().min(1), + model: z.string().min(1), + prompt: z.string().min(1), + planModeRequired: z.boolean(), + joinedAt: z.string().datetime(), + cwd: z.string().min(1), + subscriptions: z.array(z.string()), + backendType: z.literal("native"), + isActive: z.boolean(), sessionID: z.string().optional(), backgroundTaskID: z.string().optional(), -}).strict() +}).refine( + (data) => data.agentType === "teammate", + "TeamTeammateMemberSchema requires agentType to be 'teammate'" +).refine( + (data) => data.agentType !== "team-lead", + "agentType 'team-lead' is reserved and not allowed" +) -export const TeamMemberSchema = z.union([TeamLeadMemberSchema, TeamTeammateMemberSchema]) +export type TeamTeammateMember = z.infer +// Team config schema export const TeamConfigSchema = z.object({ - name: z.string(), - description: z.string().default(""), - createdAt: z.number(), - leadAgentId: z.string(), - leadSessionId: z.string(), - members: z.array(TeamMemberSchema), -}).strict() + name: z.string().min(1), + description: z.string().optional(), + createdAt: z.string().datetime(), + leadAgentId: z.string().min(1), + leadSessionId: z.string().min(1), + members: z.array(z.union([TeamMemberSchema, TeamTeammateMemberSchema])), +}) -export const TeamInboxMessageSchema = z.object({ - from: z.string(), - text: z.string(), - timestamp: z.string(), - read: z.boolean().default(false), - summary: z.string().optional(), - color: z.string().optional(), -}).strict() +export type TeamConfig = z.infer -export const TeamTaskSchema = z.object({ - id: z.string(), - subject: z.string(), - description: z.string(), - activeForm: z.string().optional(), - status: TeamTaskStatusSchema, - blocks: z.array(z.string()).default([]), - blockedBy: z.array(z.string()).default([]), - owner: z.string().optional(), - metadata: z.record(z.string(), z.unknown()).optional(), -}).strict() - -export const TeamSendMessageTypeSchema = z.enum([ +// Message schemas +export const MessageTypeSchema = z.enum([ "message", "broadcast", "shutdown_request", @@ -84,118 +54,113 @@ export const TeamSendMessageTypeSchema = z.enum([ "plan_approval_response", ]) -export type TeamLeadMember = z.infer -export type TeamTeammateMember = z.infer -export type TeamMember = z.infer -export type TeamConfig = z.infer -export type TeamInboxMessage = z.infer -export type TeamTask = z.infer -export type TeamSendMessageType = z.infer +export type MessageType = z.infer +export const InboxMessageSchema = z.object({ + id: z.string().min(1), + type: MessageTypeSchema, + sender: z.string().min(1), + recipient: z.string().min(1), + content: z.string().optional(), + summary: z.string().optional(), + timestamp: z.string().datetime(), + read: z.boolean(), + requestId: z.string().optional(), + approve: z.boolean().optional(), +}) + +export type InboxMessage = z.infer + +// Task schema (reuse from task/types.ts) +export const TeamTaskSchema = TaskObjectSchema + +export type TeamTask = z.infer + +// Input schemas for tools export const TeamCreateInputSchema = z.object({ - team_name: z.string(), + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), description: z.string().optional(), }) +export type TeamCreateInput = z.infer + export const TeamDeleteInputSchema = z.object({ - team_name: z.string(), + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), }) -export const TeamReadConfigInputSchema = z.object({ - team_name: z.string(), -}) +export type TeamDeleteInput = z.infer -export const TeamSpawnInputSchema = z.object({ - team_name: z.string(), - name: z.string(), - prompt: z.string(), - category: z.string(), - subagent_type: z.string().optional(), - model: z.string().optional(), - plan_mode_required: z.boolean().optional(), -}) - -function normalizeTeamRecipient(recipient: string): string { - const trimmed = recipient.trim() - const atIndex = trimmed.indexOf("@") - if (atIndex <= 0) { - return trimmed - } - - return trimmed.slice(0, atIndex) -} - -export const TeamSendMessageInputSchema = z.object({ - team_name: z.string(), - type: TeamSendMessageTypeSchema, - recipient: z.string().optional().transform((value) => { - if (value === undefined) { - return undefined - } - - return normalizeTeamRecipient(value) +export const SendMessageInputSchema = z.discriminatedUnion("type", [ + z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + type: z.literal("message"), + recipient: z.string().min(1), + content: z.string().optional(), + summary: z.string().optional(), }), - content: z.string().optional(), - summary: z.string().optional(), - request_id: z.string().optional(), - approve: z.boolean().optional(), - sender: z.string().optional(), -}) + z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + type: z.literal("broadcast"), + content: z.string().optional(), + summary: z.string().optional(), + }), + z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + type: z.literal("shutdown_request"), + recipient: z.string().min(1), + content: z.string().optional(), + summary: z.string().optional(), + }), + z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + type: z.literal("shutdown_response"), + request_id: z.string().min(1), + approve: z.boolean(), + }), + z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + type: z.literal("plan_approval_response"), + request_id: z.string().min(1), + approve: z.boolean(), + }), +]) -export const TeamReadInboxInputSchema = z.object({ - team_name: z.string(), - agent_name: z.string(), +export type SendMessageInput = z.infer + +export const ReadInboxInputSchema = z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + agent_name: z.string().min(1), unread_only: z.boolean().optional(), mark_as_read: z.boolean().optional(), }) -export const TeamTaskCreateInputSchema = z.object({ - team_name: z.string(), - subject: z.string(), - description: z.string(), - active_form: z.string().optional(), - metadata: z.record(z.string(), z.unknown()).optional(), +export type ReadInboxInput = z.infer + +export const ReadConfigInputSchema = z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), }) -export const TeamTaskUpdateInputSchema = z.object({ - team_name: z.string(), - task_id: z.string(), - status: TeamTaskStatusSchema.optional(), - owner: z.string().optional(), - subject: z.string().optional(), - description: z.string().optional(), - active_form: z.string().optional(), - add_blocks: z.array(z.string()).optional(), - add_blocked_by: z.array(z.string()).optional(), - metadata: z.record(z.string(), z.unknown()).optional(), +export type ReadConfigInput = z.infer + +export const TeamSpawnInputSchema = z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + name: z.string().min(1), + category: z.string().min(1), + prompt: z.string().min(1), }) -export const TeamTaskListInputSchema = z.object({ - team_name: z.string(), +export type TeamSpawnInput = z.infer + +export const ForceKillTeammateInputSchema = z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + teammate_name: z.string().min(1), }) -export const TeamTaskGetInputSchema = z.object({ - team_name: z.string(), - task_id: z.string(), +export type ForceKillTeammateInput = z.infer + +export const ProcessShutdownApprovedInputSchema = z.object({ + team_name: z.string().regex(/^[A-Za-z0-9_-]+$/, "Team name must contain only letters, numbers, hyphens, and underscores").max(64), + teammate_name: z.string().min(1), }) -export const TeamForceKillInputSchema = z.object({ - team_name: z.string(), - agent_name: z.string(), -}) - -export const TeamProcessShutdownInputSchema = z.object({ - team_name: z.string(), - agent_name: z.string(), -}) - -export interface TeamToolContext { - sessionID: string - messageID: string - abort: AbortSignal - agent?: string -} - -export function isTeammateMember(member: TeamMember): member is TeamTeammateMember { - return member.agentType !== "team-lead" -} +export type ProcessShutdownApprovedInput = z.infer