Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
5ce0067
fix(ci): stabilize shared test state after 21012
stephenschoettler May 14, 2026
e84fe48
feat(discord): channel history backfill for multi-user sessions
simpolism May 14, 2026
4abfb6b
feat(discord): default history backfill on, expand to per-user + threads
teknium1 May 14, 2026
ed84637
fix(web): make sync-assets script cross-platform
HxT9 May 14, 2026
1907152
fix(lsp): shift baseline diagnostics into post-edit coordinates (#25978)
teknium1 May 14, 2026
0854640
fix(web): cross-platform sync-assets + surface build errors on failure
johnisag May 14, 2026
38ea2a5
fix(web): handle non-UTF8 Windows console encodings in _build_web_ui
johnisag Apr 21, 2026
db82c45
chore(release): map agorgianitisj@hotmail.com -> johnisag
teknium1 May 14, 2026
09d9701
fix(proxy): suppress false-positive windows-footgun on guarded add_si…
teknium1 May 14, 2026
d6c488f
fix(cli): wire /sessions slash command in the classic CLI
explainanalyze May 13, 2026
74e47c0
chore(release): map phil.thomas@gametime.co -> explainanalyze
teknium1 May 14, 2026
55622b5
chore(release): map phil.thomas@gametime.co -> explainanalyze
teknium1 May 14, 2026
1702a94
Merge pull request #25957 from stephenschoettler/fix/main-ci-unblocke…
ethernet8023 May 15, 2026
8ed2ef6
fix(browser): use correct env var for --no-sandbox bypass
Jaggia May 13, 2026
4695d27
fix(browser): honor pre-set AGENT_BROWSER_ARGS and document the bypass
teknium1 May 14, 2026
eabd8c1
fix(cli): fall back to SelectSelector when kqueue can't watch stdin
geocaching-jeremy May 6, 2026
d3d5916
chore(release): add AUTHOR_MAP entry for outdoorsea
teknium1 May 15, 2026
e8b9f5f
fix(aux): surface Nous auth-unavailable warning in auxiliary client
0xharryriddle May 15, 2026
4c94396
feat: add ACP registry metadata for Zed
am423 May 14, 2026
d364132
chore(release): bump ACP Registry assets in lockstep with pyproject
teknium1 May 15, 2026
5af672c
chore: remove Atropos RL environments and tinker-atropos integration …
alt-glitch May 15, 2026
c8c6ce1
feat(acp-registry): switch to uvx distribution, drop npm launcher
teknium1 May 15, 2026
bcca5ed
fix(deps): pin brotlicffi so aiohttp can decode Discord's Brotli atta…
teknium1 May 15, 2026
f8745f5
fix(cli): kill resize scrollback duplication + light-mode visibility
OutThisLife May 15, 2026
cbd1f8e
test(cli): cover light-mode detection + SkinConfig.get_color remap
teknium1 May 15, 2026
965ae7f
revert(cli): drop scrollback box width clamp (#25975), restore full-w…
teknium1 May 15, 2026
f9ad740
fix(goals): raise judge max_tokens 200 → 4096, make configurable
teknium1 May 15, 2026
6bdad1f
ci: add PyPI publish workflow (salvaged from #25901) (#26148)
alt-glitch May 15, 2026
d57a4b3
feat(yuanbao): add _parse_resource_id and update _extract_text for yb…
libo1106 May 9, 2026
80efe66
feat(yuanbao): add quote_media_refs extraction to QuoteContextMiddleware
libo1106 May 9, 2026
3df26b9
feat(yuanbao): prioritize quote media refs over history backfill in D…
libo1106 May 9, 2026
fc2754d
fix(yuanbao): resolve quoted file/image via transcript lookup when qu…
libo1106 May 9, 2026
0086cda
refactor(yuanbao): improve quote media fallback — move to DispatchMid…
libo1106 May 9, 2026
e0e4856
feat(skills-hub): add huggingface/skills as trusted default tap (#2549)
kshitijk4poor May 15, 2026
e0e7397
fix(session): persist auto-reset state across gateway restarts
teyrebaz33 Mar 22, 2026
23ac522
fix(gateway): isinstance-guard string-form 429 error body
KiraKatana May 15, 2026
814c600
fix: clean stale conversation mappings on response eviction/deletion
CoinTheHat Mar 23, 2026
0161d4b
chore(release): add AUTHOR_MAP entry for CoinTheHat
teknium1 May 15, 2026
681778a
fix(whatsapp): fail fast when Baileys sendMessage hangs
wysie May 15, 2026
04b1fda
security(deps): add upper bounds to 5 loose deps + document supply ch…
alt-glitch May 15, 2026
9329e06
feat(image-gen): actionable setup message when no FAL backend is reac…
teknium1 May 15, 2026
05d9f64
docs(cron): worked recipes for the wakeAgent pre-run gate (#26229)
teknium1 May 15, 2026
6682f91
feat(cron): support name-based lookup for job operations
buntingszn May 15, 2026
9f57f22
chore(release): add AUTHOR_MAP entry for buntingszn
teknium1 May 15, 2026
85782a4
feat(acp): hermes acp --setup-browser bootstraps browser tools for re…
teknium1 May 15, 2026
09d9724
feat(gateway): add SimpleX Chat platform plugin
Mibayy May 15, 2026
47614db
chore: wire simplex docs into sidebar + AUTHOR_MAP
teknium1 May 15, 2026
b6e0741
feat(cli): show YOLO mode warning in banner and status bar
Mibayy May 15, 2026
4f8aaf1
perf(run_agent): accumulate length-continuation prefix via list+join
InB4DevOps May 15, 2026
647cc0b
chore(release): add AUTHOR_MAP entries for InB4DevOps
teknium1 May 15, 2026
5360b54
fix(providers): set User-Agent on ProviderProfile.fetch_models
teknium1 May 15, 2026
55f3262
fix(mcp): pre-compile env-var regex and unify interpolation
amethystani Mar 24, 2026
59c7cc6
chore(release): add AUTHOR_MAP entry for amethystani
teknium1 May 15, 2026
c4a21d7
fix(cli): log swallowed exception in runtime model auto-detection
nidhi-singh02 Mar 24, 2026
5301cc2
chore(release): add AUTHOR_MAP entry for nidhi-singh02
teknium1 May 15, 2026
eacb398
fix(tools): add return_exceptions to asyncio.gather in web_tools
nidhi-singh02 May 15, 2026
94bdc63
chore(release): add AUTHOR_MAP entry for nidhi-singh02
teknium1 May 15, 2026
8373956
fix(slack): guard split()[0] against whitespace-only command text
nidhi-singh02 May 15, 2026
6af9942
fix(url-safety): allow only http and https schemes
aydnOktay Mar 24, 2026
13c72fb
fix(tools): wrap browser provider network calls with error handling
nidhi-singh02 May 15, 2026
2742173
fix(codex-runtime): keep migrated root keys top-level
steezkelly May 14, 2026
7727607
fix(codex-runtime): de-dup [plugins.X] tables and stop leaking HERMES…
kshitijk4poor May 15, 2026
f199cd9
chore(release): map brian@dralth.com to btorresgil for #22345 salvage…
kshitijk4poor May 15, 2026
db84a78
fix(langfuse): complete observability fix — trace I/O, tool outputs, …
kshitijk4poor May 15, 2026
d541628
fix(tui): autonomous background process completion notifications (#26…
alt-glitch May 15, 2026
9fb40e6
fix(tui): restrict fast-echo bypass to ASCII so Vietnamese/CJK/IME in…
OutThisLife May 15, 2026
7cee931
feat: port Tlon adapter to latest gateway
wca4a May 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 7 additions & 18 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@ BROWSER_SESSION_TIMEOUT=300
# Browser sessions are automatically closed after this period of no activity
BROWSER_INACTIVITY_TIMEOUT=120

# Extra Chromium launch flags passed to agent-browser, comma- or newline-separated.
# Hermes auto-injects "--no-sandbox,--disable-dev-shm-usage" when it detects root
# or AppArmor-restricted unprivileged user namespaces (Ubuntu 23.10+, DGX Spark,
# many container images), so leave this unset unless you need extra flags.
# Setting this disables the auto-injection.
# AGENT_BROWSER_ARGS=--no-sandbox

# Camofox local anti-detection browser (Camoufox-based Firefox).
# Set CAMOFOX_URL to route the browser tools through a local Camofox server
# instead of agent-browser/Browserbase. See docs/user-guide/features/browser.md.
Expand Down Expand Up @@ -387,24 +394,6 @@ IMAGE_TOOLS_DEBUG=false
# 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)
# =============================================================================
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/supply-chain-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- '**/sitecustomize.py'
- '**/usercustomize.py'
- '**/__init__.pth'
- 'pyproject.toml'

permissions:
pull-requests: write
Expand Down Expand Up @@ -137,3 +138,68 @@ jobs:
run: |
echo "::error::CRITICAL supply chain risk patterns detected in this PR. See the PR comment for details."
exit 1

dep-bounds:
name: Check PyPI dependency upper bounds
runs-on: ubuntu-latest
if: contains(github.event.pull_request.changed_files_url, 'pyproject.toml') || true
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- name: Check for unbounded PyPI deps
id: bounds
run: |
set -euo pipefail

BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"

# Only check added lines in pyproject.toml
ADDED=$(git diff "$BASE".."$HEAD" -- pyproject.toml | grep '^+' | grep -v '^+++' || true)

if [ -z "$ADDED" ]; then
echo "found=false" >> "$GITHUB_OUTPUT"
exit 0
fi

# Match PyPI dep specs that have >= but no < ceiling.
# Pattern: "package>=version" without a following ",<" bound.
# Excludes git+ URLs (which use commit SHAs) and comments.
UNBOUNDED=$(echo "$ADDED" | grep -oE '"[a-zA-Z0-9_-]+(\[[^\]]*\])?>=[ 0-9.]+"' | grep -v ',<' || true)

if [ -n "$UNBOUNDED" ]; then
echo "found=true" >> "$GITHUB_OUTPUT"
echo "$UNBOUNDED" > /tmp/unbounded.txt
else
echo "found=false" >> "$GITHUB_OUTPUT"
fi

- name: Post unbounded dep warning
if: steps.bounds.outputs.found == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BODY="## ⚠️ Unbounded PyPI Dependency Detected

This PR adds PyPI dependencies without a \`<next_major\` upper bound. Per our [supply chain policy](../blob/main/CONTRIBUTING.md#dependency-pinning-policy-supply-chain-hardening), all PyPI deps must be pinned as \`>=floor,<next_major\`.

**Unbounded specs found:**
\`\`\`
$(cat /tmp/unbounded.txt)
\`\`\`

**Fix:** Add an upper bound, e.g. \`\"package>=1.2.0,<2\"\`

---
*See PR #2810 and CONTRIBUTING.md for the full policy rationale.*"

gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY" || echo "::warning::Could not post PR comment (expected for fork PRs)"

- name: Fail on unbounded deps
if: steps.bounds.outputs.found == 'true'
run: |
echo "::error::PyPI dependencies without upper bounds detected. Add <next_major ceiling per CONTRIBUTING.md policy."
exit 1
137 changes: 137 additions & 0 deletions .github/workflows/upload_to_pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
name: Publish to PyPI

# Triggered by CalVer tag pushes from scripts/release.py (e.g. v2026.5.15)
# Can also be triggered manually from the Actions tab as an escape hatch.
on:
push:
tags:
- 'v20*' # CalVer tags: v2026.5.15, v2026.5.15.2, etc.
workflow_dispatch:
inputs:
confirm_tag:
description: 'Tag to publish (e.g. v2026.5.15). Must already exist.'
required: true
type: string

# Restrict default token to read-only; each job escalates as needed.
permissions:
contents: read

# Prevent overlapping publishes (e.g. two same-day tags pushed quickly).
concurrency:
group: pypi-publish
cancel-in-progress: false

jobs:
build:
name: Build distribution 📦
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
# On workflow_dispatch, check out the confirmed tag.
ref: ${{ inputs.confirm_tag || github.ref }}
fetch-tags: true

- name: Validate tag exists
if: github.event_name == 'workflow_dispatch'
run: |
if ! git tag -l "${{ inputs.confirm_tag }}" | grep -q .; then
echo "::error::Tag '${{ inputs.confirm_tag }}' does not exist in the repo"
exit 1
fi

- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.13'

- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6

- name: Build wheel and sdist
run: uv build --sdist --wheel

- name: Upload distribution artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: python-package-distributions
path: dist/

publish:
name: Publish to PyPI
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/hermes-agent
permissions:
id-token: write # OIDC trusted publishing

steps:
- name: Download distribution artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: python-package-distributions
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
with:
skip-existing: true

sign:
name: Sign and attach to GitHub Release
# Only runs on tag pushes — release.py creates the GitHub Release,
# and workflow_dispatch won't have a matching release to attach to.
if: startsWith(github.ref, 'refs/tags/')
needs: publish
runs-on: ubuntu-latest
permissions:
contents: write # attach assets to the existing release
id-token: write # sigstore signing

steps:
- name: Download distribution artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: python-package-distributions
path: dist/

- name: Wait for GitHub Release to exist
env:
GITHUB_TOKEN: ${{ github.token }}
# release.py creates the GitHub Release after pushing the tag,
# but this workflow starts from the tag push — wait for it.
run: |
for i in $(seq 1 30); do
if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "Release $GITHUB_REF_NAME found"
exit 0
fi
echo "Waiting for release... ($i/30)"
sleep 10
done
echo "::warning::Release $GITHUB_REF_NAME not found after 5 minutes — skipping signature upload"
echo "skip_sign=true" >> "$GITHUB_ENV"

- name: Sign with Sigstore
if: env.skip_sign != 'true'
uses: sigstore/gh-action-sigstore-python@f514d46b907ebcd5bedc05145c03b69c1edd8b46 # v3.0.0
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl

- name: Attach signed artifacts to GitHub Release
if: env.skip_sign != 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
# release.py already created the GitHub Release — just upload
# the Sigstore signatures alongside the existing assets.
run: >-
gh release upload
"$GITHUB_REF_NAME" dist/*.sigstore.json
--repo "$GITHUB_REPOSITORY"
--clobber
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

24 changes: 23 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ hermes-agent/
├── tui_gateway/ # Python JSON-RPC backend for the TUI
├── acp_adapter/ # ACP server (VS Code / Zed / JetBrains integration)
├── cron/ # Scheduler — jobs.py, scheduler.py
├── environments/ # RL training environments (Atropos)
├── scripts/ # run_tests.sh, release.py, auxiliary scripts
├── website/ # Docusaurus docs site
└── tests/ # Pytest suite (~17k tests across ~900 files as of May 2026)
Expand Down Expand Up @@ -309,6 +308,29 @@ The registry handles schema collection, dispatch, availability checking, and err

---

## Dependency Pinning Policy

All dependencies must have upper bounds to limit supply-chain attack surface.
This policy was established after the litellm compromise (PR #2796, #2810) and
reinforced after the Mini Shai-Hulud worm campaign (May 2026).

| Source type | Treatment | Example |
|---|---|---|
| PyPI package | `>=floor,<next_major` | `"httpx>=0.28.1,<1"` |
| Git URL | Commit SHA | `git+https://...@<40-char-sha>` |
| GitHub Actions | Commit SHA + comment | `uses: actions/checkout@<sha> # v4` |
| CI-only pip | `==exact` | `pyyaml==6.0.2` |

**When adding a new dependency to `pyproject.toml`:**
1. Pin to `>=current_version,<next_major` for post-1.0 (e.g. `>=1.5.0,<2`).
2. For pre-1.0 packages, use `<0.(current_minor + 2)` (e.g. `>=0.29,<0.32`).
3. Never commit a bare `>=X.Y.Z` without a ceiling — CI and reviewers will reject it.
4. Run `uv lock` to regenerate `uv.lock` with hashes.

Reference: #2810 (bounds pass), #9801 (SHA pinning + audit CI).

---

## Adding Configuration

### config.yaml options:
Expand Down
45 changes: 41 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ export VIRTUAL_ENV="$(pwd)/venv"
# Install with all extras (messaging, cron, CLI menus, dev tools)
uv pip install -e ".[all,dev]"

# Optional: RL training submodule
# git submodule update --init tinker-atropos && uv pip install -e "./tinker-atropos"

# Optional: browser tools
npm install
```
Expand Down Expand Up @@ -196,7 +193,6 @@ hermes-agent/
├── skills/ # Bundled skills (copied to ~/.hermes/skills/ on install)
├── optional-skills/ # Official optional skills (discoverable via hub, not activated by default)
├── environments/ # RL training environments (Atropos integration)
├── tests/ # Test suite
├── website/ # Documentation site (hermes-agent.nousresearch.com)
Expand Down Expand Up @@ -804,6 +800,47 @@ Hermes has terminal access. Security matters.

If your PR affects security, note it explicitly in the description.

### Dependency pinning policy (supply chain hardening)

After the [litellm supply chain compromise](https://github.com/BerriAI/litellm/issues/24512) in March 2026 and the [Mini Shai-Hulud worm campaign](https://socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack) in May 2026, all dependencies must follow these rules:

| Source type | Required treatment | Rationale |
|---|---|---|
| **PyPI package** | `>=floor,<next_major` | PyPI versions are immutable once published, but new versions can be pushed into your range. A `<next_major` ceiling stops a 1.x install from upgrading to a malicious 2.0.0. |
| **Git URL** (atroposlib, tinker, yc-bench, Baileys) | Full commit SHA | Branches and tags are mutable refs; SHA is content-addressed. |
| **GitHub Actions** | Full commit SHA + version comment | Action tags are mutable refs (e.g. tj-actions/changed-files March 2025). Pin as `uses: owner/action@<sha> # vX.Y.Z` |
| **CI-only pip installs** | `==exact` | Hermetic CI builds; churn is acceptable. |

**Every new PyPI dependency in a PR must have a `<next_major` upper bound.** PRs adding unbounded `>=X.Y.Z` specs will be rejected by reviewers. The `supply-chain-audit.yml` CI workflow also flags dependency manifest changes for manual review.

**How to determine the ceiling:**
- If the package is at version `1.x.y`, use `<2`.
- If the package is at version `0.x.y` (pre-1.0), use `<0.(current_minor + 2)` — e.g. if current is `0.29.x`, use `<0.32`. This gives ~2 minor versions of headroom while keeping the window small enough that a hostile takeover version is unlikely to land inside it.
- Exception: packages with very stable APIs (e.g. `aiohttp-socks`) can use `<1` at reviewer discretion.

**Examples:**
```toml
# ✅ Correct — post-1.0
"openai>=2.21.0,<3"
"pydantic>=2.12.5,<3"

# ✅ Correct — pre-1.0 (tight minor window)
"asyncpg>=0.29,<0.32"
"aiosqlite>=0.20,<0.23"
"hindsight-client>=0.4.22,<0.5"

# ❌ Rejected — no upper bound
"some-package>=1.2.3"

# ❌ Rejected — too tight (blocks legitimate patches)
"some-package==1.2.3"

# ❌ Rejected — too loose for pre-1.0 (allows 80 minor versions)
"some-package>=0.20,<1"
```

**Reference PRs:** #2796 (litellm removal), #2810 (upper bounds pass), #9801 (SHA pinning + supply-chain-audit CI).

---

## Pull Request Process
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Use any model you want — [Nous Portal](https://portal.nousresearch.com), [Open
<tr><td><b>Scheduled automations</b></td><td>Built-in cron scheduler with delivery to any platform. Daily reports, nightly backups, weekly audits — all in natural language, running unattended.</td></tr>
<tr><td><b>Delegates and parallelizes</b></td><td>Spawn isolated subagents for parallel workstreams. Write Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns.</td></tr>
<tr><td><b>Runs anywhere, not just your laptop</b></td><td>Seven terminal backends — local, Docker, SSH, Singularity, Modal, Daytona, and Vercel Sandbox. Daytona and Modal offer serverless persistence — your agent's environment hibernates when idle and wakes on demand, costing nearly nothing between sessions. Run it on a $5 VPS or a GPU cluster.</td></tr>
<tr><td><b>Research-ready</b></td><td>Batch trajectory generation, Atropos RL environments, trajectory compression for training the next generation of tool-calling models.</td></tr>
<tr><td><b>Research-ready</b></td><td>Batch trajectory generation, trajectory compression for training the next generation of tool-calling models.</td></tr>
</table>

---
Expand Down Expand Up @@ -175,8 +175,6 @@ uv pip install -e ".[all,dev]"
scripts/run_tests.sh
```

> **RL Training (optional):** The RL/Atropos integration (`environments/`) — see [`CONTRIBUTING.md`](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#development-setup) for the full setup.

---

## Community
Expand Down
Loading
Loading