1import { isWorkspaceImagePreviewPath } from "@t3tools/shared/filePreview";
2import { COMPUTER_USE_TOOL_SOURCE, dynamicToolSurface } from "@t3tools/shared/toolActivity";
3import type {
4 ToolActivityIcon,
5 ToolActivityNativeAppReference,
6 ToolActivitySource,
7 ToolActivitySurface,
8} from "@t3tools/contracts";
9
10export interface ExtractedToolActivityPresentation {
11 readonly viewedImagePath?: string;
12 readonly toolSurface?: ToolActivitySurface;
13 readonly toolIcon?: ToolActivityIcon;
14 readonly toolSource?: ToolActivitySource;
15}
16
17function asRecord(value: unknown): Record<string, unknown> | undefined {
18 return value !== null && typeof value === "object" && !Array.isArray(value)
19 ? (value as Record<string, unknown>)
20 : undefined;
21}
22
23function trimmedString(value: unknown, maxLength: number): string | undefined {
24 if (typeof value !== "string") return undefined;
25 const trimmed = value.trim();
26 return trimmed && trimmed.length <= maxLength ? trimmed : undefined;
27}
28
29function imageUrl(value: unknown): string | undefined {
30 const raw = trimmedString(value, 4096);
31 if (!raw) return undefined;
32 try {
33 const url = new URL(raw);
34 return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "data:"
35 ? url.href
36 : undefined;
37 } catch {
38 return undefined;
39 }
40}
41
42function pageUrl(value: unknown): string | undefined {
43 const raw = trimmedString(value, 4096);
44 if (!raw) return undefined;
45 try {
46 const url = new URL(raw);
47 return url.protocol === "http:" || url.protocol === "https:" ? url.href : undefined;
48 } catch {
49 return undefined;
50 }
51}
52
53function nativeAppReference(value: unknown): ToolActivityNativeAppReference | undefined {
54 const app = asRecord(value);
55 const appId = trimmedString(app?.appId, 512);
56 if (app?._tag === "app-id" && appId && /^[A-Za-z0-9._-]+$/u.test(appId)) {
57 return { _tag: "app-id", appId };
58 }
59 const displayName = trimmedString(app?.displayName, 160);
60 if (app?._tag === "display-name" && displayName) {
61 return { _tag: "display-name", displayName };
62 }
63 return undefined;
64}
65
66function activityIcon(value: unknown): ToolActivityIcon | undefined {
67 const icon = asRecord(value);
68 if (icon?._tag === "website") {
69 const resolvedPageUrl = pageUrl(icon.pageUrl);
70 const faviconUrl = imageUrl(icon.faviconUrl);
71 const faviconUrlDark = imageUrl(icon.faviconUrlDark);
72 if (resolvedPageUrl) {
73 return {
74 _tag: "website",
75 pageUrl: resolvedPageUrl,
76 ...(faviconUrl ? { faviconUrl } : {}),
77 ...(faviconUrlDark ? { faviconUrlDark } : {}),
78 };
79 }
80 }
81 if (icon?._tag === "native-app") {
82 const app = nativeAppReference(icon.app);
83 if (app) return { _tag: "native-app", app };
84 }
85 if (icon?._tag === "themed-logo") {
86 const logoUrl = imageUrl(icon.logoUrl);
87 const logoUrlDark = imageUrl(icon.logoUrlDark);
88 if (logoUrl) {
89 return {
90 _tag: "themed-logo",
91 logoUrl,
92 ...(logoUrlDark ? { logoUrlDark } : {}),
93 };
94 }
95 }
96 return undefined;
97}
98
99function activitySource(value: unknown): ToolActivitySource | undefined {
100 const source = asRecord(value);
101 const key = trimmedString(source?.key, 512);
102 const name = trimmedString(source?.name, 160);
103 const kind = source?.kind;
104 if (!key || !name || (kind !== "browser" && kind !== "computer" && kind !== "integration")) {
105 return undefined;
106 }
107 const icon = activityIcon(source?.icon);
108 return { key, name, kind, ...(icon ? { icon } : {}) };
109}
110
111export function extractToolActivityPresentation(
112 payloadValue: unknown,
113): ExtractedToolActivityPresentation {
114 const payload = asRecord(payloadValue);
115 const retainedSource = activitySource(payload?.toolSource);
116 const toolSurface =
117 payload?.toolSurface === "browser" || payload?.toolSurface === "computer"
118 ? payload.toolSurface
119 : retainedSource?.kind === "computer" || retainedSource?.kind === "browser"
120 ? retainedSource.kind
121 : payload?.type === "dynamic_tool"
122 ? dynamicToolSurface(
123 typeof payload.toolName === "string" ? payload.toolName : undefined,
124 payload.input,
125 )
126 : undefined;
127 const toolSource =
128 toolSurface === "computer" && retainedSource?.kind !== "computer"
129 ? COMPUTER_USE_TOOL_SOURCE
130 : retainedSource;
131 const toolIcon = activityIcon(payload?.toolIcon);
132 const viewedImagePath = trimmedString(payload?.viewedImagePath, 4096);
133 return {
134 ...(toolSurface ? { toolSurface } : {}),
135 ...(toolIcon ? { toolIcon } : {}),
136 ...(toolSource ? { toolSource } : {}),
137 ...(viewedImagePath &&
138 !/[\r\n]/.test(viewedImagePath) &&
139 isWorkspaceImagePreviewPath(viewedImagePath)
140 ? { viewedImagePath }
141 : {}),
142 };
143}