fix: confine Agent and MCP data access

This commit is contained in:
2026-07-10 16:38:08 +08:00
parent 73fa28a178
commit f4968d6657
25 changed files with 1521 additions and 221 deletions
+17 -11
View File
@@ -1,19 +1,29 @@
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { dirname, join } from "node:path";
import { Readable } from "node:stream";
import { describe, it, expect, beforeEach, afterEach, afterAll, vi } from "vitest";
import { DEFAULT_ORG_ID, prisma, resetDb, mockFeishuRuntime, seedProject, silentLogger } from "./helpers.js";
import { InMemoryModelRegistry } from "../../src/agent/models.js";
import { makeTriggerHandler, extractPrompt } from "../../src/feishu/trigger.js";
import { makeTriggerHandler as makeProductionTriggerHandler, extractPrompt } from "../../src/feishu/trigger.js";
import { TriggerQueue } from "../../src/feishu/triggerQueue.js";
import type { MessageReceiveEvent, CardActionEvent } from "../../src/feishu/client.js";
import type { RunRequest, RunResult } from "../../src/agent/runner.js";
import type { RuntimeSettings } from "../../src/settings/runtime.js";
const bot = { key: "@_user_1", id: { open_id: "ou_bot" }, name: "Bot" };
const itOnLinux = process.platform === "linux" ? it : it.skip;
type TestRunner = (req: RunRequest) => Promise<RunResult>;
const workspaceRoots: string[] = [];
type TestTriggerDeps = Omit<Parameters<typeof makeProductionTriggerHandler>[0], "projectWorkspaceRoot"> & {
readonly projectWorkspaceRoot?: string;
};
function makeTriggerHandler(deps: TestTriggerDeps): ReturnType<typeof makeProductionTriggerHandler> {
return makeProductionTriggerHandler({ projectWorkspaceRoot: "/tmp", ...deps });
}
function makeTestSettings(models: InMemoryModelRegistry): RuntimeSettings {
return {
async provider(providerId) {
@@ -153,19 +163,15 @@ describe("trigger full lifecycle (integration)", () => {
expect(run.costSource).toBe("provider_reported");
});
it("includes downloaded post image paths in the agent prompt", async () => {
itOnLinux("includes downloaded post image paths in the agent prompt", async () => {
const workspaceDir = await tempWorkspaceRoot();
await seedProject("proj-post-image", "chat-post-image");
await prisma.project.update({
where: { id: "proj-post-image" },
data: { workspaceDir },
});
let downloadedPath: string | undefined;
const messageResourceGet = vi.fn(async () => ({
writeFile: async (filePath: string) => {
downloadedPath = filePath;
await writeFile(filePath, "image bytes");
},
getReadableStream: () => Readable.from([Buffer.from("image bytes")]),
}));
const imV1 = (rt.client as unknown as {
im: { v1: { messageResource?: { get: typeof messageResourceGet } } };
@@ -192,6 +198,7 @@ describe("trigger full lifecycle (integration)", () => {
settings,
logger: silentLogger,
runAgent,
projectWorkspaceRoot: dirname(workspaceDir),
messageBatcherOptions: { maxMessages: 1 },
});
@@ -200,8 +207,7 @@ describe("trigger full lifecycle (integration)", () => {
await vi.waitFor(() => {
expect(runAgentCalls).toHaveLength(1);
});
expect(downloadedPath).toBeDefined();
expect(runAgentCalls[0]?.prompt).toContain(downloadedPath);
expect(runAgentCalls[0]?.prompt).toContain(join(await realpath(workspaceDir), ".cph", "inbox"));
expect(messageResourceGet).toHaveBeenCalledWith({
params: { type: "image" },
path: { message_id: event.message.message_id, file_key: "img-key-1" },