evOo-local/packages/testkit/src/mention-targets.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

352 lines
12 KiB
TypeScript

import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { ComposioEmulator } from "@rakazo/adapters";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import type { createApp } from "../../../apps/api/src/app.ts";
import { sessionCookieHeader } from "./index.js";
type App = { request: (input: string, init?: RequestInit) => Promise<Response> };
type AppHandles = Awaited<ReturnType<typeof createApp>>;
process.env.WAKEUP_DRIVER = "memory";
process.env.SANDBOX_PROVIDER = "fake";
process.env.AGENT_RUNTIME = "scripted";
const hasDb = process.env.VERIFY_DATABASE === "1" && Boolean(process.env.DATABASE_URL);
const describeWithDatabase = hasDb ? describe : describe.skip;
describeWithDatabase("structured @ mention targets", () => {
let handles: AppHandles;
let app: App;
let prisma: AppHandles["prisma"];
const stamp = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
const dataDir = mkdtempSync(path.join(tmpdir(), "rakazo-mention-targets-"));
beforeAll(async () => {
const { createApp } = await import("../../../apps/api/src/app.ts");
handles = await createApp({
databaseUrl: process.env.DATABASE_URL!,
dataDir,
sandboxProvider: "fake",
agentRuntime: "scripted",
composio: new ComposioEmulator(),
signupsEnabled: "true",
});
app = handles.app;
prisma = handles.prisma;
});
afterAll(async () => {
await handles?.stop();
rmSync(dataDir, { recursive: true, force: true });
});
it("starts a routine test run on the owning bot", async () => {
const cookie = await signup(app, `mention-routine-${stamp}@rakazo.test`, "Routine Owner");
const bot = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "Chief",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const routine = await rpc<{ id: string }>(app, cookie, "routines/create", {
botId: bot.id,
name: "Daily digest",
prompt: "Summarize inbox",
crons: ["0 9 * * 1"],
timezone: "UTC",
notify: false,
active: false,
});
const tested = await rpc<{ runId: string }>(app, cookie, "routines/testRun", {
routineId: routine.id,
});
const run = await prisma.run.findUniqueOrThrow({ where: { id: tested.runId } });
expect(run.botId).toBe(bot.id);
expect(run.trigger).toBe("routine");
});
it("replays routine testRun with the same clientNonce", async () => {
const cookie = await signup(app, `mention-routine-replay-${stamp}@rakazo.test`, "Replay Owner");
const bot = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "ReplayBot",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const routine = await rpc<{ id: string }>(app, cookie, "routines/create", {
botId: bot.id,
name: "Replay digest",
prompt: "Replay inbox",
crons: ["0 9 * * 1"],
timezone: "UTC",
notify: false,
active: false,
});
const nonce = `routine-mention:${stamp}:replay`;
const first = await rpc<{ runId: string }>(app, cookie, "routines/testRun", {
routineId: routine.id,
clientNonce: nonce,
});
const second = await rpc<{ runId: string }>(app, cookie, "routines/testRun", {
routineId: routine.id,
clientNonce: nonce,
});
expect(second.runId).toBe(first.runId);
expect(
await prisma.run.count({
where: { clientNonce: `routine-test:${nonce}` },
}),
).toBe(1);
});
it("includes connector intent on a 1:1 send prompt", async () => {
const cookie = await signup(app, `mention-connector-${stamp}@rakazo.test`, "Connector Owner");
const bot = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "Chief",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const started = await rpc<{ connectionId: string }>(app, cookie, "connections/begin", {
provider: "gmail",
displayName: "Gmail",
});
await rpc(app, cookie, "connections/complete", { connectionId: started.connectionId });
const sent = await rpc<{ runId: string }>(app, cookie, "threads/send", {
botId: bot.id,
text: "check my email",
mentions: [{ kind: "connector", id: started.connectionId }],
});
const run = await prisma.run.findUniqueOrThrow({
where: { id: sent.runId },
include: { task: true },
});
expect(run.task.prompt).toContain("Use these connectors if relevant: Gmail");
});
it("lands a group-targeted send in the group transcript, not the 1:1 bot thread", async () => {
const cookie = await signup(app, `mention-group-${stamp}@rakazo.test`, "Group Owner");
const botA = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "BotA",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const botB = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "BotB",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const group = await rpc<{ id: string; threadId: string }>(app, cookie, "groups/create", {
name: "Planning",
botIds: [botA.id, botB.id],
});
const messageText = "follow up from 1:1 reroute";
await rpc(app, cookie, "threads/send", {
groupId: group.id,
text: messageText,
});
const groupSnap = await rpc<{
messages: Array<{ blocks: Array<{ kind: string; text?: string }> }>;
}>(app, cookie, "threads/get", { groupId: group.id });
const botSnap = await rpc<{
messages: Array<{ blocks: Array<{ kind: string; text?: string }> }>;
}>(app, cookie, "threads/get", { botId: botA.id });
const groupHasMessage = groupSnap.messages.some((message) =>
message.blocks.some((block) => block.kind === "text" && block.text?.includes(messageText)),
);
const botHasMessage = botSnap.messages.some((message) =>
message.blocks.some((block) => block.kind === "text" && block.text?.includes(messageText)),
);
expect(groupHasMessage).toBe(true);
expect(botHasMessage).toBe(false);
});
it("wakes exactly one bot on an unmentioned group send", async () => {
const cookie = await signup(app, `mention-group-default-${stamp}@rakazo.test`, "Group Default");
const botA = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "BotA",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const botB = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "BotB",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const group = await rpc<{ id: string; threadId: string }>(app, cookie, "groups/create", {
name: "Squad",
botIds: [botA.id, botB.id],
});
const before = await prisma.run.count({
where: { threadId: group.threadId, trigger: "user" },
});
await rpc(app, cookie, "threads/send", { groupId: group.id, text: "hello team" });
const after = await prisma.run.count({
where: { threadId: group.threadId, trigger: "user" },
});
expect(after - before).toBe(1);
});
it("wakes a mentioned group member from typed bot chips and ignores non-members", async () => {
const cookie = await signup(app, `mention-out-${stamp}@rakazo.test`, "Out Of Chat");
const botA = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "MemberA",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const botB = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "MemberB",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const outsider = await rpc<{ id: string }>(app, cookie, "bots/create", {
name: "Outsider",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const group = await rpc<{ id: string; threadId: string }>(app, cookie, "groups/create", {
name: "Core",
botIds: [botA.id, botB.id],
});
const sent = await rpc<{ runId: string; runIds?: string[] }>(app, cookie, "threads/send", {
groupId: group.id,
text: "@MemberB @Outsider please review",
mentions: [
{ kind: "bot", id: botB.id },
{ kind: "bot", id: outsider.id },
],
});
const runIds = sent.runIds ?? [sent.runId];
const runs = await prisma.run.findMany({
where: { id: { in: runIds } },
select: { botId: true },
});
expect(runs.map((run) => run.botId).sort()).toEqual([botB.id].sort());
});
it("rejects another user's routine, connection, and group mentions", async () => {
const ada = await signup(app, `mention-auth-ada-${stamp}@rakazo.test`, "Ada Auth");
const bob = await signup(app, `mention-auth-bob-${stamp}@rakazo.test`, "Bob Auth");
const adaBot = await rpc<{ id: string }>(app, ada, "bots/create", {
name: "AdaBot",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const bobBot = await rpc<{ id: string }>(app, bob, "bots/create", {
name: "BobBot",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const adaBotB = await rpc<{ id: string }>(app, ada, "bots/create", {
name: "AdaBotB",
title: "",
description: "",
instructions: "",
notifyOnFinish: true,
});
const routine = await rpc<{ id: string }>(app, ada, "routines/create", {
botId: adaBot.id,
name: "Private routine",
prompt: "do work",
crons: ["0 9 * * 1"],
timezone: "UTC",
notify: false,
active: false,
});
const started = await rpc<{ connectionId: string }>(app, ada, "connections/begin", {
provider: "gmail",
displayName: "Gmail",
});
await rpc(app, ada, "connections/complete", { connectionId: started.connectionId });
const group = await rpc<{ id: string }>(app, ada, "groups/create", {
name: "Ada group",
botIds: [adaBot.id, adaBotB.id],
});
const routineAttempt = await raw(app, bob, "routines/testRun", { routineId: routine.id });
expect(routineAttempt.status).toBeGreaterThanOrEqual(400);
const connectionAttempt = await raw(app, bob, "threads/send", {
botId: bobBot.id,
text: "use connector",
mentions: [{ kind: "connector", id: started.connectionId }],
});
expect(connectionAttempt.status).toBeGreaterThanOrEqual(400);
const groupAttempt = await raw(app, bob, "threads/send", {
groupId: group.id,
text: "intrude",
});
expect(groupAttempt.status).toBeGreaterThanOrEqual(400);
const routineRuns = await prisma.run.count({ where: { botId: adaBot.id, trigger: "routine" } });
expect(routineRuns).toBe(0);
});
});
async function signup(app: App, email: string, name: string) {
const res = await app.request("/api/auth/sign-up/email", {
method: "POST",
headers: {
"content-type": "application/json",
origin: "http://127.0.0.1:5173",
},
body: JSON.stringify({ email, password: "password12", name }),
});
if (res.status >= 400) {
throw new Error(`signup failed ${res.status}: ${await res.text()}`);
}
return sessionCookieHeader(res);
}
async function raw(app: App, cookie: string, proc: string, body: unknown = {}) {
return app.request(`/rpc/${proc}`, {
method: "POST",
headers: {
"content-type": "application/json",
cookie,
origin: "http://127.0.0.1:5173",
},
body: JSON.stringify({ json: body }),
});
}
async function rpc<T>(app: App, cookie: string, proc: string, body: unknown = {}): Promise<T> {
const res = await raw(app, cookie, proc, body);
const text = await res.text();
let parsed: { json?: T; error?: { message?: string } };
try {
parsed = JSON.parse(text) as { json?: T; error?: { message?: string } };
} catch {
throw new Error(`${proc} ${res.status}: ${text}`);
}
if (res.status >= 400 || parsed.error) {
throw new Error(`${proc} ${res.status}: ${parsed.error?.message ?? text}`);
}
return parsed.json as T;
}