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, 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// 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(); }); });