import { askUserTool, submitPlanTool } from "@mastra/core/harness";
import {
GovernedToolFactory,
ToolObservationDispatcher,
createCustomEvent,
createToolObservationBoundary,
createDataFoundryRunContext
} from "../packages/agent-runtime/dist/testing.js";
import {
extractInteractionResume,
InteractionRuntimeAdapter
} from "../apps/api/dist/interaction-runtime-adapter.js";
import { createMetadataStore } from "../packages/metadata/dist/index.js";
import { rmSync } from "node:fs";
import { createVerifiedTestIdentity } from "./lib/metadata-test-identity.mjs";
const storageDir = `storage/collaboration-smoke/${Date.now()}`;
const store = createMetadataStore({ database_path: `${storageDir}/metadata.sqlite` });
const __testIdentity = createVerifiedTestIdentity(store);
const userId = __testIdentity.userId;
const workspaceId = __testIdentity.workspaceId;
const sessionId = "collaboration-session";
const runId = "collaboration-run";
try {
store.sessions.create({ user_id: userId, id: sessionId, title: "collaboration smoke" });
store.runs.create({
user_id: userId,
id: runId,
session_id: sessionId,
request_fingerprint: "initial-request",
user_input: "ask me",
status: "running"
});
const runContext = createDataFoundryRunContext({
user_id: userId,
session_id: sessionId,
run_id: runId,
user_input: "ask me",
chat_mode: "copilotkit",
selected_datasource_id: "unused",
enabled_datasource_ids: ["unused"]
});
const runScope = {
modelName: runContext.model_name,
resourceId: runContext.user_id,
runId: runContext.run_id,
sessionId: runContext.session_id
};
const { packager } = createToolObservationBoundary({
identity: {
resourceId: runContext.user_id,
runId: runContext.run_id,
sessionId: runContext.session_id
}
});
const factory = new GovernedToolFactory(new ToolObservationDispatcher(packager, runScope));
const askUser = factory.governTool("ask_user", askUserTool);
const submitPlan = factory.governTool("submit_plan", submitPlanTool);
let askSuspendPayload;
const suspended = await askUser.execute(
{ question: "Which datasource?", options: [{ label: "orders" }] },
{ agent: { suspend: async (payload) => { askSuspendPayload = payload; } } }
);
assert(suspended === undefined, "ask_user should preserve Mastra suspension");
assert(askSuspendPayload.question === "Which datasource?", "ask_user suspend payload should be preserved");
const interrupt = {
type: "mastra_suspend",
toolCallId: "call-ask-1",
toolName: "ask_user",
suspendPayload: askSuspendPayload,
args: { question: "Which datasource?" },
resumeSchema: { type: "string" },
runId
};
const runtime = new InteractionRuntimeAdapter(store, userId, sessionId, runId);
const requested = runtime.capture(createCustomEvent("on_interrupt", JSON.stringify(interrupt)));
assert(requested?.event.name === "interaction.requested", "interrupt should project to interaction.requested");
const stored = store.interactions.getByToolCall({
user_id: userId,
run_id: runId,
tool_call_id: interrupt.toolCallId
});
assert(
stored.interrupt_event_json?.includes("mastra_suspend"),
"interrupt_event_json should be persisted for restored resume"
);
const pending = store.interactions.listPendingBySession({
user_id: userId,
session_id: sessionId
});
assert(pending.length === 1, "pending interaction should be listed by session");
const resume = extractInteractionResume({
threadId: sessionId,
runId,
messages: [],
tools: [],
context: [],
state: {},
forwardedProps: { command: { resume: "orders", interruptEvent: JSON.stringify(interrupt) } }
});
assert(Boolean(resume), "resume command should parse");
const answer = await askUser.execute(
{ question: "Which datasource?", options: [{ label: "orders" }] },
{ agent: { resumeData: "orders" } }
);
assert(answer.content.includes("orders"), "governed ask_user result should reach the model");
const resolved = runtime.resolve(resume);
assert(resolved.name === "interaction.resolved", "resume should project to interaction.resolved");
runtime.resolve(resume);
const changedResume = extractInteractionResume({
threadId: sessionId,
runId,
messages: [],
tools: [],
context: [],
state: {},
forwardedProps: { command: { resume: "customers", interruptEvent: JSON.stringify(interrupt) } }
});
await assertRejects(() => runtime.resolve(changedResume), "INTERACTION_RESUME_MISMATCH");
let planSuspendPayload;
await submitPlan.execute(
{ title: "Plan", plan: "1. Inspect\n2. Query" },
{ agent: { suspend: async (payload) => { planSuspendPayload = payload; } } }
);
const planInterrupt = {
type: "mastra_suspend",
toolCallId: "call-plan-1",
toolName: "submit_plan",
suspendPayload: planSuspendPayload,
args: { title: "Plan" },
resumeSchema: { type: "object" },
runId
};
runtime.capture(createCustomEvent("on_interrupt", JSON.stringify(planInterrupt)));
const cancellation = extractInteractionResume({
threadId: sessionId,
runId,
messages: [],
tools: [],
context: [],
state: {},
forwardedProps: { command: { resume: false, interruptEvent: JSON.stringify(planInterrupt) } }
});
assert(runtime.cancel(cancellation).value.status === "canceled", "resume=false should cancel the interaction");
const planResult = await submitPlan.execute(
{ title: "Plan", plan: "1. Inspect\n2. Query" },
{ agent: { resumeData: { action: "approved" } } }
);
assert(planResult.content.includes("approved"), "submit_plan approval should reach the model");
console.log("Collaboration tools smoke OK: suspend, resume idempotency, cancel, adapters, and plan approval");
} finally {
store.close();
rmSync(storageDir, { force: true, recursive: true });
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
async function assertRejects(thunk, expectedMessage) {
try {
await thunk();
} catch (error) {
if (error instanceof Error && error.message.includes(expectedMessage)) {
return;
}
throw error;
}
throw new Error(`Expected rejection with ${expectedMessage}`);
}