Files
tutti/packages/agent/gui/actions/workspaceLinkActions.spec.ts
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

574 lines
18 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
resolveLocalAssetPreviewLinkAction,
resolveWorkspaceLinkAction,
resolveWorkspaceMentionLinkAction,
resolveWorkspaceFileLinkAction,
resolveWorkspaceFilePathCandidate
} from "./workspaceLinkActions";
import {
registerAgentCustomMentionKind,
resetAgentCustomMentionKindsForTests
} from "../shared/agentCustomMentionKinds";
describe("resolveWorkspaceFileLinkAction", () => {
it("opens local absolute paths without remapping workspace-prefixed paths", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/project/tutti/src/App.tsx",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/project/tutti/src/App.tsx",
directoryPath: "/Users/test/project/tutti/src",
workspaceRoot: "/Users/test/project/tutti"
});
expect(
resolveWorkspaceFileLinkAction({
path: "/workspace/src/App.tsx",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/workspace/src/App.tsx",
directoryPath: "/workspace/src",
workspaceRoot: "/Users/test/project/tutti"
});
});
it("opens Markdown source links by removing their terminal line anchor", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/project/tutti/src/App.tsx:87",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/project/tutti/src/App.tsx",
directoryPath: "/Users/test/project/tutti/src",
workspaceRoot: "/Users/test/project/tutti"
});
expect(
resolveWorkspaceFileLinkAction({
path: "C:\\Users\\test\\project\\tutti\\src\\App.tsx:87",
workspaceRoot: "C:\\Users\\test\\project\\tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/C:/Users/test/project/tutti/src/App.tsx",
directoryPath: "/C:/Users/test/project/tutti/src",
workspaceRoot: "/C:/Users/test/project/tutti"
});
});
it("allows local absolute paths when the workspace root is the filesystem root", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/project/tutti/src/App.tsx",
workspaceRoot: "/",
basePath: "/",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/project/tutti/src/App.tsx",
directoryPath: "/Users/test/project/tutti/src",
workspaceRoot: "/"
});
});
it("allows explicit local absolute paths outside the selected workspace root", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/var/folders/17/demo/T/codex-presentations/test-note.md",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-file-change"
})
).toMatchObject({
type: "open-workspace-file",
path: "/var/folders/17/demo/T/codex-presentations/test-note.md",
directoryPath: "/var/folders/17/demo/T/codex-presentations",
workspaceRoot: "/Users/test/project/tutti"
});
expect(
resolveWorkspaceFileLinkAction({
path: "/tmp/report.txt",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-file-change"
})
).toMatchObject({
type: "open-workspace-file",
path: "/tmp/report.txt",
directoryPath: "/tmp",
workspaceRoot: "/Users/test/project/tutti"
});
});
it("rejects special local device and network share paths before launch", () => {
for (const path of [
"/dev/null",
"/dev/./null",
"/dev//null",
"NUL",
"NUL.txt",
"C:\\tmp\\NUL",
"\\\\server\\share\\file.txt",
"//server/share/file.txt"
]) {
expect(
resolveWorkspaceFileLinkAction({
path,
workspaceRoot: "/",
basePath: "/",
source: "agent-file-change"
})
).toBeNull();
}
});
it("allows direct generated image paths under Tutti state outside the workspace root", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_images/imagegen/ig_123.png",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_images/imagegen/ig_123.png",
directoryPath:
"/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_images/imagegen",
workspaceRoot: "/Users/test/project/tutti"
});
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/secrets.txt",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/secrets.txt",
directoryPath: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home",
workspaceRoot: "/Users/test/project/tutti"
});
});
it("allows direct generated video paths under Tutti state outside the workspace root", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_videos/dance.mp4",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_videos/dance.mp4",
directoryPath:
"/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_videos",
workspaceRoot: "/Users/test/project/tutti"
});
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_videos/raw.mov",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_videos/raw.mov",
directoryPath:
"/Users/test/.tutti-dev/agent/runs/session-1/codex-home/generated_videos",
workspaceRoot: "/Users/test/project/tutti"
});
});
it("allows direct workspace app data paths without a selected project", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/.tutti-dev/apps/workspaces/workspace-1/group-chat/data/rooms/room-1/uploads/image.png",
workspaceRoot: "",
basePath: "",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/.tutti-dev/apps/workspaces/workspace-1/group-chat/data/rooms/room-1/uploads/image.png",
directoryPath:
"/Users/test/.tutti-dev/apps/workspaces/workspace-1/group-chat/data/rooms/room-1/uploads",
workspaceRoot:
"/Users/test/.tutti-dev/apps/workspaces/workspace-1/group-chat/data/rooms/room-1/uploads"
});
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/Downloads/image.png",
workspaceRoot: "",
basePath: "",
source: "agent-markdown"
})
).toBeNull();
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/.tutti-dev/apps/workspaces/workspace-1",
workspaceRoot: "",
basePath: "",
source: "agent-markdown"
})
).toBeNull();
});
it("preserves the selected workspace root for direct workspace app data paths", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/.tutti-dev/apps/workspaces/workspace-1/group-chat/data/file.txt",
workspaceRoot: "/Users/test/project/tutti",
basePath: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/Users/test/.tutti-dev/apps/workspaces/workspace-1/group-chat/data/file.txt",
directoryPath:
"/Users/test/.tutti-dev/apps/workspaces/workspace-1/group-chat/data",
workspaceRoot: "/Users/test/project/tutti"
});
});
it("rejects staged local asset paths from workspace file candidates", () => {
expect(
resolveWorkspaceFilePathCandidate({
path: "/var/cache/tsh/local-assets/room-1/user-1/photo.png",
workspaceRoot: "/workspace/project-a"
})
).toBeNull();
});
it("resolves relative paths through the same workspace file candidate contract", () => {
expect(
resolveWorkspaceFilePathCandidate({
path: "src/App.tsx",
workspaceRoot: "/Users/test/project/tutti"
})
).toMatchObject({
path: "/Users/test/project/tutti/src/App.tsx",
directoryPath: "/Users/test/project/tutti/src",
workspaceRoot: "/Users/test/project/tutti"
});
});
it("uses the agent session cwd when a no-project conversation has no workspace root", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "/Users/test/Documents/tutti/session-1/index.html",
workspaceRoot: null,
basePath: "/Users/test/Documents/tutti/session-1",
source: "agent-markdown"
})
).toEqual({
type: "open-workspace-file",
path: "/Users/test/Documents/tutti/session-1/index.html",
directoryPath: "/Users/test/Documents/tutti/session-1",
workspaceRoot: "/Users/test/Documents/tutti/session-1",
source: "agent-markdown"
});
});
it("preserves home-relative paths for the desktop launch layer", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "~/docs/spec.md",
workspaceRoot: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "~/docs/spec.md",
directoryPath: "~/docs",
workspaceRoot: "/Users/test/project/tutti"
});
expect(
resolveWorkspaceFileLinkAction({
path: "~",
workspaceRoot: "/Users/test/project/tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "~",
directoryPath: "~",
workspaceRoot: "/Users/test/project/tutti"
});
});
it("allows Windows local paths inside the workspace root", () => {
expect(
resolveWorkspaceFileLinkAction({
path: "C:\\Users\\test\\project\\tutti\\src\\App.tsx",
workspaceRoot: "C:\\Users\\test\\project\\tutti",
source: "agent-markdown"
})
).toMatchObject({
type: "open-workspace-file",
path: "/C:/Users/test/project/tutti/src/App.tsx",
directoryPath: "/C:/Users/test/project/tutti/src",
workspaceRoot: "/C:/Users/test/project/tutti"
});
expect(
resolveWorkspaceFileLinkAction({
path: "/c/Users/test/project/tutti/src/App.tsx",
workspaceRoot: "C:\\Users\\test\\project\\tutti",
source: "agent-file-change"
})
).toMatchObject({
type: "open-workspace-file",
path: "/C:/Users/test/project/tutti/src/App.tsx",
directoryPath: "/C:/Users/test/project/tutti/src",
workspaceRoot: "/C:/Users/test/project/tutti"
});
});
});
describe("resolveWorkspaceMentionLinkAction", () => {
it("parses canonical workspace-issue mention context", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://workspace-issue/issue-1?workspaceId=workspace-1&mode=execute&topicId=topic-1&taskId=task-1&runId=run-1&outputDir=%2Fworkspace%2Fissues%2Fissue-1",
source: "agent-markdown"
})
).toEqual({
type: "open-workspace-issue",
workspaceId: "workspace-1",
issueId: "issue-1",
mode: "execute",
topicId: "topic-1",
taskId: "task-1",
runId: "run-1",
outputDir: "/workspace/issues/issue-1",
source: "agent-markdown"
});
});
it("resolves clickable registered custom mentions to open-custom-mention", () => {
registerAgentCustomMentionKind({
kind: "external-note",
clickable: true,
present: (mention) => ({ name: mention.label })
});
try {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://external-note/note-a?ids=note-a%2Cnote-b&preview=hello&spaceId=space-1",
source: "agent-markdown"
})
).toEqual({
type: "open-custom-mention",
kind: "external-note",
href: "mention://external-note/note-a?ids=note-a%2Cnote-b&preview=hello&spaceId=space-1",
source: "agent-markdown"
});
} finally {
resetAgentCustomMentionKindsForTests();
}
});
it("does not resolve non-clickable registered custom mentions", () => {
registerAgentCustomMentionKind({
kind: "external-note",
present: (mention) => ({ name: mention.label })
});
try {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://external-note/note-a?spaceId=space-1",
source: "agent-markdown"
})
).toBeNull();
} finally {
resetAgentCustomMentionKindsForTests();
}
});
it("does not resolve unregistered custom mention kinds", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://external-note/note-a?spaceId=space-1",
source: "agent-markdown"
})
).toBeNull();
});
it("parses workspace-app mention context", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://workspace-app/weather?workspaceId=workspace-1",
source: "agent-markdown"
})
).toEqual({
type: "open-workspace-app",
workspaceId: "workspace-1",
appId: "weather",
source: "agent-markdown"
});
});
it("parses workspace-app mention navigation params", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://workspace-app/group-chat?workspaceId=workspace-1&messageId=msg-1&conversationId=conv-1",
source: "agent-markdown"
})
).toEqual({
type: "open-workspace-app",
workspaceId: "workspace-1",
appId: "group-chat",
messageId: "msg-1",
conversationId: "conv-1",
source: "agent-markdown"
});
});
it("opens the owning app for an app artifact reference", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://workspace-reference/ai-canvas?groupId=outputs&source=app&workspaceId=workspace-1",
source: "agent-markdown"
})
).toEqual({
type: "open-workspace-app",
workspaceId: "workspace-1",
appId: "ai-canvas",
source: "agent-markdown"
});
});
it("keeps task artifact references out of workspace app routing", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://workspace-reference/topic-1?groupId=issue-1&source=task&workspaceId=workspace-1",
source: "agent-markdown"
})
).toBeNull();
});
it("rejects legacy query-only workspace mention context", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://workspace-issue?workspaceId=workspace-1&id=issue-1",
source: "agent-markdown"
})
).toBeNull();
});
it("rejects reserved mention scope keys", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://agent-session/session-1?workspaceId=workspace-1&provider=codex",
source: "agent-markdown"
})
).toBeNull();
});
it("parses agent-session mention context without a captured provider", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://agent-session/session-1?workspaceId=workspace-1",
source: "agent-markdown"
})
).toEqual({
type: "open-agent-session",
workspaceId: "workspace-1",
agentSessionId: "session-1",
source: "agent-markdown"
});
});
it("parses the mentioned session's agent target scope", () => {
expect(
resolveWorkspaceMentionLinkAction({
href: "mention://agent-session/session-1?agentTargetId=local%3Aclaude-code&workspaceId=workspace-1",
source: "agent-markdown"
})
).toEqual({
type: "open-agent-session",
workspaceId: "workspace-1",
agentSessionId: "session-1",
agentTargetId: "local:claude-code",
source: "agent-markdown"
});
});
});
describe("resolveLocalAssetPreviewLinkAction", () => {
it("resolves staged local asset paths as preview actions", () => {
expect(
resolveLocalAssetPreviewLinkAction({
path: "/var/cache/tsh/local-assets/room-1/user-1/asset.png",
source: "agent-markdown"
})
).toEqual({
type: "open-local-asset-preview",
path: "/var/cache/tsh/local-assets/room-1/user-1/asset.png",
name: "asset.png",
source: "agent-markdown"
});
});
it("rejects local asset metadata sidecars", () => {
expect(
resolveLocalAssetPreviewLinkAction({
path: "/var/cache/tsh/local-assets/room-1/user-1/asset.png.metadata.json",
source: "agent-markdown"
})
).toBeNull();
});
it("rejects other VM absolute paths", () => {
expect(
resolveLocalAssetPreviewLinkAction({
path: "/var/lib/tsh/notes.md",
source: "agent-markdown"
})
).toBeNull();
});
});
describe("resolveWorkspaceLinkAction", () => {
it("opens staged local asset links with the preview action", () => {
expect(
resolveWorkspaceLinkAction({
href: "/var/cache/tsh/local-assets/room-1/user-1/photo.png",
workspaceRoot: "/workspace/project-a",
source: "agent-markdown"
})
).toEqual({
type: "open-local-asset-preview",
path: "/var/cache/tsh/local-assets/room-1/user-1/photo.png",
name: "photo.png",
source: "agent-markdown"
});
});
});