@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/OpenVikingStatusChip.tsx","../src/client/api.ts","../src/client/recallParser.ts","../src/client/OpenVikingStatusPopover.tsx","../src/client/index.tsx"],"sourcesContent":["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","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"],"mappings":"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","names":["useState","useEffect","useCallback","useRef","jsx","jsxs","useState","useRef","useCallback","useEffect"]}
package/lib/index.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * OpenViking Status Backend plugin definition for Cordis runtime
3
+ */
4
+ declare const name = "dsh-openviking-status";
5
+ declare const inject: string[];
6
+ declare function apply(ctx: any): void;
7
+
8
+ export { apply, inject, name };
package/lib/index.js ADDED
@@ -0,0 +1,12 @@
1
+ // src/index.ts
2
+ var name = "dsh-openviking-status";
3
+ var inject = ["sessions"];
4
+ function apply(ctx) {
5
+ ctx.logger?.info?.("[dsh-openviking-status] Plugin loaded");
6
+ }
7
+ export {
8
+ apply,
9
+ inject,
10
+ name
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * OpenViking Status Backend plugin definition for Cordis runtime\n */\n\nexport const name = \"dsh-openviking-status\";\nexport const inject = [\"sessions\"];\n\nexport function apply(ctx: any) {\n // Cordis lifecycle hooks and client module injection\n ctx.logger?.info?.(\"[dsh-openviking-status] Plugin loaded\");\n}\n"],"mappings":";AAIO,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,UAAU;AAE1B,SAAS,MAAM,KAAU;AAE9B,MAAI,QAAQ,OAAO,uCAAuC;AAC5D;","names":[]}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@dipertq/dsh-openviking-status",
3
+ "version": "0.1.5",
4
+ "description": "Minimalist OpenViking status chip and popover for DeepSeek Harness composer",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "module": "lib/index.js",
8
+ "types": "lib/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./lib/index.d.ts",
12
+ "default": "./lib/index.js"
13
+ },
14
+ "./client": {
15
+ "types": "./lib/client.d.cts",
16
+ "import": "./lib/client.js",
17
+ "default": "./lib/client.cjs"
18
+ },
19
+ "./cordis.patch.yml": "./cordis.patch.yml",
20
+ "./package.json": "./package.json"
21
+ },
22
+ "files": [
23
+ "lib",
24
+ "cordis.patch.yml",
25
+ "README.md",
26
+ "package.json"
27
+ ],
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ },
32
+ "client": {
33
+ "inject": [
34
+ "@deepseek-ai/dsh-client-ui-slots"
35
+ ],
36
+ "platform": "web"
37
+ }
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "pretest": "tsup",
42
+ "dev": "tsup --watch",
43
+ "test": "node --test tests/*.test.ts",
44
+ "prepare": "tsup && (husky || true)",
45
+ "prepack": "tsup",
46
+ "preversion": "pnpm run build && pnpm test",
47
+ "release:patch": "npm version patch && git push --follow-tags",
48
+ "release:minor": "npm version minor && git push --follow-tags",
49
+ "release:major": "npm version major && git push --follow-tags"
50
+ },
51
+ "keywords": [
52
+ "dsh-plugin",
53
+ "openviking",
54
+ "deepseek-harness",
55
+ "status-bar",
56
+ "memory"
57
+ ],
58
+ "author": "",
59
+ "license": "MIT",
60
+ "devDependencies": {
61
+ "@types/react": "^18.3.18",
62
+ "@types/react-dom": "^18.3.5",
63
+ "husky": "^9.1.7",
64
+ "lint-staged": "^17.5.1",
65
+ "prettier": "^3.9.8",
66
+ "react": "^18.3.1",
67
+ "react-dom": "^18.3.1",
68
+ "tsup": "^8.3.6",
69
+ "typescript": "^5.7.3"
70
+ },
71
+ "peerDependencies": {
72
+ "react": ">=18.0.0",
73
+ "react-dom": ">=18.0.0"
74
+ }
75
+ }