Compare commits
No commits in common. "cfb98a58b6549bc00ac13341d4e6e2afa13f3b9f" and "805f7a017e66383169e9fcaf05a428fa048eb9f2" have entirely different histories.
cfb98a58b6
...
805f7a017e
1128 changed files with 1089 additions and 232 deletions
336
.env.example
336
.env.example
|
|
@ -1,16 +1,326 @@
|
||||||
OPENAI_BASE_URL=
|
# Hermes Agent Environment Configuration
|
||||||
OPENAI_API_KEY=
|
# Copy this file to .env and fill in your API keys
|
||||||
MODEL_DEFAULT=
|
|
||||||
|
|
||||||
TERMINAL_DOCKER_IMAGE=python:3.12-slim
|
# =============================================================================
|
||||||
TERMINAL_ENV=docker
|
# LLM PROVIDER (OpenRouter)
|
||||||
HERMES_MAX_ITERATIONS=90
|
# =============================================================================
|
||||||
HERMES_HOME=/app/hermes_data
|
# OpenRouter provides access to many models through one API
|
||||||
HERMES_WORKSPACE_PATH=app/workspace
|
# All LLM calls go through OpenRouter - no direct provider keys needed
|
||||||
|
# Get your key at: https://openrouter.ai/keys
|
||||||
|
OPENROUTER_API_KEY=
|
||||||
|
|
||||||
TELEGRAM_BOT_TOKEN=
|
# Default model to use (OpenRouter format: provider/model)
|
||||||
TELEGRAM_ALLOWED_USERS=
|
# Examples: anthropic/claude-opus-4.6, openai/gpt-4o, google/gemini-3-flash-preview, zhipuai/glm-4-plus
|
||||||
TELEGRAM_HOME_CHANNEL=
|
LLM_MODEL=anthropic/claude-opus-4.6
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LLM PROVIDER (z.ai / GLM)
|
||||||
|
# =============================================================================
|
||||||
|
# z.ai provides access to ZhipuAI GLM models (GLM-4-Plus, etc.)
|
||||||
|
# Get your key at: https://z.ai or https://open.bigmodel.cn
|
||||||
|
GLM_API_KEY=
|
||||||
|
# GLM_BASE_URL=https://api.z.ai/api/paas/v4 # Override default base URL
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LLM PROVIDER (Kimi / Moonshot)
|
||||||
|
# =============================================================================
|
||||||
|
# Kimi Code provides access to Moonshot AI coding models (kimi-k2.5, etc.)
|
||||||
|
# Get your key at: https://platform.kimi.ai (Kimi Code console)
|
||||||
|
# Keys prefixed sk-kimi- use the Kimi Code API (api.kimi.com) by default.
|
||||||
|
# Legacy keys from platform.moonshot.ai need KIMI_BASE_URL override below.
|
||||||
|
KIMI_API_KEY=
|
||||||
|
# KIMI_BASE_URL=https://api.kimi.com/coding/v1 # Default for sk-kimi- keys
|
||||||
|
# KIMI_BASE_URL=https://api.moonshot.ai/v1 # For legacy Moonshot keys
|
||||||
|
# KIMI_BASE_URL=https://api.moonshot.cn/v1 # For Moonshot China keys
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LLM PROVIDER (MiniMax)
|
||||||
|
# =============================================================================
|
||||||
|
# MiniMax provides access to MiniMax models (global endpoint)
|
||||||
|
# Get your key at: https://www.minimax.io
|
||||||
|
MINIMAX_API_KEY=
|
||||||
|
# MINIMAX_BASE_URL=https://api.minimax.io/v1 # Override default base URL
|
||||||
|
|
||||||
|
# MiniMax China endpoint (for users in mainland China)
|
||||||
|
MINIMAX_CN_API_KEY=
|
||||||
|
# MINIMAX_CN_BASE_URL=https://api.minimaxi.com/v1 # Override default base URL
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LLM PROVIDER (OpenCode Zen)
|
||||||
|
# =============================================================================
|
||||||
|
# OpenCode Zen provides curated, tested models (GPT, Claude, Gemini, MiniMax, GLM, Kimi)
|
||||||
|
# Pay-as-you-go pricing. Get your key at: https://opencode.ai/auth
|
||||||
|
OPENCODE_ZEN_API_KEY=
|
||||||
|
# OPENCODE_ZEN_BASE_URL=https://opencode.ai/zen/v1 # Override default base URL
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LLM PROVIDER (OpenCode Go)
|
||||||
|
# =============================================================================
|
||||||
|
# OpenCode Go provides access to open models (GLM-5, Kimi K2.5, MiniMax M2.5)
|
||||||
|
# $10/month subscription. Get your key at: https://opencode.ai/auth
|
||||||
|
OPENCODE_GO_API_KEY=
|
||||||
|
# OPENCODE_GO_BASE_URL=https://opencode.ai/zen/go/v1 # Override default base URL
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# TOOL API KEYS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Parallel API Key - AI-native web search and extract
|
||||||
|
# Get at: https://parallel.ai
|
||||||
|
PARALLEL_API_KEY=
|
||||||
|
|
||||||
|
# Firecrawl API Key - Web search, extract, and crawl
|
||||||
|
# Get at: https://firecrawl.dev/
|
||||||
|
FIRECRAWL_API_KEY=
|
||||||
|
|
||||||
|
|
||||||
|
# FAL.ai API Key - Image generation
|
||||||
|
# Get at: https://fal.ai/
|
||||||
|
FAL_KEY=
|
||||||
|
|
||||||
|
# Honcho - Cross-session AI-native user modeling (optional)
|
||||||
|
# Builds a persistent understanding of the user across sessions and tools.
|
||||||
|
# Get at: https://app.honcho.dev
|
||||||
|
# Also requires ~/.honcho/config.json with enabled=true (see README).
|
||||||
|
HONCHO_API_KEY=
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# TERMINAL TOOL CONFIGURATION (mini-swe-agent backend)
|
||||||
|
# =============================================================================
|
||||||
|
# Backend type: "local", "singularity", "docker", "modal", or "ssh"
|
||||||
|
# Terminal backend is configured in ~/.hermes/config.yaml (terminal.backend).
|
||||||
|
# Use 'hermes setup' or 'hermes config set terminal.backend docker' to change.
|
||||||
|
# Supported: local, docker, singularity, modal, ssh
|
||||||
|
#
|
||||||
|
# Only override here if you need to force a backend without touching config.yaml:
|
||||||
|
# TERMINAL_ENV=local
|
||||||
|
|
||||||
|
# Container images (for singularity/docker/modal backends)
|
||||||
|
# TERMINAL_DOCKER_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20
|
||||||
|
# TERMINAL_SINGULARITY_IMAGE=docker://nikolaik/python-nodejs:python3.11-nodejs20
|
||||||
|
TERMINAL_MODAL_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20
|
||||||
|
|
||||||
|
|
||||||
|
# Working directory for terminal commands
|
||||||
|
# For local backend: "." means current directory (resolved automatically)
|
||||||
|
# For remote backends (ssh/docker/modal/singularity): use an absolute path
|
||||||
|
# INSIDE the target environment, or leave unset for the backend's default
|
||||||
|
# (/root for modal, / for docker, ~ for ssh). Do NOT use a host-local path.
|
||||||
|
# Usually managed by config.yaml (terminal.cwd) — uncomment to override
|
||||||
|
# TERMINAL_CWD=.
|
||||||
|
|
||||||
|
# Default command timeout in seconds
|
||||||
|
TERMINAL_TIMEOUT=60
|
||||||
|
|
||||||
|
# Cleanup inactive environments after this many seconds
|
||||||
|
TERMINAL_LIFETIME_SECONDS=300
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# SSH REMOTE EXECUTION (for TERMINAL_ENV=ssh)
|
||||||
|
# =============================================================================
|
||||||
|
# Run terminal commands on a remote server via SSH.
|
||||||
|
# Agent code stays on your machine, commands execute remotely.
|
||||||
|
#
|
||||||
|
# SECURITY BENEFITS:
|
||||||
|
# - Agent cannot read your .env file (API keys protected)
|
||||||
|
# - Agent cannot modify its own code
|
||||||
|
# - Remote server acts as isolated sandbox
|
||||||
|
# - Can safely configure passwordless sudo on remote
|
||||||
|
#
|
||||||
|
# TERMINAL_SSH_HOST=192.168.1.100
|
||||||
|
# TERMINAL_SSH_USER=agent
|
||||||
|
# TERMINAL_SSH_PORT=22
|
||||||
|
# TERMINAL_SSH_KEY=~/.ssh/id_rsa
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# SUDO SUPPORT (works with ALL terminal backends)
|
||||||
|
# =============================================================================
|
||||||
|
# If set, enables sudo commands by piping password via `sudo -S`.
|
||||||
|
# Works with: local, docker, singularity, modal, and ssh backends.
|
||||||
|
#
|
||||||
|
# SECURITY WARNING: Password stored in plaintext. Only use on trusted machines.
|
||||||
|
#
|
||||||
|
# ALTERNATIVES:
|
||||||
|
# - For SSH backend: Configure passwordless sudo on the remote server
|
||||||
|
# - For containers: Run as root inside the container (no sudo needed)
|
||||||
|
# - For local: Configure /etc/sudoers for specific commands
|
||||||
|
# - For CLI: Leave unset - you'll be prompted interactively with 45s timeout
|
||||||
|
#
|
||||||
|
# SUDO_PASSWORD=your_password_here
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# MODAL CLOUD BACKEND (Optional - for TERMINAL_ENV=modal)
|
||||||
|
# =============================================================================
|
||||||
|
# Modal uses CLI authentication, not environment variables.
|
||||||
|
# Run: pip install modal && modal setup
|
||||||
|
# This will authenticate via browser and store credentials locally.
|
||||||
|
# No API key needed in .env - Modal handles auth automatically.
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BROWSER TOOL CONFIGURATION (agent-browser + Browserbase)
|
||||||
|
# =============================================================================
|
||||||
|
# Browser automation requires Browserbase cloud service for remote browser execution.
|
||||||
|
# This allows the agent to navigate websites, fill forms, and extract information.
|
||||||
|
#
|
||||||
|
# STEALTH MODES:
|
||||||
|
# - Basic Stealth: ALWAYS active (random fingerprints, auto CAPTCHA solving)
|
||||||
|
# - Advanced Stealth: Requires BROWSERBASE_ADVANCED_STEALTH=true (Scale Plan only)
|
||||||
|
|
||||||
|
# Browserbase API Key - Cloud browser execution
|
||||||
|
# Get at: https://browserbase.com/
|
||||||
|
BROWSERBASE_API_KEY=
|
||||||
|
|
||||||
|
# Browserbase Project ID - From your Browserbase dashboard
|
||||||
|
BROWSERBASE_PROJECT_ID=
|
||||||
|
|
||||||
|
# Enable residential proxies for better CAPTCHA solving (default: true)
|
||||||
|
# Routes traffic through residential IPs, significantly improves success rate
|
||||||
|
BROWSERBASE_PROXIES=true
|
||||||
|
|
||||||
|
# Enable advanced stealth mode (default: false, requires Scale Plan)
|
||||||
|
# Uses custom Chromium build to avoid bot detection altogether
|
||||||
|
BROWSERBASE_ADVANCED_STEALTH=false
|
||||||
|
|
||||||
|
# Browser session timeout in seconds (default: 300)
|
||||||
|
# Sessions are cleaned up after this duration of inactivity
|
||||||
|
BROWSER_SESSION_TIMEOUT=300
|
||||||
|
|
||||||
|
# Browser inactivity timeout - auto-cleanup inactive sessions (default: 120 = 2 min)
|
||||||
|
# Browser sessions are automatically closed after this period of no activity
|
||||||
|
BROWSER_INACTIVITY_TIMEOUT=120
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# SESSION LOGGING
|
||||||
|
# =============================================================================
|
||||||
|
# Session trajectories are automatically saved to logs/ directory
|
||||||
|
# Format: logs/session_YYYYMMDD_HHMMSS_UUID.json
|
||||||
|
# Contains full conversation history in trajectory format for debugging/replay
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# VOICE TRANSCRIPTION & OPENAI TTS
|
||||||
|
# =============================================================================
|
||||||
|
# Required for voice message transcription (Whisper) and OpenAI TTS voices.
|
||||||
|
# Uses OpenAI's API directly (not via OpenRouter).
|
||||||
|
# Named VOICE_TOOLS_OPENAI_KEY to avoid interference with OpenRouter.
|
||||||
|
# Get at: https://platform.openai.com/api-keys
|
||||||
|
VOICE_TOOLS_OPENAI_KEY=
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# SLACK INTEGRATION
|
||||||
|
# =============================================================================
|
||||||
|
# Slack Bot Token - From Slack App settings (OAuth & Permissions)
|
||||||
|
# Get at: https://api.slack.com/apps
|
||||||
|
# SLACK_BOT_TOKEN=xoxb-...
|
||||||
|
|
||||||
|
# Slack App Token - For Socket Mode (App-Level Tokens in Slack App settings)
|
||||||
|
# SLACK_APP_TOKEN=xapp-...
|
||||||
|
|
||||||
|
# Slack allowed users (comma-separated Slack user IDs)
|
||||||
|
# SLACK_ALLOWED_USERS=
|
||||||
|
|
||||||
|
# WhatsApp (built-in Baileys bridge — run `hermes whatsapp` to pair)
|
||||||
|
# WHATSAPP_ENABLED=false
|
||||||
|
# WHATSAPP_ALLOWED_USERS=15551234567
|
||||||
|
|
||||||
|
# Email (IMAP/SMTP — send and receive emails as Hermes)
|
||||||
|
# For Gmail: enable 2FA → create App Password at https://myaccount.google.com/apppasswords
|
||||||
|
# EMAIL_ADDRESS=hermes@gmail.com
|
||||||
|
# EMAIL_PASSWORD=xxxx xxxx xxxx xxxx
|
||||||
|
# EMAIL_IMAP_HOST=imap.gmail.com
|
||||||
|
# EMAIL_IMAP_PORT=993
|
||||||
|
# EMAIL_SMTP_HOST=smtp.gmail.com
|
||||||
|
# EMAIL_SMTP_PORT=587
|
||||||
|
# EMAIL_POLL_INTERVAL=15
|
||||||
|
# EMAIL_ALLOWED_USERS=your@email.com
|
||||||
|
# EMAIL_HOME_ADDRESS=your@email.com
|
||||||
|
|
||||||
|
# Gateway-wide: allow ALL users without an allowlist (default: false = deny)
|
||||||
|
# Only set to true if you intentionally want open access.
|
||||||
|
# GATEWAY_ALLOW_ALL_USERS=false
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# RESPONSE PACING
|
||||||
|
# =============================================================================
|
||||||
|
# Human-like delays between message chunks on messaging platforms.
|
||||||
|
# Makes the bot feel less robotic.
|
||||||
|
# HERMES_HUMAN_DELAY_MODE=off # off | natural | custom
|
||||||
|
# HERMES_HUMAN_DELAY_MIN_MS=800 # Min delay in ms (custom mode)
|
||||||
|
# HERMES_HUMAN_DELAY_MAX_MS=2500 # Max delay in ms (custom mode)
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# DEBUG OPTIONS
|
||||||
|
# =============================================================================
|
||||||
|
WEB_TOOLS_DEBUG=false
|
||||||
|
VISION_TOOLS_DEBUG=false
|
||||||
|
MOA_TOOLS_DEBUG=false
|
||||||
|
IMAGE_TOOLS_DEBUG=false
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CONTEXT COMPRESSION (Auto-shrinks long conversations)
|
||||||
|
# =============================================================================
|
||||||
|
# When conversation approaches model's context limit, middle turns are
|
||||||
|
# automatically summarized to free up space.
|
||||||
|
#
|
||||||
|
# Context compression is configured in ~/.hermes/config.yaml under compression:
|
||||||
|
# CONTEXT_COMPRESSION_ENABLED=true # Enable auto-compression (default: true)
|
||||||
|
# CONTEXT_COMPRESSION_THRESHOLD=0.85 # Compress at 85% of context limit
|
||||||
|
# Model is set via compression.summary_model in config.yaml (default: google/gemini-3-flash-preview)
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# RL TRAINING (Tinker + Atropos)
|
||||||
|
# =============================================================================
|
||||||
|
# Run reinforcement learning training on language models using the Tinker API.
|
||||||
|
# Requires the rl-server to be running (from tinker-atropos package).
|
||||||
|
|
||||||
|
# Tinker API Key - RL training service
|
||||||
|
# Get at: https://tinker-console.thinkingmachines.ai/keys
|
||||||
|
TINKER_API_KEY=
|
||||||
|
|
||||||
|
# Weights & Biases API Key - Experiment tracking and metrics
|
||||||
|
# Get at: https://wandb.ai/authorize
|
||||||
|
WANDB_API_KEY=
|
||||||
|
|
||||||
|
# RL API Server URL (default: http://localhost:8080)
|
||||||
|
# Change if running the rl-server on a different host/port
|
||||||
|
# RL_API_URL=http://localhost:8080
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# SKILLS HUB (GitHub integration for skill search/install/publish)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# GitHub Personal Access Token — for higher API rate limits on skill search/install
|
||||||
|
# Get at: https://github.com/settings/tokens (Fine-grained recommended)
|
||||||
|
# GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
# GitHub App credentials (optional — for bot identity on PRs)
|
||||||
|
# GITHUB_APP_ID=
|
||||||
|
# GITHUB_APP_PRIVATE_KEY_PATH=
|
||||||
|
# GITHUB_APP_INSTALLATION_ID=
|
||||||
|
|
||||||
|
# Groq API key (free tier — used for Whisper STT in voice mode)
|
||||||
|
# GROQ_API_KEY=
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STT PROVIDER SELECTION
|
||||||
|
# =============================================================================
|
||||||
|
# Default STT provider is "local" (faster-whisper) — runs on your machine, no API key needed.
|
||||||
|
# Install with: pip install faster-whisper
|
||||||
|
# Model downloads automatically on first use (~150 MB for "base").
|
||||||
|
# To use cloud providers instead, set GROQ_API_KEY or VOICE_TOOLS_OPENAI_KEY above.
|
||||||
|
# Provider priority: local > groq > openai
|
||||||
|
# Configure in config.yaml: stt.provider: local | groq | openai
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STT ADVANCED OVERRIDES (optional)
|
||||||
|
# =============================================================================
|
||||||
|
# Override default STT models per provider (normally set via stt.model in config.yaml)
|
||||||
|
# STT_GROQ_MODEL=whisper-large-v3-turbo
|
||||||
|
# STT_OPENAI_MODEL=whisper-1
|
||||||
|
|
||||||
|
# Override STT provider endpoints (for proxies or self-hosted instances)
|
||||||
|
# GROQ_BASE_URL=https://api.groq.com/openai/v1
|
||||||
|
# STT_OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
|
|
||||||
|
HERMES_DATA_PATH= # Укажите путь к папке .hermes
|
||||||
|
HERMES_WORKSPACE_PATH= # Укажите путь к воркспейсу гермеса
|
||||||
|
|
||||||
BROWSER_URL=http://browser:9222
|
|
||||||
BROWSER_VIEW_URL=
|
|
||||||
144
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
144
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
name: "🐛 Bug Report"
|
||||||
|
description: Report a bug — something that's broken, crashes, or behaves incorrectly.
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for reporting a bug! Please fill out the sections below so we can reproduce and fix it quickly.
|
||||||
|
|
||||||
|
**Before submitting**, please:
|
||||||
|
- [ ] Search [existing issues](https://github.com/NousResearch/hermes-agent/issues) to avoid duplicates
|
||||||
|
- [ ] Update to the latest version (`hermes update`) and confirm the bug still exists
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Bug Description
|
||||||
|
description: A clear description of what's broken. Include error messages, tracebacks, or screenshots if relevant.
|
||||||
|
placeholder: |
|
||||||
|
What happened? What did you expect to happen instead?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Steps to Reproduce
|
||||||
|
description: Minimal steps to trigger the bug. The more specific, the faster we can fix it.
|
||||||
|
placeholder: |
|
||||||
|
1. Run `hermes chat`
|
||||||
|
2. Send the message "..."
|
||||||
|
3. Agent calls tool X
|
||||||
|
4. Error appears: ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: What should have happened instead?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: Actual Behavior
|
||||||
|
description: What actually happened? Include full error output if available.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: component
|
||||||
|
attributes:
|
||||||
|
label: Affected Component
|
||||||
|
description: Which part of Hermes is affected?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- CLI (interactive chat)
|
||||||
|
- Gateway (Telegram/Discord/Slack/WhatsApp)
|
||||||
|
- Setup / Installation
|
||||||
|
- Tools (terminal, file ops, web, code execution, etc.)
|
||||||
|
- Skills (skill loading, skill hub, skill guard)
|
||||||
|
- Agent Core (conversation loop, context compression, memory)
|
||||||
|
- Configuration (config.yaml, .env, hermes setup)
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: platform
|
||||||
|
attributes:
|
||||||
|
label: Messaging Platform (if gateway-related)
|
||||||
|
description: Which platform adapter is affected?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- N/A (CLI only)
|
||||||
|
- Telegram
|
||||||
|
- Discord
|
||||||
|
- Slack
|
||||||
|
- WhatsApp
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
description: e.g. Ubuntu 24.04, macOS 15.2, Windows 11
|
||||||
|
placeholder: Ubuntu 24.04
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: python-version
|
||||||
|
attributes:
|
||||||
|
label: Python Version
|
||||||
|
description: Output of `python --version`
|
||||||
|
placeholder: "3.11.9"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: hermes-version
|
||||||
|
attributes:
|
||||||
|
label: Hermes Version
|
||||||
|
description: Output of `hermes version`
|
||||||
|
placeholder: "2.1.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant Logs / Traceback
|
||||||
|
description: Paste any error output, traceback, or log messages. This will be auto-formatted as code.
|
||||||
|
render: shell
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: root-cause
|
||||||
|
attributes:
|
||||||
|
label: Root Cause Analysis (optional)
|
||||||
|
description: |
|
||||||
|
If you've dug into the code and identified the root cause, share it here.
|
||||||
|
Include file paths, line numbers, and code snippets if possible. This massively speeds up fixes.
|
||||||
|
placeholder: |
|
||||||
|
The bug is in `gateway/run.py` line 949. `len(history)` counts session_meta entries
|
||||||
|
but `agent_messages` was built from filtered history...
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: proposed-fix
|
||||||
|
attributes:
|
||||||
|
label: Proposed Fix (optional)
|
||||||
|
description: If you have a fix in mind (or a PR ready), describe it here.
|
||||||
|
placeholder: |
|
||||||
|
Replace `.get()` with `.pop()` on line 289 of `gateway/platforms/base.py`
|
||||||
|
to actually clear the pending message after retrieval.
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: pr-ready
|
||||||
|
attributes:
|
||||||
|
label: Are you willing to submit a PR for this?
|
||||||
|
options:
|
||||||
|
- label: I'd like to fix this myself and submit a PR
|
||||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: 💬 Nous Research Discord
|
||||||
|
url: https://discord.gg/NousResearch
|
||||||
|
about: For quick questions, showcasing projects, sharing skills, and community chat.
|
||||||
|
- name: 📖 Documentation
|
||||||
|
url: https://github.com/NousResearch/hermes-agent/blob/main/README.md
|
||||||
|
about: Check the README and docs before opening an issue.
|
||||||
|
- name: 🤝 Contributing Guide
|
||||||
|
url: https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md
|
||||||
|
about: Read this before submitting a PR.
|
||||||
73
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
73
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
name: "✨ Feature Request"
|
||||||
|
description: Suggest a new feature or improvement.
|
||||||
|
title: "[Feature]: "
|
||||||
|
labels: ["enhancement"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for the suggestion! Before submitting, please consider:
|
||||||
|
|
||||||
|
- **Is this a new skill?** Most capabilities should be [skills, not tools](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#should-it-be-a-skill-or-a-tool). If it's a specialized integration (crypto, NFT, niche SaaS), it belongs on the Skills Hub, not bundled.
|
||||||
|
- **Search [existing issues](https://github.com/NousResearch/hermes-agent/issues)** — someone may have already proposed this.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: Problem or Use Case
|
||||||
|
description: What problem does this solve? What are you trying to do that you can't today?
|
||||||
|
placeholder: |
|
||||||
|
I'm trying to use Hermes with [provider/platform/workflow] but currently
|
||||||
|
there's no way to...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Proposed Solution
|
||||||
|
description: How do you think this should work? Be as specific as you can — CLI flags, config options, UI behavior.
|
||||||
|
placeholder: |
|
||||||
|
Add a `--foo` flag to `hermes chat` that enables...
|
||||||
|
Or: Add a config key `bar.baz` that controls...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives Considered
|
||||||
|
description: What other approaches did you consider? Why is the proposed solution better?
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: type
|
||||||
|
attributes:
|
||||||
|
label: Feature Type
|
||||||
|
options:
|
||||||
|
- New tool
|
||||||
|
- New bundled skill
|
||||||
|
- CLI improvement
|
||||||
|
- Gateway / messaging improvement
|
||||||
|
- Configuration option
|
||||||
|
- Performance / reliability
|
||||||
|
- Developer experience (tests, docs, CI)
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: scope
|
||||||
|
attributes:
|
||||||
|
label: Scope
|
||||||
|
description: How big is this change?
|
||||||
|
options:
|
||||||
|
- Small (single file, < 50 lines)
|
||||||
|
- Medium (few files, < 300 lines)
|
||||||
|
- Large (new module or significant refactor)
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: pr-ready
|
||||||
|
attributes:
|
||||||
|
label: Contribution
|
||||||
|
options:
|
||||||
|
- label: I'd like to implement this myself and submit a PR
|
||||||
100
.github/ISSUE_TEMPLATE/setup_help.yml
vendored
Normal file
100
.github/ISSUE_TEMPLATE/setup_help.yml
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
name: "🔧 Setup / Installation Help"
|
||||||
|
description: Having trouble installing or configuring Hermes? Ask here.
|
||||||
|
title: "[Setup]: "
|
||||||
|
labels: ["setup"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Sorry you're having trouble! Please fill out the details below so we can help.
|
||||||
|
|
||||||
|
**Quick checks first:**
|
||||||
|
- Run `hermes doctor` and include the output below
|
||||||
|
- Try `hermes update` to get the latest version
|
||||||
|
- Check the [README troubleshooting section](https://github.com/NousResearch/hermes-agent#troubleshooting)
|
||||||
|
- For general questions, consider the [Nous Research Discord](https://discord.gg/NousResearch) for faster help
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: What's Going Wrong?
|
||||||
|
description: Describe what you're trying to do and where it fails.
|
||||||
|
placeholder: |
|
||||||
|
I ran `hermes setup` and selected Nous Portal, but when I try to
|
||||||
|
start the gateway I get...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps Taken
|
||||||
|
description: What did you do? Include the exact commands you ran.
|
||||||
|
placeholder: |
|
||||||
|
1. Ran the install script: `curl -fsSL ... | bash`
|
||||||
|
2. Ran `hermes setup` and chose "Quick setup"
|
||||||
|
3. Selected OpenRouter, entered API key
|
||||||
|
4. Ran `hermes chat` and got error...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: install-method
|
||||||
|
attributes:
|
||||||
|
label: Installation Method
|
||||||
|
options:
|
||||||
|
- Install script (curl | bash)
|
||||||
|
- Manual clone + pip/uv install
|
||||||
|
- PowerShell installer (Windows)
|
||||||
|
- Docker
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
placeholder: Ubuntu 24.04 / macOS 15.2 / Windows 11
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: python-version
|
||||||
|
attributes:
|
||||||
|
label: Python Version
|
||||||
|
description: Output of `python --version` (or `python3 --version`)
|
||||||
|
placeholder: "3.11.9"
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: hermes-version
|
||||||
|
attributes:
|
||||||
|
label: Hermes Version
|
||||||
|
description: Output of `hermes version` (if install got that far)
|
||||||
|
placeholder: "2.1.0"
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: doctor-output
|
||||||
|
attributes:
|
||||||
|
label: Output of `hermes doctor`
|
||||||
|
description: Run `hermes doctor` and paste the full output. This will be auto-formatted.
|
||||||
|
render: shell
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: error-output
|
||||||
|
attributes:
|
||||||
|
label: Full Error Output
|
||||||
|
description: Paste the complete error message or traceback. This will be auto-formatted.
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: tried
|
||||||
|
attributes:
|
||||||
|
label: What I've Already Tried
|
||||||
|
description: List any fixes or workarounds you've already attempted.
|
||||||
|
placeholder: |
|
||||||
|
- Ran `hermes update`
|
||||||
|
- Tried reinstalling with `pip install -e ".[all]"`
|
||||||
|
- Checked that OPENROUTER_API_KEY is set in ~/.hermes/.env
|
||||||
75
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
75
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
## What does this PR do?
|
||||||
|
|
||||||
|
<!-- Describe the change clearly. What problem does it solve? Why is this approach the right one? -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Related Issue
|
||||||
|
|
||||||
|
<!-- Link the issue this PR addresses. If no issue exists, consider creating one first. -->
|
||||||
|
|
||||||
|
Fixes #
|
||||||
|
|
||||||
|
## Type of Change
|
||||||
|
|
||||||
|
<!-- Check the one that applies. -->
|
||||||
|
|
||||||
|
- [ ] 🐛 Bug fix (non-breaking change that fixes an issue)
|
||||||
|
- [ ] ✨ New feature (non-breaking change that adds functionality)
|
||||||
|
- [ ] 🔒 Security fix
|
||||||
|
- [ ] 📝 Documentation update
|
||||||
|
- [ ] ✅ Tests (adding or improving test coverage)
|
||||||
|
- [ ] ♻️ Refactor (no behavior change)
|
||||||
|
- [ ] 🎯 New skill (bundled or hub)
|
||||||
|
|
||||||
|
## Changes Made
|
||||||
|
|
||||||
|
<!-- List the specific changes. Include file paths for code changes. -->
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
## How to Test
|
||||||
|
|
||||||
|
<!-- Steps to verify this change works. For bugs: reproduction steps + proof that the fix works. -->
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!-- Complete these before requesting review. -->
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
- [ ] I've read the [Contributing Guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md)
|
||||||
|
- [ ] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) (`fix(scope):`, `feat(scope):`, etc.)
|
||||||
|
- [ ] I searched for [existing PRs](https://github.com/NousResearch/hermes-agent/pulls) to make sure this isn't a duplicate
|
||||||
|
- [ ] My PR contains **only** changes related to this fix/feature (no unrelated commits)
|
||||||
|
- [ ] I've run `pytest tests/ -q` and all tests pass
|
||||||
|
- [ ] I've added tests for my changes (required for bug fixes, strongly encouraged for features)
|
||||||
|
- [ ] I've tested on my platform: <!-- e.g. Ubuntu 24.04, macOS 15.2, Windows 11 -->
|
||||||
|
|
||||||
|
### Documentation & Housekeeping
|
||||||
|
|
||||||
|
<!-- Check all that apply. It's OK to check "N/A" if a category doesn't apply to your change. -->
|
||||||
|
|
||||||
|
- [ ] I've updated relevant documentation (README, `docs/`, docstrings) — or N/A
|
||||||
|
- [ ] I've updated `cli-config.yaml.example` if I added/changed config keys — or N/A
|
||||||
|
- [ ] I've updated `CONTRIBUTING.md` or `AGENTS.md` if I changed architecture or workflows — or N/A
|
||||||
|
- [ ] I've considered cross-platform impact (Windows, macOS) per the [compatibility guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#cross-platform-compatibility) — or N/A
|
||||||
|
- [ ] I've updated tool descriptions/schemas if I changed tool behavior — or N/A
|
||||||
|
|
||||||
|
## For New Skills
|
||||||
|
|
||||||
|
<!-- Only fill this out if you're adding a skill. Delete this section otherwise. -->
|
||||||
|
|
||||||
|
- [ ] This skill is **broadly useful** to most users (if bundled) — see [Contributing Guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#should-the-skill-be-bundled)
|
||||||
|
- [ ] SKILL.md follows the [standard format](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#skillmd-format) (frontmatter, trigger conditions, steps, pitfalls)
|
||||||
|
- [ ] No external dependencies that aren't already available (prefer stdlib, curl, existing Hermes tools)
|
||||||
|
- [ ] I've tested the skill end-to-end: `hermes --toolsets skills -q "Use the X skill to do Y"`
|
||||||
|
|
||||||
|
## Screenshots / Logs
|
||||||
|
|
||||||
|
<!-- If applicable, add screenshots or log output showing the fix/feature in action. -->
|
||||||
|
|
||||||
60
.github/workflows/deploy-site.yml
vendored
Normal file
60
.github/workflows/deploy-site.yml
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
name: Deploy Site
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'website/**'
|
||||||
|
- 'landingpage/**'
|
||||||
|
- '.github/workflows/deploy-site.yml'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pages
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deploy.outputs.page_url }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: website/package-lock.json
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
working-directory: website
|
||||||
|
|
||||||
|
- name: Build Docusaurus
|
||||||
|
run: npm run build
|
||||||
|
working-directory: website
|
||||||
|
|
||||||
|
- name: Stage deployment
|
||||||
|
run: |
|
||||||
|
mkdir -p _site/docs
|
||||||
|
# Landing page at root
|
||||||
|
cp -r landingpage/* _site/
|
||||||
|
# Docusaurus at /docs/
|
||||||
|
cp -r website/build/* _site/docs/
|
||||||
|
# CNAME so GitHub Pages keeps the custom domain between deploys
|
||||||
|
echo "hermes-agent.nousresearch.com" > _site/CNAME
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: _site
|
||||||
|
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deploy
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
39
.github/workflows/docs-site-checks.yml
vendored
Normal file
39
.github/workflows/docs-site-checks.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
name: Docs Site Checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'website/**'
|
||||||
|
- '.github/workflows/docs-site-checks.yml'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docs-site-checks:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: website/package-lock.json
|
||||||
|
|
||||||
|
- name: Install website dependencies
|
||||||
|
run: npm ci
|
||||||
|
working-directory: website
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Install ascii-guard
|
||||||
|
run: python -m pip install ascii-guard
|
||||||
|
|
||||||
|
- name: Lint docs diagrams
|
||||||
|
run: npm run lint:diagrams
|
||||||
|
working-directory: website
|
||||||
|
|
||||||
|
- name: Build Docusaurus
|
||||||
|
run: npm run build
|
||||||
|
working-directory: website
|
||||||
192
.github/workflows/supply-chain-audit.yml
vendored
Normal file
192
.github/workflows/supply-chain-audit.yml
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
name: Supply Chain Audit
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
scan:
|
||||||
|
name: Scan PR for supply chain risks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Scan diff for suspicious patterns
|
||||||
|
id: scan
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BASE="${{ github.event.pull_request.base.sha }}"
|
||||||
|
HEAD="${{ github.event.pull_request.head.sha }}"
|
||||||
|
|
||||||
|
# Get the full diff (added lines only)
|
||||||
|
DIFF=$(git diff "$BASE".."$HEAD" -- . ':!uv.lock' ':!*.lock' ':!package-lock.json' ':!yarn.lock' || true)
|
||||||
|
|
||||||
|
FINDINGS=""
|
||||||
|
CRITICAL=false
|
||||||
|
|
||||||
|
# --- .pth files (auto-execute on Python startup) ---
|
||||||
|
PTH_FILES=$(git diff --name-only "$BASE".."$HEAD" | grep '\.pth$' || true)
|
||||||
|
if [ -n "$PTH_FILES" ]; then
|
||||||
|
CRITICAL=true
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### 🚨 CRITICAL: .pth file added or modified
|
||||||
|
Python \`.pth\` files in \`site-packages/\` execute automatically when the interpreter starts — no import required. This is the exact mechanism used in the [litellm supply chain attack](https://github.com/BerriAI/litellm/issues/24512).
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
\`\`\`
|
||||||
|
${PTH_FILES}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- base64 + exec/eval combo (the litellm attack pattern) ---
|
||||||
|
B64_EXEC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'base64\.(b64decode|decodebytes|urlsafe_b64decode)' | grep -iE 'exec\(|eval\(' | head -10 || true)
|
||||||
|
if [ -n "$B64_EXEC_HITS" ]; then
|
||||||
|
CRITICAL=true
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### 🚨 CRITICAL: base64 decode + exec/eval combo
|
||||||
|
This is the exact pattern used in the [litellm supply chain attack](https://github.com/BerriAI/litellm/issues/24512) — base64-decoded strings passed to exec/eval to hide credential-stealing payloads.
|
||||||
|
|
||||||
|
**Matches:**
|
||||||
|
\`\`\`
|
||||||
|
${B64_EXEC_HITS}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- base64 decode/encode (alone — legitimate uses exist) ---
|
||||||
|
B64_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'base64\.(b64decode|b64encode|decodebytes|encodebytes|urlsafe_b64decode)|atob\(|btoa\(|Buffer\.from\(.*base64' | head -20 || true)
|
||||||
|
if [ -n "$B64_HITS" ]; then
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### ⚠️ WARNING: base64 encoding/decoding detected
|
||||||
|
Base64 has legitimate uses (images, JWT, etc.) but is also commonly used to obfuscate malicious payloads. Verify the usage is appropriate.
|
||||||
|
|
||||||
|
**Matches (first 20):**
|
||||||
|
\`\`\`
|
||||||
|
${B64_HITS}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- exec/eval with string arguments ---
|
||||||
|
EXEC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -E '(exec|eval)\s*\(' | grep -v '^\+\s*#' | grep -v 'test_\|mock\|assert\|# ' | head -20 || true)
|
||||||
|
if [ -n "$EXEC_HITS" ]; then
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### ⚠️ WARNING: exec() or eval() usage
|
||||||
|
Dynamic code execution can hide malicious behavior, especially when combined with base64 or network fetches.
|
||||||
|
|
||||||
|
**Matches (first 20):**
|
||||||
|
\`\`\`
|
||||||
|
${EXEC_HITS}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- subprocess with encoded/obfuscated commands ---
|
||||||
|
PROC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -E 'subprocess\.(Popen|call|run)\s*\(' | grep -iE 'base64|decode|encode|\\x|chr\(' | head -10 || true)
|
||||||
|
if [ -n "$PROC_HITS" ]; then
|
||||||
|
CRITICAL=true
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### 🚨 CRITICAL: subprocess with encoded/obfuscated command
|
||||||
|
Subprocess calls with encoded arguments are a strong indicator of payload execution.
|
||||||
|
|
||||||
|
**Matches:**
|
||||||
|
\`\`\`
|
||||||
|
${PROC_HITS}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Network calls to non-standard domains ---
|
||||||
|
EXFIL_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'requests\.(post|put)\(|httpx\.(post|put)\(|urllib\.request\.urlopen' | grep -v '^\+\s*#' | grep -v 'test_\|mock\|assert' | head -10 || true)
|
||||||
|
if [ -n "$EXFIL_HITS" ]; then
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### ⚠️ WARNING: Outbound network calls (POST/PUT)
|
||||||
|
Outbound POST/PUT requests in new code could be data exfiltration. Verify the destination URLs are legitimate.
|
||||||
|
|
||||||
|
**Matches (first 10):**
|
||||||
|
\`\`\`
|
||||||
|
${EXFIL_HITS}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- setup.py / setup.cfg install hooks ---
|
||||||
|
SETUP_HITS=$(git diff --name-only "$BASE".."$HEAD" | grep -E '(setup\.py|setup\.cfg|__init__\.pth|sitecustomize\.py|usercustomize\.py)$' || true)
|
||||||
|
if [ -n "$SETUP_HITS" ]; then
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### ⚠️ WARNING: Install hook files modified
|
||||||
|
These files can execute code during package installation or interpreter startup.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
\`\`\`
|
||||||
|
${SETUP_HITS}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Compile/marshal/pickle (code object injection) ---
|
||||||
|
MARSHAL_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'marshal\.loads|pickle\.loads|compile\(' | grep -v '^\+\s*#' | grep -v 'test_\|re\.compile\|ast\.compile' | head -10 || true)
|
||||||
|
if [ -n "$MARSHAL_HITS" ]; then
|
||||||
|
FINDINGS="${FINDINGS}
|
||||||
|
### ⚠️ WARNING: marshal/pickle/compile usage
|
||||||
|
These can deserialize or construct executable code objects.
|
||||||
|
|
||||||
|
**Matches:**
|
||||||
|
\`\`\`
|
||||||
|
${MARSHAL_HITS}
|
||||||
|
\`\`\`
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Output results ---
|
||||||
|
if [ -n "$FINDINGS" ]; then
|
||||||
|
echo "found=true" >> "$GITHUB_OUTPUT"
|
||||||
|
if [ "$CRITICAL" = true ]; then
|
||||||
|
echo "critical=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "critical=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
# Write findings to a file (multiline env vars are fragile)
|
||||||
|
echo "$FINDINGS" > /tmp/findings.md
|
||||||
|
else
|
||||||
|
echo "found=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "critical=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Post warning comment
|
||||||
|
if: steps.scan.outputs.found == 'true'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
SEVERITY="⚠️ Supply Chain Risk Detected"
|
||||||
|
if [ "${{ steps.scan.outputs.critical }}" = "true" ]; then
|
||||||
|
SEVERITY="🚨 CRITICAL Supply Chain Risk Detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BODY="## ${SEVERITY}
|
||||||
|
|
||||||
|
This PR contains patterns commonly associated with supply chain attacks. This does **not** mean the PR is malicious — but these patterns require careful human review before merging.
|
||||||
|
|
||||||
|
$(cat /tmp/findings.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
*Automated scan triggered by [supply-chain-audit](/.github/workflows/supply-chain-audit.yml). If this is a false positive, a maintainer can approve after manual review.*"
|
||||||
|
|
||||||
|
gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY"
|
||||||
|
|
||||||
|
- name: Fail on critical findings
|
||||||
|
if: steps.scan.outputs.critical == 'true'
|
||||||
|
run: |
|
||||||
|
echo "::error::CRITICAL supply chain risk patterns detected in this PR. See the PR comment for details."
|
||||||
|
exit 1
|
||||||
42
.github/workflows/tests.yml
vendored
Normal file
42
.github/workflows/tests.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
# Cancel in-progress runs for the same PR/branch
|
||||||
|
concurrency:
|
||||||
|
group: tests-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
|
|
||||||
|
- name: Set up Python 3.11
|
||||||
|
run: uv python install 3.11
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
uv venv .venv --python 3.11
|
||||||
|
source .venv/bin/activate
|
||||||
|
uv pip install -e ".[all,dev]"
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m pytest tests/ -q --ignore=tests/integration --tb=short -n auto
|
||||||
|
env:
|
||||||
|
# Ensure tests don't accidentally call real APIs
|
||||||
|
OPENROUTER_API_KEY: ""
|
||||||
|
OPENAI_API_KEY: ""
|
||||||
|
NOUS_API_KEY: ""
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -13,12 +13,7 @@ __pycache__/
|
||||||
.env.test
|
.env.test
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
|
||||||
hermes_code/test_browser.py
|
test_browser.py
|
||||||
.git
|
|
||||||
.github
|
|
||||||
.idea
|
|
||||||
hermes_data
|
|
||||||
workspace
|
|
||||||
|
|
||||||
export*
|
export*
|
||||||
__pycache__/model_tools.cpython-310.pyc
|
__pycache__/model_tools.cpython-310.pyc
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,5 @@ RUN uv sync --frozen --no-install-project --extra tg
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN uv sync --frozen --extra tg
|
RUN uv sync --frozen --extra tg
|
||||||
|
|
||||||
|
CMD ["python", "-m", "gateway.run"]
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
|
@ -1,5 +1,3 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export DISPLAY=:99
|
export DISPLAY=:99
|
||||||
|
|
||||||
mkdir -p /var/run/dbus
|
mkdir -p /var/run/dbus
|
||||||
|
|
|
||||||
|
|
@ -1,189 +0,0 @@
|
||||||
model:
|
|
||||||
default: qwen3.5-122b
|
|
||||||
provider: custom
|
|
||||||
base_url: https://llm.lambda.coredump.ru/v1
|
|
||||||
toolsets:
|
|
||||||
- all
|
|
||||||
agent:
|
|
||||||
max_turns: 60
|
|
||||||
verbose: false
|
|
||||||
reasoning_effort: medium
|
|
||||||
personalities:
|
|
||||||
helpful: You are a helpful, friendly AI assistant.
|
|
||||||
technical: You are a technical expert. Provide detailed, accurate technical information.
|
|
||||||
terminal:
|
|
||||||
backend: docker
|
|
||||||
cwd: .
|
|
||||||
timeout: 180
|
|
||||||
docker_image: python:3.12-slim
|
|
||||||
singularity_image: docker://python:3.12-slim
|
|
||||||
modal_image: python:3.12-slim
|
|
||||||
daytona_image: python:3.12-slim
|
|
||||||
container_cpu: 1.0
|
|
||||||
container_memory: 2048
|
|
||||||
container_disk: 15360
|
|
||||||
container_persistent: true
|
|
||||||
docker_volumes:
|
|
||||||
lifetime_seconds: 300
|
|
||||||
browser:
|
|
||||||
inactivity_timeout: 120
|
|
||||||
record_sessions: false
|
|
||||||
checkpoints:
|
|
||||||
enabled: false
|
|
||||||
max_snapshots: 50
|
|
||||||
compression:
|
|
||||||
enabled: true
|
|
||||||
threshold: 0.8
|
|
||||||
summary_model: google/gemini-3-flash-preview
|
|
||||||
summary_provider: auto
|
|
||||||
auxiliary:
|
|
||||||
vision:
|
|
||||||
provider: auto
|
|
||||||
model: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
web_extract:
|
|
||||||
provider: auto
|
|
||||||
model: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
compression:
|
|
||||||
provider: auto
|
|
||||||
model: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
session_search:
|
|
||||||
provider: auto
|
|
||||||
model: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
skills_hub:
|
|
||||||
provider: auto
|
|
||||||
model: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
mcp:
|
|
||||||
provider: auto
|
|
||||||
model: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
flush_memories:
|
|
||||||
provider: auto
|
|
||||||
model: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
display:
|
|
||||||
compact: false
|
|
||||||
personality: helpful
|
|
||||||
resume_display: full
|
|
||||||
bell_on_complete: false
|
|
||||||
show_reasoning: false
|
|
||||||
skin: default
|
|
||||||
tool_progress: all
|
|
||||||
background_process_notifications: all
|
|
||||||
tts:
|
|
||||||
provider: edge
|
|
||||||
edge:
|
|
||||||
voice: en-US-AriaNeural
|
|
||||||
elevenlabs:
|
|
||||||
voice_id: pNInz6obpgDQGcFmaJgB
|
|
||||||
model_id: eleven_multilingual_v2
|
|
||||||
openai:
|
|
||||||
model: gpt-4o-mini-tts
|
|
||||||
voice: alloy
|
|
||||||
stt:
|
|
||||||
enabled: true
|
|
||||||
provider: local
|
|
||||||
local:
|
|
||||||
model: base
|
|
||||||
openai:
|
|
||||||
model: whisper-1
|
|
||||||
model: whisper-1
|
|
||||||
voice:
|
|
||||||
record_key: ctrl+b
|
|
||||||
max_recording_seconds: 120
|
|
||||||
auto_tts: false
|
|
||||||
silence_threshold: 200
|
|
||||||
silence_duration: 3.0
|
|
||||||
human_delay:
|
|
||||||
mode: 'off'
|
|
||||||
min_ms: 800
|
|
||||||
max_ms: 2500
|
|
||||||
memory:
|
|
||||||
memory_enabled: true
|
|
||||||
user_profile_enabled: true
|
|
||||||
memory_char_limit: 2200
|
|
||||||
user_char_limit: 1375
|
|
||||||
nudge_interval: 10
|
|
||||||
flush_min_turns: 6
|
|
||||||
delegation:
|
|
||||||
model: ''
|
|
||||||
provider: ''
|
|
||||||
base_url: ''
|
|
||||||
api_key: ''
|
|
||||||
max_iterations: 50
|
|
||||||
default_toolsets:
|
|
||||||
- terminal
|
|
||||||
- file
|
|
||||||
- web
|
|
||||||
prefill_messages_file: ''
|
|
||||||
honcho: {}
|
|
||||||
timezone: ''
|
|
||||||
discord:
|
|
||||||
require_mention: true
|
|
||||||
free_response_channels: ''
|
|
||||||
auto_thread: true
|
|
||||||
command_allowlist: []
|
|
||||||
quick_commands: {}
|
|
||||||
personalities: {}
|
|
||||||
security:
|
|
||||||
redact_secrets: true
|
|
||||||
tirith_enabled: true
|
|
||||||
tirith_path: tirith
|
|
||||||
tirith_timeout: 5
|
|
||||||
tirith_fail_open: true
|
|
||||||
_config_version: 8
|
|
||||||
session_reset:
|
|
||||||
mode: both
|
|
||||||
idle_minutes: 150
|
|
||||||
at_hour: 5
|
|
||||||
skills:
|
|
||||||
creation_nudge_interval: 15
|
|
||||||
platform_toolsets:
|
|
||||||
cli:
|
|
||||||
- hermes-cli
|
|
||||||
telegram:
|
|
||||||
- hermes-telegram
|
|
||||||
discord:
|
|
||||||
- hermes-discord
|
|
||||||
whatsapp:
|
|
||||||
- hermes-whatsapp
|
|
||||||
slack:
|
|
||||||
- hermes-slack
|
|
||||||
signal:
|
|
||||||
- hermes-signal
|
|
||||||
homeassistant:
|
|
||||||
- hermes-homeassistant
|
|
||||||
code_execution:
|
|
||||||
timeout: 300
|
|
||||||
max_tool_calls: 50
|
|
||||||
|
|
||||||
# ── Fallback Model ────────────────────────────────────────────────────
|
|
||||||
# Automatic provider failover when primary is unavailable.
|
|
||||||
# Uncomment and configure to enable. Triggers on rate limits (429),
|
|
||||||
# overload (529), service errors (503), or connection failures.
|
|
||||||
#
|
|
||||||
# Supported providers:
|
|
||||||
# openrouter (OPENROUTER_API_KEY) — routes to any model
|
|
||||||
# openai-codex (OAuth — hermes login) — OpenAI Codex
|
|
||||||
# nous (OAuth — hermes login) — Nous Portal
|
|
||||||
# zai (ZAI_API_KEY) — Z.AI / GLM
|
|
||||||
# kimi-coding (KIMI_API_KEY) — Kimi / Moonshot
|
|
||||||
# minimax (MINIMAX_API_KEY) — MiniMax
|
|
||||||
# minimax-cn (MINIMAX_CN_API_KEY) — MiniMax (China)
|
|
||||||
#
|
|
||||||
# For custom OpenAI-compatible endpoints, add base_url and api_key_env.
|
|
||||||
#
|
|
||||||
# fallback_model:
|
|
||||||
# provider: openrouter
|
|
||||||
# model: anthropic/claude-sonnet-4
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
services:
|
services:
|
||||||
agent:
|
agent:
|
||||||
build:
|
build: .
|
||||||
context: ./hermes_code
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: hermes-brain
|
container_name: hermes-brain
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
volumes:
|
||||||
|
- .:/app/hermes_code:ro
|
||||||
|
|
||||||
|
- ${HERMES_DATA_PATH}/config.yaml:/app/hermes_data/config.yaml:ro
|
||||||
|
- ${HERMES_DATA_PATH}/SOUL.md:/app/hermes_data/SOUL.md:ro
|
||||||
|
- ./.env:/app/hermes_data/.env:ro
|
||||||
|
|
||||||
|
- ${HERMES_DATA_PATH}/state.db:/app/hermes_data/state.db:rw
|
||||||
|
- ${HERMES_DATA_PATH}/sessions:/app/hermes_data/sessions:rw
|
||||||
|
- ${HERMES_DATA_PATH}/logs:/app/hermes_data/logs:rw
|
||||||
|
- ${HERMES_DATA_PATH}/skills:/app/hermes_data/skills:rw
|
||||||
|
- ${HERMES_DATA_PATH}/sandboxes:/app/hermes_data/sandboxes:rw
|
||||||
|
- ${HERMES_DATA_PATH}/memories:/app/hermes_data/memories:rw
|
||||||
|
- ${HERMES_WORKSPACE_PATH}/hermes:/app/hermes_data/workspace:rw
|
||||||
environment:
|
environment:
|
||||||
- BROWSER_URL=http://browser:9222
|
- BROWSER_URL=http://browser:9222
|
||||||
- HERMES_HOME=/app/hermes_data
|
|
||||||
volumes:
|
|
||||||
- ./hermes_code:/app/hermes_code:ro
|
|
||||||
- ./hermes_data:/app/hermes_data:rw
|
|
||||||
- ./workspace:/app/workspace:rw
|
|
||||||
- ./config.example.yaml:/app/config.example.yaml:ro
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- browser
|
- browser
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
|
|
@ -25,23 +31,12 @@ services:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
memory: 1.5G
|
memory: 1.5G
|
||||||
command: >
|
|
||||||
bash -c "
|
|
||||||
if [ ! -f /app/hermes_data/config.yaml ]; then
|
|
||||||
echo 'Config not found, copying from example...';
|
|
||||||
cp /app/config.example.yaml /app/hermes_data/config.yaml;
|
|
||||||
fi;
|
|
||||||
exec python -m gateway.run
|
|
||||||
"
|
|
||||||
|
|
||||||
browser:
|
browser:
|
||||||
build:
|
build:
|
||||||
context: ./browser_env
|
context: ./browser_env
|
||||||
dockerfile: Dockerfile.browser
|
dockerfile: Dockerfile.browser
|
||||||
container_name: hermes-browser
|
container_name: hermes-browser
|
||||||
ports:
|
|
||||||
- "6080:6080"
|
|
||||||
- "9222:9222"
|
|
||||||
networks:
|
networks:
|
||||||
hermes-net:
|
hermes-net:
|
||||||
aliases:
|
aliases:
|
||||||
|
|
@ -55,7 +50,7 @@ services:
|
||||||
image: cloudflare/cloudflared:latest
|
image: cloudflare/cloudflared:latest
|
||||||
container_name: hermes-tunnel
|
container_name: hermes-tunnel
|
||||||
restart: always
|
restart: always
|
||||||
command: tunnel --protocol http2 --url http://browser:6080 --no-tls-verify
|
command: tunnel --url http://browser:6080
|
||||||
networks:
|
networks:
|
||||||
- hermes-net
|
- hermes-net
|
||||||
|
|
||||||
|
|
@ -65,3 +60,12 @@ volumes:
|
||||||
networks:
|
networks:
|
||||||
hermes-net:
|
hermes-net:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
|
|
||||||
|
# Optional: SSL configuration if needed
|
||||||
|
# extra_hosts:
|
||||||
|
# - "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
# Uncomment the following if you need persistent logging or data
|
||||||
|
# volumes:
|
||||||
|
# - ./logs:/tmp/logs
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue