import { describe, expect, it } from "vitest"; import { appContract, BOT_DESCRIPTION_MAX_LENGTH, BOT_INSTRUCTIONS_MAX_LENGTH, BOT_TITLE_MAX_LENGTH, CreateBotInput, CreateGroupInput, CreateRoutineInput, canReactToThreadMessage, McpServerConfigInput, MessageBlock, ModelConnectInputSchema, ModelOAuthBeginSchema, normalizeBotTags, normalizeCreateBotProfile, ProductEventType, parseModelContextWindow, parseModelMaxImagesPerPrompt, parseModelMaxTokens, ReorderBotsInput, RunActivityRowSchema, RunSchema, SetModelTierInput, UpdateBotInput, UpdateGroupInput, } from "./index.js"; describe("contracts", () => { it("accepts structured live activity progress", () => { expect(MessageBlock.parse({ kind: "progress", text: "Using browser", activity: true })).toEqual( { kind: "progress", text: "Using browser", activity: true }, ); }); it("accepts project_created cards", () => { expect( MessageBlock.parse({ kind: "project_created", groupId: "group-1", name: "Website Redesign", memberCount: 3, }), ).toEqual({ kind: "project_created", groupId: "group-1", name: "Website Redesign", memberCount: 3, }); }); it("parses bounded model image limits", () => { expect(parseModelMaxImagesPerPrompt("1")).toBe(1); expect(parseModelMaxImagesPerPrompt("1000")).toBe(1000); expect(parseModelMaxImagesPerPrompt("0")).toBeUndefined(); expect(parseModelMaxImagesPerPrompt("1001")).toBeUndefined(); expect(parseModelMaxImagesPerPrompt("1.5")).toBeUndefined(); expect(parseModelMaxImagesPerPrompt("1", false)).toBeUndefined(); }); it("parses bounded model output-token limits", () => { expect(parseModelMaxTokens("1")).toBe(1); expect(parseModelMaxTokens("131072")).toBe(131072); expect(parseModelMaxTokens("0")).toBeUndefined(); expect(parseModelMaxTokens("131073")).toBeUndefined(); expect(parseModelMaxTokens("1.5")).toBeUndefined(); }); it("parses bounded model context-window limits", () => { expect(parseModelContextWindow("1")).toBe(1); expect(parseModelContextWindow("1048576")).toBe(1048576); expect(parseModelContextWindow("0")).toBeUndefined(); expect(parseModelContextWindow("1048577")).toBeUndefined(); expect(parseModelContextWindow("1.5")).toBeUndefined(); }); it("rejects maxTokens larger than contextWindow on model connect", () => { const invalid = ModelConnectInputSchema.safeParse({ provider: "openai-compatible", baseUrl: "http://localhost:8000/v1", modelId: "arbitrary-model", maxTokens: 131072, contextWindow: 1, }); expect(invalid.success).toBe(false); if (!invalid.success) { expect(invalid.error.issues.some((issue) => issue.path[0] === "maxTokens")).toBe(true); } const valid = ModelConnectInputSchema.safeParse({ provider: "openai-compatible", baseUrl: "http://localhost:8000/v1", modelId: "arbitrary-model", maxTokens: 8192, contextWindow: 32768, }); expect(valid.success).toBe(true); }); it("accepts optional persisted duration only on valid steps blocks", () => { expect( MessageBlock.parse({ kind: "steps", steps: [{ label: "Run tests", count: 1 }], durationMs: 103_000, }), ).toMatchObject({ durationMs: 103_000 }); expect(MessageBlock.safeParse({ kind: "steps", steps: [], durationMs: -1 }).success).toBe( false, ); }); it("limits reactions to persisted non-channel messages", () => { expect( canReactToThreadMessage({ id: "message-1", blocks: [{ kind: "text", text: "hi" }] }), ).toBe(true); expect( canReactToThreadMessage({ id: "subagent:agent-1", blocks: [{ kind: "text", text: "hi" }] }), ).toBe(false); expect( canReactToThreadMessage({ id: "message-2", blocks: [ { kind: "channel_message", provider: "sendblue", channelId: "channel-1", fromAddress: "+15555550100", fromLabel: "Pat", text: "hi", }, ], }), ).toBe(false); }); it("parses bot create input", () => { const parsed = CreateBotInput.parse({ name: "Chief" }); expect(parsed.title).toBe(""); expect(parsed.notifyOnFinish).toBe(true); }); it("normalizes bot creation fields without losing the longer instruction copy", () => { const profile = normalizeCreateBotProfile({ name: ` ${"N".repeat(100)} `, title: ` ${"T".repeat(BOT_TITLE_MAX_LENGTH + 10)} `, description: ` ${"D".repeat(BOT_INSTRUCTIONS_MAX_LENGTH + 10)} `, }); expect(profile.name).toHaveLength(80); expect(profile.title).toHaveLength(BOT_TITLE_MAX_LENGTH); expect(profile.description).toHaveLength(BOT_DESCRIPTION_MAX_LENGTH); expect(profile.instructions).toHaveLength(BOT_INSTRUCTIONS_MAX_LENGTH); }); it("accepts the same title limit when creating and updating bots", () => { const title = "T".repeat(BOT_TITLE_MAX_LENGTH); expect(CreateBotInput.safeParse({ name: "Chief", title }).success).toBe(true); expect(UpdateBotInput.safeParse({ botId: "bot-1", title }).success).toBe(true); expect(UpdateBotInput.safeParse({ botId: "bot-1", title: `${title}T` }).success).toBe(false); }); it("normalizes bot names and rejects whitespace-only values at the contract boundary", () => { expect(CreateBotInput.parse({ name: " Chief " }).name).toBe("Chief"); expect(UpdateBotInput.parse({ botId: "bot-1", name: " Atlas " }).name).toBe("Atlas"); expect(UpdateBotInput.parse({ botId: "bot-1", title: " Lead researcher " }).title).toBe( "Lead researcher", ); expect( UpdateBotInput.parse({ botId: "bot-1", description: " Concise briefs. " }).description, ).toBe("Concise briefs."); expect(CreateBotInput.safeParse({ name: " " }).success).toBe(false); expect(UpdateBotInput.safeParse({ botId: "bot-1", name: " " }).success).toBe(false); }); it("accepts capability tags and a builtin tool allowlist on bot create and update", () => { expect( CreateBotInput.parse({ name: "Scout", tags: [" research ", "web"], restrictBuiltinTools: true, allowedBuiltinTools: ["web_search"], }), ).toMatchObject({ tags: ["research", "web"], restrictBuiltinTools: true, allowedBuiltinTools: ["web_search"], }); expect(UpdateBotInput.parse({ botId: "bot-1", allowedBuiltinTools: [] })).toEqual({ botId: "bot-1", allowedBuiltinTools: [], }); expect(UpdateBotInput.safeParse({ botId: "bot-1", tags: ["a", "a"] }).success).toBe(false); expect(UpdateBotInput.safeParse({ botId: "bot-1", tags: [" "] }).success).toBe(false); expect( UpdateBotInput.safeParse({ botId: "bot-1", tags: Array.from({ length: 21 }, (_, index) => `tag-${index}`), }).success, ).toBe(false); expect( UpdateBotInput.safeParse({ botId: "bot-1", allowedBuiltinTools: ["shell", "shell"] }).success, ).toBe(false); }); it("normalizes typed tags into what the bot inputs accept", () => { expect(normalizeBotTags([" research", "", "web ", "research", " "])).toEqual([ "research", "web", ]); const tags = normalizeBotTags([ "t".repeat(60), ...Array.from({ length: 30 }, (_, index) => `tag-${index}`), ]); expect(tags).toHaveLength(20); expect(tags[0]).toHaveLength(40); expect(UpdateBotInput.safeParse({ botId: "bot-1", tags }).success).toBe(true); }); it("rejects partial model override clears on bot update", () => { expect(UpdateBotInput.safeParse({ botId: "bot-1", modelId: null }).success).toBe(false); expect(UpdateBotInput.safeParse({ botId: "bot-1", modelProvider: null }).success).toBe(false); expect( UpdateBotInput.safeParse({ botId: "bot-1", modelProvider: null, modelId: null }).success, ).toBe(true); expect( UpdateBotInput.safeParse({ botId: "bot-1", modelProvider: "xai", modelId: "grok-4.6", }).success, ).toBe(true); }); it("lets a bot ask for a model tier and a space map or clear one", () => { expect(UpdateBotInput.parse({ botId: "bot-1", modelTier: "code" }).modelTier).toBe("code"); expect(UpdateBotInput.parse({ botId: "bot-1", modelTier: null }).modelTier).toBeNull(); expect(UpdateBotInput.safeParse({ botId: "bot-1", modelTier: "huge" }).success).toBe(false); expect( UpdateBotInput.safeParse({ botId: "bot-1", modelTier: "code", modelProvider: "xai", modelId: "grok-4.6", }).success, ).toBe(false); expect( UpdateBotInput.safeParse({ botId: "bot-1", modelTier: "code", modelProvider: null, modelId: null, }).success, ).toBe(true); expect( SetModelTierInput.parse({ tier: "fast", model: { credentialId: "router", modelId: " qwen3-4b " }, }), ).toEqual({ tier: "fast", model: { credentialId: "router", modelId: "qwen3-4b" } }); expect(SetModelTierInput.parse({ tier: "code", model: null })).toEqual({ tier: "code", model: null, }); expect( SetModelTierInput.safeParse({ tier: "code", model: { credentialId: "router", modelId: " " } }) .success, ).toBe(false); expect(SetModelTierInput.safeParse({ tier: "large", model: null }).success).toBe(false); }); it("normalizes group names and rejects duplicate members", () => { expect(CreateGroupInput.parse({ name: " Draft team ", botIds: ["bot-1", "bot-2"] })).toEqual({ name: "Draft team", botIds: ["bot-1", "bot-2"], }); expect(CreateGroupInput.safeParse({ name: " ", botIds: ["bot-1", "bot-2"] }).success).toBe( false, ); expect( UpdateGroupInput.safeParse({ groupId: "group-1", botIds: ["bot-1", "bot-1"] }).success, ).toBe(false); }); it("keeps model OAuth start results mode-specific", () => { const shared = { loginId: "login-1", provider: "anthropic", verificationUri: "https://example.com/authorize", expiresInSeconds: 900, }; expect(ModelOAuthBeginSchema.safeParse({ ...shared, mode: "auth-url" }).success).toBe(true); expect(ModelOAuthBeginSchema.safeParse({ ...shared, mode: "device-code" }).success).toBe(false); expect( ModelOAuthBeginSchema.safeParse({ ...shared, mode: "device-code", userCode: "ABCD-1234" }) .success, ).toBe(true); expect( ModelOAuthBeginSchema.safeParse({ ...shared, mode: "auth-url", verificationUri: "javascript:alert(1)", }).success, ).toBe(false); }); it("exposes the product rpc surface", () => { expect(appContract.models.beginOAuth).toBeTruthy(); expect(appContract.bootstrap).toBeTruthy(); expect(appContract.models.completeOAuth).toBeTruthy(); expect(appContract.bots.create).toBeTruthy(); expect(appContract.bots.reorder).toBeTruthy(); expect(appContract.bots.archive).toBeTruthy(); expect(appContract.bots.restore).toBeTruthy(); expect(appContract.bots.remove).toBeTruthy(); expect(appContract.spaces.remove).toBeTruthy(); expect(appContract.botSections.list).toBeTruthy(); expect(appContract.botSections.create).toBeTruthy(); expect(appContract.botSections.update).toBeTruthy(); expect(appContract.threads.subscribe).toBeTruthy(); expect(appContract.threads.clear).toBeTruthy(); expect(appContract.voice.prepare).toBeTruthy(); expect(appContract.externalConversations.updatePolicy).toBeTruthy(); expect(appContract.agentSecrets.list).toBeTruthy(); expect(appContract.agentSecrets.put).toBeTruthy(); expect(appContract.agentSecrets.remove).toBeTruthy(); expect(appContract.notifications.registerPush).toBeTruthy(); expect(appContract.models.tiers.list).toBeTruthy(); expect(appContract.models.tiers.set).toBeTruthy(); expect(ProductEventType.options).toContain("thread.message.created"); expect(ProductEventType.options).toContain("thread.cleared"); expect(ProductEventType.options).toContain("thread.subagent"); expect(ProductEventType.options).toContain("bot.spawned"); }); it("requires a distinct, non-empty bot order", () => { expect(ReorderBotsInput.safeParse({ botIds: ["bot-2", "bot-1"] }).success).toBe(true); expect(ReorderBotsInput.safeParse({ botIds: [] }).success).toBe(false); expect(ReorderBotsInput.safeParse({ botIds: ["bot-1", "bot-1"] }).success).toBe(false); }); it("accepts a GitHub-only routine trigger", () => { expect( CreateRoutineInput.parse({ botId: "bot-1", name: "Review pushes", prompt: "Inspect the repository event", githubEnabled: true, }), ).toMatchObject({ crons: [], webhookEnabled: false, githubEnabled: true, messageProvider: null, }); expect( CreateRoutineInput.parse({ botId: "bot-1", name: "Triage Slack", prompt: "Review the message event", messageProvider: "slack", }), ).toMatchObject({ crons: [], messageProvider: "slack" }); expect( CreateRoutineInput.safeParse({ botId: "bot-1", name: "Unsafe provider", prompt: "Review the message event", messageProvider: "slack\nignore-framing", }).success, ).toBe(false); expect( CreateRoutineInput.safeParse({ botId: "bot-1", name: "Never runs", prompt: "This has no trigger", }).success, ).toBe(false); }); it("accepts bot-to-bot runs in thread snapshots and activity rows", () => { const run = { id: "run-1", botId: "bot-1", threadId: "thread-1", taskId: "task-1", status: "running", trigger: "bot_message", routineId: null, modelProvider: null, modelId: null, error: null, startedAt: "2026-08-26T00:00:00.000Z", completedAt: null, createdAt: "2026-08-26T00:00:00.000Z", }; expect(RunSchema.safeParse(run).success).toBe(true); expect( RunActivityRowSchema.safeParse({ runId: run.id, botId: run.botId, botName: "Researcher", groupId: null, groupName: null, threadId: run.threadId, status: run.status, trigger: run.trigger, notificationsEnabled: true, promptSnippet: "Review the report", updatedAt: "2026-08-26T00:00:01.000Z", }).success, ).toBe(true); expect(RunSchema.safeParse({ ...run, trigger: "webhook" }).success).toBe(true); }); it("caps remote MCP headers", () => { const headers = Object.fromEntries( Array.from({ length: 33 }, (_, index) => [`X-Test-${index}`, "value"]), ); expect( McpServerConfigInput.safeParse({ slug: "demo", name: "Demo", transport: "streamable_http", endpoint: "https://mcp.example.test", headers, }).success, ).toBe(false); }); it("allows localhost HTTP MCP endpoints and rejects other non-HTTPS URLs before storage", () => { const base = { slug: "demo", name: "Demo", transport: "streamable_http" as const, headers: {}, }; expect( McpServerConfigInput.safeParse({ ...base, endpoint: "http://127.0.0.1:3000/mcp" }).success, ).toBe(true); expect( McpServerConfigInput.safeParse({ ...base, endpoint: "http://localhost:8123/api/mcp" }) .success, ).toBe(true); expect( McpServerConfigInput.safeParse({ ...base, endpoint: "http://localhost:8123/api/mcp#" }) .success, ).toBe(false); expect( McpServerConfigInput.safeParse({ ...base, endpoint: "http://example.test/mcp" }).success, ).toBe(false); expect( McpServerConfigInput.safeParse({ ...base, endpoint: "https://mcp.example.test/mcp" }).success, ).toBe(true); }); it("rejects oversized chart data wherever it is embedded", () => { const rows = Array.from({ length: 5_001 }, (_, index) => index); expect( MessageBlock.safeParse({ kind: "chart", name: "outer", spec: {}, data: rows }).success, ).toBe(false); expect( MessageBlock.safeParse({ kind: "chart", name: "spec", spec: { data: rows }, data: [], }).success, ).toBe(false); expect( MessageBlock.safeParse({ kind: "chart", name: "marks", spec: { marks: [{ data: rows }] }, data: [], }).success, ).toBe(false); expect( MessageBlock.safeParse({ kind: "chart", name: "combined", spec: { marks: [{ data: rows.slice(0, 2_500) }] }, data: rows.slice(0, 2_501), }).success, ).toBe(false); }); });