import { randomUUID } from "node:crypto";
import { EventType } from "@ag-ui/core";
import { extractDatasourceId, extractEffectiveRunConfig, extractLastUserText } from "../apps/api/dist/run-input.js";
import { createDataFoundry, createDataFoundryRunContext, DATA_AGENT_TOOL_NAMES } from "../packages/agent-runtime/dist/index.js";
import { TaskPlanProjector } from "../apps/api/dist/task-plan-projector.js";
const userId = randomUUID();
const baseInput = {
threadId: "thread-smoke",
runId: "run-smoke",
messages: [],
context: [],
forwardedProps: {},
state: {}
};
assert(
extractDatasourceId({
...baseInput,
forwardedProps: { datasourceId: "from-forwarded-camel" },
state: { datasourceId: "from-state" },
context: [{ description: "datasource_id", value: "from-context" }]
}) === "from-forwarded-camel",
"forwardedProps.datasourceId should win"
);
assert(
extractDatasourceId({
...baseInput,
forwardedProps: { datasource_id: "from-forwarded-snake" }
}) === "from-forwarded-snake",
"forwardedProps.datasource_id should be supported"
);
assert(
extractDatasourceId({
...baseInput,
state: { datasource_id: "from-state-snake" },
context: [{ description: "datasource_id", value: "from-context" }]
}) === "from-state-snake",
"state.datasource_id should win over context"
);
assert(
extractDatasourceId({
...baseInput,
context: [{ description: "datasource_id", value: "from-context" }]
}) === "from-context",
"context datasource_id should be supported"
);
assert(
extractLastUserText({
...baseInput,
messages: [
{ role: "user", content: "older" },
{ role: "assistant", content: "answer" },
{ role: "user", content: "latest" }
]
}) === "latest",
"latest user string message should be selected"
);
assert(
extractLastUserText({
...baseInput,
messages: [
{
role: "user",
content: [
{ type: "text", text: "分析" },
{ type: "text", text: "orders 表" }
]
}
]
}) === "分析\norders 表",
"multi-part user text should be joined"
);
const effectiveConfig = extractEffectiveRunConfig({
...baseInput,
forwardedProps: {
run_config: {
activeDatasourceId: "orders-db",
enabledDatasourceIds: ["orders-db", "warehouse"],
activeSkillId: "data-analysis",
goal: { objective: "Produce a verified orders analysis", maxRuns: 4 }
}
}
}, "default-db");
assert(effectiveConfig.activeDatasourceId === "orders-db", "run_config active datasource should be resolved");
assert(effectiveConfig.enabledDatasourceIds.length === 2, "run_config enabled datasources should be preserved");
assert(effectiveConfig.goal?.maxRuns === 4, "trusted goal config should be parsed and bounded");
const mentionedConfig = extractEffectiveRunConfig({
...baseInput,
forwardedProps: {
run_config: {
activeDatasourceId: "orders-db",
enabledDatasourceIds: ["orders-db", "warehouse"],
enabledKnowledgeIds: ["kb-orders", "kb-faq"],
enabledMcpServerIds: ["mcp-local"],
enabledSkillIds: ["data-analysis"],
mentioned: {
db: ["orders-db", "ghost-db"],
kb: ["kb-faq", "kb-ghost"],
mcp: ["mcp-ghost"],
skill: ["data-analysis"]
}
}
}
}, "default-db");
assert(mentionedConfig.mentioned, "mentioned should be parsed into EffectiveRunConfig");
assert(JSON.stringify(mentionedConfig.mentioned.db) === JSON.stringify(["orders-db"]), "mentioned.db should clamp to enabled datasources");
assert(JSON.stringify(mentionedConfig.mentioned.kb) === JSON.stringify(["kb-faq"]), "mentioned.kb should clamp to enabled knowledge bases");
assert(JSON.stringify(mentionedConfig.mentioned.mcp) === JSON.stringify([]), "mentioned.mcp should clamp to enabled MCP servers (all out-of-scope dropped)");
assert(JSON.stringify(mentionedConfig.mentioned.skill) === JSON.stringify(["data-analysis"]), "mentioned.skill should clamp to enabled skills");
const excluded = mentionedConfig.mentioned.excluded ?? [];
assert(excluded.length === 3, "three out-of-scope mentioned IDs should be collected as excluded (ghost-db, kb-ghost, mcp-ghost)");
const noMentionConfig = extractEffectiveRunConfig({
...baseInput,
forwardedProps: { run_config: { activeDatasourceId: "orders-db", enabledDatasourceIds: ["orders-db"] } }
}, "default-db");
assert(noMentionConfig.mentioned === undefined, "omitted mentioned should yield undefined (backward compat)");
const pinnedConfig = extractEffectiveRunConfig({
...baseInput,
forwardedProps: {
run_config: {
activeDatasourceId: "orders-db",
enabledDatasourceIds: ["orders-db"],
pinnedPaths: ["output/report.html", "/etc/passwd", "../escape", "ok/file.csv", "a\0b"]
}
}
}, "default-db");
assert(JSON.stringify(pinnedConfig.pinnedPaths) === JSON.stringify(["output/report.html", "ok/file.csv"]), "pinnedPaths should drop absolute/traversal/NUL entries");
const noDatasourceConfig = extractEffectiveRunConfig({
...baseInput,
forwardedProps: {
run_config: {
enabledDatasourceIds: [],
enabledKnowledgeIds: []
}
}
}, "default-db");
assert(noDatasourceConfig.activeDatasourceId === undefined, "empty enabled datasources should omit activeDatasourceId");
assert(noDatasourceConfig.enabledDatasourceIds.length === 0, "empty enabled datasources should stay empty");
const legacyDatasourceButEmptyEnabled = extractEffectiveRunConfig({
...baseInput,
forwardedProps: {
datasourceId: "default-db",
run_config: {
enabledDatasourceIds: []
}
}
}, "default-db");
assert(
legacyDatasourceButEmptyEnabled.activeDatasourceId === undefined,
"legacy datasourceId should not resurrect an active datasource when enabled list is empty"
);
assert(
legacyDatasourceButEmptyEnabled.enabledDatasourceIds.length === 0,
"explicit empty enabledDatasourceIds should win over legacy datasourceId"
);
assert(
createDataFoundryRunContext({
user_id: userId,
session_id: "thread-smoke",
run_id: "run-smoke",
user_input: "你好",
chat_mode: "copilotkit"
}).enabled_datasource_ids === undefined,
"no enabled datasources should omit enabled_datasource_ids from run context"
);
const noDatasourceAgent = await createDataFoundry({
dataGateway: { listDataSources: async () => ({ datasources: [] }) },
emitter: { emit: () => undefined },
messages: [],
modelProvider: { kind: "mastra-router", model: "openai/smoke", model_name: "smoke-model" },
runContext: createDataFoundryRunContext({
user_id: userId,
session_id: "thread-smoke",
run_id: "run-smoke",
user_input: "你好",
chat_mode: "copilotkit"
})
});
const noDatasourceToolNames = Object.keys(await noDatasourceAgent.agent.listTools());
assert(
DATA_AGENT_TOOL_NAMES.every((name) => !noDatasourceToolNames.includes(name)),
"data tools should be omitted when no datasource is enabled"
);
const projector = new TaskPlanProjector({
user_id: userId,
session_id: "thread-smoke",
run_id: "run-smoke",
user_input: "analyze orders",
chat_mode: "copilotkit",
selected_datasource_id: "orders-db",
enabled_datasource_ids: ["orders-db"],
model_name: "smoke-model"
});
assert(projector.observe({
type: EventType.TOOL_CALL_START,
toolCallId: "task-call-1",
toolCallName: "task_write"
}).length === 0, "task tool start should not emit a plan");
const projected = projector.observe({
type: EventType.TOOL_CALL_RESULT,
messageId: "tool-message-1",
toolCallId: "task-call-1",
content: JSON.stringify({
tasks: [{ id: "inspect", content: "Inspect schema", activeForm: "Inspecting schema", status: "in_progress" }]
})
});
assert(projected.length === 1, "task result should emit one PLAN snapshot");
assert(projected[0].activityType === "PLAN", "projected activity should use PLAN type");
assert(projected[0].content.tasks[0].status === "running", "in_progress should map to AG-UI running");
console.log("CopilotKit context smoke OK: run config, user text, and task PLAN projection are stable");
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}