### Install Tailscale on Linux
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Install Tailscale on Linux by piping the official install script to sh. Sign in to Tailscale afterwards.
```bash
curl -fsSL https://tailscale.com/install.sh | sh
```
--------------------------------
### Remote Access Setup Guide
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Provides instructions for setting up remote access to Hermes UI using Tailscale. Emphasizes security and ease of use without port forwarding.
```javascript
Setup Guide
```
--------------------------------
### Install and Configure Tailscale
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/README.md
Commands to install Tailscale on the server and expose the Hermes UI service.
```bash
brew install tailscale # macOS
# or: curl -fsSL https://tailscale.com/install.sh | sh # Linux
tailscale up
```
```text
http://100.x.x.x:3333/hermes-ui.html
```
```bash
tailscale serve --bg 3333
# Accessible at https://your-machine.tail1234.ts.net
```
--------------------------------
### Install Tailscale on macOS
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Use Homebrew to install Tailscale on macOS. Ensure you are signed in to Tailscale afterwards.
```bash
brew install tailscale
```
--------------------------------
### Remote Access with Tailscale
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Commands to install, start, and expose the Hermes UI securely over a Tailscale VPN.
```bash
# Install Tailscale on your server
brew install tailscale # macOS
# or: curl -fsSL https://tailscale.com/install.sh | sh # Linux
# Start Tailscale
tailscale up
# Get your Tailscale IP
tailscale ip
# Output: 100.x.x.x
# Access from any Tailscale-connected device
# http://100.x.x.x:3333/hermes-ui.html
# Optional: Enable HTTPS with Tailscale Serve
tailscale serve --bg 3333
# Now accessible at: https://your-machine.tail1234.ts.net
```
--------------------------------
### Start Hermes UI Gateway Proxy
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Clone the repository and start the gateway proxy server. You can specify a custom port if needed. Open the provided URL in your browser to access the UI.
```bash
git clone https://github.com/pyrate-llama/hermes-ui.git
cd hermes-ui
python3 serve.py
# Or specify a custom port
python3 serve.py 8080
# Open in browser
open http://localhost:3333/hermes-ui.html
```
--------------------------------
### Sign in to Tailscale
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
After installation on any device, run this command to sign in to your Tailscale account.
```bash
tailscale up
```
--------------------------------
### Start Streaming Chat Session
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Initiate a chat session with the AI agent using a two-step SSE flow. The first step starts the chat and returns a stream ID, and the second step connects to the SSE stream to receive responses.
```bash
# Step 1: Start chat and get stream_id
curl -X POST http://localhost:3333/api/chat/start \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "Hello, what can you do?"}],
"session_id": "my_session_123"
}'
# Response:
{
"stream_id": "abc123def456",
"session_id": "my_session_123"
}
# Step 2: Connect to SSE stream
curl -N "http://localhost:3333/api/chat/stream?stream_id=abc123def456"
# SSE Events received:
event: token
data: {"text": "Hello"}
event: token
data: {"text": "! I'm"}
event: tool
data: {"name": "web_search", "preview": "Searching...", "args": {"query": "weather"}}
event: tool_complete
data: {"name": "web_search", "duration": 1.2}
event: reasoning
data: {"text": "Let me think about this..."}
event: done
data: {"session": {"session_id": "my_session_123", "messages": [...]}, "usage": {"input_tokens": 150, "output_tokens": 89}}
```
--------------------------------
### Handle Tool Event Started
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Processes the start of a tool event, updating the UI and logging the event. Resets the stall timer and adds the tool call to the state.
```javascript
function(toolEvent) {
if (toolEvent.type === 'started') {
if (!thinkingGotResponse) {
thinkingGotResponse = true;
}
lastChunkTime = Date.now(); // Reset stall timer โ tools count as activity
addLocalLog('INFO [tool] ' + (toolEvent.label || toolEvent.toolName) + ' started');
toolCalls.push({
toolName: toolEvent.toolName,
label: toolEvent.label,
timestamp: new Date(),
done: false,
});
// Update the thinking/streaming message with tool calls
updateStreamMessages(sessionId, function(prev) {
var updated = [...prev];
var last = updated[updated.length - 1];
if (last && last.role === 'assistant') {
updated[updated.length - 1] = { ...last, thinking: false, toolCalls: toolCalls.slice() };
}
return updated;
});
setActivityLog(function(prev) {
return [...prev, { id: Date.now() + Math.random(), emoji: '\u2699\uFE0F', label: toolEvent.label || toolEvent.toolName, timestamp: new Date(), done: false, }];
});
} else if (toolEvent.type === 'completed') {
var resultPreview = '';
if (toolEvent.result) {
var rs = typeof toolEvent.result === 'string' ? toolEvent.result : JSON.stringify(toolEvent.result);
rs = rs.replace(/\\n/g, ' ').replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
if (rs.length > 200) rs = rs.slice(0, 200) + '...';
resultPreview = ' => ' + rs;
}
addLocalLog('INFO [tool] ' + (toolEvent.toolName || 'tool') + ' completed' + resultPreview);
var lastTc = toolCalls[toolCalls.length - 1];
if (lastTc) {
lastTc.result = toolEvent.result;
lastTc.done = true;
}
setActivityLog(function(prev) {
const updated = [...prev];
const lastEntry = updated[updated.length - 1];
if (lastEntry) lastEntry.done = true;
return updated;
});
}
}
```
--------------------------------
### Skills API - List All Skills
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Lists all installed Hermes skills, including their name, category, description, and disabled status.
```bash
# List all skills
curl http://localhost:3333/api/skills
```
```json
{
"skills": [
{
"name": "web-search",
"category": "tools",
"description": "Search the web using DuckDuckGo",
"disabled": false
},
{
"name": "code-review",
"category": "development",
"description": "Review code for best practices",
"disabled": false
}
],
"total": 42
}
```
--------------------------------
### Clone and Run Hermes UI
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/README.md
Clone the repository, navigate to the directory, and start the Python proxy server. You can specify a custom port if needed. Open the provided URL in your browser to access the UI.
```bash
git clone https://github.com/pyrate-llama/hermes-ui.git
cd hermes-ui
python3 serve.py
# Or specify a custom port
python3 serve.py 8080
```
--------------------------------
### Skills API
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Lists and manages installed Hermes skills.
```APIDOC
## Skills API
### Description
Lists and manages installed Hermes skills.
#### GET /api/skills
### Method
GET
### Endpoint
`/api/skills`
### Response
#### Success Response (200)
- **skills** (array) - A list of installed skills.
- **name** (string) - The name of the skill.
- **category** (string) - The category of the skill.
- **description** (string) - A brief description of the skill.
- **disabled** (boolean) - Indicates if the skill is disabled.
- **total** (integer) - The total number of skills available.
### Response Example
```json
{
"skills": [
{
"name": "web-search",
"category": "tools",
"description": "Search the web using DuckDuckGo",
"disabled": false
},
{
"name": "code-review",
"category": "development",
"description": "Review code for best practices",
"disabled": false
}
],
"total": 42
}
```
#### GET /api/skills/{skill_name}
### Method
GET
### Endpoint
`/api/skills/{skill_name}`
#### Path Parameters
- **skill_name** (string) - Required - The name of the skill to retrieve details for.
### Response
#### Success Response (200)
- **name** (string) - The name of the skill.
- **category** (string) - The category of the skill.
- **success** (boolean) - Indicates if the skill details were retrieved successfully.
- **content** (string) - Detailed content or description of the skill.
- **files** (array of strings) - List of files associated with the skill.
### Response Example
```json
{
"name": "web-search",
"category": "tools",
"success": true,
"content": "# Web Search Skill\n\nThis skill allows searching the web...",
"files": ["SKILL.md", "search.py"]
}
```
#### GET /skills/dates
### Method
GET
### Endpoint
`/skills/dates`
### Response
#### Success Response (200)
- **dates** (object) - An object containing skill names as keys and their modification timestamps as values.
### Response Example
```json
{
"dates": {
"web-search": 1705312345.123,
"code-review": 1705298765.456
}
}
```
```
--------------------------------
### Skills API - Get Skill Details
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Retrieves detailed information for a specific skill, including its content and associated files.
```bash
# Get skill details
curl http://localhost:3333/api/skills/web-search
```
```json
{
"name": "web-search",
"category": "tools",
"success": true,
"content": "# Web Search Skill\n\nThis skill allows searching the web...",
"files": ["SKILL.md", "search.py"]
}
```
--------------------------------
### Start Streaming and Activity Logging
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Initiates a streaming request to Hermes and logs the action. This function is typically called after sending a user message.
```javascript
const abortCtrl = new AbortController();
startStreaming(sessionId, abortCtrl);
setActivityLog([]);
addLocalLog('INFO Sending message to session ' + sessionId);
```
--------------------------------
### Chat API - Start Streaming Session
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Initiates a chat session and allows for real-time streaming of responses via Server-Sent Events (SSE).
```APIDOC
## POST /api/chat/start
### Description
Starts a new chat session with the AI agent and returns a `stream_id` for subsequent interactions. This is the first step in a two-step SSE streaming flow.
### Method
POST
### Endpoint
`/api/chat/start`
### Parameters
#### Request Body
- **messages** (array) - Required - An array of message objects, each with 'role' and 'content'.
- **session_id** (string) - Optional - An identifier for the chat session.
### Request Example
```json
{
"messages": [{"role": "user", "content": "Hello, what can you do?"}],
"session_id": "my_session_123"
}
```
### Response
#### Success Response (200)
- **stream_id** (string) - A unique identifier for the streaming session.
- **session_id** (string) - The identifier for the chat session.
#### Response Example
```json
{
"stream_id": "abc123def456",
"session_id": "my_session_123"
}
```
## GET /api/chat/stream
### Description
Connects to the Server-Sent Events (SSE) stream for a given chat session using the `stream_id` obtained from `/api/chat/start`. This endpoint streams tokens, tool calls, reasoning, and session completion events.
### Method
GET
### Endpoint
`/api/chat/stream`
### Query Parameters
- **stream_id** (string) - Required - The ID of the streaming session to connect to.
### Response
#### Success Response (200 - SSE Stream)
Events are streamed in Server-Sent Events format. Common event types include:
- **token**: Contains a chunk of text generated by the AI.
- **tool**: Indicates a tool call with its arguments.
- **tool_complete**: Signals the completion of a tool execution.
- **reasoning**: Provides the AI's thought process.
- **done**: Marks the end of the session, including final messages and usage statistics.
#### Response Example (SSE Events)
```
event: token
data: {"text": "Hello"}
event: token
data: {"text": "! I'm"}
event: tool
data: {"name": "web_search", "preview": "Searching...", "args": {"query": "weather"}}
event: tool_complete
data: {"name": "web_search", "duration": 1.2}
event: reasoning
data: {"text": "Let me think about this..."}
event: done
data: {"session": {"session_id": "my_session_123", "messages": [...]}, "usage": {"input_tokens": 150, "output_tokens": 89}}
```
```
--------------------------------
### Memory API - Get Entries
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Retrieves memory entries for both general memory and user profile. Displays entries and their character usage.
```bash
# Get memory entries
curl http://localhost:3333/api/memory
```
```json
{
"targets": [
{
"target": "memory",
"entries": ["User prefers concise responses", "Works on Python projects"],
"usage": "156/4000 chars"
},
{
"target": "user",
"entries": ["Name: Alex", "Timezone: UTC-5"],
"usage": "45/1375 chars"
}
]
}
```
--------------------------------
### Start Hermes UI Direct Mode Server
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Run Hermes UI in direct mode, connecting directly to the AIAgent without a gateway. You can specify a custom port for the server.
```bash
# Start lite server on default port
python3 serve_lite.py
# Or specify custom port
python3 serve_lite.py --port 8080
# Example output:
# โญโ Hermes UI Direct โโโโโโโโโโโโโโโโโโโโฎ
# โ UI: http://127.0.0.1:3333 โ
# โ Agent: โ loaded โ
# โ Model: anthropic/claude-sonnet โ
# โ Provider: minimax โ
# โ Mode: Direct (no gateway) โ
# โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
```
--------------------------------
### Create a New Conversation
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Initializes a new chat conversation by creating a session, setting up the conversation object, and resetting the input and messages state. This is used when starting a fresh chat.
```javascript
const newConversation = () => {
const sessionId = api.createSession();
const conv = {
id: sessionId,
title: 'New Chat',
created: new Date().toLocaleString(),
messages: []
};
setConversations(prev => [conv, ...prev]);
setActiveConvId(sessionId);
setMessages([]);
setInput('');
};
```
--------------------------------
### Skills API - Get Modification Timestamps
Source: https://context7.com/pyrate-llama/hermes-ui/llms.txt
Fetches the modification timestamps for installed skills, useful for sorting skills by recency.
```bash
# Get skill modification timestamps (for sorting by newest)
curl http://localhost:3333/skills/dates
```
```json
{
"dates": {
"web-search": 1705312345.123,
"code-review": 1705298765.456
}
}
```
--------------------------------
### Enable HTTPS with Tailscale Serve
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Run this command to enable HTTPS for Hermes UI, providing a secure connection and a clean URL. Traffic is served in the background.
```bash
tailscale serve --bg 3333
```
--------------------------------
### Troubleshoot Context Compression
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/README.md
Fixes for agent hangs caused by incorrect summary base URL configurations.
```yaml
compression:
summary_base_url: null # โ this causes a 404 and hangs the agent
```
```yaml
compression:
summary_base_url: https://api.minimax.io/anthropic
```
```bash
hermes restart
```
--------------------------------
### Initialize Refs
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Ref declarations for DOM elements and persistent data storage.
```javascript
const textareaRef = useRef(null); const messagesEndRef = useRef(null); const fileInputRef = useRef(null); // abortControllerRef removed โ now in streamingMap per-session const messagesRef = useRef([]); const activeConvRef = useRef(null); const conversationsRef = useRef([]); const activityLogRef = useRef(null);
```
--------------------------------
### Render Model Status UI
Source: https://github.com/pyrate-llama/hermes-ui/blob/main/hermes-ui.html
Displays model information, uptime, and installed skills using inline styles.
```jsx
style={{display:'flex',alignItems:'center',gap:'12px'}}>
{'๐ง '}