import type { CanonicalDiagnostics, CanonicalWorkspace, EvidencePolicy, EvidenceSource, RestDiagnosticRequest, WorkspaceEvidence, } from "../api/workspaces"; export { workspacePreferences, type WorkspacePreference } from "./preferences"; export const WORKSPACE_SUMMARY_ERROR = "Could not load workspace registry. Please retry."; export const WORKSPACE_POLICY_ERROR = "Could not load selected workspace policy. Please retry."; export class WorkspaceSelectionError extends Error {} type PolicySelection = { workspaceId: string; generation: number; state: "summary" | "pending" | "ready" | "error"; error?: Error; settled: Promise; settle: () => void; superseded: Promise; supersede: () => void; }; let selection: PolicySelection | undefined; let nextGeneration = 0; function pendingSelection(workspaceId: string, state: "summary" | "pending"): PolicySelection { let settle!: () => void; let supersede!: () => void; return { workspaceId, generation: ++nextGeneration, state, settled: new Promise((resolve) => { settle = resolve; }), settle, superseded: new Promise((resolve) => { supersede = resolve; }), supersede, }; } /** In-memory coordination between the footer's selected policy and session creation. */ export const workspacePolicyGate = { beginSummary(workspaceId: string): void { if (selection?.workspaceId === workspaceId && selection.state === "summary") return; selection?.supersede(); selection = pendingSelection(workspaceId, "summary"); }, select(workspaceId: string): void { if (selection?.workspaceId === workspaceId) { if (selection.state === "summary") selection.state = "pending"; if (selection.state === "pending") return; } selection?.supersede(); selection = pendingSelection(workspaceId, "pending"); }, allowLegacy(workspaceId: string): void { if (selection?.workspaceId !== workspaceId) { this.clear(); return; } if (selection.state === "summary") { selection.state = "ready"; selection.settle(); } }, rejectSummary(workspaceId: string): void { if (!selection || selection.workspaceId !== workspaceId || selection.state !== "summary") return; selection.state = "error"; selection.error = new WorkspaceSelectionError(WORKSPACE_SUMMARY_ERROR); selection.settle(); }, clear(): void { selection?.supersede(); selection = undefined; }, resolve(workspaceId: string): void { if (!selection || selection.workspaceId !== workspaceId || selection.state !== "pending") return; selection.state = "ready"; selection.settle(); }, reject(workspaceId: string): void { if (!selection || selection.workspaceId !== workspaceId || selection.state !== "pending") return; selection.state = "error"; selection.error = new WorkspaceSelectionError(WORKSPACE_POLICY_ERROR); selection.settle(); }, async waitForCurrent(read: () => T): Promise { while (true) { const current = read(); const pending = selection; if (!current.workspaceId || pending?.workspaceId !== current.workspaceId) return current; const generation = pending.generation; if (pending.state === "ready") return read(); if (pending.state === "error") throw pending.error; await Promise.race([pending.settled, pending.superseded]); const completed = selection; if (!completed || completed.generation !== generation || read().workspaceId !== current.workspaceId) continue; if (completed.state === "error") throw completed.error; if (completed.state === "ready") return read(); } }, }; function record(value: unknown): Record | undefined { return value && typeof value === "object" && !Array.isArray(value) ? value as Record : undefined; } function exactRecord(value: unknown, keys: readonly string[]): Record | undefined { const source = record(value); return source && Object.keys(source).every((key) => keys.includes(key)) ? source : undefined; } function text(value: unknown): string | undefined { return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined; } function identifier(value: unknown): string | undefined { return typeof value === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(value) ? value : undefined; } function workspaceId(value: unknown): string | undefined { return typeof value === "string" && /^[a-z][a-z0-9-]{2,62}$/.test(value) ? value : undefined; } function modelReference(value: unknown): `${string}/${string}` | undefined { return typeof value === "string" && /^[^/\s]+\/[^/\s]+$/.test(value) ? value as `${string}/${string}` : undefined; } function positiveInteger(value: unknown, max = Number.MAX_SAFE_INTEGER): number | undefined { return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= max ? value : undefined; } function nonnegativeInteger(value: unknown): number | undefined { return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined; } function isSafeEvidencePattern(value: string): boolean { const parts = value.split("/"); return value.length > 0 && !value.startsWith("/") && !value.includes("\\") && !/[\u0000-\u001f\u007f]/u.test(value) && parts.every((part) => part !== "" && part !== "." && part !== ".."); } function parsePublicHttpUri(value: string): URL | undefined { if (value.trim() !== value || /[\u0000-\u001f\u007f\\]/u.test(value)) return undefined; try { const parsed = new URL(value); if ( !["http:", "https:"].includes(parsed.protocol) || parsed.hostname.length === 0 || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "" ) return undefined; return parsed; } catch { return undefined; } } function isSafeS3Uri(value: string): boolean { if (value.trim() !== value || /[\u0000-\u001f\u007f\\]/u.test(value)) return false; try { const parsed = new URL(value); const bucket = parsed.hostname; const validBucket = /^(?=.{3,63}$)(?!-)(?!.*\.\.)(?!.*\.-)(?!.*-\.)[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/.test(bucket) && !/^\d{1,3}(?:\.\d{1,3}){3}$/.test(bucket); return parsed.protocol === "s3:" && validBucket && parsed.port === "" && parsed.username === "" && parsed.password === "" && parsed.search === "" && parsed.hash === "" && parsed.href === value; } catch { return false; } } function isSafeS3Endpoint(value: string): boolean { const parsed = parsePublicHttpUri(value); return parsed !== undefined && (parsed.pathname === "/" || parsed.pathname === ""); } function copyEvidencePolicy(value: unknown): EvidencePolicy | undefined { const source = exactRecord(value, ["max_chunk_chars", "retain_published_generations"]); const maxChunkChars = positiveInteger(source?.max_chunk_chars); const retainedGenerations = positiveInteger(source?.retain_published_generations); return source && maxChunkChars && retainedGenerations ? { max_chunk_chars: maxChunkChars, retain_published_generations: retainedGenerations } : undefined; } function copyFilesystemEvidence(value: unknown, id: string): EvidenceSource | undefined { const source = exactRecord(value, ["type", "uri", "patterns", "max_bytes"]); const uri = typeof source?.uri === "string" ? source.uri : undefined; const patterns = source?.patterns; const maxBytes = positiveInteger(source?.max_bytes); if ( source?.type !== "filesystem" || uri !== `${id}/evidence` || !Array.isArray(patterns) || patterns.length === 0 || !patterns.every((pattern) => typeof pattern === "string" && isSafeEvidencePattern(pattern)) || new Set(patterns).size !== patterns.length || !maxBytes ) return undefined; return { type: "filesystem", uri, patterns: [...patterns] as string[], max_bytes: maxBytes }; } function copyHttpEvidence(value: unknown): EvidenceSource | undefined { const source = exactRecord(value, [ "type", "uris", "authentication", "connect_timeout_ms", "read_timeout_ms", "max_bytes", "max_redirects", "allow_private_hosts", "max_cache_bytes", ]); const uris = source?.uris; const authentication = oneOf(source?.authentication, ["none", "signed_urls_file"] as const); const connectTimeout = positiveInteger(source?.connect_timeout_ms); const readTimeout = positiveInteger(source?.read_timeout_ms); const maxBytes = positiveInteger(source?.max_bytes); const maxRedirects = nonnegativeInteger(source?.max_redirects); const maxCacheBytes = positiveInteger(source?.max_cache_bytes); if ( source?.type !== "http" || !Array.isArray(uris) || uris.length === 0 || !uris.every((uri) => typeof uri === "string" && parsePublicHttpUri(uri) !== undefined) || new Set(uris.map((uri) => parsePublicHttpUri(uri as string)?.href)).size !== uris.length || !authentication || !connectTimeout || !readTimeout || !maxBytes || maxRedirects === undefined || typeof source.allow_private_hosts !== "boolean" || !maxCacheBytes ) return undefined; return { type: "http", uris: [...uris] as string[], authentication, connect_timeout_ms: connectTimeout, read_timeout_ms: readTimeout, max_bytes: maxBytes, max_redirects: maxRedirects, allow_private_hosts: source.allow_private_hosts, max_cache_bytes: maxCacheBytes, }; } function copyS3Evidence(value: unknown): EvidenceSource | undefined { const source = exactRecord(value, [ "type", "uri", "endpoint_url", "region", "credentials", "trusted_endpoint", "allow_private_endpoint", "allow_insecure_endpoint", "max_bytes", "max_objects", "max_pages", "page_size", ]); const uri = typeof source?.uri === "string" && isSafeS3Uri(source.uri) ? source.uri : undefined; const endpoint = source?.endpoint_url === undefined ? undefined : typeof source.endpoint_url === "string" && isSafeS3Endpoint(source.endpoint_url) ? source.endpoint_url : null; const region = source?.region === undefined ? undefined : text(source.region); const credentials = oneOf(source?.credentials, ["ambient", "static_files"] as const); const maxBytes = positiveInteger(source?.max_bytes); const maxObjects = positiveInteger(source?.max_objects); const maxPages = positiveInteger(source?.max_pages); const pageSize = positiveInteger(source?.page_size, 1_000); if ( source?.type !== "s3" || !uri || endpoint === null || (source.region !== undefined && !region) || !credentials || typeof source.trusted_endpoint !== "boolean" || typeof source.allow_private_endpoint !== "boolean" || typeof source.allow_insecure_endpoint !== "boolean" || !maxBytes || !maxObjects || !maxPages || !pageSize || (endpoint === undefined && ( source.trusted_endpoint || source.allow_private_endpoint || source.allow_insecure_endpoint )) || (endpoint !== undefined && !source.trusted_endpoint) || (endpoint !== undefined && parsePublicHttpUri(endpoint)?.protocol === "http:" && !source.allow_insecure_endpoint) ) return undefined; return { type: "s3", uri, ...(endpoint === undefined ? {} : { endpoint_url: endpoint }), ...(region === undefined ? {} : { region }), credentials, trusted_endpoint: source.trusted_endpoint, allow_private_endpoint: source.allow_private_endpoint, allow_insecure_endpoint: source.allow_insecure_endpoint, max_bytes: maxBytes, max_objects: maxObjects, max_pages: maxPages, page_size: pageSize, }; } function copyEvidence(value: unknown, id: string): WorkspaceEvidence | undefined { const source = exactRecord(value, ["source", "policy"]); if (!source) return undefined; const type = record(source.source)?.type; const evidenceSource = type === "filesystem" ? copyFilesystemEvidence(source.source, id) : type === "http" ? copyHttpEvidence(source.source) : type === "s3" ? copyS3Evidence(source.source) : undefined; const policy = copyEvidencePolicy(source.policy); return evidenceSource && policy ? { source: evidenceSource, policy } : undefined; } function oneOf(value: unknown, choices: readonly T[]): T | undefined { return typeof value === "string" && choices.includes(value as T) ? value as T : undefined; } function uniqueChoices(value: unknown, choices: readonly T[]): T[] | undefined { if (!Array.isArray(value) || value.length === 0) return undefined; const result: T[] = []; for (const item of value) { const choice = oneOf(item, choices); if (!choice || result.includes(choice)) return undefined; result.push(choice); } return result; } function uniqueModels(value: unknown): `${string}/${string}`[] | undefined { if (!Array.isArray(value) || value.length === 0) return undefined; const result: `${string}/${string}`[] = []; for (const item of value) { const model = modelReference(item); if (!model || result.includes(model)) return undefined; result.push(model); } return result; } function originRelativePath(value: unknown): string | undefined { return typeof value === "string" && /^\/(?!\/)[^\\\u0000-\u001F\u007F?#]*$/.test(value) && !/%5c/i.test(value) ? value : undefined; } function copyRequest(value: unknown, extraKeys: readonly string[] = []): RestDiagnosticRequest | undefined { const source = exactRecord(value, ["method", "path", "auth", ...extraKeys]); const method = oneOf(source?.method, ["GET", "POST"] as const); const path = originRelativePath(source?.path); const auth = oneOf(source?.auth, ["none", "bearer", "x-api-key"] as const); return method && path && auth ? { method, path, auth } : undefined; } function copyDiagnostics(value: unknown): CanonicalDiagnostics | undefined { const source = exactRecord(value, ["dwh_rest"]); if (!source) return undefined; const diagnostics: CanonicalDiagnostics = {}; if (source.dwh_rest !== undefined) { const request = copyRequest(source.dwh_rest, ["response"]); const raw = exactRecord(source.dwh_rest, ["method", "path", "auth", "response"]); const response = exactRecord(raw?.response, ["database", "schema"]); const database = identifier(response?.database); const schema = identifier(response?.schema); if (!request || !database || !schema) return undefined; diagnostics.dwh_rest = { ...request, response: { database, schema } }; } return diagnostics; } /** Drops unknown fields before a server response can become a browser draft or conflict view. */ export function sanitizeCanonicalWorkspace(value: unknown): CanonicalWorkspace | undefined { const source = exactRecord(value, [ "workspace", "dwh", "semantic_index", "llm_policy", "diagnostics", "evidence", ]); const metadata = exactRecord(source?.workspace, ["schema_version", "id", "name", "description", "language"]); const dwh = exactRecord(source?.dwh, ["engine", "database", "schema", "port", "timeout_ms", "supported_transports"]); const semanticIndex = exactRecord(source?.semantic_index, ["vector_store", "embedding"]); const vectorStore = exactRecord(semanticIndex?.vector_store, ["engine", "collection", "dimensions", "distance"]); const embedding = exactRecord(semanticIndex?.embedding, ["provider", "model", "dimensions"]); const policy = exactRecord(source?.llm_policy, ["default", "allowed"]); const diagnostics = source?.diagnostics === undefined ? undefined : copyDiagnostics(source.diagnostics); if (!metadata || !dwh || !semanticIndex || !vectorStore || !embedding || !policy) return undefined; const id = workspaceId(metadata.id); const evidence = id && source?.evidence !== undefined ? copyEvidence(source.evidence, id) : undefined; const name = text(metadata.name); const language = oneOf(metadata.language, ["en", "it"] as const); const description = metadata.description === undefined ? undefined : text(metadata.description); const database = identifier(dwh.database); const schema = identifier(dwh.schema); const dwhPort = dwh.port === undefined ? undefined : positiveInteger(dwh.port, 65_535); const dwhTimeout = dwh.timeout_ms === undefined ? undefined : positiveInteger(dwh.timeout_ms); const dwhTransports = uniqueChoices(dwh.supported_transports, ["postgres_direct", "rest_api", "ssh_tunnel"] as const); const collection = workspaceId(vectorStore.collection); const vectorDimensions = positiveInteger(vectorStore.dimensions, 32_768); const distance = oneOf(vectorStore.distance, ["cosine"] as const); const embeddingProvider = oneOf(embedding.provider, ["ollama_internal"] as const); const embeddingModel = text(embedding.model); const embeddingDimensions = positiveInteger(embedding.dimensions, 32_768); const allowedModels = uniqueModels(policy.allowed); const defaultModel = policy.default === undefined ? undefined : modelReference(policy.default); if ( metadata.schema_version !== 3 || !id || !name || !language || (metadata.description !== undefined && !description) || dwh.engine !== "postgres" || !database || !schema || (dwh.port !== undefined && !dwhPort) || (dwh.timeout_ms !== undefined && !dwhTimeout) || !dwhTransports || vectorStore.engine !== "qdrant" || !collection || !vectorDimensions || !distance || !embeddingProvider || !embeddingModel || !embeddingDimensions || !allowedModels || (defaultModel !== undefined && !allowedModels.includes(defaultModel)) || vectorDimensions !== embeddingDimensions || vectorDimensions !== 1024 || embeddingDimensions !== 1024 || embeddingModel !== "qwen3-embedding:0.6b" ) return undefined; if (source?.diagnostics !== undefined && !diagnostics) return undefined; if (source?.evidence !== undefined && !evidence) return undefined; if (diagnostics?.dwh_rest && !dwhTransports.includes("rest_api")) return undefined; return { workspace: { schema_version: 3, id, name, ...(description ? { description } : {}), language, }, dwh: { engine: "postgres", database, schema, ...(dwhPort ? { port: dwhPort } : {}), ...(dwhTimeout ? { timeout_ms: dwhTimeout } : {}), supported_transports: dwhTransports, }, semantic_index: { vector_store: { engine: "qdrant", collection, dimensions: 1024, distance: "cosine", }, embedding: { provider: "ollama_internal", model: "qwen3-embedding:0.6b", dimensions: 1024, }, }, llm_policy: { ...(defaultModel ? { default: defaultModel } : {}), allowed: allowedModels, }, ...(diagnostics ? { diagnostics } : {}), ...(evidence ? { evidence } : {}), }; }