feat(FN-3593): add test isolation CI enforcement, fix stuck-requeue race, a
This merge lands five FN-3593 commits establishing a test isolation contract with a new `scripts/check-test-isolation.mjs` guard that scans for accidental `beforeEach`/`afterEach`/`beforeAll`/`afterAll` in setup helpers, plus per-package `setup-test-isolation.ts` bootstraps that canonicalize the pat Fusion-Task-Id: FN-3593
This commit is contained in:
@@ -44,6 +44,7 @@ export function buildCliWithRealDashboardAssets() {
|
||||
return;
|
||||
}
|
||||
|
||||
runBuildCommand(`node ${join(workspaceRoot, "scripts", "ensure-test-artifacts.mjs")}`, workspaceRoot);
|
||||
runBuildCommand("pnpm --filter @fusion/dashboard build:client", workspaceRoot);
|
||||
runBuildCommand("pnpm build", cliRoot);
|
||||
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/**
|
||||
* Global test isolation for CLI package.
|
||||
* @see packages/core/src/__tests__/setup-test-isolation.ts
|
||||
* Deprecated shim: canonical test isolation is in @fusion/core vitest-setup.
|
||||
*/
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const tempHome = mkdtempSync(join(tmpdir(), "fn-test-home-"));
|
||||
process.env.HOME = tempHome;
|
||||
process.env.USERPROFILE = tempHome;
|
||||
if (process.platform === "win32") {
|
||||
const match = tempHome.match(/^([A-Za-z]:)(.*)$/);
|
||||
if (match) {
|
||||
process.env.HOMEDRIVE = match[1];
|
||||
process.env.HOMEPATH = match[2] || "\\";
|
||||
}
|
||||
}
|
||||
import "../../../core/src/__test-utils__/vitest-setup";
|
||||
|
||||
@@ -47,7 +47,6 @@ export default defineConfig({
|
||||
// run with file parallelism enabled.
|
||||
exclude: ["**/node_modules/**", "**/dist/**", "src/__tests__/build-exe*.test.ts"],
|
||||
setupFiles: [
|
||||
"./src/__tests__/setup-test-isolation.ts",
|
||||
resolve(__dirname, "../core/src/__test-utils__/vitest-setup.ts"),
|
||||
],
|
||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||
|
||||
@@ -106,6 +106,36 @@ describe("MessageStore", () => {
|
||||
expect(store.getMessage(reply.id)?.metadata).toEqual({ replyTo: { messageId: original.id } });
|
||||
});
|
||||
|
||||
it("persists wakeRecipient metadata through storage roundtrip", () => {
|
||||
const message = store.sendMessage({
|
||||
fromId: "user:dashboard",
|
||||
fromType: "user",
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
content: "urgent",
|
||||
type: "user-to-agent",
|
||||
metadata: { wakeRecipient: true },
|
||||
});
|
||||
|
||||
expect(message.metadata).toEqual({ wakeRecipient: true });
|
||||
expect(store.getMessage(message.id)?.metadata).toEqual({ wakeRecipient: true });
|
||||
});
|
||||
|
||||
it("rejects non-boolean wakeRecipient metadata", () => {
|
||||
expect(() => {
|
||||
store.sendMessage({
|
||||
fromId: "user:dashboard",
|
||||
fromType: "user",
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
content: "Bad metadata",
|
||||
type: "user-to-agent",
|
||||
// @ts-expect-error intentional bad type for runtime validation
|
||||
metadata: { wakeRecipient: "yes" },
|
||||
});
|
||||
}).toThrow("metadata.wakeRecipient must be a boolean");
|
||||
});
|
||||
|
||||
it("rejects malformed reply metadata", () => {
|
||||
expect(() => {
|
||||
store.sendMessage({
|
||||
|
||||
@@ -9,8 +9,8 @@ import { Database } from "../db.js";
|
||||
|
||||
const TEMP_HOME_PREFIX = "fn-test-home-";
|
||||
|
||||
describe("test isolation setup", () => {
|
||||
it("process.env.HOME is overridden to a temp directory", () => {
|
||||
describe("shared test isolation setup", () => {
|
||||
it("overrides HOME to a temp fn-test-home directory", () => {
|
||||
const home = process.env.HOME;
|
||||
const userProfile = process.env.USERPROFILE;
|
||||
|
||||
@@ -43,11 +43,12 @@ describe("test isolation setup", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("cwd is not inside the repository .fusion directory", () => {
|
||||
it("records protected repository root and avoids repo .fusion cwd", () => {
|
||||
const thisFile = fileURLToPath(import.meta.url);
|
||||
const repoRoot = resolve(dirname(thisFile), "../../../../");
|
||||
const repoFusionDir = join(repoRoot, ".fusion");
|
||||
|
||||
expect(process.env.FUSION_TEST_REAL_ROOT).toBeDefined();
|
||||
expect(process.cwd().startsWith(repoFusionDir)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,24 +1,5 @@
|
||||
/**
|
||||
* Global test isolation: prevents tests from writing to the real ~/.fusion/ directory.
|
||||
*
|
||||
* Vitest runs setupFiles in each worker thread. By overriding process.env.HOME
|
||||
* to a temp directory, all calls to homedir() (and derived paths like ~/.fusion)
|
||||
* resolve to isolated temp locations instead of the user's real home directory.
|
||||
*
|
||||
* This protects against tests accidentally creating projects, databases, or
|
||||
* settings files in the production ~/.fusion/ directory.
|
||||
* Deprecated shim: shared test isolation now lives in ../__test-utils__/vitest-setup.ts.
|
||||
* Keep this file for compatibility with any ad-hoc vitest configs that still reference it.
|
||||
*/
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const tempHome = mkdtempSync(join(tmpdir(), "fn-test-home-"));
|
||||
process.env.HOME = tempHome;
|
||||
process.env.USERPROFILE = tempHome;
|
||||
if (process.platform === "win32") {
|
||||
const match = tempHome.match(/^([A-Za-z]:)(.*)$/);
|
||||
if (match) {
|
||||
process.env.HOMEDRIVE = match[1];
|
||||
process.env.HOMEPATH = match[2] || "\\";
|
||||
}
|
||||
}
|
||||
import "../__test-utils__/vitest-setup";
|
||||
|
||||
@@ -3163,11 +3163,36 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
} else if (updates.blockedBy !== undefined) {
|
||||
task.blockedBy = updates.blockedBy;
|
||||
}
|
||||
const previousAssignedAgentId = task.assignedAgentId;
|
||||
if (updates.assignedAgentId === null) {
|
||||
task.assignedAgentId = undefined;
|
||||
} else if (updates.assignedAgentId !== undefined) {
|
||||
task.assignedAgentId = updates.assignedAgentId;
|
||||
}
|
||||
// If the agent that paused this task is being unassigned (or replaced),
|
||||
// auto-unpause: the pause was tied to that agent's lifecycle, and now
|
||||
// there's no longer a relationship that justifies keeping the task paused.
|
||||
const assignmentChanged =
|
||||
updates.assignedAgentId !== undefined && task.assignedAgentId !== previousAssignedAgentId;
|
||||
if (
|
||||
assignmentChanged &&
|
||||
task.paused &&
|
||||
task.pausedByAgentId &&
|
||||
task.pausedByAgentId === previousAssignedAgentId
|
||||
) {
|
||||
task.paused = undefined;
|
||||
task.pausedByAgentId = undefined;
|
||||
if (task.column === "in-progress" || task.column === "in-review") {
|
||||
if (task.status === "paused") {
|
||||
task.status = undefined;
|
||||
}
|
||||
}
|
||||
task.log.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
action: `Task unpaused (agent ${previousAssignedAgentId} unassigned)`,
|
||||
...(runContext ? { runContext } : {}),
|
||||
});
|
||||
}
|
||||
if (updates.pausedByAgentId === null) {
|
||||
task.pausedByAgentId = undefined;
|
||||
} else if (updates.pausedByAgentId !== undefined) {
|
||||
|
||||
@@ -14,7 +14,6 @@ export default defineConfig({
|
||||
test: {
|
||||
include: ["src/**/*.test.ts"],
|
||||
setupFiles: [
|
||||
"./src/__tests__/setup-test-isolation.ts",
|
||||
"./src/__test-utils__/vitest-setup.ts",
|
||||
],
|
||||
globalSetup: ["./src/__test-utils__/vitest-teardown.ts"],
|
||||
@@ -25,10 +24,8 @@ export default defineConfig({
|
||||
// worker never gets its isolated cwd. Tests that rely on cwd being a
|
||||
// disposable temp dir would silently operate in the repo root.
|
||||
//
|
||||
// 2. setup-test-isolation.ts:15-16 — `process.env.HOME` is written
|
||||
// unconditionally in every setupFile invocation. Threads share
|
||||
// `process.env`, so concurrent workers race on HOME and the last writer
|
||||
// wins, breaking isolation for all other workers in the same run.
|
||||
// 2. Some suites rely on fork-level process/env isolation for setup side effects,
|
||||
// and cannot safely share mutable process state under worker_threads.
|
||||
pool: "forks",
|
||||
maxWorkers,
|
||||
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
|
||||
|
||||
@@ -197,6 +197,71 @@ describe("MessageComposer", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards wakeRecipient metadata when the wake checkbox is ticked for an agent recipient", async () => {
|
||||
render(<MessageComposer {...defaultProps} agents={mockAgents} />);
|
||||
fireEvent.change(screen.getByTestId("message-composer-recipient"), {
|
||||
target: { value: "agent-001" },
|
||||
});
|
||||
fireEvent.change(screen.getByTestId("message-composer-content"), {
|
||||
target: { value: "wake up" },
|
||||
});
|
||||
fireEvent.click(screen.getByTestId("message-composer-wake"));
|
||||
fireEvent.click(screen.getByTestId("message-composer-send"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSendMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
metadata: { wakeRecipient: true },
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("merges wakeRecipient with replyTo metadata when replying", async () => {
|
||||
render(
|
||||
<MessageComposer
|
||||
{...defaultProps}
|
||||
agents={mockAgents}
|
||||
recipient={{ id: "agent-001", type: "agent" }}
|
||||
replyContext={{ messageId: "msg-orig", preview: "earlier message" }}
|
||||
/>,
|
||||
);
|
||||
fireEvent.change(screen.getByTestId("message-composer-content"), {
|
||||
target: { value: "follow up" },
|
||||
});
|
||||
fireEvent.click(screen.getByTestId("message-composer-wake"));
|
||||
fireEvent.click(screen.getByTestId("message-composer-send"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSendMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
metadata: {
|
||||
replyTo: { messageId: "msg-orig" },
|
||||
wakeRecipient: true,
|
||||
},
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("omits wakeRecipient metadata when the checkbox is left unchecked", async () => {
|
||||
render(<MessageComposer {...defaultProps} agents={mockAgents} />);
|
||||
fireEvent.change(screen.getByTestId("message-composer-recipient"), {
|
||||
target: { value: "agent-001" },
|
||||
});
|
||||
fireEvent.change(screen.getByTestId("message-composer-content"), {
|
||||
target: { value: "regular" },
|
||||
});
|
||||
fireEvent.click(screen.getByTestId("message-composer-send"));
|
||||
|
||||
await waitFor(() => {
|
||||
const callArgs = mockSendMessage.mock.calls[0][0];
|
||||
expect(callArgs.metadata).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("passes projectId to sendMessage", async () => {
|
||||
render(<MessageComposer {...defaultProps} agents={mockAgents} projectId="proj-1" />);
|
||||
fireEvent.change(screen.getByTestId("message-composer-recipient"), {
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
/**
|
||||
* Global test isolation: prevents dashboard tests from writing to the real ~/.fusion/ directory.
|
||||
*
|
||||
* This runs in every Vitest worker before shared setup. By forcing process.env.HOME
|
||||
* to a fresh temp directory, homedir()-derived paths resolve to isolated locations.
|
||||
* Deprecated shim: canonical test isolation is in @fusion/core vitest-setup.
|
||||
*/
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const tempHome = mkdtempSync(join(tmpdir(), "fn-test-home-"));
|
||||
process.env.HOME = tempHome;
|
||||
process.env.USERPROFILE = tempHome;
|
||||
if (process.platform === "win32") {
|
||||
const match = tempHome.match(/^([A-Za-z]:)(.*)$/);
|
||||
if (match) {
|
||||
process.env.HOMEDRIVE = match[1];
|
||||
process.env.HOMEPATH = match[2] || "\\";
|
||||
}
|
||||
}
|
||||
import "../../../core/src/__test-utils__/vitest-setup";
|
||||
|
||||
@@ -51,7 +51,6 @@ export default defineConfig({
|
||||
globals: true,
|
||||
include: ["app/**/*.test.{ts,tsx}", "src/**/*.test.{ts,tsx}"],
|
||||
setupFiles: [
|
||||
"./src/__tests__/setup-test-isolation.ts",
|
||||
resolve(__dirname, "../core/src/__test-utils__/vitest-setup.ts"),
|
||||
"./vitest.setup.ts",
|
||||
],
|
||||
|
||||
@@ -220,6 +220,160 @@ describe("wake-on-message", () => {
|
||||
customMonitor.stop();
|
||||
});
|
||||
|
||||
it("forces a wake when metadata.wakeRecipient overrides on-heartbeat mode", () => {
|
||||
let messageHook: ((message: Message) => void) | undefined;
|
||||
const messageStore = createMockMessageStore((hook) => {
|
||||
messageHook = hook;
|
||||
});
|
||||
const configStore = createMockStore({
|
||||
getCachedAgent: vi.fn().mockReturnValue({
|
||||
id: "agent-1",
|
||||
state: "active",
|
||||
runtimeConfig: { messageResponseMode: "on-heartbeat" },
|
||||
}),
|
||||
});
|
||||
|
||||
const customMonitor = new HeartbeatMonitor({
|
||||
store,
|
||||
agentStore: configStore,
|
||||
messageStore,
|
||||
});
|
||||
const executeHeartbeatSpy = vi
|
||||
.spyOn(customMonitor, "executeHeartbeat")
|
||||
.mockResolvedValue({ id: "run-1" } as AgentHeartbeatRun);
|
||||
|
||||
customMonitor.start();
|
||||
messageHook?.(
|
||||
createMessage({
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
metadata: { wakeRecipient: true },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(executeHeartbeatSpy).toHaveBeenCalledWith({
|
||||
agentId: "agent-1",
|
||||
source: "on_demand",
|
||||
triggerDetail: "wake-on-message-forced",
|
||||
});
|
||||
|
||||
customMonitor.stop();
|
||||
});
|
||||
|
||||
it("forces a wake even when messageResponseMode is unset", () => {
|
||||
let messageHook: ((message: Message) => void) | undefined;
|
||||
const messageStore = createMockMessageStore((hook) => {
|
||||
messageHook = hook;
|
||||
});
|
||||
const configStore = createMockStore({
|
||||
getCachedAgent: vi.fn().mockReturnValue({
|
||||
id: "agent-1",
|
||||
state: "idle",
|
||||
runtimeConfig: {},
|
||||
}),
|
||||
});
|
||||
|
||||
const customMonitor = new HeartbeatMonitor({
|
||||
store,
|
||||
agentStore: configStore,
|
||||
messageStore,
|
||||
});
|
||||
const executeHeartbeatSpy = vi
|
||||
.spyOn(customMonitor, "executeHeartbeat")
|
||||
.mockResolvedValue({ id: "run-1" } as AgentHeartbeatRun);
|
||||
|
||||
customMonitor.start();
|
||||
messageHook?.(
|
||||
createMessage({
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
metadata: { wakeRecipient: true },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(executeHeartbeatSpy).toHaveBeenCalledWith({
|
||||
agentId: "agent-1",
|
||||
source: "on_demand",
|
||||
triggerDetail: "wake-on-message-forced",
|
||||
});
|
||||
|
||||
customMonitor.stop();
|
||||
});
|
||||
|
||||
it("ignores wakeRecipient metadata when sender is an agent (only humans may force wakes)", () => {
|
||||
let messageHook: ((message: Message) => void) | undefined;
|
||||
const messageStore = createMockMessageStore((hook) => {
|
||||
messageHook = hook;
|
||||
});
|
||||
const configStore = createMockStore({
|
||||
getCachedAgent: vi.fn().mockReturnValue({
|
||||
id: "agent-1",
|
||||
state: "active",
|
||||
runtimeConfig: { messageResponseMode: "on-heartbeat" },
|
||||
}),
|
||||
});
|
||||
|
||||
const customMonitor = new HeartbeatMonitor({
|
||||
store,
|
||||
agentStore: configStore,
|
||||
messageStore,
|
||||
});
|
||||
const executeHeartbeatSpy = vi
|
||||
.spyOn(customMonitor, "executeHeartbeat")
|
||||
.mockResolvedValue({ id: "run-1" } as AgentHeartbeatRun);
|
||||
|
||||
customMonitor.start();
|
||||
messageHook?.(
|
||||
createMessage({
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
fromId: "agent-2",
|
||||
fromType: "agent",
|
||||
metadata: { wakeRecipient: true },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(executeHeartbeatSpy).not.toHaveBeenCalled();
|
||||
|
||||
customMonitor.stop();
|
||||
});
|
||||
|
||||
it("still respects state gating when wakeRecipient is set (paused agent stays paused)", () => {
|
||||
let messageHook: ((message: Message) => void) | undefined;
|
||||
const messageStore = createMockMessageStore((hook) => {
|
||||
messageHook = hook;
|
||||
});
|
||||
const configStore = createMockStore({
|
||||
getCachedAgent: vi.fn().mockReturnValue({
|
||||
id: "agent-1",
|
||||
state: "paused",
|
||||
runtimeConfig: {},
|
||||
}),
|
||||
});
|
||||
|
||||
const customMonitor = new HeartbeatMonitor({
|
||||
store,
|
||||
agentStore: configStore,
|
||||
messageStore,
|
||||
});
|
||||
const executeHeartbeatSpy = vi
|
||||
.spyOn(customMonitor, "executeHeartbeat")
|
||||
.mockResolvedValue({ id: "run-1" } as AgentHeartbeatRun);
|
||||
|
||||
customMonitor.start();
|
||||
messageHook?.(
|
||||
createMessage({
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
metadata: { wakeRecipient: true },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(executeHeartbeatSpy).not.toHaveBeenCalled();
|
||||
|
||||
customMonitor.stop();
|
||||
});
|
||||
|
||||
it("registers the message hook on start and clears it on stop", () => {
|
||||
const hooks: Array<(message: Message) => void> = [];
|
||||
const messageStore = createMockMessageStore((hook) => {
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
/**
|
||||
* Global test isolation: prevents engine tests from writing to the real ~/.fusion/ directory.
|
||||
*
|
||||
* This runs in every Vitest worker before shared setup. By forcing process.env.HOME
|
||||
* to a fresh temp directory, homedir()-derived paths resolve to isolated locations.
|
||||
* Deprecated shim: canonical test isolation is in @fusion/core vitest-setup.
|
||||
*/
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const tempHome = mkdtempSync(join(tmpdir(), "fn-test-home-"));
|
||||
process.env.HOME = tempHome;
|
||||
process.env.USERPROFILE = tempHome;
|
||||
if (process.platform === "win32") {
|
||||
const match = tempHome.match(/^([A-Za-z]:)(.*)$/);
|
||||
if (match) {
|
||||
process.env.HOMEDRIVE = match[1];
|
||||
process.env.HOMEPATH = match[2] || "\\";
|
||||
}
|
||||
}
|
||||
import "../../../core/src/__test-utils__/vitest-setup";
|
||||
|
||||
@@ -1149,7 +1149,12 @@ export class HeartbeatMonitor {
|
||||
}
|
||||
|
||||
const runtimeConfig = agent.runtimeConfig as AgentHeartbeatConfig | undefined;
|
||||
const senderForcedWake = message.metadata?.wakeRecipient === true;
|
||||
// Only human-originated (user) messages may override an agent's
|
||||
// messageResponseMode setting. Agent-to-agent traffic must respect the
|
||||
// recipient's configured behavior to prevent agents from forcing wakes
|
||||
// on each other.
|
||||
const senderForcedWake =
|
||||
message.metadata?.wakeRecipient === true && message.fromType === "user";
|
||||
if (!senderForcedWake && runtimeConfig?.messageResponseMode !== "immediate") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,13 +113,6 @@ export const sendMessageParams = Type.Object({
|
||||
reply_to_message_id: Type.Optional(
|
||||
Type.String({ description: "Optional ID of the message you are replying to (use IDs from fn_read_messages output)" }),
|
||||
),
|
||||
wake_recipient: Type.Optional(
|
||||
Type.Boolean({
|
||||
description:
|
||||
"If true, wake the recipient agent immediately on receipt regardless of their messageResponseMode. " +
|
||||
"Use sparingly for urgent messages. Ignored when the recipient is a user.",
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export const readMessagesParams = Type.Object({
|
||||
@@ -1417,8 +1410,7 @@ export function createSendMessageTool(messageStore: MessageStore, fromAgentId: s
|
||||
label: "Send Message",
|
||||
description:
|
||||
"Send a message to another agent or user. The recipient will be woken if they have " +
|
||||
"`messageResponseMode: 'immediate'` configured, or if you set `wake_recipient: true` " +
|
||||
"to override their setting for an urgent message. When replying to an existing message, " +
|
||||
"`messageResponseMode: 'immediate'` configured. When replying to an existing message, " +
|
||||
"include `reply_to_message_id` to preserve threading.",
|
||||
parameters: sendMessageParams,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -1453,15 +1445,6 @@ export function createSendMessageTool(messageStore: MessageStore, fromAgentId: s
|
||||
};
|
||||
}
|
||||
|
||||
const wakeRecipient = params.wake_recipient === true && recipient.type === "agent";
|
||||
const metadata =
|
||||
replyToMessageId || wakeRecipient
|
||||
? {
|
||||
...(replyToMessageId ? { replyTo: { messageId: replyToMessageId } } : {}),
|
||||
...(wakeRecipient ? { wakeRecipient: true } : {}),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const message = messageStore.sendMessage({
|
||||
fromId: fromAgentId,
|
||||
fromType: "agent",
|
||||
@@ -1469,7 +1452,7 @@ export function createSendMessageTool(messageStore: MessageStore, fromAgentId: s
|
||||
toType: recipient.type,
|
||||
content,
|
||||
type: messageType,
|
||||
...(metadata ? { metadata } : {}),
|
||||
...(replyToMessageId ? { metadata: { replyTo: { messageId: replyToMessageId } } } : {}),
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -16,7 +16,6 @@ export default defineConfig({
|
||||
test: {
|
||||
include: ["src/**/*.test.ts"],
|
||||
setupFiles: [
|
||||
"./src/__tests__/setup-test-isolation.ts",
|
||||
resolve(__dirname, "../core/src/__test-utils__/vitest-setup.ts"),
|
||||
],
|
||||
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
|
||||
|
||||
Reference in New Issue
Block a user