Adds the second wave of Hermes Workspace upgrades:
- Profiles screen backed by ~/.hermes/profiles with create/activate/rename/delete
- MCP Servers settings page with config inspection and reload flow
- Skills marketplace hub/fallback search plus install/uninstall API routes
- Context usage fixes: correct 1M limits for Claude 4.6 / GPT-5.4, exclude cache
tokens from active window usage, and improve session fallback
- Navigation updates for Profiles across desktop/mobile surfaces
- Remove duplicate legacy context meter in chat
This complements the Knowledge Browser work already on the branch and rounds out
this release into a broader workspace management update.
Adds a Knowledge tab alongside the existing Memory tab under /memory.
The knowledge browser renders markdown wiki pages from ~/.hermes/knowledge/
with support for:
- YAML frontmatter parsing (title, type, domain, status, tags)
- Directory-based navigation with folder tree sidebar
- [[wikilink]] resolution as clickable in-app navigation
- Backlinks ('Pages that link here')
- Full-text search across all wiki pages
- Tag filtering
- Metadata display (type, domain, status, created/updated)
- Graph view dialog showing page connections
- 'Ask agent about this' button linking to chat
- Graceful empty state when no knowledge directory exists
Server side:
- src/server/knowledge-browser.ts — wiki scanner, frontmatter parser,
wikilink extractor, graph builder
- 4 API routes: /api/knowledge/list, /read, /search, /graph
Frontend:
- src/routes/memory.tsx — tabbed layout (Memory | Knowledge)
- src/screens/memory/knowledge-browser-screen.tsx — full browser UI
Knowledge tab works independently of gateway capabilities (reads local
filesystem directly). No new dependencies added.
Closes#30
- Render ContextBar in chat screen (was imported but never placed)
- Pass sessionId to context-usage API for accurate token data
- Count all tokens (cached + uncached) for real context window usage
- Model-aware max tokens (200k for Claude, 128k for GPT)
- Return model name in context-usage response
- Remove header border-b (context bar replaces separator)
- Remove context bar own border-b for clean look
- Change dev server port from 3000 to 3002
Co-authored-by: outsourc-e <eric@outsourc.e>
The scripts/qa/test-redaction.ts file was referenced in CI but never
created. The step was added in 31a7062, removed in 051d136, then
accidentally reintroduced in 4f848a4. pnpm test (vitest) is the
actual test command and is sufficient.
Co-authored-by: dontcallmejames <dontcallmejames@users.noreply.github.com>
ModelCard component referenced sessionsAvailable from parent scope
but it's a separate function component. Added local useFeatureAvailable
call to fix the ReferenceError crash on the dashboard page.
When the Hermes gateway is running but the HTTP API server is not
enabled, the onboarding screen shows generic advice that doesn't
address the actual problem. Users can have a fully functional gateway
(serving Telegram, Discord, etc.) while the workspace can't connect.
Changes:
- Onboarding connection error now shows step-by-step instructions:
1. Add API_SERVER_ENABLED=true to ~/.hermes/.env
2. Restart the gateway
- Updated .env.example to document the requirement
- Added Ollama/LiteLLM/vLLM as explicit alternative backend options
Fixes#26
The inspector panel already uses relative /api/memory paths (fixed in main),
but the parent GET route was missing — requests fell through to the client
router and returned HTML instead of JSON.
This adds a proper TanStack createFileRoute handler that proxies memory
requests through the workspace server with auth, so the inspector works
correctly over Tailscale, LAN, and remote access.
Rebased from PR #4 onto current main.
The auth-check endpoint had its own isBackendReachable() function that
hardcoded http://127.0.0.1:8642 and never tried the fallback port 8643.
This bypassed the multi-port auto-detection logic in gateway-capabilities.ts,
causing the startup screen to loop on 'Connecting...' when the gateway
was running on an alternate port.
Replace the standalone reachability check with ensureGatewayProbed() which
already handles port auto-detection (8642 → 8643) and caches the result.
- Models grouped by provider (openai-codex, openrouter, anthropic, ollama, etc.)
- Pinned models section at top with star toggle (persisted to localStorage)
- Pin/unpin on hover for each model entry
- Active model shows accent border + dot indicator
- Local models show 'local' badge
- Empty state with helpful message
- Matches ClawSuite model switcher design on both mobile and desktop
Each model in the dropdown now sends its own provider (e.g. ollama for
local models, openai-codex for cloud) instead of the global current
provider. Local models show a 'local' badge.
modelsQuery.data is {ok, models, configuredProviders, ...} not a
Record<string, Array>. Object.entries iterated all fields and called
.map on non-array values like 'ok' and 'currentProvider'.
- Desktop: model badge is now clickable, opens dropdown with all available models
- Mobile: model sheet renders actual model list instead of hardcoded 'Hermes Agent'
- Handles both string and object ModelCatalogEntry types
- Highlights currently active model with accent dot
- Calls existing handleModelSelect/switchModel on selection
- Falls back gracefully when no models available (portable mode)
Adds dedicated section for portable mode (direct to Ollama) and
enhanced mode (through Hermes gateway with custom_providers config).
Updated troubleshooting for common Ollama issues.
Tailwind dark: prefix doesn't work with the data-theme system.
Replaced all dark: prefixes with var(--theme-text), var(--theme-muted),
var(--theme-card2), var(--theme-accent-border) for proper theming.
Quick action card labels now use var(--theme-text) directly.
Monaco loads its core from CDN asynchronously, causing the file preview
to show 'Loading...' indefinitely when the CDN is slow or blocked.
Replaced with a plain textarea — simpler, faster, no external deps.
Also fixed file tree depth (maxDepth 0→3) so folder contents are
visible on initial load.
Dashboard activity chart flatlined because these fields were never
mapped from the gateway session response to the frontend summary.
Emits both snake_case (dashboard compat) and camelCase variants.
auth-check previously only probed /health, which Ollama doesn't expose
(returns 404). This caused the ConnectionStartupScreen to permanently
block users from reaching the workspace — even when chat completions
and model listing both worked fine.
Now probes three endpoints in priority order:
1. /health (Hermes gateway)
2. /v1/models (OpenAI-compat — Ollama, LiteLLM, vLLM, etc.)
3. / root (Ollama returns 200 'Ollama is running')
If ANY responds, the backend is considered reachable and the workspace
loads. Capability detection still runs in the background to determine
which enhanced features (sessions, memory, skills) are available.
Fixes: users stuck on 'Connecting to your backend...' splash screen
when using local Ollama or any non-Hermes OpenAI-compatible backend.
Replaces static isFeatureAvailable() with useFeatureAvailable() React hook that fetches from /api/gateway-status. Fixes enhanced features always showing 'unavailable' after client hydration. Credit: @ClintMoody
Previously hermes-api.ts helper functions (hermesGet, hermesPost,
hermesPatch, hermesDeleteReq, streamChat) sent no Authorization header,
causing 401 'Invalid API key' errors when the gateway has API_SERVER_KEY
configured.
Also exports BEARER_TOKEN from gateway-capabilities.ts so hermes-api.ts
can import and reuse it for auth headers.
Co-authored-by: mgnyc11 <mgnyc11@users.noreply.github.com>
The composer used explicit light/dark background classes across
layout modes, so it now relies on the shared `bg-surface` token
and simpler border styling for more consistent theming.
Constraint: Must preserve existing layout behavior while changing only presentation
Rejected: Add a new composer-specific surface abstraction | unnecessary for a 1-file polish
Confidence: medium
Scope-risk: narrow
Reversibility: clean
Directive: Prefer shared theme tokens here unless design introduces a dedicated composer token
Tested: Not run locally
Not-tested: build, test, and manual light/dark visual QA
In portable mode, streaming state now reads directly from
useStreamingMessage's local React state instead of going through
the Zustand store. Eliminates timing bugs where messages got stuck
on 'Thinking...' or disappeared after streaming completed.
Also adds conversation history support — last 20 messages sent
with each request for multi-turn context.
Remaining: duplicate message on first render, model pill display
Three fixes for 'Thinking... forever' in portable mode:
1. derivedStreamingInfo: Don't report isStreaming=true when last message
is a complete (non-streaming) assistant message. Previously this caused
streamingButEmpty=true (isStreaming=true, streamingText=undefined) which
kept ThinkingBubble visible even after the response arrived.
2. waitingForResponse clearing: Track last assistant message identity at
send time (not just count). Fast Ollama responses arrive in the same
React batch as waitingForResponse=true, so finalDisplayMessages.length
never grows (was already 2 when the effect snapshotted it). Now we also
check if the assistant message identity changed.
3. isRealtimeStreaming transition: Clear waitingForResponse immediately
when SSE streaming ends (isRealtimeStreaming true→false). This is the
most reliable signal in portable mode since useChatStream is a stub
and never calls onDone, so lastCompletedRunAt is always null.
- chat-mode.ts: resolves backend type from gateway capabilities
- openai-compat-api.ts: OpenAI-compatible /v1/chat/completions client with SSE streaming
- chat-backends.ts: unified sendChatUnified/streamChatUnified that routes to correct backend