"use strict"; const crypto = require("node:crypto"); const fs = require("node:fs"); const http = require("node:http"); const https = require("node:https"); const os = require("node:os"); const pathModule = require("node:path"); const zlib = require("node:zlib"); const DEFAULT_HOST = "claude.ai"; const DEFAULT_GATEWAY_URL = "http://127.0.0.1:3456"; const DEFAULT_GATEWAY_MODEL = "claude-sonnet-4-20250514"; const DEFAULT_UPSTREAM_ORIGIN = "https://claude.ai"; const DEFAULT_DESIGN_ORIGIN = "https://claude.ai"; const DEFAULT_DESIGN_REFERRER = "https://claude.ai/design"; const DEFAULT_FALLBACK_ROUTE_HOSTS = ["claude.com", "www.anthropic.com", "anthropic.com"]; const CLAUDE_APP_ION_DIST_ENV_KEYS = ["CCR_CLAUDE_APP_ION_DIST_DIR", "CLAUDE_APP_ION_DIST_DIR"]; const CLAUDE_APP_PATH_ENV_KEYS = ["CCR_CLAUDE_APP_PATH", "CLAUDE_APP_PATH"]; const CLAUDE_APP_DESIGN_IFRAME_QUERY = "__ccr_design_iframe"; const CLAUDE_APP_DESIGN_PATH_QUERY = "path"; const CLAUDE_APP_DESIGN_SHELL_PATH = "/desktop-design"; const CLAUDE_APP_SPA_ROUTE_PATHS = [ CLAUDE_APP_DESIGN_SHELL_PATH, "/discover/design", "/admin-settings/claude-design" ]; const OMELETTE_RPC_PATH_PREFIX = "/design/anthropic.omelette.api.v1alpha.OmeletteService"; const AUTH_ESCAPE_ROUTE_PATHS = ["/login", "/auth", "/oauth"]; const BOOTSTRAP_ROUTE_PATHS = ["/_bootstrap", "/api/bootstrap", "/edge-api/bootstrap"]; const REQUIRED_ROUTE_PATHS = ["/", OMELETTE_RPC_PATH_PREFIX, "/v1/design", ...CLAUDE_APP_SPA_ROUTE_PATHS, ...BOOTSTRAP_ROUTE_PATHS, ...AUTH_ESCAPE_ROUTE_PATHS]; const DEFAULT_ROUTE_PATHS = ["/design", "/v1/design", "/api", "/organizations", "/cdn-cgi", ...BOOTSTRAP_ROUTE_PATHS, ...AUTH_ESCAPE_ROUTE_PATHS]; const FALLBACK_ROUTE_PATHS = ["/app-unavailable-in-region", "/cdn-cgi", "/design", ...AUTH_ESCAPE_ROUTE_PATHS]; const DEFAULT_DIRECT_GATEWAY_ROUTE_PATHS = ["/design", "/v1/design", "/api", "/organizations", "/assets", "/cdn-cgi", "/app-unavailable-in-region", ...BOOTSTRAP_ROUTE_PATHS, ...AUTH_ESCAPE_ROUTE_PATHS]; const DEFAULT_SCRIPT_PATH = "/design/assets/v1/index-C0BEUHEw.js"; const DEFAULT_STYLE_PATH = "/design/assets/v1/index-CqhNJH1o.css"; const DEFAULT_DESIGN_CRITICAL_MODULE_PRELOAD_PATHS = [ "/design/assets/v1/rolldown-runtime-CMxvf4Kt.js", "/design/assets/v1/preload-helper-7XptfjGJ.js", "/design/assets/v1/react-BthIFXYf.js", "/design/assets/v1/client-lite-CK-dmuh6.js", "/design/assets/v1/useOrg-DmMewMgN.js", "/design/assets/v1/cn-BvRk9kiK.js", "/design/assets/v1/cn-D-uX0T7P.js", "/design/assets/v1/Button-XfKvB69T.js" ]; const DEFAULT_DESIGN_LAZY_MODULE_PRELOAD_PATHS = [ "/design/assets/v1/Button-BtUXY0oi.js", "/design/assets/v1/DsBrowseModal-hSTModTp.js", "/design/assets/v1/Form-B3y7m-2_.js", "/design/assets/v1/FormList-Bv2urBSD.js", "/design/assets/v1/Kbd-CLyZAmwA.js", "/design/assets/v1/MetaText-DDKRoRv8.js", "/design/assets/v1/ModalHeader-Jq2fIJBg.js", "/design/assets/v1/ProjectsPage-PDmge7e_.js", "/design/assets/v1/SegmentedControl-oWx7ZWcd.js", "/design/assets/v1/SpinnerCursorContext-FnwGR7gg.js", "/design/assets/v1/Switch-C1hJI3Wa.js", "/design/assets/v1/TextInput-DBxrS72B.js", "/design/assets/v1/TextLink-CSMbfMRn.js", "/design/assets/v1/Tooltip-DkOyUUQf.js", "/design/assets/v1/client-CoyioTSK.js", "/design/assets/v1/client-event-bus-CUOhmLFi.js", "/design/assets/v1/completion-BAv5dQBO.js", "/design/assets/v1/components-CiWVw_5Z.js", "/design/assets/v1/connectrpc-B0zPEr5l.js", "/design/assets/v1/data-C1nvXn42.js", "/design/assets/v1/ds-contract-C8bG_fzg.js", "/design/assets/v1/ds-manifest-guards-CDOctgob.js", "/design/assets/v1/home-analytics-EoW6gFrM.js", "/design/assets/v1/host-BwlQdwMG.js", "/design/assets/v1/platform-BJ0ekVkb.js", "/design/assets/v1/registry-DDfaFazS.js", "/design/assets/v1/useLabelableId-YQk8Dx5K.js", "/design/assets/v1/useModelSelection-DahB-QBH.js", "/design/assets/v1/useMutation-ndQNPSAH.js", "/design/assets/v1/viewer-handle-BbaClWB_.js" ]; const DEFAULT_DESIGN_STYLE_PRELOAD_PATHS = [ "/design/assets/v1/Button-C3hHoRdu.css", "/design/assets/v1/FormList-DPI5FmbR.css", "/design/assets/v1/ProjectsPage-CRTuKvXp.css", "/design/assets/v1/components-DnFFPXN_.css", "/design/assets/v1/home-analytics-DAgH1hGY.css" ]; const LEGACY_SCRIPT_PATHS = new Set([ "/design/assets/index-DWa5J5J9.js", "/design/assets/index-DYd5ifc6.js", "/design/assets/index-BxFzSrWf.js" ]); const LEGACY_STYLE_PATHS = new Set([ "/design/assets/index-DZOB93ZB.css", "/design/assets/index-j8_-aIUE.css" ]); const DEFAULT_EXTERNAL_ASSET_BASE_URLS = ["https://assets-proxy.anthropic.com/claude-ai/v2/assets/v1/"]; const DESIGN_INDEX_ASSET_DISCOVERY_TTL_MS = 5 * 60 * 1000; const MAX_UPSTREAM_ASSET_REDIRECTS = 5; const MAX_LOG_BODY_CHARS = 128 * 1024; const COMMENT_COLLECTION = "comments"; const DESIGN_SYSTEM_COLLECTION = "design_systems"; const EVENT_COLLECTION = "events"; const PROJECT_COLLECTION = "projects"; const SESSION_COLLECTION = "sessions"; const THUMBNAIL_COLLECTION = "thumbnails"; const PROJECT_TYPE_PROJECT = 1; const PROJECT_TYPE_TEMPLATE = 2; const PROJECT_TYPE_DESIGN_SYSTEM = 3; const TRANSPARENT_SVG = `\n`; const QUESTION_TOOL_NAMES = new Set(["questions", "questions_v2"]); const CLAUDE_DESIGN_ROUTE_TYPES = new Set(["always", "image", "long-context", "model", "model-prefix", "thinking", "web-search"]); const CLAUDE_DESIGN_MODEL_ALIASES = new Map([ ["claude-haiku-4-5-20251001", DEFAULT_GATEWAY_MODEL], ["claude-opus-4-6", DEFAULT_GATEWAY_MODEL], ["claude-opus-4-7", DEFAULT_GATEWAY_MODEL], ["claude-opus-4-8", DEFAULT_GATEWAY_MODEL], ["claude-sonnet-4-6", DEFAULT_GATEWAY_MODEL] ]); const FALLBACK_ASSET_CACHE_PREFIX = "fallback:claude-design-v1:"; const FALLBACK_ASSET_CACHE_TTL_MS = 60 * 60 * 1000; const OMELETTE_PREVIEW_EVAL_BRIDGE_SCRIPT = `(function(){ if (window.__omEvalBridgeInstalled) return; window.__omEvalBridgeInstalled = true; window.addEventListener('message', function(event) { var message = event && event.data; if (!message || !message.__om_eval || typeof message.code !== 'string') return; if (event.source !== window.parent) return; var targetOrigin = event.origin && event.origin !== 'null' ? event.origin : '*'; var reply = function(payload) { try { event.source.postMessage(Object.assign({ __om_eval_r: true, id: message.id }, payload), targetOrigin); } catch (_) { window.parent.postMessage(Object.assign({ __om_eval_r: true, id: message.id }, payload), '*'); } }; Promise.resolve() .then(function() { return (0, eval)(message.code); }) .then(function(value) { var serialized; if (value !== undefined) { try { serialized = JSON.stringify(value); } catch (_) { serialized = JSON.stringify(String(value)); } } reply(serialized === undefined ? { ok: true } : { ok: true, v: serialized }); }) .catch(function(error) { reply({ ok: false, e: error && error.message ? String(error.message) : String(error) }); }); }); })();`; const DEFAULT_ME = { accountUuid: "12345678", organizationUuid: "87654321", email: "aa@example.com", displayName: "aa", orgName: "aa's Organization", growthbookPayload: "{}", modelPresets: [ { id: DEFAULT_GATEWAY_MODEL, label: "Claude Sonnet 4", maxTokens: 1000000, supportsAdaptiveThinking: true, description: "Most efficient for everyday tasks" }, { id: "claude-3-5-haiku-20241022", label: "Claude Haiku 3.5", maxTokens: 200000, description: "Fast for quick answers" }, { id: "claude-3-5-sonnet-20241022", label: "Claude Sonnet 3.5", maxTokens: 200000, supportsAdaptiveThinking: true, overflow: true }, { id: "claude-3-opus-20240229", label: "Claude Opus 3", maxTokens: 200000, supportsAdaptiveThinking: true, overflow: true } ], defaultModelId: DEFAULT_GATEWAY_MODEL, overrideStickyModel: true, isPersonalOrg: true, accessLevel: "ACCESS_LEVEL_FULL", hasOauthTokens: true, canManageDs: true, memberships: [ { uuid: "123456787", name: "aa's Organization" } ] }; module.exports = { async setup(ctx) { const options = isRecord(ctx.pluginConfig) ? ctx.pluginConfig : {}; const routeHost = stringValue(options.host) || DEFAULT_HOST; const configuredRoutePaths = stringArray(options.paths) || DEFAULT_ROUTE_PATHS; const routePaths = Array.from(new Set([...REQUIRED_ROUTE_PATHS, ...configuredRoutePaths])); const fallbackRouteHosts = normalizeFallbackRouteHosts(options.fallbackHosts, routeHost); const upstreamOrigin = stringValue(options.upstreamOrigin) || DEFAULT_UPSTREAM_ORIGIN; const assetProxy = options.assetProxy !== false; const configuredAssetDir = stringValue(options.assetDir); const claudeAppAssetDir = (configuredAssetDir || options.claudeAppAssets === false) ? "" : resolveClaudeAppIonDistDir(options, ctx.logger); const assetDir = configuredAssetDir || claudeAppAssetDir; const usingClaudeAppAssets = Boolean(claudeAppAssetDir) || Boolean(configuredAssetDir && isClaudeAppIonDistDir(expandHomePath(configuredAssetDir))); const assetSource = usingClaudeAppAssets ? "claude-app" : configuredAssetDir ? "configured" : "none"; const assetPassthrough = usingClaudeAppAssets ? false : options.assetPassthrough === true || (options.assetPassthrough !== false && !localAssetDirExists(assetDir)); if (usingClaudeAppAssets && options.assetPassthrough === true) { ctx.logger.info("Claude Design disabled assetPassthrough because Claude app ion-dist assets must be served locally."); } const configuredScriptPath = normalizePath(stringValue(options.scriptPath) || DEFAULT_SCRIPT_PATH); const configuredStylePath = normalizePath(stringValue(options.stylePath) || DEFAULT_STYLE_PATH); const scriptPath = shouldKeepCurrentScriptPath(configuredScriptPath) ? configuredScriptPath : DEFAULT_SCRIPT_PATH; const stylePath = shouldKeepCurrentStylePath(configuredStylePath) ? configuredStylePath : DEFAULT_STYLE_PATH; const assetAutoUpdate = options.assetAutoUpdate !== false; const autoAnswerQuestions = options.autoAnswerQuestions !== false; const gatewayUrl = stringValue(options.gatewayUrl) || DEFAULT_GATEWAY_URL; const gatewayApiKey = stringValue(options.gatewayApiKey) || configuredGatewayApiKey(ctx.config); const gatewayConfigPath = stringValue(options.gatewayConfigPath) || stringValue(ctx.config?.gateway?.generatedConfigFile) || defaultClaudeDesignGatewayConfigPath(); const gatewayConfig = loadClaudeDesignGatewayConfig(gatewayConfigPath, ctx.logger); const modelSourceConfig = claudeDesignModelSourceConfig(ctx.config, gatewayConfig); const configuredDefaultGatewayModel = normalizeRouteTarget( stringValue(options.defaultGatewayModel) || stringValue(options.gatewayModel) ); const defaultGatewayModel = configuredDefaultGatewayModel || normalizeRouteTarget(stringValue(ctx.config?.Router?.default)) || DEFAULT_GATEWAY_MODEL; const frontendDefaultModel = normalizeRouteTarget( stringValue(options.frontendDefaultModel) || stringValue(options.defaultModelId) || configuredDefaultGatewayModel ) || DEFAULT_GATEWAY_MODEL; const availableProviderNames = claudeDesignProviderSelectorNames(modelSourceConfig); const gatewayModelPresets = claudeDesignGatewayModelPresets(modelSourceConfig, frontendDefaultModel); const routing = normalizeClaudeDesignRouting(options.routing, options); const upstreamOrigins = normalizeUpstreamOrigins(options.upstreamOrigins, upstreamOrigin); const me = normalizeMe(options.me, frontendDefaultModel, gatewayModelPresets); const browserAppUrl = claudeDesignBrowserAppUrl(routeHost, usingClaudeAppAssets); ctx.registerApp({ description: usingClaudeAppAssets ? "Open Claude Design UI through the Claude App shell and CCR gateway." : "Open Claude Design through the CCR browser proxy.", id: "claude-design", name: "Claude Design", url: browserAppUrl }); const store = await ctx.openSqliteStore({ filename: "claude-design.sqlite", migrate(database) { database.run(` CREATE TABLE IF NOT EXISTS claude_design_requests ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TEXT NOT NULL, method TEXT NOT NULL, path TEXT NOT NULL, search TEXT NOT NULL DEFAULT '', request_headers TEXT NOT NULL DEFAULT '{}', request_body TEXT NOT NULL DEFAULT '', response_status INTEGER NOT NULL, response_body TEXT NOT NULL DEFAULT '' ); CREATE INDEX IF NOT EXISTS claude_design_requests_created_at_idx ON claude_design_requests(created_at); CREATE INDEX IF NOT EXISTS claude_design_requests_path_idx ON claude_design_requests(method, path); CREATE TABLE IF NOT EXISTS claude_design_assets ( path TEXT PRIMARY KEY, upstream_url TEXT NOT NULL, content_type TEXT NOT NULL, body_base64 TEXT NOT NULL, fetched_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS claude_design_responses ( method TEXT NOT NULL, path TEXT NOT NULL, status INTEGER NOT NULL DEFAULT 200, headers_json TEXT NOT NULL DEFAULT '{}', body TEXT NOT NULL DEFAULT '{}', updated_at TEXT NOT NULL, PRIMARY KEY (method, path) ); CREATE TABLE IF NOT EXISTS claude_design_items ( collection TEXT NOT NULL, uuid TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, title TEXT NOT NULL, model TEXT NOT NULL, data_json TEXT NOT NULL DEFAULT '{}', messages_json TEXT NOT NULL DEFAULT '[]', PRIMARY KEY (collection, uuid) ); CREATE INDEX IF NOT EXISTS claude_design_items_updated_at_idx ON claude_design_items(collection, updated_at); CREATE TABLE IF NOT EXISTS claude_design_files ( project_id TEXT NOT NULL, path TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, content_type TEXT NOT NULL DEFAULT 'text/plain', body_base64 TEXT NOT NULL DEFAULT '', version INTEGER NOT NULL DEFAULT 1, PRIMARY KEY (project_id, path) ); CREATE INDEX IF NOT EXISTS claude_design_files_project_updated_at_idx ON claude_design_files(project_id, updated_at); `); } }); purgeBadCachedAssets(store); const runtime = { autoAnswerQuestions, assetProxy, assetAutoUpdate, assetDir, assetSource, assetPassthrough, designIndexAssets: { checkedAt: 0, html: "", scriptPath, source: "configured", stylePath }, standaloneDesignIndexAssets: { checkedAt: 0, html: "", scriptPath: DEFAULT_SCRIPT_PATH, source: "default", stylePath: DEFAULT_STYLE_PATH }, gatewayApiKey, gatewayConfigPath, defaultGatewayModel, frontendDefaultModel, gatewayModelPresets, gatewayUrl, logger: ctx.logger, me, routing, availableProviderNames, unavailableRouteTargetWarnings: new Set(), routeHost, fallbackRouteHosts, routePaths, scriptPath, store, stylePath, upstreamOrigin, upstreamOrigins }; const backend = await ctx.registerHttpBackend({ id: "claude-design-mock", async handler(request, response) { await handleMockRequest(runtime, request, response); } }); if (assetPassthrough) { ctx.registerProxyRoute({ host: routeHost, id: "claude-design-assets-passthrough", paths: ["/design/assets", "/assets"], preserveHost: false, upstream: upstreamOrigin }); } if (options.directGatewayRoutes !== false) { for (const pathPrefix of normalizeDirectGatewayRoutePaths(options.directGatewayPaths)) { ctx.registerGatewayRoute({ auth: "none", handler(request, response) { return handleMockRequest(runtime, request, response); }, id: `claude-design-ui-${sanitizeRouteId(pathPrefix)}`, methods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"], pathPrefix }); } } ctx.registerProxyRoute({ host: routeHost, id: "claude-design-proxy", paths: routePaths, preserveHost: true, upstream: backend.url }); for (const fallbackHost of fallbackRouteHosts) { ctx.registerProxyRoute({ host: fallbackHost, id: `claude-design-fallback-${fallbackHost}`, paths: FALLBACK_ROUTE_PATHS, preserveHost: true, upstream: backend.url }); } ctx.registerGatewayRoute({ auth: "none", handler(request, response, helpers) { return handleAdminRequest(runtime, backend, request, response, helpers); }, id: "claude-design-admin", methods: ["DELETE", "GET", "POST", "PUT"], pathPrefix: "/plugins/claude-design" }); ctx.logger.info(`Claude Design mock listening at ${backend.url} for ${routeHost} ${routePaths.join(", ")}`); } }; async function handleMockRequest(runtime, request, response) { const url = new URL(request.url || "/", "http://claude-design.local"); const method = (request.method || "GET").toUpperCase(); const requestBody = await readRequestBody(request); let result; try { if (method === "OPTIONS") { result = { body: "", headers: corsHeaders({ "access-control-max-age": "86400" }), status: 204 }; } else if (method === "HEAD") { const getResult = await routeMockRequest(runtime, "GET", url, request, requestBody); result = { ...getResult, body: "" }; } else { result = await routeMockRequest(runtime, method, url, request, requestBody); } } catch (error) { result = jsonResponse(500, { error: { message: error instanceof Error ? error.message : String(error) } }); } sendResponse(response, result); logRequest(runtime.store, { method, path: url.pathname, requestBody, requestHeaders: request.headers, responseBody: result.body, responseStatus: result.status, search: url.search }); } async function routeMockRequest(runtime, method, url, request, requestBody) { const path = normalizePath(url.pathname); if (method === "GET" && path === "/health") { return jsonResponse(200, { ok: true, plugin: "claude-design" }); } if (isDesignAuthEscapeRoute(path)) { if (method === "GET" || method === "HEAD") { return redirectResponse(302, "/design"); } return jsonResponse(200, authSessionPayload(runtime.me)); } if (method === "GET" && runtime.assetSource === "claude-app" && isDesignSpaRoute(path)) { if (isClaudeAppDesignIframeRequest(url, request)) { const assets = await resolveStandaloneDesignIndexAssets(runtime, request); return htmlResponse(renderDefaultDesignIndex(runtime.me, assets.scriptPath, assets.stylePath)); } const redirectUrl = claudeAppDesignEntrypointRedirectUrl(url); if (redirectUrl) { return redirectResponse(302, redirectUrl); } } if (method === "GET" && runtime.assetSource === "claude-app" && isClaudeAppSpaRoute(path)) { const assets = await resolveDesignIndexAssets(runtime, request); return htmlResponse(renderDesignIndex(runtime.me, assets.scriptPath, assets.stylePath, assets.html)); } if (method === "GET" && (path === "/" || isDesignSpaRoute(path))) { const assets = await resolveDesignIndexAssets(runtime, request); return htmlResponse(renderDesignIndex(runtime.me, assets.scriptPath, assets.stylePath, assets.html)); } if (method === "GET" && path === "/app-unavailable-in-region") { const assets = await resolveDesignIndexAssets(runtime, request); return htmlResponse(renderDesignIndex(runtime.me, assets.scriptPath, assets.stylePath, assets.html)); } if (method === "GET" && path === "/design/favicon.png") { return binaryResponse(200, Buffer.from(FAVICON_PNG_BASE64, "base64"), { "cache-control": "public, max-age=86400", "content-type": "image/png" }); } if (method === "GET" && path === "/design/introvid.html") { return htmlResponse("
\n"); } if (method === "GET" && path.startsWith("/assets/")) { return serveAsset(runtime, `/design${path}`, request); } if (method === "GET" && path.startsWith("/design/assets/")) { return serveAsset(runtime, path, request); } if (method === "GET" && isClaudeAppStaticRoutePath(path)) { return serveAsset(runtime, path, request); } if (method === "GET" && isDesignStaticAsset(path)) { return serveAsset(runtime, path, request); } if (method === "GET" && path.startsWith("/cdn-cgi/")) { return textResponse(200, "/* Cloudflare challenge disabled by Claude Design mock. */\n", { "cache-control": "no-store", "content-type": "application/javascript; charset=utf-8" }); } const storedResponse = readStoredResponse(runtime.store, method, path) || readStoredResponse(runtime.store, "ANY", path); if (storedResponse) { return storedResponse; } if (path.startsWith("/design/anthropic.omelette.api.v1alpha.OmeletteService/")) { return handleOmeletteConnectRpc(runtime, method, path, request, requestBody); } if (path.startsWith("/design/v1/design/") || path.startsWith("/v1/design/")) { return await handleDesignRestApi(runtime, method, path, request, requestBody); } if (isBootstrapRoutePath(path)) { return jsonResponse(200, bootstrapPayload(runtime.me)); } const bootstrapSubresource = handleBootstrapSubresource(runtime, method, path); if (bootstrapSubresource) { return bootstrapSubresource; } if (path === "/api/auth/session" || path === "/api/session") { return jsonResponse(200, authSessionPayload(runtime.me)); } if (path === "/api/me" || path === "/api/user" || path === "/api/account") { return jsonResponse(200, accountPayload(runtime.me)); } if (path === "/api/organizations") { return jsonResponse(200, organizationsPayload(runtime.me)); } if (path.startsWith("/api/organizations/")) { return handleOrganizationApi(runtime, method, path, request, requestBody); } if (path.startsWith("/api/")) { return handleGenericApi(method, path, requestBody); } return jsonResponse(404, { error: { message: `Claude Design mock has no route for ${method} ${path}` } }); } async function handleOmeletteConnectRpc(runtime, method, path, request, requestBody) { if (method !== "POST") { return jsonResponse(405, { error: { message: `Claude Design mock only supports POST for ${path}` } }); } const rpcName = path.split("/").pop(); const isConnectProtoRequest = headerIncludes(request.headers["content-type"], "application/connect+proto"); const rpcBody = rpcName === "Chat" || isConnectProtoRequest ? decodeConnectEnvelope(requestBody) : requestBody; switch (rpcName) { case "CreateProject": { const project = createOmeletteProject(runtime, rpcBody); return protoResponse(encodeCreateProjectResponse(project.uuid)); } case "UpdateProject": updateOmeletteProject(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "DeleteProject": deleteOmeletteProject(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "BundleProject": return protoResponse(bundleProject(runtime, rpcBody)); case "GetProject": { const projectId = decodeProtoStringField(rpcBody, 1); const project = getOmeletteProject(runtime, projectId); return protoResponse(encodeGetProjectResponse(project, runtime.me, getProjectDataBytes(runtime, project.projectId))); } case "GetProjectData": { const projectId = decodeProtoStringField(rpcBody, 1); return protoResponse(encodeGetProjectDataResponse(getProjectDataBytes(runtime, projectId))); } case "UpdateProjectData": return protoResponse(updateProjectDataBytes(runtime, rpcBody)); case "GetChatMessages": return protoResponse(encodeGetChatMessagesResponse(runtime, rpcBody)); case "ListChatsForExport": return protoResponse(listChatsForExport(runtime, rpcBody)); case "ExportChatMessages": return protoResponse(exportChatMessages(runtime, rpcBody)); case "CreateClaudeCodeSession": return protoResponse(createClaudeCodeSession(runtime, rpcBody)); case "UpdateSharing": return protoResponse(updateOmeletteProjectSharing(runtime, rpcBody)); case "DuplicateProject": return protoResponse(duplicateOmeletteProject(runtime, rpcBody, {})); case "RemixProject": return protoResponse(duplicateOmeletteProject(runtime, rpcBody, { includeChats: decodeProtoBoolField(rpcBody, 2) })); case "CreateTemplateFromProject": return protoResponse(duplicateOmeletteProject(runtime, rpcBody, { type: PROJECT_TYPE_TEMPLATE })); case "UpdateProjectType": return protoResponse(updateProjectType(runtime, rpcBody)); case "SetProjectPublished": return protoResponse(setProjectPublished(runtime, rpcBody)); case "UpdateProjectInfo": updateProjectInfo(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "UpdateProjectDesignSystems": return protoResponse(updateProjectDesignSystems(runtime, rpcBody)); case "PatchDesignSystemBinding": return protoResponse(patchDesignSystemBinding(runtime, rpcBody)); case "RefreshBoundDesignSystem": return protoResponse(protoInt32(1, 0)); case "SetProjectFavorite": updateProjectFavorite(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "SetProjectThumbnail": setProjectThumbnail(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "ListFiles": return protoResponse(encodeListFilesResponse(runtime, rpcBody)); case "GetFile": return protoResponse(encodeGetFileResponse(runtime, rpcBody)); case "WriteFiles": return protoResponse(writeProjectFiles(runtime, rpcBody)); case "DeleteFile": return protoResponse(deleteProjectFile(runtime, rpcBody)); case "DeleteFiles": return protoResponse(deleteProjectFiles(runtime, rpcBody)); case "CopyFile": return protoResponse(copyProjectFile(runtime, rpcBody)); case "EditFile": return protoResponse(editProjectFile(runtime, rpcBody)); case "GrepFiles": return protoResponse(grepProjectFiles(runtime, rpcBody)); case "CreateFileStream": return protoResponse(createFileStream(runtime, rpcBody)); case "WriteFileStream": return protoResponse(writeFileStream(runtime, rpcBody)); case "AbortFileStream": return protoResponse(Buffer.alloc(0)); case "UploadFile": { const responseBody = uploadFile(runtime, rpcBody); return isConnectProtoRequest ? connectStreamResponse([responseBody]) : protoResponse(responseBody); } case "ListProjectAssets": return protoResponse(listProjectAssets(runtime, rpcBody)); case "RecordAsset": recordProjectAsset(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "SetAssetStatus": setProjectAssetStatus(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "DeleteAsset": deleteProjectAsset(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "GetMe": return protoResponse(encodeGetMeResponse(runtime.me)); case "GetUsageStatus": return protoResponse(Buffer.alloc(0)); case "UpdateOrgSettings": updateOrgSettings(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "GetOrgSettings": return protoResponse(encodeGetOrgSettingsResponse(runtime)); case "ListOrgProjects": return protoResponse(encodeListProjectsResponse(runtime, decodeProtoEnumField(rpcBody, 1), decodeProtoBoolField(rpcBody, 2))); case "ListProjects": return protoResponse(encodeListProjectsResponse(runtime)); case "MintPreviewToken": return protoResponse(encodeMintPreviewTokenResponse(runtime)); case "MintHandoffToken": case "MintDesignSyncCode": return protoResponse(encodeTokenResponse()); case "CountTokens": return protoResponse(await countGatewayTokens(runtime, rpcBody)); case "Chat": return await chatWithGateway(runtime, rpcBody); case "TrackEvent": recordTrackEvent(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "CreateComment": return protoResponse(createComment(runtime, rpcBody)); case "UpdateComment": return protoResponse(updateComment(runtime, rpcBody)); case "DeleteComment": deleteComment(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "ListComments": return protoResponse(listComments(runtime, rpcBody)); case "CreateCommentReply": return protoResponse(createCommentReply(runtime, rpcBody)); case "UpdateCommentReply": return protoResponse(updateCommentReply(runtime, rpcBody)); case "DeleteCommentReply": deleteCommentReply(runtime, rpcBody); return protoResponse(Buffer.alloc(0)); case "SendCommentsToChat": return protoResponse(sendCommentsToChat(runtime, rpcBody)); case "SendMultiplayerMessage": return protoResponse(sendMultiplayerMessage(runtime, rpcBody)); case "DeleteAccount": case "DeleteOrganization": return protoResponse(Buffer.alloc(0)); case "FigmaCallTool": case "McpCallTool": return protoResponse(encodeToolCallResponse(rpcName, rpcBody)); case "FigmaDisconnect": case "FigmaExchangeCode": case "FigmaStartAuth": case "GithubDisconnect": case "GithubExchangeCode": case "GithubStartAuth": return protoResponse(encodeIntegrationAuthResponse(rpcName)); case "FigmaListTools": case "GithubListRepos": case "GithubGetTree": case "GithubReadFile": case "GithubImportRepo": return protoResponse(encodeIntegrationListResponse(rpcName, rpcBody)); case "RenewTurn": case "ReleaseTurn": return protoResponse(Buffer.alloc(0)); case "MarkCommentsRead": return protoResponse(markCommentsRead(runtime, rpcBody)); case "ListExperiences": case "TrackExperience": case "ExecuteExperienceAction": case "LintFiles": case "FigmaGetStatus": case "GithubGetStatus": case "McpListConnected": case "McpListConnectors": case "McpListDesignImportPartners": case "McpListTools": return protoResponse(Buffer.alloc(0)); default: return protoResponse(Buffer.alloc(0)); } } async function serveAsset(runtime, path, request) { const cached = readCachedAsset(runtime.store, path); if (cached) { const cachedBody = Buffer.from(cached.bodyBase64, "base64"); const reusableFallback = isReusableFallbackAsset(cached, path); if ((!isFallbackAsset(cached) || reusableFallback) && isUsableServedAssetBody(path, cached.contentType, cachedBody)) { if (!reusableFallback) { recordDesignIndexAssetHint(runtime, path, "cache"); } return binaryResponse(200, cachedBody, { "cache-control": reusableFallback ? "no-store" : "public, max-age=86400", "content-type": cached.contentType, "x-claude-design-asset-source": cached.upstreamUrl || "cache" }); } deleteCachedAsset(runtime.store, path); } const localAsset = readLocalAsset(runtime.assetDir, path); if (localAsset && isUsableServedAssetBody(path, localAsset.contentType, localAsset.body)) { writeCachedAsset(runtime.store, path, localAsset.source, localAsset.contentType, localAsset.body); recordDesignIndexAssetHint(runtime, path, "local"); return binaryResponse(200, localAsset.body, { "cache-control": "public, max-age=86400", "content-type": localAsset.contentType, "x-claude-design-asset-source": "local" }); } if (runtime.assetProxy) { for (const origin of runtime.upstreamOrigins) { for (const upstreamUrl of upstreamAssetUrlCandidates(path, origin)) { const fetched = await fetchUpstreamAsset(upstreamUrl, request).catch((error) => { runtime.logger?.warn?.( `Claude Design failed to fetch upstream asset ${upstreamUrl.toString()}: ${ error instanceof Error ? error.message : String(error) }` ); return undefined; }); if (fetched && fetched.status >= 200 && fetched.status < 300 && isUsableServedAssetBody(path, fetched.contentType, fetched.body)) { writeCachedAsset(runtime.store, path, fetched.url || upstreamUrl.toString(), fetched.contentType, fetched.body); recordDesignIndexAssetHint(runtime, path, "remote"); return binaryResponse(200, fetched.body, { "cache-control": "public, max-age=86400", "content-type": fetched.contentType, "x-claude-design-asset-source": fetched.url || upstreamUrl.toString() }); } if (fetched) { logRejectedUpstreamAsset(runtime, path, upstreamUrl, fetched); } } } } const refreshedIndexAsset = await serveRefreshedDesignIndexAsset(runtime, path, request); if (refreshedIndexAsset) { return refreshedIndexAsset; } if (path === "/design/pictogram-bookapple.svg") { return textResponse(200, TRANSPARENT_SVG, { "cache-control": "public, max-age=86400", "content-type": "image/svg+xml" }); } const fallback = fallbackDesignAsset(path); if (fallback) { if (isCacheableFallbackAsset(path)) { writeCachedAsset(runtime.store, path, fallbackAssetCacheSource(path), fallback.contentType, fallback.body); } return binaryResponse(200, fallback.body, { "cache-control": "no-store", "content-type": fallback.contentType, "x-claude-design-asset-source": "fallback" }); } return jsonResponse(502, { error: { message: `Claude Design asset is not cached and upstream fetch failed: ${path}`, hint: "Provide the real Claude Design asset through config.assetDir or allow upstream asset proxying." } }); } function logRejectedUpstreamAsset(runtime, path, upstreamUrl, fetched) { const message = `Claude Design rejected upstream asset ${upstreamUrl.toString()}: status=${fetched.status} content-type=${ fetched.contentType || "unknown" } bytes=${fetched.body.length} final-url=${fetched.url || upstreamUrl.toString()}`; if (isCacheableFallbackAsset(path)) { runtime.logger?.debug?.(`${message}; using local fallback`); return; } runtime.logger?.warn?.(message); } async function serveRefreshedDesignIndexAsset(runtime, path, request) { const requestPath = normalizePath(path); const isScript = isDesignIndexScriptPath(requestPath); const isStyle = isDesignIndexStylePath(requestPath); if (!runtime.assetAutoUpdate || (!isScript && !isStyle)) { return undefined; } const refreshed = await resolveDesignIndexAssets(runtime, request, { force: true }); const replacementPath = isScript ? refreshed.scriptPath : refreshed.stylePath; if (!replacementPath || replacementPath === requestPath) { return undefined; } const replacement = readResolvedDesignIndexAsset(runtime, replacementPath); if (!replacement || !isUsableDesignIndexAssetResponse(replacementPath, replacement.contentType, replacement.body)) { return undefined; } return binaryResponse(200, replacement.body, { "cache-control": "no-store", "content-type": replacement.contentType, "x-claude-design-asset-source": `${replacement.source}; replacement=${replacementPath}` }); } function readResolvedDesignIndexAsset(runtime, path) { const cached = runtime.store ? readCachedAsset(runtime.store, path) : undefined; if (cached) { return { body: Buffer.from(cached.bodyBase64 || "", "base64"), contentType: cached.contentType, source: cached.upstreamUrl || "cache" }; } const local = readLocalAsset(runtime.assetDir, path); return local ? { body: local.body, contentType: local.contentType, source: local.source } : undefined; } function fallbackDesignAsset(path) { if (!path.startsWith("/design/")) { return undefined; } if (!path.startsWith("/design/assets/") && !/\.(?:gif|png|webp|jpe?g|svg|ico)$/i.test(path)) { return undefined; } if (/\/QuestionsViewer-[^/]+\.js$/i.test(path)) { return { body: Buffer.from(questionsViewerFallbackModule(), "utf8"), contentType: "application/javascript; charset=utf-8" }; } if (isDesignIndexScriptPath(path)) { return undefined; } if (path.endsWith(".js")) { return { body: Buffer.from( [ "export function HandoffModal(){ return null; }", "export default function ClaudeDesignMissingChunk(){ return null; }" ].join("\n"), "utf8" ), contentType: "application/javascript; charset=utf-8" }; } if (path.endsWith(".css")) { return { body: Buffer.alloc(0), contentType: "text/css; charset=utf-8" }; } if (/\.(?:gif|png|webp|jpe?g|svg|ico)$/i.test(path)) { return { body: Buffer.from(TRANSPARENT_SVG, "utf8"), contentType: "image/svg+xml" }; } return undefined; } function isCacheableFallbackAsset(path) { return path.startsWith("/design/assets/") && !isDesignIndexScriptPath(path) && !isDesignIndexStylePath(path) && /\.(?:js|mjs|gif|png|webp|jpe?g|svg|ico)$/i.test(path); } function fallbackAssetCacheSource(path) { return `${FALLBACK_ASSET_CACHE_PREFIX}${path}`; } function isUsableAssetBody(path, contentType, body) { const payload = Buffer.isBuffer(body) ? body : Buffer.from(body || []); const normalizedContentType = stringValue(contentType)?.toLowerCase() || ""; if (payload.length === 0 && /\.(?:js|mjs|css|json|wasm|png|jpe?g|gif|webp|svg|ico)$/i.test(path)) { return false; } if (normalizedContentType.includes("text/html") && !/\.html?$/i.test(path)) { return false; } if (/\.(?:js|mjs)$/i.test(path)) { return !normalizedContentType.includes("text/html"); } if (/\.css$/i.test(path)) { return !normalizedContentType.includes("text/html"); } return true; } function isUsableServedAssetBody(path, contentType, body) { if (!isUsableAssetBody(path, contentType, body)) { return false; } if (isDesignIndexScriptPath(path)) { return isUsableDesignIndexScriptBody(path, contentType, body); } return true; } function normalizeUpstreamOrigins(value, primaryOrigin) { const origins = []; const addOrigin = (origin) => { const raw = stringValue(origin); if (!raw) { return; } try { const parsed = new URL(raw); const normalized = `${parsed.protocol}//${parsed.host}`; if (!origins.includes(normalized)) { origins.push(normalized); } } catch { // Ignore malformed source origins; they cannot be used for asset proxying. } }; addOrigin(primaryOrigin); if (Array.isArray(value)) { value.forEach(addOrigin); } else { addOrigin(value); } return origins.length > 0 ? origins : [DEFAULT_UPSTREAM_ORIGIN]; } function upstreamAssetUrlCandidates(path, origin) { const requestPath = normalizePath(path); const candidates = [new URL(requestPath, origin)]; if (requestPath.startsWith("/design/assets/")) { candidates.push(new URL(requestPath.replace(/^\/design\/assets\//, "/assets/"), origin)); const assetName = requestPath.split("/").pop(); if (assetName) { for (const baseUrl of DEFAULT_EXTERNAL_ASSET_BASE_URLS) { candidates.push(new URL(assetName, baseUrl)); } } } const seen = new Set(); return candidates.filter((url) => { const key = url.toString(); if (seen.has(key)) { return false; } seen.add(key); return true; }); } function normalizeFallbackRouteHosts(value, primaryHost) { const hosts = []; const addHost = (host) => { const raw = stringValue(host); if (!raw) { return; } const normalized = raw.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").toLowerCase(); if (normalized && normalized !== primaryHost.toLowerCase() && !hosts.includes(normalized)) { hosts.push(normalized); } }; DEFAULT_FALLBACK_ROUTE_HOSTS.forEach(addHost); if (Array.isArray(value)) { value.forEach(addHost); } else { addHost(value); } return hosts; } function claudeDesignBrowserAppUrl(routeHost, usingClaudeAppAssets) { if (!usingClaudeAppAssets) { return `https://${routeHost}/design`; } const targetPath = withClaudeAppDesignIframeMarker("/design"); return `https://${routeHost}${CLAUDE_APP_DESIGN_SHELL_PATH}?${CLAUDE_APP_DESIGN_PATH_QUERY}=${encodeURIComponent(targetPath)}`; } function normalizeDirectGatewayRoutePaths(value) { const configured = stringArray(value); const paths = configured?.length ? configured : DEFAULT_DIRECT_GATEWAY_ROUTE_PATHS; return Array.from(new Set(paths.map(normalizePath).filter(Boolean).filter((path) => path !== "/"))); } function sanitizeRouteId(value) { return String(value || "") .replace(/[^a-zA-Z0-9]+/g, "-") .replace(/^-+|-+$/g, "") || "root"; } async function resolveDesignIndexAssets(runtime, request, options = {}) { const current = runtime.designIndexAssets || { checkedAt: 0, html: "", scriptPath: runtime.scriptPath || DEFAULT_SCRIPT_PATH, source: "default", stylePath: runtime.stylePath || DEFAULT_STYLE_PATH }; if (!runtime.assetAutoUpdate) { return current; } const now = Date.now(); const forceRefresh = options.force === true || requestHasNoCache(request); const hasRenderableIndex = isUsableDesignShellHtml(current.html) || Boolean(current.scriptPath || current.stylePath); if (!forceRefresh && hasRenderableIndex && current.checkedAt && now - current.checkedAt < DESIGN_INDEX_ASSET_DISCOVERY_TTL_MS) { return current; } const fromRequests = discoverRequestedDesignIndexAssets(runtime.store); const fromLocal = discoverLocalDesignIndexAssets(runtime.assetDir); const fromCache = discoverCachedDesignIndexAssets(runtime.store); const shouldDiscoverRemote = runtime.assetSource !== "claude-app"; const fromRemote = shouldDiscoverRemote ? await discoverRemoteDesignIndexAssets(runtime, request) : undefined; const fromRemoteCache = shouldDiscoverRemote ? await cacheRemoteDesignIndexAssets(runtime, fromRemote, request) : undefined; const fallback = mergeDesignIndexAssetPartials(fromLocal, fromCache) || {}; const seeded = { html: isUsableDesignShellHtml(fromLocal?.html) ? fromLocal.html : current.html, scriptPath: fallback.scriptPath || current.scriptPath, source: fallback.source || current.source || "current", stylePath: fallback.stylePath || current.stylePath }; const discovered = mergeDesignIndexAssets(seeded, fromRequests, fromRemoteCache || fromRemote, fromLocal); const safeDiscovered = selectUsableDesignIndexAssets(runtime, discovered, fallback, current); return updateDesignIndexAssets(runtime, safeDiscovered, safeDiscovered.source || "discovered", { checkedAt: now }); } async function resolveStandaloneDesignIndexAssets(runtime, request) { if (runtime.assetSource !== "claude-app") { return resolveDesignIndexAssets(runtime, request); } const current = runtime.standaloneDesignIndexAssets || { checkedAt: 0, html: "", scriptPath: DEFAULT_SCRIPT_PATH, source: "default", stylePath: DEFAULT_STYLE_PATH }; if (!runtime.assetAutoUpdate) { return current; } const now = Date.now(); const forceRefresh = requestHasNoCache(request); if (!forceRefresh && current.checkedAt && now - current.checkedAt < DESIGN_INDEX_ASSET_DISCOVERY_TTL_MS) { return current; } const fromCache = discoverCachedStandaloneDesignIndexAssets(runtime.store); const fromRemote = await discoverRemoteDesignIndexAssets(runtime, request); const fromRemoteCache = await cacheRemoteDesignIndexAssets(runtime, fromRemote, request); const discovered = mergeDesignIndexAssets(current, fromCache, fromRemoteCache || fromRemote); const assets = { checkedAt: now, html: "", scriptPath: shouldKeepCurrentScriptPath(discovered.scriptPath) ? discovered.scriptPath : DEFAULT_SCRIPT_PATH, source: discovered.source || current.source || "standalone", stylePath: shouldKeepCurrentStylePath(discovered.stylePath) ? discovered.stylePath : DEFAULT_STYLE_PATH, upstreamUrls: { ...(current.upstreamUrls || {}), ...(discovered.upstreamUrls || {}) } }; runtime.standaloneDesignIndexAssets = assets; return assets; } function requestHasNoCache(request) { return headerIncludes(request?.headers?.["cache-control"], "no-cache") || headerIncludes(request?.headers?.pragma, "no-cache"); } function updateDesignIndexAssets(runtime, assets, source, options = {}) { const current = runtime.designIndexAssets || { checkedAt: 0, html: "", scriptPath: runtime.scriptPath || DEFAULT_SCRIPT_PATH, source: "default", stylePath: runtime.stylePath || DEFAULT_STYLE_PATH }; const candidateScriptPath = normalizePath(stringValue(assets?.scriptPath) || current.scriptPath || DEFAULT_SCRIPT_PATH); const candidateStylePath = normalizePath(stringValue(assets?.stylePath) || current.stylePath || DEFAULT_STYLE_PATH); const scriptPath = shouldKeepCurrentScriptPath(candidateScriptPath) ? candidateScriptPath : DEFAULT_SCRIPT_PATH; const stylePath = shouldKeepCurrentStylePath(candidateStylePath) ? candidateStylePath : DEFAULT_STYLE_PATH; const html = isUsableDesignShellHtml(assets?.html) ? String(assets.html) : isUsableDesignShellHtml(current.html) ? String(current.html) : ""; runtime.scriptPath = scriptPath; runtime.stylePath = stylePath; runtime.designIndexAssets = { checkedAt: options.checkedAt ?? Date.now(), html, scriptPath, source, stylePath, upstreamUrls: { ...(current.upstreamUrls || {}), ...(assets?.upstreamUrls || {}) } }; return runtime.designIndexAssets; } function mergeDesignIndexAssets(current, ...candidates) { const merged = { html: isUsableDesignShellHtml(current.html) ? String(current.html) : "", scriptPath: shouldKeepCurrentScriptPath(current.scriptPath) ? current.scriptPath : DEFAULT_SCRIPT_PATH, source: current.source || "current", stylePath: shouldKeepCurrentStylePath(current.stylePath) ? current.stylePath : DEFAULT_STYLE_PATH, upstreamUrls: { ...(current.upstreamUrls || {}) } }; for (const candidate of candidates) { if (!candidate) { continue; } if (candidate.upstreamUrls) { merged.upstreamUrls = { ...merged.upstreamUrls, ...candidate.upstreamUrls }; } if (isUsableDesignShellHtml(candidate.html)) { merged.html = String(candidate.html); merged.source = candidate.source || merged.source; } if (candidate.scriptPath) { merged.scriptPath = candidate.scriptPath; merged.source = candidate.source || merged.source; } if (candidate.stylePath) { merged.stylePath = candidate.stylePath; merged.source = candidate.source || merged.source; } } return merged; } function mergeDesignIndexAssetPartials(...candidates) { const merged = { source: "remote", upstreamUrls: {} }; for (const candidate of candidates) { if (!candidate) { continue; } if (candidate.upstreamUrls) { merged.upstreamUrls = { ...merged.upstreamUrls, ...candidate.upstreamUrls }; } if (candidate.scriptPath) { merged.scriptPath = candidate.scriptPath; merged.source = candidate.source || merged.source; } if (candidate.stylePath) { merged.stylePath = candidate.stylePath; merged.source = candidate.source || merged.source; } } return merged.scriptPath || merged.stylePath ? merged : undefined; } function selectUsableDesignIndexAssets(runtime, discovered, fallback, current) { const currentScriptPath = shouldKeepCurrentScriptPath(current.scriptPath) ? current.scriptPath : DEFAULT_SCRIPT_PATH; const currentStylePath = shouldKeepCurrentStylePath(current.stylePath) ? current.stylePath : DEFAULT_STYLE_PATH; const html = isUsableDesignShellHtml(discovered.html) ? discovered.html : isUsableDesignShellHtml(current.html) ? current.html : ""; const scriptPath = isUsableDesignIndexScript(runtime, discovered.scriptPath) ? discovered.scriptPath : isUsableDesignIndexScript(runtime, fallback.scriptPath) ? fallback.scriptPath : currentScriptPath; const stylePath = isUsableDesignIndexStyle(runtime, discovered.stylePath) ? discovered.stylePath : isUsableDesignIndexStyle(runtime, fallback.stylePath) ? fallback.stylePath : currentStylePath; const source = scriptPath === discovered.scriptPath || stylePath === discovered.stylePath ? discovered.source : fallback.source || current.source || "current"; return { html, scriptPath, source, stylePath }; } function recordDesignIndexAssetHint(runtime, path, source) { if (!runtime.assetAutoUpdate) { return; } const normalizedPath = normalizePath(path); if (isDesignIndexScriptPath(normalizedPath)) { if (!LEGACY_SCRIPT_PATHS.has(normalizedPath) && isAcceptableRequestedEntryScript(runtime, normalizedPath)) { updateDesignIndexAssets(runtime, { scriptPath: normalizedPath }, source); } return; } if (isDesignIndexStylePath(normalizedPath)) { if (!LEGACY_STYLE_PATHS.has(normalizedPath)) { updateDesignIndexAssets(runtime, { stylePath: normalizedPath }, source); } } } async function discoverRemoteDesignIndexAssets(runtime, request) { for (const origin of runtime.upstreamOrigins) { for (const shellUrl of upstreamDesignShellUrlCandidates(origin, runtime)) { const shell = await fetchUpstreamDesignShell(shellUrl, request).catch((error) => { runtime.logger?.warn?.( `Claude Design failed to discover upstream design assets from ${shellUrl.toString()}: ${ error instanceof Error ? error.message : String(error) }` ); return undefined; }); if (!shell) { continue; } logRemoteDesignShell(runtime, shellUrl, shell); const assets = mergeDesignIndexAssetPartials( extractDesignIndexAssetsFromLinkHeaders(shell.linkHeaders, shell.url), extractDesignIndexAssetsFromHtml(shell.body, shell.url) ); const html = isUsableDesignShellHtml(shell.body) ? shell.body : ""; if (html || assets?.scriptPath || assets?.stylePath) { return { html, origin, scriptPath: assets?.scriptPath, source: "remote", stylePath: assets?.stylePath, upstreamUrls: assets?.upstreamUrls }; } } } return undefined; } function logRemoteDesignShell(runtime, shellUrl, shell) { const summary = summarizeRemoteDesignShell(shell); runtime.logger?.warn?.( [ "Claude Design upstream /design raw HTML:", `request-url=${shellUrl.toString()}`, `final-url=${shell.url || shellUrl.toString()}`, `status=${shell.status}`, `content-type=${shell.contentType || "unknown"}`, "----- BEGIN RAW HTML -----", shell.body || "", "----- END RAW HTML -----", "Claude Design upstream /design raw HTML summary:", `title=${summary.title || "unknown"}`, `has-assets-proxy=${summary.hasAssetsProxy}`, `has-design-assets=${summary.hasDesignAssets}`, `has-app-unavailable=${summary.hasAppUnavailable}`, `has-cloudflare-challenge=${summary.hasCloudflareChallenge}`, `module-scripts=${summary.moduleScripts.join(", ") || "none"}`, `stylesheets=${summary.stylesheets.join(", ") || "none"}` ].join("\n") ); } function summarizeRemoteDesignShell(shell) { const html = String(shell?.body || ""); return { hasAppUnavailable: /App unavailable in region/i.test(html), hasAssetsProxy: /https:\/\/assets-proxy\.anthropic\.com\/claude-ai\/v2\/assets\/v1\//i.test(html), hasCloudflareChallenge: /\bJust a moment\b|cf_chl|Performing security verification/i.test(html), hasDesignAssets: /(?:src|href)=["'][^"']*(?:\/design)?\/assets\//i.test(html), moduleScripts: firstMatches(html, /`; if (/]*>/i.test(source)) { return source.replace(/]*)>/i, (tag) => `${tag}\n${scriptTag}`); } if (/]*>/i.test(source)) { return source.replace(/]*)>/i, (tag) => `${tag}\n${scriptTag}`); } return `${scriptTag}\n${source}`; } function normalizeBabelJsxScriptTags(html) { if (!/(?:@babel\/standalone|babel\.min\.js|type=["']text\/babel["'])/i.test(html)) { return html; } return String(html).replace(/`; }); } function resolveLocalProjectScriptPath(filePath, src) { const rawSrc = String(src || "").trim(); if (!rawSrc || rawSrc.startsWith("/") || rawSrc.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(rawSrc)) { return ""; } const pathOnly = rawSrc.split("#", 1)[0].split("?", 1)[0]; let decodedPath = pathOnly; try { decodedPath = decodeURIComponent(pathOnly); } catch { decodedPath = pathOnly; } const baseDir = pathModule.posix.dirname(sanitizeProjectFilePath(filePath)); return sanitizeProjectFilePath(pathModule.posix.join(baseDir === "." ? "" : baseDir, decodedPath)); } function escapeInlineScriptBody(body) { return String(body || "").replace(/<\/script/gi, "<\\/script"); } function titleFromTurnMessage(message) { const text = String(message || "") .replace(/\s+/g, " ") .trim(); if (!text) { return "New turn"; } if (/questions timed out/i.test(text)) { return "Continue with defaults"; } return text.length > 60 ? `${text.slice(0, 57).trim()}...` : text; } async function countGatewayTokens(runtime, requestBody) { const messagesRequest = decodeProtoBytesField(requestBody, 1) || Buffer.alloc(0); const body = parseGatewayMessagesRequestBuffer(messagesRequest) || extractGatewayMessagesRequestFromProto(requestBody) || {}; const normalized = normalizeGatewayMessagesRequest(runtime, body); const routingDecision = applyClaudeDesignRouting(runtime, normalized); try { const response = await fetchGateway(runtime, "/v1/messages/count_tokens", normalized, routingDecision); if (response.ok) { const payload = await response.json(); return protoInt32(1, Number(payload.input_tokens) || estimateTokenCount(normalized)); } } catch { // Fall through to local estimate. } return protoInt32(1, estimateTokenCount(normalized)); } async function chatWithGateway(runtime, requestBody) { const projectId = decodeProtoStringField(requestBody, 1) || ""; const chatId = decodeProtoStringField(requestBody, 3) || (projectId ? defaultProjectChatId(projectId) : randomUuid()); const request = { assistantMessageId: decodeProtoStringField(requestBody, 4) || randomUuid(), chatId, messagesRequest: decodeProtoBytesField(requestBody, 2) || Buffer.alloc(0), projectId }; const originalBody = parseGatewayMessagesRequestBuffer(request.messagesRequest) || extractGatewayMessagesRequestFromProto(requestBody) || gatewayMessagesRequestFromProjectChat(runtime, request) || {}; if (!hasGatewayMessagesInput(originalBody)) { return connectStreamResponse([ encodeChatResponseMessageStart(request.assistantMessageId), encodeChatResponseError("Claude Design chat request did not include a messages payload.") ]); } const gatewayBody = normalizeGatewayMessagesRequest(runtime, { ...originalBody, stream: false }); const routingDecision = applyClaudeDesignRouting(runtime, gatewayBody); const sessionContext = claudeDesignSessionContext(request); let assistantText = ""; let toolCalls = []; let stopReason = "end_turn"; let messageId = request.assistantMessageId; let model = gatewayBody.model || runtime.me.defaultModelId; const events = [encodeChatResponseMessageStart(messageId), encodeChatResponseRaw("message_start", { message: { content: [], id: messageId, model, role: "assistant" } })]; try { const response = await fetchGateway(runtime, "/v1/messages", gatewayBody, routingDecision, sessionContext); const payloadText = await response.text(); const payload = parseMaybeJson(payloadText, {}); if (!response.ok) { const message = payload?.error?.message || payloadText || `Gateway request failed with HTTP ${response.status}`; events.push(encodeChatResponseError(message)); return connectStreamResponse(events); } assistantText = extractGatewayAssistantText(payload); toolCalls = extractGatewayToolCalls(payload); const gatewayStopReason = extractGatewayStopReason(payload, stopReason); if (runtime.autoAnswerQuestions) { const absorbed = absorbQuestionToolCalls(assistantText, toolCalls); assistantText = absorbed.assistantText; toolCalls = absorbed.toolCalls; } stopReason = toolCalls.length > 0 ? "tool_use" : gatewayStopReason === "tool_use" ? "end_turn" : gatewayStopReason; messageId = stringValue(payload.id) || messageId; model = publicGatewayModelSelector(routingDecision.routedModel || gatewayBody.model || model) || model; } catch (error) { events.push(encodeChatResponseError(error instanceof Error ? error.message : String(error))); return connectStreamResponse(events); } if (!assistantText && toolCalls.length === 0) { assistantText = "Claude Design gateway returned an empty response."; } if (assistantText) { events.push(encodeChatResponseTextDelta(assistantText)); events.push(encodeChatResponseRaw("text_block", { index: 0, text: assistantText })); } const firstToolIndex = assistantText ? 1 : 0; toolCalls.forEach((toolCall, index) => { const blockIndex = firstToolIndex + index; events.push( encodeChatResponseRaw("tool_delta", { id: toolCall.id, index: blockIndex, partial_json: JSON.stringify(toolCall.input || {}), tool: toolCall.name }) ); events.push( encodeChatResponseRaw("tool_block_complete", { id: toolCall.id, index: blockIndex, input: toolCall.input || {}, name: toolCall.name }) ); }); const messageContent = gatewayMessageContent(assistantText, toolCalls); events.push( encodeChatResponseRaw("done", { message: { content: messageContent, id: messageId, model, role: "assistant", stop_reason: stopReason } }) ); events.push(encodeChatResponseMessageStop(stopReason)); appendChatExchange(runtime, request, gatewayBody, assistantText, messageId, gatewayContentBlocks(assistantText, toolCalls)); return connectStreamResponse(events); } function parseGatewayMessagesRequestBuffer(buffer) { if (!Buffer.isBuffer(buffer) || buffer.length === 0) { return undefined; } const text = buffer.toString("utf8").trim(); if (!text || !text.startsWith("{")) { return undefined; } const body = parseMaybeJson(text, undefined); return hasGatewayMessagesInput(body) ? body : undefined; } function extractGatewayMessagesRequestFromProto(buffer, depth = 0, seen = new Set()) { if (!Buffer.isBuffer(buffer) || buffer.length === 0 || depth > 6 || seen.has(buffer)) { return undefined; } seen.add(buffer); const direct = parseGatewayMessagesRequestBuffer(buffer); if (direct) { return direct; } let found; forEachProtoField(buffer, (field) => { if (found || field.wireType !== 2 || !Buffer.isBuffer(field.value) || field.value.length === 0) { return; } const directValue = parseGatewayMessagesRequestBuffer(field.value); if (directValue) { found = directValue; return; } const textValue = field.value.toString("utf8"); const embeddedValue = extractGatewayMessagesRequestFromText(textValue); if (embeddedValue) { found = embeddedValue; return; } if (field.value.length <= 512 * 1024) { found = extractGatewayMessagesRequestFromProto(field.value, depth + 1, seen); } }); return found; } function extractGatewayMessagesRequestFromText(text) { if (typeof text !== "string" || !text.includes("messages")) { return undefined; } const start = text.indexOf("{"); const end = text.lastIndexOf("}"); if (start < 0 || end <= start) { return undefined; } const body = parseMaybeJson(text.slice(start, end + 1), undefined); return hasGatewayMessagesInput(body) ? body : undefined; } function hasGatewayMessagesInput(body) { if (!isRecord(body)) { return false; } if (stringValue(body.message) || stringValue(body.prompt)) { return true; } if (!Array.isArray(body.messages) || body.messages.length === 0) { return false; } return body.messages.some((message) => { if (!isRecord(message)) { return false; } return Boolean(stringValue(message.content) || storedProjectMessageText(message) || (Array.isArray(message.content) && message.content.some((block) => typeof block === "string" || (isRecord(block) && (stringValue(block.text) || stringValue(block.content) || stringValue(block.output_text))) ))); }); } function gatewayMessagesRequestFromProjectChat(runtime, request) { const row = getProjectRow(runtime, request.projectId); if (!row) { return undefined; } const data = normalizedProjectDataFromRow(runtime, row); const chatId = request.chatId || stringValue(data.viewState?.activeChatId) || defaultProjectChatId(row.uuid); const chat = isRecord(data.chats) ? data.chats[chatId] : undefined; const messages = Array.isArray(chat?.messages) ? chat.messages : []; const gatewayMessages = messages .map(gatewayMessageFromStoredProjectMessage) .filter(Boolean); return gatewayMessages.length > 0 ? { messages: gatewayMessages, model: runtime.me.defaultModelId } : undefined; } function normalizedProjectDataFromRow(runtime, row) { const record = parseMaybeJson(row?.data_json, {}); const base64 = stringValue(record.project_data_base64); const decoded = base64 ? parseMaybeJson(Buffer.from(base64, "base64").toString("utf8"), record) : record; return normalizeProjectStoreData(runtime, row, decoded); } function gatewayMessageFromStoredProjectMessage(message) { if (!isRecord(message)) { return undefined; } const role = stringValue(message.role); if (role !== "user" && role !== "assistant") { return undefined; } const content = cleanVisibleUserText(storedProjectMessageText(message)); if (!content) { return undefined; } return { content, role }; } function storedProjectMessageText(message) { const parts = []; const content = stringValue(message.content); if (content) { parts.push(content); } const blocks = Array.isArray(message.contentBlocks) ? message.contentBlocks : Array.isArray(message.content_blocks) ? message.content_blocks : undefined; const blockText = textFromMessageContent(blocks); if (blockText) { parts.push(blockText); } if (Array.isArray(message.attachments)) { for (const attachment of message.attachments) { if (!isRecord(attachment)) { continue; } const attachmentContent = stringValue(attachment.content) || stringValue(attachment.text); if (!attachmentContent) { continue; } const attachmentName = stringValue(attachment.name); parts.push(attachmentName ? `${attachmentName}\n${attachmentContent}` : attachmentContent); } } return parts.join("\n\n"); } function normalizeGatewayMessagesRequest(runtime, body) { const record = isRecord(body) ? body : {}; const text = stringValue(record.message) || stringValue(record.prompt) || ""; const normalizedMessages = Array.isArray(record.messages) ? normalizeGatewayMessagesForRequest(record.messages) : []; const messages = normalizedMessages.length > 0 ? normalizedMessages : text ? [{ content: text, role: "user" }] : [{ content: "Continue.", role: "user" }]; const maxTokens = Math.max(128, Number(record.max_tokens || record.maxTokens) || 4096); return { ...record, max_tokens: maxTokens, messages, model: publicGatewayModelSelector(stringValue(record.model) || runtime.me.defaultModelId) || runtime.me.defaultModelId, stream: false }; } function normalizeGatewayMessagesForRequest(messages) { return messages .map((message) => { if (!isRecord(message)) { return undefined; } const role = stringValue(message.role) || "user"; if (role !== "system" && role !== "user" && role !== "assistant") { return undefined; } if (stringValue(message.content) || Array.isArray(message.content)) { return { ...message, role }; } const content = cleanVisibleUserText(storedProjectMessageText(message)); return content ? { content, role } : undefined; }) .filter(Boolean); } function applyClaudeDesignRouting(runtime, body) { const requestedModel = stringValue(body?.model) || runtime.me.defaultModelId; const routing = runtime.routing; if (!routing || routing.enabled === false) { return { requestedModel }; } const route = resolveClaudeDesignRoute(routing, body, requestedModel, runtime.defaultGatewayModel, runtime.gatewayModelPresets); if (!route?.target) { return { requestedModel }; } const target = usableClaudeDesignRouteTarget(runtime, route.target); if (!target) { return { requestedModel }; } body.model = target; return { requestedModel, reason: target === route.target ? route.reason : `${route.reason}:fallback`, routedModel: target }; } function resolveClaudeDesignRoute(routing, body, requestedModel, defaultGatewayModel, gatewayModelPresets) { for (const rule of routing.rules || []) { if (rule.enabled === false || !rule.target) { continue; } if (matchesClaudeDesignRouteRule(rule, body, requestedModel)) { return { reason: rule.id ? `plugin-rule:${rule.id}` : `plugin-rule:${rule.type}`, target: rule.target }; } } const aliasedModel = claudeDesignModelAliasTarget(requestedModel, defaultGatewayModel); if (aliasedModel) { return { reason: "plugin-model-alias", target: aliasedModel }; } if (routing.defaultTarget && !isKnownClaudeDesignGatewayModel(gatewayModelPresets, requestedModel)) { return { reason: "plugin-default", target: routing.defaultTarget }; } return undefined; } function claudeDesignModelAliasTarget(requestedModel, defaultGatewayModel) { const normalizedModel = stringValue(requestedModel); if (!normalizedModel) { return undefined; } const target = CLAUDE_DESIGN_MODEL_ALIASES.get(normalizedModel); if (!target) { return undefined; } return normalizeRouteTarget(defaultGatewayModel) || target; } function isKnownClaudeDesignGatewayModel(gatewayModelPresets, requestedModel) { const publicModel = publicGatewayModelSelector(requestedModel); if (!publicModel || !Array.isArray(gatewayModelPresets)) { return false; } return gatewayModelPresets.some((preset) => isRecord(preset) && stringValue(preset.id)?.toLowerCase() === publicModel.toLowerCase()); } function usableClaudeDesignRouteTarget(runtime, target) { const normalized = normalizeRouteTarget(target); if (!normalized) { return undefined; } if (routeTargetProviderAvailable(runtime.availableProviderNames, normalized)) { return normalized; } const fallback = usableClaudeDesignFallbackTarget(runtime); warnUnavailableClaudeDesignRouteTarget(runtime, normalized, fallback); return fallback; } function usableClaudeDesignFallbackTarget(runtime) { const configured = normalizeRouteTarget(runtime?.defaultGatewayModel); if (configured && routeTargetProviderAvailable(runtime?.availableProviderNames, configured)) { return configured; } return DEFAULT_GATEWAY_MODEL; } function routeTargetProviderAvailable(availableProviderNames, target) { if (!(availableProviderNames instanceof Set) || availableProviderNames.size === 0) { return true; } const provider = routeTargetProviderName(target); return !provider || availableProviderNames.has(provider.toLowerCase()); } function routeTargetProviderName(target) { const normalized = normalizeRouteTarget(target); if (!normalized) { return undefined; } const separator = normalized.indexOf("/"); if (separator <= 0 || separator >= normalized.length - 1) { return undefined; } return normalized.slice(0, separator).trim(); } function warnUnavailableClaudeDesignRouteTarget(runtime, target, fallback) { const key = `${target}\n${fallback}`; if (runtime?.unavailableRouteTargetWarnings?.has(key)) { return; } runtime?.unavailableRouteTargetWarnings?.add(key); runtime?.logger?.warn?.( `Claude Design routing target "${target}" references a provider that is not configured in CCR; using "${fallback}" instead.` ); } function matchesClaudeDesignRouteRule(rule, body, requestedModel) { switch (rule.type) { case "always": return true; case "image": return hasImageContent(body?.messages); case "long-context": return estimateTokenCount(body) > (rule.threshold || 200000); case "model": return Boolean(rule.model && requestedModel === rule.model); case "model-prefix": return Boolean(rule.pattern && requestedModel?.startsWith(rule.pattern)); case "thinking": return Boolean(body?.thinking); case "web-search": return hasWebSearchTool(body?.tools); default: return false; } } function normalizeClaudeDesignRouting(value, options = {}) { const fallbackTarget = normalizeRouteTarget(composeRouteTarget(options.targetProvider, options.targetModel) || stringValue(options.targetModel)); const record = isRecord(value) ? value : {}; const rules = []; if (isRecord(record.modelMap)) { for (const [model, target] of Object.entries(record.modelMap)) { const normalizedTarget = normalizeRouteTarget(stringValue(target)); const normalizedModel = stringValue(model); if (!normalizedModel || !normalizedTarget) { continue; } rules.push({ enabled: true, id: `model-${sanitizeRouteId(normalizedModel)}`, model: normalizedModel, name: normalizedModel, target: normalizedTarget, type: "model" }); } } if (Array.isArray(record.rules)) { record.rules.forEach((rule, index) => { const normalized = normalizeClaudeDesignRoutingRule(rule, index); if (normalized) { rules.push(normalized); } }); } return { defaultTarget: normalizeRouteTarget(stringValue(record.default) || stringValue(record.defaultTarget)) || fallbackTarget, enabled: value === false ? false : record.enabled !== false, rules }; } function normalizeClaudeDesignRoutingRule(value, index) { if (!isRecord(value)) { return undefined; } const type = stringValue(value.type) || "model"; if (!CLAUDE_DESIGN_ROUTE_TYPES.has(type)) { return undefined; } const target = normalizeRouteTarget( stringValue(value.target) || composeRouteTarget(value.targetProvider, value.targetModel) || stringValue(value.targetModel) ); if (!target) { return undefined; } const model = stringValue(value.model) || stringValue(value.sourceModel); const pattern = stringValue(value.pattern) || (type === "model-prefix" ? model : undefined); const threshold = positiveNumber(value.threshold) || positiveNumber(value.tokenThreshold); const id = stringValue(value.id) || `${type}-${index + 1}`; return { enabled: value.enabled !== false, id, name: stringValue(value.name) || id, ...(model ? { model } : {}), ...(pattern ? { pattern } : {}), target, ...(threshold ? { threshold } : {}), type }; } function normalizeRouteTarget(value) { const raw = stringValue(value); if (!raw) { return undefined; } const commaIndex = raw.indexOf(","); if (commaIndex > 0 && commaIndex < raw.length - 1) { const provider = raw.slice(0, commaIndex).trim(); const model = raw.slice(commaIndex + 1).trim(); return provider && model ? `${provider}/${model}` : undefined; } return raw; } function composeRouteTarget(providerValue, modelValue) { const provider = stringValue(providerValue); const model = stringValue(modelValue); if (provider && model) { return `${provider}/${model}`; } return model || provider; } function claudeDesignProviderSelectorNames(config) { const names = new Set(); const providers = claudeDesignProviderConfigs(config); for (const provider of providers) { if (!isRecord(provider)) { continue; } const name = stringValue(provider.name); if (!name) { continue; } addProviderSelectorName(names, name); addProviderSelectorName(names, provider.provider); const capabilities = Array.isArray(provider.capabilities) ? provider.capabilities : []; for (const capability of capabilities) { if (!isRecord(capability)) { continue; } const protocol = normalizeGatewayProviderProtocol(capability.type || capability.protocol); if (protocol && stringValue(capability.baseUrl || capability.base_url)) { addProviderSelectorName(names, `${name}::${protocol}`); } } const credentialProtocols = claudeDesignProviderProtocols(provider); providerCredentials(provider).forEach((credential, index) => { if (!providerCredentialApiKey(credential)) { return; } const credentialSlug = providerCredentialSlug(providerCredentialRuntimeId(credential, index)); for (const protocol of credentialProtocols) { addProviderSelectorName(names, `${name}::${protocol}::cred:${credentialSlug}`); } }); } return names; } function defaultClaudeDesignGatewayConfigPath() { const home = stringValue(process.env.HOME) || stringValue(process.env.USERPROFILE); return home ? pathModule.join(home, ".claude-code-router", "gateway.config.json") : undefined; } function loadClaudeDesignGatewayConfig(file, logger) { const configFile = stringValue(file); if (!configFile || !fs.existsSync(configFile)) { return {}; } try { return parseMaybeJson(fs.readFileSync(configFile, "utf8"), {}); } catch (error) { logger?.warn?.(`Claude Design could not read gateway config ${configFile}: ${error?.message || error}`); return {}; } } function claudeDesignModelSourceConfig(config, gatewayConfig) { return { ...(isRecord(config) ? config : {}), Providers: uniqueProviderRecords([ ...claudeDesignProviderConfigs(config), ...claudeDesignProviderConfigs(gatewayConfig) ]), virtualModelProfiles: uniqueVirtualModelProfiles([ ...claudeDesignVirtualModelProfilesForConfig(config), ...claudeDesignVirtualModelProfilesForConfig(gatewayConfig) ]) }; } function claudeDesignProviderConfigs(config) { if (!isRecord(config)) { return []; } return [ ...(Array.isArray(config.Providers) ? config.Providers : []), ...(Array.isArray(config.providers) ? config.providers : []) ].filter(isRecord); } function claudeDesignVirtualModelProfilesForConfig(config) { if (!isRecord(config)) { return []; } return [ ...(Array.isArray(config.virtualModelProfiles) ? config.virtualModelProfiles : []), ...(Array.isArray(config.virtual_model_profiles) ? config.virtual_model_profiles : []) ].filter(isRecord); } function uniqueProviderRecords(providers) { const seen = new Set(); const result = []; for (const provider of providers) { const name = stringValue(provider.name); const key = `${name}\n${JSON.stringify(Array.isArray(provider.models) ? provider.models : [])}`; if (!name || seen.has(key)) { continue; } seen.add(key); result.push(provider); } return result; } function uniqueVirtualModelProfiles(profiles) { const seen = new Set(); const result = []; for (const profile of profiles) { const key = stringValue(profile.id) || stringValue(profile.name) || JSON.stringify(profile.match || profile); if (!key || seen.has(key)) { continue; } seen.add(key); result.push(profile); } return result; } function claudeDesignGatewayModelPresets(config, defaultGatewayModel) { const defaultModel = publicGatewayModelSelector(defaultGatewayModel) || DEFAULT_GATEWAY_MODEL; const selectors = uniqueCaseInsensitiveStrings([ defaultModel, ...claudeDesignProviderModelSelectors(config), ...claudeDesignVirtualModelSelectors(config), ...DEFAULT_ME.modelPresets.map((preset) => preset.id) ]); return selectors.map((selector, index) => claudeDesignGatewayModelPreset(selector, index === 0)); } function claudeDesignProviderModelSelectors(config) { const providers = claudeDesignProviderConfigs(config); const selectors = []; for (const provider of providers) { if (!Array.isArray(provider.models)) { continue; } const providerSelectors = claudeDesignProviderModelProviderSelectors(provider); for (const model of provider.models) { const modelName = stringValue(model); if (!modelName) { continue; } for (const providerSelector of providerSelectors) { selectors.push(normalizeRouteTarget(`${providerSelector}/${modelName}`)); } } } return selectors.filter(Boolean); } function claudeDesignProviderModelProviderSelectors(provider) { const selectors = []; addProviderModelSelector(selectors, publicGatewayProviderName(provider.name)); addProviderModelSelector(selectors, provider.name); addProviderModelSelector(selectors, provider.provider); const name = stringValue(provider.name); const publicName = publicGatewayProviderName(name); const capabilities = Array.isArray(provider.capabilities) ? provider.capabilities : []; for (const capability of capabilities) { const protocol = isRecord(capability) ? normalizeGatewayProviderProtocol(capability.type || capability.protocol) : undefined; if (publicName && protocol) { addProviderModelSelector(selectors, `${publicName}::${protocol}`); } if (name && protocol) { addProviderModelSelector(selectors, `${name}::${protocol}`); } } providerCredentials(provider).forEach((credential, index) => { if (!providerCredentialApiKey(credential)) { return; } const credentialSlug = providerCredentialSlug(providerCredentialRuntimeId(credential, index)); for (const protocol of claudeDesignProviderProtocols(provider)) { if (publicName) { addProviderModelSelector(selectors, `${publicName}::${protocol}::cred:${credentialSlug}`); } if (name) { addProviderModelSelector(selectors, `${name}::${protocol}::cred:${credentialSlug}`); } } }); return uniqueCaseInsensitiveStrings(selectors); } function addProviderModelSelector(selectors, value) { const normalized = stringValue(value); if (normalized) { selectors.push(normalized); } } function claudeDesignVirtualModelSelectors(config) { const profiles = claudeDesignVirtualModelProfilesForConfig(config); const selectors = []; for (const profile of profiles) { if (!isRecord(profile) || profile.enabled === false) { continue; } const materialization = isRecord(profile.materialization) ? profile.materialization : {}; if (materialization.enabled === false || materialization.includeInGatewayModels === false) { continue; } const match = isRecord(profile.match) ? profile.match : {}; const aliases = Array.isArray(match.exactAliases) ? match.exactAliases : []; for (const alias of aliases) { const normalizedAlias = stringValue(alias); if (!normalizedAlias) { continue; } selectors.push(normalizedAlias.toLowerCase().startsWith("fusion/") ? normalizedAlias : `Fusion/${normalizedAlias}`); } } return selectors; } function claudeDesignGatewayModelPreset(selector, isDefault) { return { id: selector, label: gatewayModelPresetLabel(selector), maxTokens: 1000000, supportsAdaptiveThinking: true, description: isDefault ? "CCR gateway default" : "CCR gateway model" }; } function gatewayModelPresetLabel(selector) { const value = stringValue(selector) || DEFAULT_GATEWAY_MODEL; const separator = value.indexOf("/"); if (separator <= 0 || separator >= value.length - 1) { return value; } const provider = value.slice(0, separator); const model = value.slice(separator + 1); return `${model} (${provider})`; } function publicGatewayModelSelector(value) { const normalized = normalizeRouteTarget(value); if (!normalized) { return undefined; } const separator = normalized.indexOf("/"); if (separator <= 0 || separator >= normalized.length - 1) { return normalized; } const provider = normalized.slice(0, separator).trim(); const model = normalized.slice(separator + 1).trim(); const protocolSeparator = provider.indexOf("::"); const publicProvider = protocolSeparator > 0 ? provider.slice(0, protocolSeparator) : provider; return publicProvider && model ? `${publicProvider}/${model}` : normalized; } function publicGatewayProviderName(value) { const name = stringValue(value); if (!name) { return undefined; } const protocolSeparator = name.indexOf("::"); return protocolSeparator > 0 ? name.slice(0, protocolSeparator) : name; } function uniqueCaseInsensitiveStrings(values) { const seen = new Set(); const result = []; for (const value of values) { const normalized = stringValue(value); if (!normalized) { continue; } const key = normalized.toLowerCase(); if (seen.has(key)) { continue; } seen.add(key); result.push(normalized); } return result; } function addProviderSelectorName(names, value) { const name = stringValue(value); if (name) { names.add(name.toLowerCase()); const publicName = publicGatewayProviderName(name); if (publicName) { names.add(publicName.toLowerCase()); } } } function claudeDesignProviderProtocols(provider) { const protocols = []; const seen = new Set(); const capabilities = Array.isArray(provider.capabilities) ? provider.capabilities : []; for (const capability of capabilities) { if (!isRecord(capability)) { continue; } const protocol = normalizeGatewayProviderProtocol(capability.type || capability.protocol); if (protocol && stringValue(capability.baseUrl || capability.base_url) && !seen.has(protocol)) { seen.add(protocol); protocols.push(protocol); } } const direct = normalizeGatewayProviderProtocol(provider.type) || normalizeGatewayProviderProtocol(provider.provider) || inferGatewayProviderProtocol(provider); if (direct && !seen.has(direct)) { protocols.push(direct); } return protocols; } function normalizeGatewayProviderProtocol(value) { const normalized = stringValue(value)?.toLowerCase(); if (normalized === "openai" || normalized === "openai_responses") { return "openai_responses"; } if (normalized === "openai_chat" || normalized === "openai_chat_completions") { return "openai_chat_completions"; } if (normalized === "anthropic" || normalized === "anthropic_messages") { return "anthropic_messages"; } if (normalized === "gemini" || normalized === "gemini_generate_content") { return "gemini_generate_content"; } return undefined; } function inferGatewayProviderProtocol(provider) { const url = stringValue(provider.baseurl || provider.baseUrl || provider.api_base_url)?.toLowerCase() || ""; const transformerNames = JSON.stringify(provider.transformer ?? "").toLowerCase(); if (url.includes("generativelanguage.googleapis.com") || transformerNames.includes("gemini")) { return "gemini_generate_content"; } if (url.includes("anthropic") || transformerNames.includes("anthropic")) { return "anthropic_messages"; } return "openai_chat_completions"; } function providerCredentials(provider) { return Array.isArray(provider?.credentials) ? provider.credentials.filter((credential) => isRecord(credential) && credential.enabled !== false) : []; } function providerCredentialApiKey(credential) { return stringValue(credential.api_key) || stringValue(credential.apiKey) || stringValue(credential.apikey); } function providerCredentialRuntimeId(credential, index) { const explicitId = stringValue(credential.id); if (explicitId) { return explicitId; } const oneBasedIndex = index >= 0 ? index + 1 : 1; const label = stringValue(credential.name) || stringValue(credential.label); return label ? `${providerCredentialSlug(label)}-${oneBasedIndex}` : `key-${oneBasedIndex}`; } function providerCredentialSlug(value) { return stringValue(value) .toLowerCase() .replace(/[^a-z0-9_.-]+/g, "-") .replace(/^-+|-+$/g, "") || "key"; } function positiveNumber(value) { const number = Number(value); return Number.isFinite(number) && number > 0 ? number : undefined; } function hasWebSearchTool(tools) { return Array.isArray(tools) && tools.some((tool) => isRecord(tool) && stringValue(tool.type)?.startsWith("web_search")); } function hasImageContent(messages) { return Array.isArray(messages) && messages.some((message) => JSON.stringify(message).includes("\"image\"")); } function sanitizeRouteId(value) { return String(value || "") .toLowerCase() .replace(/[^a-z0-9._-]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 80) || "route"; } function fetchGateway(runtime, path, body, routingDecision, sessionContext) { const url = new URL(path, runtime.gatewayUrl.replace(/\/+$/, "")); const headers = { "anthropic-version": "2023-06-01", "content-type": "application/json", "x-ccr-client": "Claude Design", "x-claude-design-proxy": "1" }; if (runtime.gatewayApiKey) { headers.authorization = `Bearer ${runtime.gatewayApiKey}`; headers["x-api-key"] = runtime.gatewayApiKey; } if (routingDecision?.requestedModel) { headers["x-claude-design-requested-model"] = routingDecision.requestedModel; } if (routingDecision?.routedModel) { headers["x-claude-design-routed-model"] = routingDecision.routedModel; } if (routingDecision?.reason) { headers["x-claude-design-route-reason"] = routingDecision.reason; } if (sessionContext?.sessionId) { headers["x-agent-session-id"] = sessionContext.sessionId; } if (sessionContext?.projectId) { headers["x-claude-design-project-id"] = sessionContext.projectId; } if (sessionContext?.chatId) { headers["x-claude-design-chat-id"] = sessionContext.chatId; } return fetch(url, { body: `${JSON.stringify(body)}\n`, headers, method: "POST" }); } function claudeDesignSessionContext(value) { const projectId = stringValue(value?.projectId); const chatId = stringValue(value?.chatId); const sessionId = projectId && chatId ? `${projectId}:${chatId}` : chatId || projectId; return { chatId, projectId, sessionId }; } function readClaudeDesignProjectId(value) { if (!isRecord(value)) { return undefined; } const metadata = isRecord(value.metadata) ? value.metadata : undefined; return ( stringValue(value.project_id) || stringValue(value.projectId) || stringValue(metadata?.project_id) || stringValue(metadata?.projectId) ); } function readClaudeDesignChatId(value) { if (!isRecord(value)) { return undefined; } const metadata = isRecord(value.metadata) ? value.metadata : undefined; return ( stringValue(value.chat_id) || stringValue(value.chatId) || stringValue(metadata?.chat_id) || stringValue(metadata?.chatId) || stringValue(value.conversation_id) || stringValue(value.conversationId) || stringValue(metadata?.conversation_id) || stringValue(metadata?.conversationId) ); } function extractGatewayAssistantText(payload) { if (typeof payload === "string") { return payload; } if (!isRecord(payload)) { return ""; } if (typeof payload.completion === "string") { return payload.completion; } if (typeof payload.output_text === "string") { return payload.output_text; } if (Array.isArray(payload.content)) { return textFromMessageContent(payload.content); } if (Array.isArray(payload.output)) { return textFromMessageContent(payload.output); } if (Array.isArray(payload.choices)) { return payload.choices .map((choice) => { if (!isRecord(choice)) { return ""; } const message = isRecord(choice.message) ? choice.message : {}; const delta = isRecord(choice.delta) ? choice.delta : {}; return textFromMessageContent(message.content) || textFromMessageContent(delta.content) || stringValue(choice.text) || ""; }) .filter(Boolean) .join("\n"); } return ""; } function extractGatewayToolCalls(payload) { const toolCalls = []; const addToolCall = (value, fallbackIndex) => { const toolCall = normalizeGatewayToolCall(value, fallbackIndex); if (toolCall) { toolCalls.push(toolCall); } }; if (!isRecord(payload)) { return toolCalls; } if (Array.isArray(payload.content)) { payload.content.forEach(addToolCall); } if (Array.isArray(payload.output)) { payload.output.forEach(addToolCall); } if (Array.isArray(payload.tool_calls)) { payload.tool_calls.forEach(addToolCall); } if (Array.isArray(payload.choices)) { for (const choice of payload.choices) { if (!isRecord(choice)) { continue; } const message = isRecord(choice.message) ? choice.message : {}; const delta = isRecord(choice.delta) ? choice.delta : {}; if (Array.isArray(message.tool_calls)) { message.tool_calls.forEach(addToolCall); } if (Array.isArray(delta.tool_calls)) { delta.tool_calls.forEach(addToolCall); } if (Array.isArray(message.content)) { message.content.forEach(addToolCall); } } } return toolCalls; } function normalizeGatewayToolCall(value, fallbackIndex) { if (!isRecord(value)) { return undefined; } const fn = isRecord(value.function) ? value.function : {}; const name = stringValue(value.name) || stringValue(fn.name) || stringValue(value.tool); if (!name) { return undefined; } const inputSource = value.input ?? value.arguments ?? fn.arguments ?? {}; const input = normalizeToolInput(inputSource); return { id: stringValue(value.id) || `toolu_${String(fallbackIndex || 0)}_${randomUuid().replace(/-/g, "").slice(0, 18)}`, input: normalizeGatewayToolInput(name, input), name }; } function normalizeGatewayToolInput(name, input) { if (name === "questions_v2") { return normalizeQuestionsV2Input(input); } if (name === "questions") { return normalizeLegacyQuestionsInput(input); } return input; } function absorbQuestionToolCalls(assistantText, toolCalls) { const kept = []; const questions = []; for (const toolCall of Array.isArray(toolCalls) ? toolCalls : []) { if (isQuestionToolName(toolCall?.name)) { questions.push(toolCall); } else { kept.push(toolCall); } } if (questions.length === 0) { return { assistantText, toolCalls: kept }; } const questionText = questionToolCallsToText(questions); return { assistantText: [assistantText, questionText].filter(Boolean).join("\n\n"), toolCalls: kept }; } function questionToolCallsToText(toolCalls) { const summaries = []; for (const toolCall of Array.isArray(toolCalls) ? toolCalls : []) { const input = isRecord(toolCall?.input) ? toolCall.input : {}; const title = stringValue(input.title); const questions = Array.isArray(input.questions) ? input.questions : []; const questionTitles = questions .map((question, index) => { if (!isRecord(question)) { return ""; } return stringValue(question.title) || stringValue(question.question) || `Question ${index + 1}`; }) .filter(Boolean); if (title || questionTitles.length > 0) { summaries.push([title, ...questionTitles].filter(Boolean).join(": ")); } } const suffix = summaries.length > 0 ? ` (${summaries.join("; ")})` : ""; return `Claude Design questions were skipped; continuing with default answers.${suffix}`; } function isQuestionToolName(name) { return QUESTION_TOOL_NAMES.has(String(name || "")); } function normalizeToolInput(value) { if (typeof value === "string") { const parsed = parseMaybeJson(value, undefined); if (isRecord(parsed)) { return parsed; } if (Array.isArray(parsed)) { return { value: parsed }; } return value.trim() ? { arguments: value } : {}; } if (isRecord(value)) { return value; } if (Array.isArray(value)) { return { value }; } return value === undefined || value === null ? {} : { value }; } function normalizeQuestionsV2Input(value) { const record = isRecord(value) ? value : {}; const rawQuestions = Array.isArray(record.questions) ? record.questions : []; const questions = rawQuestions.map(normalizeQuestionsV2Question).filter(Boolean); const title = stringValue(record.title) || "Claude has some questions"; return { ...record, questions: questions.length > 0 ? questions : [defaultQuestionsV2Question()], title }; } function normalizeLegacyQuestionsInput(value) { const record = isRecord(value) ? value : {}; const rawQuestions = Array.isArray(record.questions) ? record.questions : []; return { ...record, questions: rawQuestions.map((question, index) => { if (isRecord(question)) { return { ...question, options: plainStringArray(question.options), question: stringValue(question.question) || stringValue(question.title) || `Question ${index + 1}` }; } return { options: [], question: stringValue(question) || `Question ${index + 1}` }; }) }; } function normalizeQuestionsV2Question(question, index) { const record = isRecord(question) ? question : {}; const options = plainStringArray(record.options); const title = stringValue(record.title) || stringValue(record.question) || stringValue(record.label) || `Question ${index + 1}`; let kind = stringValue(record.kind); if (!["text-options", "svg-options", "slider", "file", "freeform"].includes(kind)) { if (record.isSvg === true) { kind = "svg-options"; } else if (options.length > 0) { kind = "text-options"; } else if (record.min !== undefined || record.max !== undefined || record.step !== undefined) { kind = "slider"; } else if (stringValue(record.accept)) { kind = "file"; } else { kind = "freeform"; } } if ((kind === "text-options" || kind === "svg-options") && options.length === 0) { kind = "freeform"; } const normalized = { id: normalizeQuestionId(stringValue(record.id) || stringValue(record.name) || title, index), kind, title }; const subtitle = stringValue(record.subtitle); if (subtitle) { normalized.subtitle = subtitle; } if (options.length > 0) { normalized.options = options; } if (record.multi === true) { normalized.multi = true; } if (kind === "slider") { const min = numberValue(record.min); const max = numberValue(record.max); const step = numberValue(record.step); const defaultValue = numberValue(record.default); if (min !== undefined) { normalized.min = min; } if (max !== undefined) { normalized.max = max; } if (step !== undefined) { normalized.step = step; } if (defaultValue !== undefined) { normalized.default = defaultValue; } } if (kind === "file") { const accept = stringValue(record.accept); if (accept) { normalized.accept = accept; } } return normalized; } function plainStringArray(value) { if (!Array.isArray(value)) { return []; } return value.map((item) => String(item ?? "").trim()).filter(Boolean); } function defaultQuestionsV2Question() { return { id: "details", kind: "freeform", title: "What should Claude know before continuing?" }; } function normalizeQuestionId(value, index) { const normalized = stringValue(value) .toLowerCase() .replace(/[^a-z0-9]+/g, "_") .replace(/^_+|_+$/g, "") .slice(0, 64); return normalized || `question_${index + 1}`; } function extractGatewayStopReason(payload, fallback) { if (!isRecord(payload)) { return fallback; } const direct = stringValue(payload.stop_reason) || stringValue(payload.stopReason); if (direct) { return direct; } if (Array.isArray(payload.choices)) { for (const choice of payload.choices) { const mapped = mapGatewayFinishReason(isRecord(choice) ? stringValue(choice.finish_reason) || stringValue(choice.finishReason) : undefined); if (mapped) { return mapped; } } } return fallback; } function mapGatewayFinishReason(reason) { switch (reason) { case "tool_calls": case "function_call": return "tool_use"; case "length": return "max_tokens"; case "stop": return "end_turn"; default: return reason; } } function textFromMessageContent(content) { if (typeof content === "string") { return content; } if (!Array.isArray(content)) { return ""; } return content .map((block) => { if (typeof block === "string") { return block; } if (!isRecord(block)) { return ""; } if (block.type === "tool_use" || block.type === "function_call" || isToolResultContentBlock(block)) { return ""; } return stringValue(block.text) || stringValue(block.content) || stringValue(block.output_text) || ""; }) .filter(Boolean) .join("\n"); } function gatewayMessageContent(assistantText, toolCalls) { const content = []; if (assistantText) { content.push({ text: assistantText, type: "text" }); } for (const toolCall of toolCalls) { content.push({ id: toolCall.id, input: toolCall.input || {}, name: toolCall.name, type: "tool_use" }); } return content; } function gatewayContentBlocks(assistantText, toolCalls) { const blocks = []; if (assistantText) { blocks.push({ text: assistantText, type: "text" }); } for (const toolCall of toolCalls) { blocks.push({ toolCall: { id: toolCall.id, input: toolCall.input || {}, name: toolCall.name, serverSide: false, type: gatewayToolKind(toolCall.name) }, type: "tool_call" }); } return blocks; } function sanitizeStoredMessages(messages) { if (!Array.isArray(messages)) { return []; } return dedupeStoredMessages(messages .map(sanitizeStoredMessage) .filter(isVisibleStoredMessage)); } function dedupeStoredMessages(messages) { const result = []; let previousKey; for (const message of messages) { const key = storedMessageDedupeKey(message); if (key && key === previousKey) { continue; } result.push(message); previousKey = key; } return result; } function storedMessageDedupeKey(message) { if (!isRecord(message)) { return `raw:${JSON.stringify(message)}`; } const role = stringValue(message.role) || ""; const chatId = stringValue(message.chat_id) || stringValue(message.chatId) || ""; const text = role === "user" ? cleanVisibleUserText(storedMessageDisplayText(message)) : storedMessageDisplayText(message); const blocks = Array.isArray(message.contentBlocks) ? message.contentBlocks : Array.isArray(message.content_blocks) ? message.content_blocks : []; return JSON.stringify({ blocks: role === "user" ? [] : blocks, chatId, role, text }); } function sanitizeStoredMessage(message) { if (!isRecord(message)) { return message; } const sourceBlocks = Array.isArray(message.contentBlocks) ? message.contentBlocks : Array.isArray(message.content_blocks) ? message.content_blocks : []; if (sourceBlocks.length === 0) { return sanitizeStoredMessageDisplayContent(message); } const keptBlocks = []; const questionBlocks = []; for (const block of sourceBlocks) { if (isQuestionContentBlock(block)) { questionBlocks.push(block); } else { keptBlocks.push(block); } } if (questionBlocks.length === 0) { return sanitizeStoredMessageDisplayContent(message); } const existingContent = stringValue(message.content); const content = existingContent || questionToolBlocksToText(questionBlocks); const blocks = keptBlocks.length > 0 ? keptBlocks : content ? [{ text: content, type: "text" }] : []; return sanitizeStoredMessageDisplayContent({ ...message, content, contentBlocks: blocks, content_blocks: blocks }); } function isQuestionContentBlock(block) { if (!isRecord(block)) { return false; } if (isQuestionToolName(block.name)) { return true; } const toolCall = isRecord(block.toolCall) ? block.toolCall : isRecord(block.tool_call) ? block.tool_call : undefined; if (toolCall && isQuestionToolName(toolCall.name)) { return true; } return false; } function questionToolBlocksToText(blocks) { const toolCalls = []; for (const block of Array.isArray(blocks) ? blocks : []) { if (!isRecord(block)) { continue; } const toolCall = isRecord(block.toolCall) ? block.toolCall : isRecord(block.tool_call) ? block.tool_call : block; toolCalls.push({ input: isRecord(toolCall.input) ? toolCall.input : {}, name: stringValue(toolCall.name) || "questions_v2" }); } return questionToolCallsToText(toolCalls); } function isToolResultOnlyMessage(message) { if (!isRecord(message)) { return false; } const role = stringValue(message.role); if (role && role !== "user" && role !== "tool") { return false; } const content = message.content; if (Array.isArray(content) && content.length > 0) { return content.every(isToolResultContentBlock); } const blocks = Array.isArray(message.contentBlocks) ? message.contentBlocks : Array.isArray(message.content_blocks) ? message.content_blocks : []; if (blocks.length > 0) { return blocks.every(isToolResultContentBlock); } return role === "tool"; } function isToolResultContentBlock(block) { if (!isRecord(block)) { return false; } const type = stringValue(block.type); return ( type === "tool_result" || type === "tool_result_error" || type === "function_result" || isRecord(block.toolResult) || isRecord(block.tool_result) ); } function sanitizeStoredMessageDisplayContent(message) { if (!isRecord(message)) { return message; } const role = stringValue(message.role); if (role === "user") { const content = cleanVisibleUserText(storedProjectMessageText(message)); const sanitized = { ...message, content }; if (Array.isArray(message.contentBlocks) || Array.isArray(message.content_blocks)) { const blocks = content ? [{ text: content, type: "text" }] : []; sanitized.contentBlocks = blocks; sanitized.content_blocks = blocks; } return sanitized; } if (role === "assistant") { const sourceBlocks = Array.isArray(message.contentBlocks) ? message.contentBlocks : Array.isArray(message.content_blocks) ? message.content_blocks : []; if (sourceBlocks.length === 0) { return message; } const blocks = sourceBlocks.filter((block) => !isToolResultContentBlock(block)); if (blocks.length === sourceBlocks.length) { return message; } const content = stringValue(message.content) || textFromMessageContent(blocks); return { ...message, content, contentBlocks: blocks, content_blocks: blocks }; } return message; } function isVisibleStoredMessage(message) { if (!isRecord(message)) { return true; } if (isToolResultOnlyMessage(message)) { return false; } const role = stringValue(message.role); const text = storedMessageDisplayText(message); if (isInternalChatStatusText(text)) { return false; } if (role === "user") { return Boolean(cleanVisibleUserText(text)); } if (role === "tool") { return false; } if (role === "assistant" && !text) { const blocks = Array.isArray(message.contentBlocks) ? message.contentBlocks : Array.isArray(message.content_blocks) ? message.content_blocks : []; return blocks.some((block) => isToolCallContentBlock(block) || !isToolResultContentBlock(block)); } return true; } function storedMessageDisplayText(message) { if (!isRecord(message)) { return ""; } return storedProjectMessageText(message); } function cleanVisibleUserText(text) { let value = (stringValue(text) || "") .replace(/