源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import type { UIMessage } from "ai";
|
||||
|
||||
import {
|
||||
deriveOpenTargets,
|
||||
isCollectibleArtifactTarget,
|
||||
selectAutoOpenTarget,
|
||||
} from "../src/react-app/domains/session/artifacts/open-target";
|
||||
|
||||
function message(id: string, role: "user" | "assistant", text: string): UIMessage {
|
||||
return { id, role, parts: [{ type: "text", text, state: "done" }] };
|
||||
}
|
||||
|
||||
function toolMessage(id: string, toolName: string, input: Record<string, unknown>, output: unknown) {
|
||||
return {
|
||||
id,
|
||||
role: "assistant",
|
||||
parts: [{
|
||||
type: "dynamic-tool",
|
||||
toolName,
|
||||
toolCallId: `${id}_tool`,
|
||||
state: "output-available",
|
||||
input,
|
||||
output,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
describe("deriveOpenTargets", () => {
|
||||
it("extracts file and localhost URL targets from recent assistant output", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "reports/revenue.xlsx" }, { filePath: "reports/revenue.xlsx" }),
|
||||
message("msg_1", "assistant", "Created reports/revenue.xlsx and started http://localhost:5173 for preview."),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).toContain("reports/revenue.xlsx");
|
||||
expect(targets.map((target) => target.value)).toContain("http://localhost:5173");
|
||||
expect(targets.find((target) => target.value === "reports/revenue.xlsx")?.preview).toBe("sheet");
|
||||
});
|
||||
|
||||
it("extracts websocket URLs so local socket/dev-server hints stay visible", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "dist/index.html" }, { filePath: "dist/index.html" }),
|
||||
message("msg_1", "assistant", "Socket open at ws://localhost:5173/socket and preview at dist/index.html"),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).toContain("ws://localhost:5173/socket");
|
||||
expect(targets.map((target) => target.value)).toContain("dist/index.html");
|
||||
});
|
||||
|
||||
it("normalizes Workspace/<id>/ prefixes from artifact paths", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool_1", "write", { filePath: "Workspace/32423/reports/artifact-eval.md" }, { filePath: "Workspace/32423/reports/artifact-eval.md" }),
|
||||
toolMessage("msg_tool_2", "write", { filePath: "Workspace/32423/reports/artifact-eval.csv" }, { filePath: "Workspace/32423/reports/artifact-eval.csv" }),
|
||||
message("msg_1", "assistant", "See Workspace/32423/reports/artifact-eval.md and Workspace/32423/reports/artifact-eval.csv"),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).toContain("reports/artifact-eval.md");
|
||||
expect(targets.map((target) => target.value)).toContain("reports/artifact-eval.csv");
|
||||
});
|
||||
|
||||
it("prefers explicit dynamic tool metadata over prose guesses", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { path: "summary.md" }, { path: "summary.md" }),
|
||||
]);
|
||||
|
||||
expect(targets[0]).toMatchObject({ value: "summary.md", preview: "markdown", confidence: 95 });
|
||||
});
|
||||
|
||||
it("opens standalone Mermaid files through the Markdown preview", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { path: "diagrams/architecture.mmd" }, { path: "diagrams/architecture.mmd" }),
|
||||
]);
|
||||
|
||||
expect(targets[0]).toMatchObject({ value: "diagrams/architecture.mmd", preview: "markdown", confidence: 95 });
|
||||
});
|
||||
|
||||
it("extracts filePath metadata from write tools", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "reports/summary.md" }, { filePath: "reports/summary.md" }),
|
||||
]);
|
||||
|
||||
expect(targets[0]).toMatchObject({ value: "reports/summary.md", preview: "markdown", confidence: 95 });
|
||||
});
|
||||
|
||||
it("collects written code files for the artifact editor", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "src/widget.tsx" }, { filePath: "src/widget.tsx" }),
|
||||
]);
|
||||
|
||||
const target = targets[0];
|
||||
|
||||
expect(target).toMatchObject({ value: "src/widget.tsx", preview: "code", confidence: 95 });
|
||||
expect(target ? isCollectibleArtifactTarget({ ...target, exists: true }) : false).toBe(true);
|
||||
});
|
||||
|
||||
it("uses markdown link href once when the label is the href basename", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
message("msg_1", "assistant", "I generated the file [native-link.txt](reports/native-link.txt)."),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).toEqual(["reports/native-link.txt"]);
|
||||
});
|
||||
|
||||
it("keeps distinct markdown link labels as normal file mentions", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
message("msg_1", "assistant", "I generated the file [summary.md](reports/native-link.txt)."),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value).sort()).toEqual(["reports/native-link.txt", "summary.md"]);
|
||||
});
|
||||
|
||||
it("extracts PowerPoint decks from assistant artifact summaries", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
message("msg_1", "assistant", "Updated file: decks/openwork-vertebrae-deck.pptx"),
|
||||
]);
|
||||
const deck = targets.find((target) => target.value === "decks/openwork-vertebrae-deck.pptx");
|
||||
|
||||
expect(deck).toMatchObject({ preview: "slides", confidence: 65 });
|
||||
expect(deck ? isCollectibleArtifactTarget({ ...deck, exists: true }) : false).toBe(true);
|
||||
});
|
||||
|
||||
it("extracts artifact paths from OpenWork extension call metadata", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "openwork_extension_call", {
|
||||
extensionId: "openai-image-generation",
|
||||
action: "image_generate",
|
||||
}, {
|
||||
ok: true,
|
||||
extensionId: "openai-image-generation",
|
||||
action: "image_generate",
|
||||
path: "artifacts/potato.png",
|
||||
result: {
|
||||
path: "artifacts/potato.png",
|
||||
bytes: 12345,
|
||||
model: "gpt-image-2",
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(targets[0]).toMatchObject({ value: "artifacts/potato.png", preview: "image", confidence: 95 });
|
||||
});
|
||||
|
||||
it("extracts artifact targets from attachment sources", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
{
|
||||
id: "msg_attachment",
|
||||
role: "assistant",
|
||||
parts: [{
|
||||
type: "source-document",
|
||||
sourceId: "attachment-source",
|
||||
mediaType: "text/csv",
|
||||
title: "customers.csv",
|
||||
filename: "reports/customers.csv",
|
||||
}],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(targets[0]).toMatchObject({ value: "reports/customers.csv", preview: "sheet", confidence: 95 });
|
||||
});
|
||||
|
||||
it("keeps URI-backed source documents as URL targets when filename is missing", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
{
|
||||
id: "msg_source",
|
||||
role: "assistant",
|
||||
parts: [{
|
||||
type: "source-document",
|
||||
sourceId: "url-source",
|
||||
mediaType: "text/html",
|
||||
title: "https://example.com/docs/report.html",
|
||||
}],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(targets[0]).toMatchObject({ kind: "url", value: "https://example.com/docs/report.html", preview: "browser" });
|
||||
});
|
||||
|
||||
it("does not extract file artifacts from read tool metadata or output", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage(
|
||||
"msg_tool",
|
||||
"read",
|
||||
{ filePath: "reports/source.md" },
|
||||
{ content: "Reviewed reports/source.md and referenced reports/source.csv" },
|
||||
),
|
||||
message("msg_2", "assistant", "Reviewed reports/source.md and reports/source.csv."),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).not.toContain("reports/source.md");
|
||||
expect(targets.map((target) => target.value)).not.toContain("reports/source.csv");
|
||||
});
|
||||
|
||||
it("extracts paths written by apply_patch metadata", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "apply_patch", {
|
||||
patchText: "*** Begin Patch\n*** Add File: reports/new-report.md\n+hello\n*** Update File: reports/existing-report.csv\n@@\n-old\n+new\n*** End Patch",
|
||||
}, "Success. Updated files."),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).toContain("reports/new-report.md");
|
||||
expect(targets.map((target) => target.value)).toContain("reports/existing-report.csv");
|
||||
});
|
||||
|
||||
it("does not turn package search results into artifacts", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "glob", { pattern: "**/package.json" }, {
|
||||
files: [
|
||||
"package.json",
|
||||
"apps/app/package.json",
|
||||
"packages/ui/package.json",
|
||||
"reports/revenue.csv",
|
||||
],
|
||||
}),
|
||||
message("msg_2", "assistant", "Found package.json, apps/app/package.json, and reports/revenue.csv"),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).not.toContain("package.json");
|
||||
expect(targets.map((target) => target.value)).not.toContain("apps/app/package.json");
|
||||
expect(targets.map((target) => target.value)).not.toContain("packages/ui/package.json");
|
||||
expect(targets.map((target) => target.value)).not.toContain("reports/revenue.csv");
|
||||
});
|
||||
|
||||
it("does not turn discovery tool markdown listings into artifacts", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_write", "write", { filePath: "reports/created-report.md" }, { filePath: "reports/created-report.md" }),
|
||||
toolMessage("msg_tool", "glob", { pattern: "**/*.md" }, {
|
||||
files: [
|
||||
"README.md",
|
||||
".opencode/skills/example/SKILL.md",
|
||||
"reports/created-report.md",
|
||||
],
|
||||
}),
|
||||
message("msg_2", "assistant", "Created reports/created-report.md as the deliverable."),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).toContain("reports/created-report.md");
|
||||
expect(targets.map((target) => target.value)).not.toContain("README.md");
|
||||
expect(targets.map((target) => target.value)).not.toContain(".opencode/skills/example/SKILL.md");
|
||||
});
|
||||
|
||||
it("does not collect server-verified missing file targets", () => {
|
||||
const target = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "index.html" }, { filePath: "index.html" }),
|
||||
message("msg_1", "assistant", "Preview file: index.html"),
|
||||
])[0];
|
||||
|
||||
expect(target).toMatchObject({ value: "index.html", preview: "html" });
|
||||
expect(isCollectibleArtifactTarget({ ...target, exists: false })).toBe(false);
|
||||
expect(isCollectibleArtifactTarget({ ...target, exists: true })).toBe(true);
|
||||
});
|
||||
|
||||
it("does not auto-open generated html files or localhost browser previews", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "public/index.html" }, { filePath: "public/index.html" }),
|
||||
message("msg_1", "assistant", "Created public/index.html. API: `http://localhost:3000/api/info`. App: `http://localhost:3000`."),
|
||||
]).map((target) => ({ ...target, exists: target.kind === "url" || target.value === "public/index.html" }));
|
||||
|
||||
expect(targets.map((target) => target.value)).toContain("http://localhost:3000/api/info");
|
||||
expect(targets.map((target) => target.value)).toContain("http://localhost:3000");
|
||||
expect(selectAutoOpenTarget(targets)).toBeNull();
|
||||
});
|
||||
|
||||
it("normalizes escaped localhost root URL variants into one target", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
message("msg_1", "assistant", "App: `http://localhost:3000/\\` and also http://localhost:3000//"),
|
||||
]);
|
||||
|
||||
expect(targets.filter((target) => target.value === "http://localhost:3000")).toHaveLength(1);
|
||||
expect(targets.map((target) => target.name)).not.toContain("\\");
|
||||
});
|
||||
|
||||
it("keeps accessible targets from earlier session messages", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "reports/earlier.csv" }, { filePath: "reports/earlier.csv" }),
|
||||
message("msg_1", "assistant", "Created reports/earlier.csv"),
|
||||
...Array.from({ length: 12 }, (_, index) => message(`msg_noise_${index}`, "assistant", `Status update ${index + 1}`)),
|
||||
message("msg_last", "assistant", "Server running at http://localhost:3000"),
|
||||
]);
|
||||
|
||||
expect(targets.map((target) => target.value)).toContain("reports/earlier.csv");
|
||||
expect(targets.map((target) => target.value)).toContain("http://localhost:3000");
|
||||
});
|
||||
|
||||
it("does not auto-open high-confidence deliverables or browser previews", () => {
|
||||
const targets = deriveOpenTargets([
|
||||
toolMessage("msg_tool", "write", { filePath: "data/customers.csv" }, { filePath: "data/customers.csv" }),
|
||||
message("msg_1", "assistant", "Created data/customers.csv and see https://example.com for docs."),
|
||||
]).map((target) => ({ ...target, exists: target.kind === "file" }));
|
||||
|
||||
expect(selectAutoOpenTarget(targets)).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user