@dipertq/dsh-openviking-status 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/index.tsx","../src/client/OpenVikingStatusChip.tsx","../src/client/api.ts","../src/client/recallParser.ts","../src/client/OpenVikingStatusPopover.tsx"],"sourcesContent":["import { OpenVikingStatusChip } from \"./OpenVikingStatusChip\";\n\nexport * from \"./api\";\nexport * from \"./recallParser\";\nexport * from \"./OpenVikingStatusChip\";\nexport * from \"./OpenVikingStatusPopover\";\n\nexport type OpenVikingSessionData = import(\"./api\").SessionStatus;\nexport type OpenVikingHealth = import(\"./api\").HealthStatus;\n\n/** Имя пакета, под которым DSH регистрирует клиентскую половину плагина. */\nexport const name = \"@dipertq/dsh-openviking-status\";\n\n/** Сервисы Cordis, которые должны быть готовы до вызова `apply`. */\nexport const inject = [\"slots\"];\n\n/**\n * Точка входа клиентского плагина DSH.\n *\n * Занимает одну ячейку в `conversation.input.right` — списочном слоте строки\n * ввода со скоупом сессии. Скоуп означает, что `sessionId` приходит в компонент\n * как стандартный проп: доставать его из глобального стора не требуется.\n */\nexport function apply(ctx: any) {\n ctx.effect(\n () =>\n ctx.slots.inject(\"conversation.input.right\", () =>\n ctx.slots.register(\n {\n name: \"conversation.input.right\",\n id: \"openviking-status\",\n order: 50,\n label: \"OpenViking\",\n },\n OpenVikingStatusChip\n )\n ),\n \"openviking-status: composer chip\"\n );\n}\n","import React, {\n useState,\n useEffect,\n useCallback,\n useRef,\n useMemo,\n} from \"react\";\nimport {\n HealthStatus,\n SessionStatus,\n OpenVikingClient,\n defaultOpenVikingClient,\n} from \"./api\";\nimport { RecalledMemoriesResult, parseRecalledMemories } from \"./recallParser\";\nimport { OpenVikingStatusPopover } from \"./OpenVikingStatusPopover\";\n\nexport const COMMIT_THRESHOLD = 20000;\n\nexport interface OpenVikingStatusChipProps {\n sessionId: string;\n messages?: any[];\n contextText?: string;\n client?: OpenVikingClient;\n onCommit?: () => void;\n className?: string;\n initialHealth?: HealthStatus;\n initialSessionData?: SessionStatus;\n initialOpen?: boolean;\n}\n\n/**\n * Форматирование количества токенов в килотокены (Math.round(tokens / 1000)k pend).\n */\nexport function formatPendingTokens(pendingTokens: number): string {\n const k = Math.round((pendingTokens || 0) / 1000);\n return `${k}k pend`;\n}\n\n/**\n * Формирование текстового ярлыка статусного чипа.\n * В онлайне: \"OV: <recalled> rec · <pending>k pend\"\n * В офлайне: \"OV offline\"\n */\nexport function formatStatusLabel(\n isOnline: boolean,\n recalledCount: number,\n pendingTokens: number\n): string {\n if (!isOnline) {\n return \"OV offline\";\n }\n return `OV: ${recalledCount} rec · ${formatPendingTokens(pendingTokens)}`;\n}\n\n/**\n * Формирование доступного заголовка / подсказки (title / aria-label).\n */\nexport function formatTooltipTitle({\n isOnline,\n isCommitting = false,\n recalledCount,\n pendingTokens,\n}: {\n isOnline: boolean;\n isCommitting?: boolean;\n recalledCount: number;\n pendingTokens: number;\n}): string {\n if (!isOnline) {\n return \"OpenViking: Offline\";\n }\n const countLabel = `${recalledCount} recalled`;\n const tokenLabel = `${(pendingTokens || 0).toLocaleString()} pending tokens`;\n if (isCommitting) {\n return `OpenViking: Committing... (${countLabel}, ${tokenLabel})`;\n }\n return `OpenViking: Online (${countLabel}, ${tokenLabel})`;\n}\n\n/**\n * Определение цвета индикатора состояния:\n * - Офлайн: красный\n * - Коммит: желтый / янтарный\n * - Онлайн: зеленый\n */\nexport function getStatusIndicatorColor(\n isOnline: boolean,\n isCommitting: boolean = false\n): string {\n if (!isOnline) {\n return \"var(--dsw-status-error, #f87171)\";\n }\n if (isCommitting) {\n return \"var(--dsw-status-warning, #fbbf24)\";\n }\n return \"var(--dsw-status-success, #34d399)\";\n}\n\n/**\n * Определение свечения индикатора:\n * Мягкое свечение только при статусе Online.\n */\nexport function getStatusGlow(\n isOnline: boolean,\n isCommitting: boolean = false\n): string {\n if (isOnline && !isCommitting) {\n return \"0 0 6px var(--dsw-status-success, #34d399)\";\n }\n return \"none\";\n}\n\n/**\n * Извлечение сообщений сессии из глобального хранилища DSH (Redux store или window fallback).\n */\nexport function getFallbackSessionMessages(\n sessionId: string\n): any[] | undefined {\n if (!sessionId || typeof window === \"undefined\") {\n return undefined;\n }\n const win = window as any;\n if (win.__DSH_STORE__?.getState) {\n const state = win.__DSH_STORE__.getState();\n return (\n state?.conversations?.[sessionId]?.messages ||\n state?.sessions?.[sessionId]?.messages\n );\n }\n if (win.__DSH_SESSION_MESSAGES__?.[sessionId]) {\n return win.__DSH_SESSION_MESSAGES__[sessionId];\n }\n return undefined;\n}\n\n/**\n * Status Chip UI-компонент отображения состояния памяти OpenViking.\n * Формат в соответствии с CONTEXT.md:\n * 🟢 OV: <recalled> rec · <pending>k pend (или OV offline при недоступности сервиса).\n */\nexport function OpenVikingStatusChip({\n sessionId,\n messages,\n contextText,\n client,\n onCommit,\n className,\n initialHealth,\n initialSessionData,\n initialOpen = false,\n}: OpenVikingStatusChipProps) {\n const [health, setHealth] = useState<HealthStatus | null>(\n initialHealth ?? null\n );\n const [sessionData, setSessionData] = useState<SessionStatus | null>(\n initialSessionData ?? null\n );\n const [isOpen, setIsOpen] = useState(initialOpen);\n const [isCommitting, setIsCommitting] = useState(false);\n const [commitError, setCommitError] = useState<string | null>(null);\n const popoverRef = useRef<HTMLDivElement>(null);\n\n const apiClient = client ?? defaultOpenVikingClient;\n\n // Извлечение сообщений из глобального хранилища DSH, если они не были переданы напрямую в props\n const fallbackMessages = useMemo(() => {\n if (messages || contextText) return undefined;\n return getFallbackSessionMessages(sessionId);\n }, [sessionId, messages, contextText]);\n\n // Объединение доступного контекста для парсинга воспоминаний\n const inputForParser = useMemo(() => {\n if (contextText && messages) {\n return [contextText, ...messages];\n }\n return contextText ?? messages ?? fallbackMessages;\n }, [contextText, messages, fallbackMessages]);\n\n // Парсинг воспоминаний OpenViking\n const recalledResult: RecalledMemoriesResult = useMemo(\n () => parseRecalledMemories(inputForParser),\n [inputForParser]\n );\n\n const fetchStatus = useCallback(async () => {\n try {\n const healthRes = await apiClient.checkHealth();\n setHealth(healthRes);\n\n if (!healthRes.ok) {\n return;\n }\n\n const session = await apiClient.fetchSession(sessionId);\n setSessionData(session);\n } catch {\n setHealth({ ok: false });\n }\n }, [sessionId, apiClient]);\n\n useEffect(() => {\n fetchStatus();\n const timer = setInterval(fetchStatus, 15000);\n return () => clearInterval(timer);\n }, [fetchStatus]);\n\n // Закрытие всплывающего окна при клике вне области виджета\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n }\n function handleKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n setIsOpen(false);\n }\n }\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n document.addEventListener(\"keydown\", handleKeyDown);\n }\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [isOpen]);\n\n const handleCommitNow = async () => {\n if (isCommitting) return;\n setIsCommitting(true);\n setCommitError(null);\n try {\n const res = await apiClient.commitSession(sessionId, {\n keep_recent_count: 10,\n });\n if (res.ok) {\n await fetchStatus();\n onCommit?.();\n } else {\n setCommitError(res.error || \"Commit failed\");\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n setCommitError(msg || \"Failed to commit session\");\n console.error(\"[dsh-openviking-status] Failed to commit session:\", e);\n } finally {\n setIsCommitting(false);\n }\n };\n\n const isOnline = health?.ok === true;\n const pendingTokens = sessionData?.pending_tokens ?? 0;\n const pendingTokensK = Math.round(pendingTokens / 1000);\n const recalledCount = recalledResult.recalledCount;\n\n const statusColor = getStatusIndicatorColor(isOnline, isCommitting);\n const statusGlow = getStatusGlow(isOnline, isCommitting);\n const tooltipTitle = formatTooltipTitle({\n isOnline,\n isCommitting,\n recalledCount,\n pendingTokens,\n });\n\n return (\n <div\n className={className}\n style={{\n position: \"relative\",\n display: \"inline-flex\",\n alignItems: \"center\",\n }}\n ref={popoverRef}\n >\n {/* Compact Status Chip */}\n <button\n type=\"button\"\n onClick={() => setIsOpen(!isOpen)}\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"6px\",\n height: \"26px\",\n padding: \"0 8px\",\n fontSize: \"12px\",\n fontFamily: \"var(--dsw-font-mono, monospace)\",\n borderRadius: \"6px\",\n background: \"var(--dsw-surface-base, rgba(255, 255, 255, 0.05))\",\n border:\n \"1px solid var(--dsw-border-subtle, rgba(255, 255, 255, 0.1))\",\n color: \"var(--dsw-text-muted, #94a3b8)\",\n cursor: \"pointer\",\n transition: \"all 0.15s ease\",\n userSelect: \"none\",\n }}\n title={tooltipTitle}\n aria-label={tooltipTitle}\n >\n <span\n data-testid=\"status-dot\"\n style={{\n width: \"7px\",\n height: \"7px\",\n borderRadius: \"50%\",\n backgroundColor: statusColor,\n boxShadow: statusGlow,\n flexShrink: 0,\n }}\n />\n <span\n style={{ fontWeight: 600, color: \"var(--dsw-text-default, #e2e8f0)\" }}\n >\n {isOnline ? \"OV:\" : \"OV\"}\n </span>{\" \"}\n <span>\n {isOnline\n ? `${recalledCount} rec · ${pendingTokensK}k pend`\n : \"offline\"}\n </span>\n </button>\n\n {/* Popover Details */}\n {isOpen && (\n <OpenVikingStatusPopover\n sessionId={sessionId}\n health={health}\n sessionData={sessionData}\n recalledResult={recalledResult}\n endpoint={apiClient.endpoint}\n isCommitting={isCommitting}\n commitError={commitError}\n onCommitNow={handleCommitNow}\n onClose={() => setIsOpen(false)}\n />\n )}\n </div>\n );\n}\n","/**\n * Клиент OpenViking Session REST API и доменные типы.\n * Поддерживает проверку здоровья сервиса, получение состояния сессии\n * с разрешением префиксов идентификаторов (dsh-session-* / dsh-*) и выполнение коммита.\n */\n\n/**\n * Статус подключения и здоровья сервиса OpenViking\n */\nexport interface HealthStatus {\n ok: boolean;\n version?: string;\n storage?: string;\n error?: string;\n}\n\n/**\n * Состояние активной сессии OpenViking в соответствии с CONTEXT.md\n */\nexport interface SessionStatus {\n session_id: string;\n peer_id?: string;\n pending_tokens: number;\n message_count?: number;\n commit_count?: number;\n last_commit_at?: string;\n created_at?: string;\n updated_at?: string;\n}\n\n/**\n * Параметры операции коммита сессии\n */\nexport interface CommitOptions {\n keep_recent_count?: number;\n}\n\n/**\n * Результат операции коммита сессии\n */\nexport interface CommitResult {\n ok: boolean;\n error?: string;\n}\n\n/**\n * Синонимы доменных понятий из CONTEXT.md\n */\nexport type PendingTokens = number;\nexport type PeerId = string;\n\n/**\n * Базовый URL по умолчанию для локального демона OpenViking\n */\nexport const DEFAULT_OPENVIKING_ENDPOINT = \"http://127.0.0.1:1933\";\n\n/**\n * Определение эндпоинта OpenViking из параметров, глобального контекста или localStorage\n */\nexport function resolveEndpoint(endpoint?: string): string {\n if (endpoint && endpoint.trim().length > 0) {\n return endpoint.trim().replace(/\\/+$/, \"\");\n }\n\n if (typeof window !== \"undefined\") {\n const win = window as unknown as Record<string, unknown>;\n if (\n typeof win.__OPENVIKING_ENDPOINT__ === \"string\" &&\n win.__OPENVIKING_ENDPOINT__.trim()\n ) {\n return win.__OPENVIKING_ENDPOINT__.trim().replace(/\\/+$/, \"\");\n }\n }\n\n if (typeof localStorage !== \"undefined\") {\n try {\n const stored =\n localStorage.getItem(\"openviking_endpoint\") ||\n localStorage.getItem(\"OPENVIKING_ENDPOINT\");\n if (stored && stored.trim()) {\n return stored.trim().replace(/\\/+$/, \"\");\n }\n } catch {\n // Игнорируем ошибки доступа к хранилищу (security sandbox, incognito)\n }\n }\n\n return DEFAULT_OPENVIKING_ENDPOINT;\n}\n\n/**\n * Определение API-ключа из параметров, глобального контекста или localStorage\n */\nexport function resolveApiKey(apiKey?: string): string | undefined {\n if (apiKey && apiKey.trim().length > 0) {\n return apiKey.trim();\n }\n\n if (typeof window !== \"undefined\") {\n const win = window as unknown as Record<string, unknown>;\n if (\n typeof win.__OPENVIKING_API_KEY__ === \"string\" &&\n win.__OPENVIKING_API_KEY__.trim()\n ) {\n return win.__OPENVIKING_API_KEY__.trim();\n }\n }\n\n if (typeof localStorage !== \"undefined\") {\n try {\n const stored =\n localStorage.getItem(\"openviking_api_key\") ||\n localStorage.getItem(\"OPENVIKING_API_KEY\");\n if (stored && stored.trim()) {\n return stored.trim();\n }\n } catch {\n // Игнорируем ошибки доступа к хранилищу\n }\n }\n\n return undefined;\n}\n\n/**\n * Клиент REST API для взаимодействия с сессиями демона OpenViking\n */\nexport class OpenVikingClient {\n readonly endpoint: string;\n readonly apiKey?: string;\n private resolvedSessionIds = new Map<string, string>();\n\n constructor(endpoint?: string, apiKey?: string) {\n this.endpoint = resolveEndpoint(endpoint);\n this.apiKey = resolveApiKey(apiKey);\n }\n\n /**\n * Формирование заголовков запроса, включая опциональный заголовок авторизации\n */\n private getHeaders(\n customHeaders?: Record<string, string>\n ): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...customHeaders,\n };\n if (this.apiKey) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n return headers;\n }\n\n /**\n * Генерация кандидатов ID сессии для fallback-разрешения:\n * dsh-session-${id} <-> dsh-${id}\n */\n private getCandidateSessionIds(sessionId: string): string[] {\n const raw = sessionId.trim();\n const candidates: string[] = [];\n\n const cached = this.resolvedSessionIds.get(raw);\n if (cached) {\n candidates.push(cached);\n }\n\n if (raw.startsWith(\"dsh-session-\")) {\n const suffix = raw.slice(\"dsh-session-\".length);\n candidates.push(raw, `dsh-${suffix}`);\n } else if (raw.startsWith(\"dsh-\")) {\n const suffix = raw.slice(\"dsh-\".length);\n candidates.push(raw, `dsh-session-${suffix}`);\n } else {\n // Если префикс dsh- отсутствует, сначала пробуем dsh-session-, затем dsh-, затем исходный raw\n candidates.push(`dsh-session-${raw}`, `dsh-${raw}`, raw);\n }\n\n // Удаление дубликатов с сохранением порядка следования\n return Array.from(new Set(candidates));\n }\n\n /**\n * Проверка доступности и состояния сервиса OpenViking\n */\n async checkHealth(): Promise<HealthStatus> {\n try {\n const res = await fetch(`${this.endpoint}/health`, {\n method: \"GET\",\n headers: this.getHeaders(),\n });\n\n if (!res.ok) {\n return {\n ok: false,\n error: `HTTP ${res.status}: ${res.statusText}`,\n };\n }\n\n const body = (await res.json().catch(() => ({}))) as Record<\n string,\n unknown\n >;\n const isOk =\n body.ok !== false &&\n body.status !== \"error\" &&\n (body.ok === true ||\n body.status === \"ok\" ||\n body.status === \"healthy\" ||\n res.ok);\n\n return {\n ok: isOk,\n version: typeof body.version === \"string\" ? body.version : undefined,\n storage: typeof body.storage === \"string\" ? body.storage : undefined,\n };\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /**\n * Получение метаданных сессии по идентификатору с автоматическим разрешением префикса.\n * При сетевых сбоях или ошибках авторизации возвращает null, не выбрасывая исключений.\n */\n async fetchSession(sessionId: string): Promise<SessionStatus | null> {\n if (!sessionId || !sessionId.trim()) {\n return null;\n }\n\n const candidates = this.getCandidateSessionIds(sessionId);\n\n for (const candidateId of candidates) {\n try {\n const res = await fetch(\n `${this.endpoint}/api/v1/sessions/${encodeURIComponent(candidateId)}`,\n {\n method: \"GET\",\n headers: this.getHeaders(),\n }\n );\n\n if (res.status === 404) {\n // Пробуем альтернативный формат идентификатора сессии\n continue;\n }\n\n if (!res.ok) {\n // Ошибки авторизации или внутренние сбои сервера\n return null;\n }\n\n const data = (await res.json()) as Record<string, unknown>;\n const raw = (data?.result ?? data?.data ?? data) as Record<\n string,\n unknown\n >;\n\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n\n // Запоминаем успешно разрешенный идентификатор\n this.resolvedSessionIds.set(sessionId.trim(), candidateId);\n\n return {\n session_id:\n typeof raw.session_id === \"string\" ? raw.session_id : candidateId,\n peer_id: typeof raw.peer_id === \"string\" ? raw.peer_id : undefined,\n pending_tokens:\n typeof raw.pending_tokens === \"number\" ? raw.pending_tokens : 0,\n message_count:\n typeof raw.message_count === \"number\"\n ? raw.message_count\n : undefined,\n commit_count:\n typeof raw.commit_count === \"number\" ? raw.commit_count : undefined,\n last_commit_at:\n typeof raw.last_commit_at === \"string\"\n ? raw.last_commit_at\n : typeof raw.last_commit === \"string\"\n ? raw.last_commit\n : undefined,\n created_at:\n typeof raw.created_at === \"string\" ? raw.created_at : undefined,\n updated_at:\n typeof raw.updated_at === \"string\" ? raw.updated_at : undefined,\n };\n } catch {\n // Сетевая ошибка или недоступность хоста\n return null;\n }\n }\n\n return null;\n }\n\n /**\n * Алиас для fetchSession\n */\n async getSession(sessionId: string): Promise<SessionStatus | null> {\n return this.fetchSession(sessionId);\n }\n\n /**\n * Инициация фиксации (коммита) накопленных токенов сессии в долговременную память\n */\n async commitSession(\n sessionId: string,\n options?: CommitOptions\n ): Promise<CommitResult> {\n if (!sessionId || !sessionId.trim()) {\n return { ok: false, error: \"Missing sessionId\" };\n }\n\n const candidates = this.getCandidateSessionIds(sessionId);\n const bodyPayload = JSON.stringify(options ?? { keep_recent_count: 10 });\n let lastError = \"Session commit failed\";\n\n for (const candidateId of candidates) {\n try {\n const res = await fetch(\n `${this.endpoint}/api/v1/sessions/${encodeURIComponent(candidateId)}/commit`,\n {\n method: \"POST\",\n headers: this.getHeaders(),\n body: bodyPayload,\n }\n );\n\n if (res.status === 404) {\n lastError = `Session not found: ${candidateId}`;\n continue;\n }\n\n if (!res.ok) {\n const errBody = (await res.json().catch(() => null)) as Record<\n string,\n unknown\n > | null;\n const errorMsg =\n (errBody?.error as Record<string, unknown>)?.message ||\n errBody?.message ||\n errBody?.error ||\n `HTTP ${res.status}: ${res.statusText}`;\n return { ok: false, error: String(errorMsg) };\n }\n\n this.resolvedSessionIds.set(sessionId.trim(), candidateId);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n return { ok: false, error: lastError };\n }\n}\n\n/**\n * Экземпляр клиента по умолчанию с автоматическим определением конфигурации\n */\nexport const defaultOpenVikingClient = new OpenVikingClient();\n\n/**\n * Вспомогательная функция проверки доступности OpenViking\n */\nexport function checkHealth(\n endpoint?: string,\n apiKey?: string\n): Promise<HealthStatus> {\n const client =\n endpoint || apiKey\n ? new OpenVikingClient(endpoint, apiKey)\n : defaultOpenVikingClient;\n return client.checkHealth();\n}\n\n/**\n * Вспомогательная функция получения состояния сессии\n */\nexport function fetchSession(\n sessionId: string,\n endpoint?: string,\n apiKey?: string\n): Promise<SessionStatus | null> {\n const client =\n endpoint || apiKey\n ? new OpenVikingClient(endpoint, apiKey)\n : defaultOpenVikingClient;\n return client.fetchSession(sessionId);\n}\n\n/**\n * Синоним для fetchSession\n */\nexport function getSession(\n sessionId: string,\n endpoint?: string,\n apiKey?: string\n): Promise<SessionStatus | null> {\n return fetchSession(sessionId, endpoint, apiKey);\n}\n\n/**\n * Вспомогательная функция выполнения коммита сессии\n */\nexport function commitSession(\n sessionId: string,\n options?: CommitOptions,\n endpoint?: string,\n apiKey?: string\n): Promise<CommitResult> {\n const client =\n endpoint || apiKey\n ? new OpenVikingClient(endpoint, apiKey)\n : defaultOpenVikingClient;\n return client.commitSession(sessionId, options);\n}\n","export interface RecalledMemoryItem {\n uri: string;\n category?: string;\n source: \"profile\" | \"recall\";\n score?: number;\n abstract?: string;\n}\n\nexport interface RecalledMemoriesResult {\n recalledCount: number;\n items: RecalledMemoryItem[];\n profileItems: RecalledMemoryItem[];\n recallItems: RecalledMemoryItem[];\n}\n\nconst KNOWN_CATEGORIES = new Set([\n \"preferences\",\n \"entities\",\n \"events\",\n \"skills\",\n \"resources\",\n \"profile\",\n]);\n\n/**\n * Infer the memory category from a viking:// URI.\n * Examples:\n * - viking://user/dsh/memories/preferences/... -> preferences\n * - viking://user/dsh/memories/entities/... -> entities\n * - viking://user/dsh/peers/user@example.com/memories/events/... -> events\n * - viking://user/dsh/skills/... -> skills\n * - viking://user/dsh/resources/... -> resources\n */\nexport function inferCategory(uri: string): string | undefined {\n if (!uri || typeof uri !== \"string\") return undefined;\n\n // 1. Direct match on /memories/<category>/ or /memories/<category>$\n const memoriesMatch = uri.match(/\\/memories\\/([^/#?]+)/i);\n if (memoriesMatch && memoriesMatch[1]) {\n return memoriesMatch[1].toLowerCase();\n }\n\n // 2. Strip viking:// and inspect path segments\n const cleanUri = uri.replace(/^viking:\\/\\/?/i, \"\");\n const segments = cleanUri.split(\"/\").filter(Boolean);\n\n // Check if any path segment matches a known category\n for (const seg of segments) {\n const lower = seg.toLowerCase();\n if (KNOWN_CATEGORIES.has(lower)) {\n return lower;\n }\n }\n\n // 3. Pattern: user/<user_id>/<category>/...\n if (segments[0]?.toLowerCase() === \"user\" && segments.length >= 3) {\n return segments[2].toLowerCase();\n }\n\n // 4. Pattern: <category>/...\n if (segments.length >= 2) {\n return segments[0].toLowerCase();\n }\n\n return undefined;\n}\n\n/**\n * Safely extracts combined text from strings, message arrays, or objects.\n */\nfunction extractAllText(input: unknown): string {\n if (input == null) return \"\";\n if (typeof input === \"string\") return input;\n if (typeof input === \"number\" || typeof input === \"boolean\")\n return String(input);\n if (Array.isArray(input)) {\n return input.map(extractAllText).filter(Boolean).join(\"\\n\");\n }\n if (typeof input === \"object\") {\n const obj = input as Record<string, unknown>;\n const parts: string[] = [];\n if (\"content\" in obj && obj.content != null) {\n parts.push(extractAllText(obj.content));\n }\n if (\"text\" in obj && obj.text != null) {\n parts.push(extractAllText(obj.text));\n }\n if (\"message\" in obj && obj.message != null) {\n parts.push(extractAllText(obj.message));\n }\n if (parts.length > 0) {\n return parts.join(\"\\n\");\n }\n try {\n return Object.values(obj).map(extractAllText).filter(Boolean).join(\"\\n\");\n } catch {\n return \"\";\n }\n }\n return \"\";\n}\n\ninterface ContextBlock {\n isProfile: boolean;\n attributes: string;\n content: string;\n}\n\n/**\n * Identifies <openviking-context> blocks, handling malformed and unclosed tags.\n */\nfunction findContextBlocks(text: string): ContextBlock[] {\n const blocks: ContextBlock[] = [];\n const openTagRegex = /<openviking-context\\b([^>]*)>/gi;\n let match: RegExpExecArray | null;\n\n const tagPositions: Array<{\n index: number;\n endIndex: number;\n attrs: string;\n }> = [];\n while ((match = openTagRegex.exec(text)) !== null) {\n tagPositions.push({\n index: match.index,\n endIndex: match.index + match[0].length,\n attrs: match[1] || \"\",\n });\n }\n\n // Fallback: If no <openviking-context> tag found, check for inner tags\n if (tagPositions.length === 0) {\n if (/<available-memories\\b/i.test(text)) {\n blocks.push({\n isProfile: true,\n attributes: 'source=\"profile\"',\n content: text,\n });\n } else if (/<memory\\b/i.test(text)) {\n blocks.push({\n isProfile: false,\n attributes: \"\",\n content: text,\n });\n }\n return blocks;\n }\n\n for (let i = 0; i < tagPositions.length; i++) {\n const current = tagPositions[i];\n const nextStart =\n i + 1 < tagPositions.length ? tagPositions[i + 1].index : text.length;\n const blockText = text.slice(current.endIndex, nextStart);\n\n const closeIndex = blockText.search(/<\\/openviking-context>/i);\n const content =\n closeIndex !== -1 ? blockText.slice(0, closeIndex) : blockText;\n\n const isProfile = /\\bsource\\s*=\\s*[\"']profile[\"']/i.test(current.attrs);\n blocks.push({\n isProfile,\n attributes: current.attrs,\n content,\n });\n }\n\n return blocks;\n}\n\n/**\n * Normalizes URI by trimming whitespace and trailing slash (except for bare scheme).\n */\nfunction normalizeUri(uri: string): string {\n let clean = uri.trim();\n if (clean.endsWith(\"/\") && !clean.endsWith(\"://\")) {\n clean = clean.slice(0, -1);\n }\n return clean;\n}\n\n/**\n * Parses profile items from an <openviking-context source=\"profile\"> block.\n */\nfunction parseProfileItems(blockContent: string): RecalledMemoryItem[] {\n const items: RecalledMemoryItem[] = [];\n\n // Extract content inside <available-memories>...</available-memories> if present\n let memContent = blockContent;\n const availMatch = blockContent.match(\n /<available-memories\\b[^>]*>([\\s\\S]*?)(?:<\\/available-memories>|$)/i\n );\n if (availMatch && availMatch[1]) {\n memContent = availMatch[1];\n }\n\n const lines = memContent.split(/\\r?\\n/);\n let currentBaseUri: string | null = null;\n\n for (const rawLine of lines) {\n const trimmed = rawLine.trim();\n if (!trimmed) continue;\n\n // Pattern 1: Directory prefix line:\n // e.g. \"viking://user/dsh/memories/preferences/\"\n // or \"- viking://user/dsh/memories/preferences/\"\n const baseMatch = trimmed.match(\n /^[-*•]?\\s*(viking:\\/\\/[^\\s<>\"'`]+?\\/)\\s*$/i\n );\n if (baseMatch) {\n currentBaseUri = baseMatch[1];\n continue;\n }\n\n // Pattern 2: Relative path under currentBaseUri:\n // e.g. \" - user@example.com/backup_strategy.md\"\n // \" - user/development workflow.md\"\n const bulletMatch = trimmed.match(/^[-*•]\\s+([^\\s<>\"'`].*?)$/);\n if (\n bulletMatch &&\n currentBaseUri &&\n !bulletMatch[1].startsWith(\"viking://\")\n ) {\n let relPath = bulletMatch[1].trim();\n // Remove any markdown links e.g. [name](...)\n relPath = relPath.replace(/^\\[([^\\]]+)\\](?:\\([^)]*\\))?/, \"$1\");\n relPath = relPath.replace(/[.,;:)\\]]+$/, \"\").trim();\n if (relPath) {\n const cleanBase = currentBaseUri.endsWith(\"/\")\n ? currentBaseUri\n : currentBaseUri + \"/\";\n const fullUri = normalizeUri(cleanBase + relPath.replace(/^\\/+/, \"\"));\n items.push({\n uri: fullUri,\n category: inferCategory(fullUri),\n source: \"profile\",\n });\n continue;\n }\n }\n\n // Pattern 3: Full viking:// URI on bullet point (handles potential spaces in filenames)\n const fullBulletMatch = trimmed.match(\n /^[-*•]\\s+(viking:\\/\\/[^<>\"'`\\r\\n]+)/i\n );\n if (fullBulletMatch) {\n let uri = fullBulletMatch[1].trim().replace(/[.,;:)\\]]+$/, \"\");\n if (uri.endsWith(\"/\")) {\n currentBaseUri = uri;\n } else {\n uri = normalizeUri(uri);\n items.push({\n uri,\n category: inferCategory(uri),\n source: \"profile\",\n });\n continue;\n }\n }\n\n // Pattern 4: Inline viking://[^\\s<>\"'`]+ matches\n const inlineMatches = trimmed.matchAll(/viking:\\/\\/[^\\s<>\"'`]+/gi);\n for (const m of inlineMatches) {\n let uri = m[0].replace(/[.,;:)\\]]+$/, \"\");\n if (uri.endsWith(\"/\")) {\n currentBaseUri = uri;\n } else {\n uri = normalizeUri(uri);\n items.push({\n uri,\n category: inferCategory(uri),\n source: \"profile\",\n });\n }\n }\n }\n\n return items;\n}\n\n/**\n * Parses recall items from an <openviking-context> block.\n */\nfunction parseRecallItems(blockContent: string): RecalledMemoryItem[] {\n const items: RecalledMemoryItem[] = [];\n\n // Match <memory ...> tags, handles self-closing and inner content\n const memoryTagRegex =\n /<memory\\b([^>]*?)(?:\\/>|>([\\s\\S]*?)(?:<\\/memory>|(?=<memory\\b)|$))/gi;\n let memMatch: RegExpExecArray | null;\n\n while ((memMatch = memoryTagRegex.exec(blockContent)) !== null) {\n const attrsString = memMatch[1] || \"\";\n const innerContent = memMatch[2] || \"\";\n\n // 1. Extract URI\n const uriMatch = attrsString.match(\n /\\buri\\s*=\\s*(?:[\"'](viking:\\/\\/[^\"']+)[\"']|(viking:\\/\\/[^\\s>]+))/i\n );\n if (!uriMatch) continue;\n const rawUri = uriMatch[1] || uriMatch[2];\n const uri = normalizeUri(rawUri);\n\n // 2. Extract score\n const scoreMatch = attrsString.match(\n /\\bscore\\s*=\\s*(?:[\"']([^\"']+)[\"']|([0-9.]+))/i\n );\n let score: number | undefined;\n const scoreStr = scoreMatch ? scoreMatch[1] || scoreMatch[2] : undefined;\n if (scoreStr) {\n const parsed = parseFloat(scoreStr);\n if (Number.isFinite(parsed)) {\n score = parsed;\n }\n }\n\n // 3. Extract explicit category/type attribute\n const typeMatch = attrsString.match(\n /\\b(?:type|category)\\s*=\\s*[\"']([^\"']+)[\"']/i\n );\n const explicitCategory = typeMatch ? typeMatch[1].trim() : undefined;\n const category = inferCategory(uri) || explicitCategory;\n\n // 4. Extract abstract (attribute or inner content)\n let abstractText: string | undefined;\n const abstractAttrMatch = attrsString.match(\n /\\babstract\\s*=\\s*[\"']([^\"']+)[\"']/i\n );\n if (abstractAttrMatch) {\n abstractText = abstractAttrMatch[1].trim();\n } else {\n const trimmedContent = innerContent.trim();\n if (trimmedContent) {\n abstractText = trimmedContent;\n }\n }\n\n items.push({\n uri,\n category,\n source: \"recall\",\n score,\n abstract: abstractText,\n });\n }\n\n return items;\n}\n\n/**\n * Parses conversation messages or text context to extract recalled OpenViking memories.\n *\n * Requirements:\n * - Extract profile items from <openviking-context source=\"profile\">...<available-memories>\n * - Extract recall items from <openviking-context>...<memory uri=\"...\">\n * - Infer category from URI\n * - Deduplicate by URI: if an item appears in both recall and profile, mark source: \"recall\"\n * - Ensure recalledCount === items.length\n */\nexport function parseRecalledMemories(\n input: string | any[] | null | undefined\n): RecalledMemoriesResult {\n if (input == null) {\n return {\n recalledCount: 0,\n items: [],\n profileItems: [],\n recallItems: [],\n };\n }\n\n const text = extractAllText(input);\n if (!text.trim()) {\n return {\n recalledCount: 0,\n items: [],\n profileItems: [],\n recallItems: [],\n };\n }\n\n const blocks = findContextBlocks(text);\n const rawProfileItems: RecalledMemoryItem[] = [];\n const rawRecallItems: RecalledMemoryItem[] = [];\n\n for (const block of blocks) {\n if (block.isProfile) {\n rawProfileItems.push(...parseProfileItems(block.content));\n } else {\n rawRecallItems.push(...parseRecallItems(block.content));\n }\n }\n\n // Deduplicate recall items by URI\n const recallMap = new Map<string, RecalledMemoryItem>();\n for (const item of rawRecallItems) {\n const existing = recallMap.get(item.uri);\n if (!existing) {\n recallMap.set(item.uri, { ...item });\n } else {\n if (existing.score === undefined && item.score !== undefined) {\n existing.score = item.score;\n } else if (existing.score !== undefined && item.score !== undefined) {\n existing.score = Math.max(existing.score, item.score);\n }\n if (!existing.abstract && item.abstract) {\n existing.abstract = item.abstract;\n }\n if (!existing.category && item.category) {\n existing.category = item.category;\n }\n }\n }\n\n // Deduplicate profile items by URI\n const profileMap = new Map<string, RecalledMemoryItem>();\n for (const item of rawProfileItems) {\n if (!profileMap.has(item.uri)) {\n profileMap.set(item.uri, { ...item });\n }\n }\n\n // Cross-deduplicate: if an item appears in both recall and profile, mark source: \"recall\"\n const itemsMap = new Map<string, RecalledMemoryItem>();\n\n // Add profile items first\n for (const [uri, item] of profileMap.entries()) {\n itemsMap.set(uri, item);\n }\n\n // Overlay recall items (promotes source to \"recall\" and attaches score/abstract)\n for (const [uri, item] of recallMap.entries()) {\n itemsMap.set(uri, item);\n }\n\n const items = Array.from(itemsMap.values());\n const profileItems = items.filter((it) => it.source === \"profile\");\n const recallItems = items.filter((it) => it.source === \"recall\");\n\n return {\n recalledCount: items.length,\n items,\n profileItems,\n recallItems,\n };\n}\n","import React, { useState, useEffect, useCallback, useRef } from \"react\";\nimport { HealthStatus, SessionStatus } from \"./api\";\nimport { RecalledMemoriesResult, RecalledMemoryItem } from \"./recallParser\";\nimport { COMMIT_THRESHOLD } from \"./OpenVikingStatusChip\";\n\nexport interface OpenVikingStatusPopoverProps {\n sessionId: string;\n health: HealthStatus | null;\n sessionData: SessionStatus | null;\n recalledResult?: RecalledMemoriesResult;\n endpoint?: string;\n isCommitting?: boolean;\n commitError?: string | null;\n onCommitNow?: () => Promise<void> | void;\n onClose?: () => void;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * Расчет процента заполнения пула накопленных токенов (0% - 100%).\n */\nexport function getProgressBarPercent(\n pendingTokens: number,\n threshold: number = COMMIT_THRESHOLD\n): number {\n if (!threshold || threshold <= 0) return 0;\n const ratio = (pendingTokens || 0) / threshold;\n return Math.min(100, Math.max(0, Math.round(ratio * 100)));\n}\n\n/**\n * Цветовой сдвиг шкалы токенов:\n * - зеленый (< 80%)\n * - желтый / янтарный (>= 80%)\n */\nexport function getProgressBarColor(percent: number): string {\n if (percent >= 80) {\n return \"var(--dsw-status-warning, #fbbf24)\";\n }\n return \"var(--dsw-status-success, #34d399)\";\n}\n\n/**\n * Форматирование относительного времени для метки последнего коммита (например, \"2m ago\").\n */\nexport function formatRelativeTime(\n isoOrTimestamp?: string | number | null,\n now: number = Date.now()\n): string | undefined {\n if (!isoOrTimestamp) return undefined;\n const date = new Date(isoOrTimestamp);\n const time = date.getTime();\n if (isNaN(time)) return String(isoOrTimestamp);\n\n const diffMs = now - time;\n if (diffMs < 0) return \"just now\";\n\n const diffSec = Math.floor(diffMs / 1000);\n if (diffSec < 60) return \"just now\";\n\n const diffMin = Math.floor(diffSec / 60);\n if (diffMin < 60) return `${diffMin}m ago`;\n\n const diffHour = Math.floor(diffMin / 60);\n if (diffHour < 24) return `${diffHour}h ago`;\n\n const diffDay = Math.floor(diffHour / 24);\n return `${diffDay}d ago`;\n}\n\n/**\n * Извлечение имени конечного файла или относительного пути из viking:// URI.\n * Например:\n * viking://user/dsh/memories/preferences/user/code_style.md -> user/code_style.md\n * viking://user/dsh/memories/entities/project/wrench_board.md -> project/wrench_board.md\n */\nexport function formatMemoryLeafName(uri: string): string {\n if (!uri) return \"\";\n const clean = uri.replace(/^viking:\\/\\/?/i, \"\");\n const memMatch = clean.match(/memories\\/[^/]+\\/(.+)$/i);\n if (memMatch && memMatch[1]) {\n return memMatch[1];\n }\n const segments = clean.split(\"/\").filter(Boolean);\n if (segments.length >= 2) {\n return segments.slice(-2).join(\"/\");\n }\n return segments[0] || clean;\n}\n\n/**\n * Форматирование адреса эндпоинта для отображения (удаляет протокол http:// / https://).\n */\nexport function formatEndpoint(endpoint?: string): string {\n if (!endpoint || !endpoint.trim()) {\n return \"127.0.0.1:1933\";\n }\n return endpoint.trim().replace(/^https?:\\/\\//, \"\");\n}\n\n/**\n * Усечение идентификатора сессии для компактного отображения.\n */\nexport function truncateSessionId(id: string, maxLen: number = 16): string {\n if (!id) return \"\";\n if (id.length <= maxLen) return id;\n return `${id.slice(0, maxLen)}...`;\n}\n\n/**\n * Получение стилей бейджа категории воспоминания.\n */\nexport function getCategoryBadgeStyle(category?: string): React.CSSProperties {\n switch (category?.toLowerCase()) {\n case \"preferences\":\n return {\n backgroundColor: \"rgba(168, 85, 247, 0.15)\",\n color: \"var(--dsw-status-purple, #c084fc)\",\n };\n case \"entities\":\n return {\n backgroundColor: \"rgba(59, 130, 246, 0.15)\",\n color: \"var(--dsw-status-info, #60a5fa)\",\n };\n case \"skills\":\n return {\n backgroundColor: \"rgba(236, 72, 153, 0.15)\",\n color: \"var(--dsw-status-pink, #f472b6)\",\n };\n case \"events\":\n return {\n backgroundColor: \"rgba(245, 158, 11, 0.15)\",\n color: \"var(--dsw-status-warning, #fbbf24)\",\n };\n case \"resources\":\n return {\n backgroundColor: \"rgba(20, 184, 166, 0.15)\",\n color: \"var(--dsw-status-teal, #2dd4bf)\",\n };\n default:\n return {\n backgroundColor: \"rgba(148, 163, 184, 0.15)\",\n color: \"var(--dsw-text-muted, #94a3b8)\",\n };\n }\n}\n\n/**\n * Обработка нажатия клавиши Escape для закрытия панели.\n */\nexport function handleEscapeKey(\n event: { key: string },\n onClose?: () => void\n): boolean {\n if (event.key === \"Escape\") {\n onClose?.();\n return true;\n }\n return false;\n}\n\n/**\n * Панель детального состояния контекстной памяти OpenViking (Status Popover).\n */\nexport function OpenVikingStatusPopover({\n sessionId,\n health,\n sessionData,\n recalledResult,\n endpoint,\n isCommitting = false,\n commitError = null,\n onCommitNow,\n onClose,\n className,\n style,\n}: OpenVikingStatusPopoverProps) {\n const [copied, setCopied] = useState(false);\n const copyTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Очистка таймера сброса статуса копирования при размонтировании\n useEffect(() => {\n return () => {\n if (copyTimeoutRef.current) {\n clearTimeout(copyTimeoutRef.current);\n }\n };\n }, []);\n\n const isOnline = health?.ok === true;\n const statusColor = isOnline\n ? \"var(--dsw-status-success, #34d399)\"\n : \"var(--dsw-status-error, #f87171)\";\n\n const displaySessionId = sessionData?.session_id || sessionId || \"\";\n const pendingTokens = sessionData?.pending_tokens ?? 0;\n const progressPercent = getProgressBarPercent(pendingTokens);\n const progressBarColor = getProgressBarColor(progressPercent);\n\n const memoryItems: RecalledMemoryItem[] = recalledResult?.items || [];\n const recalledCount = recalledResult?.recalledCount ?? memoryItems.length;\n\n const handleCopySessionId = useCallback(() => {\n if (!displaySessionId) return;\n if (typeof navigator !== \"undefined\" && navigator.clipboard) {\n navigator.clipboard.writeText(displaySessionId).catch(() => {});\n setCopied(true);\n if (copyTimeoutRef.current) {\n clearTimeout(copyTimeoutRef.current);\n }\n copyTimeoutRef.current = setTimeout(() => {\n setCopied(false);\n copyTimeoutRef.current = null;\n }, 1500);\n }\n }, [displaySessionId]);\n\n const isCommitDisabled = isCommitting || !isOnline || pendingTokens === 0;\n\n return (\n <div\n role=\"dialog\"\n aria-label=\"OpenViking Memory Details\"\n className={className}\n style={{\n position: \"absolute\",\n bottom: \"calc(100% + 8px)\",\n right: 0,\n width: \"320px\",\n backgroundColor: \"var(--dsw-surface-overlay, #1e293b)\",\n border: \"1px solid var(--dsw-border-default, #334155)\",\n borderRadius: \"8px\",\n padding: \"12px\",\n boxShadow: \"0 10px 25px -5px rgba(0, 0, 0, 0.5)\",\n zIndex: 1000,\n fontSize: \"12px\",\n color: \"var(--dsw-text-default, #f1f5f9)\",\n fontFamily: \"var(--dsw-font-sans, system-ui, sans-serif)\",\n boxSizing: \"border-box\",\n ...style,\n }}\n >\n <style>{`@keyframes ov-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`}</style>\n {/* Header: Title, Endpoint, Status Badge */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"flex-start\",\n marginBottom: \"10px\",\n paddingBottom: \"8px\",\n borderBottom:\n \"1px solid var(--dsw-border-subtle, rgba(255, 255, 255, 0.08))\",\n }}\n >\n <div>\n <div\n style={{\n fontWeight: 600,\n fontSize: \"13px\",\n color: \"var(--dsw-text-default, #f1f5f9)\",\n }}\n >\n OpenViking Memory\n </div>\n <div\n data-testid=\"endpoint-label\"\n style={{\n fontSize: \"10px\",\n color: \"var(--dsw-text-muted, #94a3b8)\",\n fontFamily: \"var(--dsw-font-mono, monospace)\",\n marginTop: \"1px\",\n }}\n >\n {formatEndpoint(endpoint)}\n </div>\n </div>\n\n <div\n data-testid=\"status-badge\"\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"5px\",\n fontSize: \"10px\",\n padding: \"2px 7px\",\n borderRadius: \"4px\",\n backgroundColor: isOnline\n ? \"rgba(52, 211, 153, 0.15)\"\n : \"rgba(248, 113, 113, 0.15)\",\n color: statusColor,\n fontWeight: 600,\n }}\n >\n <span\n data-testid=\"status-badge-dot\"\n style={{\n width: \"6px\",\n height: \"6px\",\n borderRadius: \"50%\",\n backgroundColor: statusColor,\n flexShrink: 0,\n }}\n />\n <span>\n {isOnline\n ? health?.version\n ? `ONLINE v${health.version}`\n : \"ONLINE\"\n : \"OFFLINE\"}\n </span>\n </div>\n </div>\n\n {/* Session Details: Session ID, Peer ID, Last Commit */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"6px\",\n marginBottom: \"10px\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n }}\n >\n <span style={{ color: \"var(--dsw-text-muted, #94a3b8)\" }}>\n Session ID:\n </span>\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"4px\",\n }}\n >\n <span\n data-testid=\"session-id-value\"\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleCopySessionId();\n }\n }}\n aria-label=\"Click to copy Session ID\"\n style={{\n fontFamily: \"var(--dsw-font-mono, monospace)\",\n cursor: \"pointer\",\n }}\n title={displaySessionId}\n onClick={handleCopySessionId}\n >\n {truncateSessionId(displaySessionId)}\n </span>\n <button\n type=\"button\"\n data-testid=\"copy-session-btn\"\n onClick={handleCopySessionId}\n title={copied ? \"Copied!\" : \"Copy Session ID\"}\n aria-label={copied ? \"Copied!\" : \"Copy Session ID\"}\n style={{\n background: \"none\",\n border: \"none\",\n cursor: \"pointer\",\n padding: \"2px 4px\",\n fontSize: \"10px\",\n color: copied\n ? \"var(--dsw-status-success, #34d399)\"\n : \"var(--dsw-text-muted, #94a3b8)\",\n borderRadius: \"3px\",\n }}\n >\n {copied ? \"✓\" : \"📋\"}\n </button>\n </div>\n </div>\n\n {sessionData?.peer_id && (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n }}\n >\n <span style={{ color: \"var(--dsw-text-muted, #94a3b8)\" }}>\n Peer ID:\n </span>\n <span\n data-testid=\"peer-id-value\"\n style={{\n maxWidth: \"180px\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n fontFamily: \"var(--dsw-font-mono, monospace)\",\n }}\n title={sessionData.peer_id}\n >\n {sessionData.peer_id}\n </span>\n </div>\n )}\n\n {sessionData?.last_commit_at && (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n }}\n >\n <span style={{ color: \"var(--dsw-text-muted, #94a3b8)\" }}>\n Last Commit:\n </span>\n <span\n data-testid=\"last-commit-value\"\n title={sessionData.last_commit_at}\n style={{ color: \"var(--dsw-text-default, #e2e8f0)\" }}\n >\n {formatRelativeTime(sessionData.last_commit_at) ||\n sessionData.last_commit_at}\n </span>\n </div>\n )}\n </div>\n\n {/* Pending Tokens & Progress Bar */}\n <div style={{ marginBottom: \"12px\" }}>\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n marginBottom: \"4px\",\n }}\n >\n <span style={{ color: \"var(--dsw-text-muted, #94a3b8)\" }}>\n Pending Tokens:\n </span>\n <span data-testid=\"pending-tokens-label\" style={{ fontWeight: 500 }}>\n {`${pendingTokens.toLocaleString()} / ${COMMIT_THRESHOLD.toLocaleString()}`}\n </span>\n </div>\n <div\n data-testid=\"progress-bar-track\"\n style={{\n width: \"100%\",\n height: \"6px\",\n borderRadius: \"3px\",\n backgroundColor: \"rgba(255, 255, 255, 0.1)\",\n overflow: \"hidden\",\n }}\n >\n <div\n data-testid=\"progress-bar-fill\"\n style={{\n width: `${progressPercent}%`,\n height: \"100%\",\n backgroundColor: progressBarColor,\n transition: \"width 0.3s ease, background-color 0.3s ease\",\n }}\n />\n </div>\n </div>\n\n {/* Recalled Memories Section */}\n <div style={{ marginBottom: \"12px\" }}>\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: \"4px\",\n }}\n >\n <span style={{ fontWeight: 600, fontSize: \"11px\" }}>\n {`Recalled Memories (${recalledCount})`}\n </span>\n </div>\n\n {memoryItems.length === 0 ? (\n <div\n data-testid=\"empty-memories-message\"\n style={{\n padding: \"8px 0\",\n color: \"var(--dsw-text-muted, #94a3b8)\",\n fontStyle: \"italic\",\n fontSize: \"11px\",\n textAlign: \"center\",\n }}\n >\n No memories recalled in this session\n </div>\n ) : (\n <div\n data-testid=\"recalled-memories-list\"\n style={{\n maxHeight: \"140px\",\n overflowY: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n paddingRight: \"2px\",\n }}\n >\n {memoryItems.map((item, idx) => (\n <div\n key={`${item.uri}-${idx}`}\n data-testid=\"recalled-memory-item\"\n title={item.uri}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"6px\",\n padding: \"4px 6px\",\n borderRadius: \"4px\",\n backgroundColor: \"rgba(255, 255, 255, 0.04)\",\n border:\n \"1px solid var(--dsw-border-subtle, rgba(255, 255, 255, 0.05))\",\n fontSize: \"11px\",\n }}\n >\n {/* Category Badge */}\n <span\n data-testid=\"memory-category-badge\"\n style={{\n fontSize: \"9px\",\n fontWeight: 600,\n padding: \"1px 4px\",\n borderRadius: \"3px\",\n textTransform: \"uppercase\",\n flexShrink: 0,\n ...getCategoryBadgeStyle(item.category),\n }}\n >\n {item.category || \"memory\"}\n </span>\n\n {/* Source Badge */}\n <span\n data-testid=\"memory-source-badge\"\n style={{\n fontSize: \"9px\",\n fontWeight: 600,\n padding: \"1px 4px\",\n borderRadius: \"3px\",\n textTransform: \"uppercase\",\n flexShrink: 0,\n ...(item.source === \"profile\"\n ? {\n backgroundColor: \"rgba(99, 102, 241, 0.15)\",\n color: \"#818cf8\",\n }\n : {\n backgroundColor: \"rgba(16, 185, 129, 0.15)\",\n color: \"var(--dsw-status-success, #34d399)\",\n }),\n }}\n >\n {item.source}\n </span>\n\n {/* Leaf Filename or Relative Path */}\n <span\n data-testid=\"memory-leaf-name\"\n style={{\n flex: 1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n fontFamily: \"var(--dsw-font-mono, monospace)\",\n color: \"var(--dsw-text-default, #f1f5f9)\",\n }}\n >\n {formatMemoryLeafName(item.uri)}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Commit Error Message (if any) */}\n {commitError && (\n <div\n data-testid=\"commit-error-message\"\n style={{\n marginBottom: \"8px\",\n padding: \"6px 8px\",\n borderRadius: \"4px\",\n backgroundColor: \"rgba(248, 113, 113, 0.1)\",\n border: \"1px solid var(--dsw-status-error, #f87171)\",\n color: \"var(--dsw-status-error, #f87171)\",\n fontSize: \"11px\",\n wordBreak: \"break-word\",\n }}\n >\n {commitError}\n </div>\n )}\n\n {/* Actions: Commit To Memory Now Button */}\n <button\n type=\"button\"\n data-testid=\"commit-now-btn\"\n onClick={() => onCommitNow?.()}\n disabled={isCommitDisabled}\n style={{\n width: \"100%\",\n padding: \"7px 0\",\n borderRadius: \"6px\",\n border: \"1px solid var(--dsw-border-default, #475569)\",\n backgroundColor: isCommitting\n ? \"var(--dsw-surface-active, #334155)\"\n : isCommitDisabled\n ? \"rgba(255, 255, 255, 0.03)\"\n : \"var(--dsw-surface-base, #1e293b)\",\n color: isCommitDisabled\n ? \"var(--dsw-text-muted, #64748b)\"\n : \"var(--dsw-text-default, #f8fafc)\",\n cursor: isCommitDisabled ? \"not-allowed\" : \"pointer\",\n fontWeight: 600,\n fontSize: \"12px\",\n transition: \"all 0.15s ease\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"6px\",\n }}\n >\n {isCommitting ? (\n <>\n <span\n style={{\n display: \"inline-block\",\n width: \"10px\",\n height: \"10px\",\n borderRadius: \"50%\",\n border: \"2px solid var(--dsw-status-warning, #fbbf24)\",\n borderTopColor: \"transparent\",\n animation: \"ov-spin 1s linear infinite\",\n }}\n />\n <span>Committing...</span>\n </>\n ) : (\n <span>Commit To Memory Now</span>\n )}\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime"]}
@@ -0,0 +1,277 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Клиент OpenViking Session REST API и доменные типы.
5
+ * Поддерживает проверку здоровья сервиса, получение состояния сессии
6
+ * с разрешением префиксов идентификаторов (dsh-session-* / dsh-*) и выполнение коммита.
7
+ */
8
+ /**
9
+ * Статус подключения и здоровья сервиса OpenViking
10
+ */
11
+ interface HealthStatus {
12
+ ok: boolean;
13
+ version?: string;
14
+ storage?: string;
15
+ error?: string;
16
+ }
17
+ /**
18
+ * Состояние активной сессии OpenViking в соответствии с CONTEXT.md
19
+ */
20
+ interface SessionStatus {
21
+ session_id: string;
22
+ peer_id?: string;
23
+ pending_tokens: number;
24
+ message_count?: number;
25
+ commit_count?: number;
26
+ last_commit_at?: string;
27
+ created_at?: string;
28
+ updated_at?: string;
29
+ }
30
+ /**
31
+ * Параметры операции коммита сессии
32
+ */
33
+ interface CommitOptions {
34
+ keep_recent_count?: number;
35
+ }
36
+ /**
37
+ * Результат операции коммита сессии
38
+ */
39
+ interface CommitResult {
40
+ ok: boolean;
41
+ error?: string;
42
+ }
43
+ /**
44
+ * Синонимы доменных понятий из CONTEXT.md
45
+ */
46
+ type PendingTokens = number;
47
+ type PeerId = string;
48
+ /**
49
+ * Базовый URL по умолчанию для локального демона OpenViking
50
+ */
51
+ declare const DEFAULT_OPENVIKING_ENDPOINT = "http://127.0.0.1:1933";
52
+ /**
53
+ * Определение эндпоинта OpenViking из параметров, глобального контекста или localStorage
54
+ */
55
+ declare function resolveEndpoint(endpoint?: string): string;
56
+ /**
57
+ * Определение API-ключа из параметров, глобального контекста или localStorage
58
+ */
59
+ declare function resolveApiKey(apiKey?: string): string | undefined;
60
+ /**
61
+ * Клиент REST API для взаимодействия с сессиями демона OpenViking
62
+ */
63
+ declare class OpenVikingClient {
64
+ readonly endpoint: string;
65
+ readonly apiKey?: string;
66
+ private resolvedSessionIds;
67
+ constructor(endpoint?: string, apiKey?: string);
68
+ /**
69
+ * Формирование заголовков запроса, включая опциональный заголовок авторизации
70
+ */
71
+ private getHeaders;
72
+ /**
73
+ * Генерация кандидатов ID сессии для fallback-разрешения:
74
+ * dsh-session-${id} <-> dsh-${id}
75
+ */
76
+ private getCandidateSessionIds;
77
+ /**
78
+ * Проверка доступности и состояния сервиса OpenViking
79
+ */
80
+ checkHealth(): Promise<HealthStatus>;
81
+ /**
82
+ * Получение метаданных сессии по идентификатору с автоматическим разрешением префикса.
83
+ * При сетевых сбоях или ошибках авторизации возвращает null, не выбрасывая исключений.
84
+ */
85
+ fetchSession(sessionId: string): Promise<SessionStatus | null>;
86
+ /**
87
+ * Алиас для fetchSession
88
+ */
89
+ getSession(sessionId: string): Promise<SessionStatus | null>;
90
+ /**
91
+ * Инициация фиксации (коммита) накопленных токенов сессии в долговременную память
92
+ */
93
+ commitSession(sessionId: string, options?: CommitOptions): Promise<CommitResult>;
94
+ }
95
+ /**
96
+ * Экземпляр клиента по умолчанию с автоматическим определением конфигурации
97
+ */
98
+ declare const defaultOpenVikingClient: OpenVikingClient;
99
+ /**
100
+ * Вспомогательная функция проверки доступности OpenViking
101
+ */
102
+ declare function checkHealth(endpoint?: string, apiKey?: string): Promise<HealthStatus>;
103
+ /**
104
+ * Вспомогательная функция получения состояния сессии
105
+ */
106
+ declare function fetchSession(sessionId: string, endpoint?: string, apiKey?: string): Promise<SessionStatus | null>;
107
+ /**
108
+ * Синоним для fetchSession
109
+ */
110
+ declare function getSession(sessionId: string, endpoint?: string, apiKey?: string): Promise<SessionStatus | null>;
111
+ /**
112
+ * Вспомогательная функция выполнения коммита сессии
113
+ */
114
+ declare function commitSession(sessionId: string, options?: CommitOptions, endpoint?: string, apiKey?: string): Promise<CommitResult>;
115
+
116
+ interface RecalledMemoryItem {
117
+ uri: string;
118
+ category?: string;
119
+ source: "profile" | "recall";
120
+ score?: number;
121
+ abstract?: string;
122
+ }
123
+ interface RecalledMemoriesResult {
124
+ recalledCount: number;
125
+ items: RecalledMemoryItem[];
126
+ profileItems: RecalledMemoryItem[];
127
+ recallItems: RecalledMemoryItem[];
128
+ }
129
+ /**
130
+ * Infer the memory category from a viking:// URI.
131
+ * Examples:
132
+ * - viking://user/dsh/memories/preferences/... -> preferences
133
+ * - viking://user/dsh/memories/entities/... -> entities
134
+ * - viking://user/dsh/peers/user@example.com/memories/events/... -> events
135
+ * - viking://user/dsh/skills/... -> skills
136
+ * - viking://user/dsh/resources/... -> resources
137
+ */
138
+ declare function inferCategory(uri: string): string | undefined;
139
+ /**
140
+ * Parses conversation messages or text context to extract recalled OpenViking memories.
141
+ *
142
+ * Requirements:
143
+ * - Extract profile items from <openviking-context source="profile">...<available-memories>
144
+ * - Extract recall items from <openviking-context>...<memory uri="...">
145
+ * - Infer category from URI
146
+ * - Deduplicate by URI: if an item appears in both recall and profile, mark source: "recall"
147
+ * - Ensure recalledCount === items.length
148
+ */
149
+ declare function parseRecalledMemories(input: string | any[] | null | undefined): RecalledMemoriesResult;
150
+
151
+ declare const COMMIT_THRESHOLD = 20000;
152
+ interface OpenVikingStatusChipProps {
153
+ sessionId: string;
154
+ messages?: any[];
155
+ contextText?: string;
156
+ client?: OpenVikingClient;
157
+ onCommit?: () => void;
158
+ className?: string;
159
+ initialHealth?: HealthStatus;
160
+ initialSessionData?: SessionStatus;
161
+ initialOpen?: boolean;
162
+ }
163
+ /**
164
+ * Форматирование количества токенов в килотокены (Math.round(tokens / 1000)k pend).
165
+ */
166
+ declare function formatPendingTokens(pendingTokens: number): string;
167
+ /**
168
+ * Формирование текстового ярлыка статусного чипа.
169
+ * В онлайне: "OV: <recalled> rec · <pending>k pend"
170
+ * В офлайне: "OV offline"
171
+ */
172
+ declare function formatStatusLabel(isOnline: boolean, recalledCount: number, pendingTokens: number): string;
173
+ /**
174
+ * Формирование доступного заголовка / подсказки (title / aria-label).
175
+ */
176
+ declare function formatTooltipTitle({ isOnline, isCommitting, recalledCount, pendingTokens, }: {
177
+ isOnline: boolean;
178
+ isCommitting?: boolean;
179
+ recalledCount: number;
180
+ pendingTokens: number;
181
+ }): string;
182
+ /**
183
+ * Определение цвета индикатора состояния:
184
+ * - Офлайн: красный
185
+ * - Коммит: желтый / янтарный
186
+ * - Онлайн: зеленый
187
+ */
188
+ declare function getStatusIndicatorColor(isOnline: boolean, isCommitting?: boolean): string;
189
+ /**
190
+ * Определение свечения индикатора:
191
+ * Мягкое свечение только при статусе Online.
192
+ */
193
+ declare function getStatusGlow(isOnline: boolean, isCommitting?: boolean): string;
194
+ /**
195
+ * Извлечение сообщений сессии из глобального хранилища DSH (Redux store или window fallback).
196
+ */
197
+ declare function getFallbackSessionMessages(sessionId: string): any[] | undefined;
198
+ /**
199
+ * Status Chip UI-компонент отображения состояния памяти OpenViking.
200
+ * Формат в соответствии с CONTEXT.md:
201
+ * 🟢 OV: <recalled> rec · <pending>k pend (или OV offline при недоступности сервиса).
202
+ */
203
+ declare function OpenVikingStatusChip({ sessionId, messages, contextText, client, onCommit, className, initialHealth, initialSessionData, initialOpen, }: OpenVikingStatusChipProps): React.JSX.Element;
204
+
205
+ interface OpenVikingStatusPopoverProps {
206
+ sessionId: string;
207
+ health: HealthStatus | null;
208
+ sessionData: SessionStatus | null;
209
+ recalledResult?: RecalledMemoriesResult;
210
+ endpoint?: string;
211
+ isCommitting?: boolean;
212
+ commitError?: string | null;
213
+ onCommitNow?: () => Promise<void> | void;
214
+ onClose?: () => void;
215
+ className?: string;
216
+ style?: React.CSSProperties;
217
+ }
218
+ /**
219
+ * Расчет процента заполнения пула накопленных токенов (0% - 100%).
220
+ */
221
+ declare function getProgressBarPercent(pendingTokens: number, threshold?: number): number;
222
+ /**
223
+ * Цветовой сдвиг шкалы токенов:
224
+ * - зеленый (< 80%)
225
+ * - желтый / янтарный (>= 80%)
226
+ */
227
+ declare function getProgressBarColor(percent: number): string;
228
+ /**
229
+ * Форматирование относительного времени для метки последнего коммита (например, "2m ago").
230
+ */
231
+ declare function formatRelativeTime(isoOrTimestamp?: string | number | null, now?: number): string | undefined;
232
+ /**
233
+ * Извлечение имени конечного файла или относительного пути из viking:// URI.
234
+ * Например:
235
+ * viking://user/dsh/memories/preferences/user/code_style.md -> user/code_style.md
236
+ * viking://user/dsh/memories/entities/project/wrench_board.md -> project/wrench_board.md
237
+ */
238
+ declare function formatMemoryLeafName(uri: string): string;
239
+ /**
240
+ * Форматирование адреса эндпоинта для отображения (удаляет протокол http:// / https://).
241
+ */
242
+ declare function formatEndpoint(endpoint?: string): string;
243
+ /**
244
+ * Усечение идентификатора сессии для компактного отображения.
245
+ */
246
+ declare function truncateSessionId(id: string, maxLen?: number): string;
247
+ /**
248
+ * Получение стилей бейджа категории воспоминания.
249
+ */
250
+ declare function getCategoryBadgeStyle(category?: string): React.CSSProperties;
251
+ /**
252
+ * Обработка нажатия клавиши Escape для закрытия панели.
253
+ */
254
+ declare function handleEscapeKey(event: {
255
+ key: string;
256
+ }, onClose?: () => void): boolean;
257
+ /**
258
+ * Панель детального состояния контекстной памяти OpenViking (Status Popover).
259
+ */
260
+ declare function OpenVikingStatusPopover({ sessionId, health, sessionData, recalledResult, endpoint, isCommitting, commitError, onCommitNow, onClose, className, style, }: OpenVikingStatusPopoverProps): React.JSX.Element;
261
+
262
+ type OpenVikingSessionData = SessionStatus;
263
+ type OpenVikingHealth = HealthStatus;
264
+ /** Имя пакета, под которым DSH регистрирует клиентскую половину плагина. */
265
+ declare const name = "@dipertq/dsh-openviking-status";
266
+ /** Сервисы Cordis, которые должны быть готовы до вызова `apply`. */
267
+ declare const inject: string[];
268
+ /**
269
+ * Точка входа клиентского плагина DSH.
270
+ *
271
+ * Занимает одну ячейку в `conversation.input.right` — списочном слоте строки
272
+ * ввода со скоупом сессии. Скоуп означает, что `sessionId` приходит в компонент
273
+ * как стандартный проп: доставать его из глобального стора не требуется.
274
+ */
275
+ declare function apply(ctx: any): void;
276
+
277
+ export { COMMIT_THRESHOLD, type CommitOptions, type CommitResult, DEFAULT_OPENVIKING_ENDPOINT, type HealthStatus, OpenVikingClient, type OpenVikingHealth, type OpenVikingSessionData, OpenVikingStatusChip, type OpenVikingStatusChipProps, OpenVikingStatusPopover, type OpenVikingStatusPopoverProps, type PeerId, type PendingTokens, type RecalledMemoriesResult, type RecalledMemoryItem, type SessionStatus, apply, checkHealth, commitSession, defaultOpenVikingClient, fetchSession, formatEndpoint, formatMemoryLeafName, formatPendingTokens, formatRelativeTime, formatStatusLabel, formatTooltipTitle, getCategoryBadgeStyle, getFallbackSessionMessages, getProgressBarColor, getProgressBarPercent, getSession, getStatusGlow, getStatusIndicatorColor, handleEscapeKey, inferCategory, inject, name, parseRecalledMemories, resolveApiKey, resolveEndpoint, truncateSessionId };