From 139b9ae1e3e093400439ee0dd2c220510ebb7991 Mon Sep 17 00:00:00 2001 From: Austin Pickett Date: Wed, 15 Apr 2026 23:09:42 -0400 Subject: [PATCH 01/19] feat: add vercel deployment, remove old landing page --- .github/workflows/deploy-site.yml | 25 +- landingpage/apple-touch-icon.png | Bin 28150 -> 0 bytes landingpage/favicon-16x16.png | Bin 870 -> 0 bytes landingpage/favicon-32x32.png | Bin 2511 -> 0 bytes landingpage/favicon.ico | Bin 8139 -> 0 bytes landingpage/hermes-agent-banner.png | Bin 12333 -> 0 bytes landingpage/icon-192.png | Bin 29805 -> 0 bytes landingpage/icon-512.png | Bin 137587 -> 0 bytes landingpage/index.html | 665 --------------- landingpage/nous-logo.png | Bin 20988 -> 0 bytes landingpage/script.js | 521 ------------ landingpage/style.css | 1178 --------------------------- 12 files changed, 11 insertions(+), 2378 deletions(-) delete mode 100644 landingpage/apple-touch-icon.png delete mode 100644 landingpage/favicon-16x16.png delete mode 100644 landingpage/favicon-32x32.png delete mode 100644 landingpage/favicon.ico delete mode 100644 landingpage/hermes-agent-banner.png delete mode 100644 landingpage/icon-192.png delete mode 100644 landingpage/icon-512.png delete mode 100644 landingpage/index.html delete mode 100644 landingpage/nous-logo.png delete mode 100644 landingpage/script.js delete mode 100644 landingpage/style.css diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 480b236f8..44da745b9 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -1,11 +1,12 @@ name: Deploy Site on: + release: + types: [published] push: branches: [main] paths: - 'website/**' - - 'landingpage/**' - 'skills/**' - 'optional-skills/**' - '.github/workflows/deploy-site.yml' @@ -20,8 +21,14 @@ concurrency: cancel-in-progress: false jobs: - build-and-deploy: - # Only run on the upstream repository, not on forks + deploy-vercel: + if: github.event_name == 'release' + runs-on: ubuntu-latest + steps: + - name: Trigger Vercel Deploy + run: curl -X POST "${{ secrets.VERCEL_DEPLOY_HOOK }}" + + deploy-docs: if: github.repository == 'NousResearch/hermes-agent' runs-on: ubuntu-latest environment: @@ -62,20 +69,10 @@ jobs: 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@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 with: - path: _site + path: website/build - name: Deploy to GitHub Pages id: deploy diff --git a/landingpage/apple-touch-icon.png b/landingpage/apple-touch-icon.png deleted file mode 100644 index c5da175f8eb397b579c00678b7687bfd930cdc78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28150 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 Oy6?^MDf-_L(Ek87?}i%y diff --git a/landingpage/hermes-agent-banner.png b/landingpage/hermes-agent-banner.png deleted file mode 100644 index 2c4a160ceb721402e21ae107cbea45bbd80702b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12333 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�!X5p5sf&LloNSTYkUfT-^- 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

ML+Q_oprI)7p(3Ki8 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

sJu{t`y8DsrS@F4j_h= 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 diff --git a/landingpage/index.html b/landingpage/index.html deleted file mode 100644 index e24ed11c4..000000000 --- a/landingpage/index.html +++ /dev/null @@ -1,665 +0,0 @@ - - - - - - Hermes Agent — An Agent That Grows With You - - - - - - - - - - - - - - - - - - - - - - - -

-
- - - -
-
-
- - Open Source • MIT License -
- - - - -

- An agent that
- grows with you. -

- -

- It's not a coding copilot tethered to an IDE or a chatbot wrapper - around a single API. It's an autonomous agent that - lives on your server, remembers what it learns, and gets more capable - the longer it runs. -

- -
-
-
-
- - - -
-
- -
-
-
- $ - curl -fsSL - https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh - | bash - -
-
-

- Works on Linux, macOS & WSL2 · No prerequisites · Installs - everything automatically -

-
- - -
-
- -
-
-
-

Get started in 60 seconds

-
- -
-
-
1
-
-

Install

-
-
-
- -
- -
-
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
-
-

- Installs uv, Python 3.11, clones the repo, sets up everything. - No sudo needed. -

-
-
- -
-
2
-
-

Configure

-
-
- bash - -
-
# Interactive setup wizard
-hermes setup
-
-# Or choose your model
-hermes model
-
-

- Connect to Nous Portal (OAuth), OpenRouter (API key), or your - own endpoint. -

-
-
- -
-
3
-
-

Start chatting

-
-
- bash - -
-
hermes
-
-

- That's it. Full interactive CLI with tools, memory, and skills. -

-
-
- -
-
4
-
-

- Go multi-platform (optional) -

-
-
- bash - -
-
# Interactive gateway setup wizard
-hermes gateway setup
-
-# Start the messaging gateway
-hermes gateway
-
-# Install as a system service
-hermes gateway install
-
-

- Walk through connecting Telegram, Discord, Slack, or WhatsApp. - Runs as a systemd service. -

-
-
- -
-
5
-
-

Keep it up to date

-
-
- bash - -
-
hermes update
-
-

- Pulls the latest changes and reinstalls dependencies. Run - anytime to get new features and fixes. -

-
-
-
- -
-

- Native Windows support is extremely experimental and unsupported. - Please install - WSL2 - and run Hermes Agent from there. -

-
-
-
- - -
-
-
-

See it in action

-
- -
-
-
- - - -
- hermes -
-
-
-
-
- - -
-
-
-

Features

-
- -
-
-
-
- - - -
-

Lives Where You Do

-
-

- Telegram, Discord, Slack, WhatsApp, and CLI from a single gateway - — start on one, pick up on another. -

-
- -
-
-
- - - - -
-

Grows the Longer It Runs

-
-

- Persistent memory and auto-generated skills — it learns your - projects and never forgets how it solved a problem. -

-
- -
-
-
- - - - -
-

Scheduled Automations

-
-

- Natural language cron scheduling for reports, backups, and - briefings — running unattended through the gateway. -

-
- -
-
-
- - - - - - -
-

Delegates & Parallelizes

-
-

- Isolated subagents with their own conversations, terminals, and - Python RPC scripts for zero-context-cost pipelines. -

-
- -
-
-
- - - - -
-

Real Sandboxing

-
-

- Five backends — local, Docker, SSH, Singularity, Modal — with - container hardening and namespace isolation. -

-
- -
-
-
- - - - - -
-

Full Web & Browser Control

-
-

- Web search, browser automation, vision, image generation, - text-to-speech, and multi-model reasoning. -

-
-
- -
- -
- -
-
-
-

Tools

-

- 40+ built-in — web search, terminal, file system, browser - automation, vision, image generation, text-to-speech, code - execution, subagent delegation, memory, task planning, cron - scheduling, multi-model reasoning, and more. -

-
- -
-

Platforms

-

- Telegram, Discord, Slack, WhatsApp, Signal, Email, and CLI — all - from a single gateway. Connect to - Nous Portal, OpenRouter, or any OpenAI-compatible API. -

-
- -
-

Environments

-

- Run locally, in Docker, over SSH, on Modal, Daytona, or - Singularity. Container hardening with read-only root, dropped - capabilities, and namespace isolation. -

-
- -
-

Skills

-

- 40+ bundled skills covering MLOps, GitHub workflows, research, - and more. The agent creates new skills on the fly and shares - them via the open - agentskills.io - format. Install community skills from - ClawHub, - LobeHub, and GitHub. -

-
- -
-

Research

-

- Batch trajectory generation with parallel workers and - checkpointing. Atropos integration for RL training. Export to - ShareGPT for fine-tuning with trajectory compression. -

-
-
-
-
-
- - - - - - diff --git a/landingpage/nous-logo.png b/landingpage/nous-logo.png deleted file mode 100644 index cfea9a661337855b90209ab3160d8e07a16e183b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20988 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 diff --git a/landingpage/script.js b/landingpage/script.js deleted file mode 100644 index 4cd097bdb..000000000 --- a/landingpage/script.js +++ /dev/null @@ -1,521 +0,0 @@ -// ========================================================================= -// Hermes Agent Landing Page — Interactions -// ========================================================================= - -// --- Platform install commands --- -const PLATFORMS = { - linux: { - command: - "curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash", - prompt: "$", - note: "Works on Linux, macOS & WSL2 · No prerequisites · Installs everything automatically", - stepNote: - "Installs uv, Python 3.11, clones the repo, sets up everything. No sudo needed.", - }, -}; - -function detectPlatform() { - return "linux"; -} - -function switchPlatform(platform) { - const cfg = PLATFORMS[platform]; - if (!cfg) return; - - // Update hero install widget - const commandEl = document.getElementById("install-command"); - const promptEl = document.getElementById("install-prompt"); - const noteEl = document.getElementById("install-note"); - - if (commandEl) commandEl.textContent = cfg.command; - if (promptEl) promptEl.textContent = cfg.prompt; - if (noteEl) noteEl.textContent = cfg.note; - - // Update active tab in hero - document.querySelectorAll(".install-tab").forEach((tab) => { - tab.classList.toggle("active", tab.dataset.platform === platform); - }); - - // Sync the step section tabs too - switchStepPlatform(platform); -} - -function switchStepPlatform(platform) { - const cfg = PLATFORMS[platform]; - if (!cfg) return; - - const commandEl = document.getElementById("step1-command"); - const copyBtn = document.getElementById("step1-copy"); - const noteEl = document.getElementById("step1-note"); - - if (commandEl) commandEl.textContent = cfg.command; - if (copyBtn) copyBtn.setAttribute("data-text", cfg.command); - if (noteEl) noteEl.textContent = cfg.stepNote; - - // Update active tab in step section - document.querySelectorAll(".code-tab").forEach((tab) => { - tab.classList.toggle("active", tab.dataset.platform === platform); - }); -} - -function toggleMobileNav() { - document.getElementById("nav-mobile").classList.toggle("open"); - document.getElementById("nav-hamburger").classList.toggle("open"); -} - -function toggleSpecs() { - const wrapper = document.getElementById("specs-wrapper"); - const btn = document.getElementById("specs-toggle"); - const label = btn.querySelector(".toggle-label"); - const isOpen = wrapper.classList.contains("open"); - - if (isOpen) { - wrapper.style.maxHeight = wrapper.scrollHeight + "px"; - requestAnimationFrame(() => { - wrapper.style.maxHeight = "0"; - }); - wrapper.classList.remove("open"); - btn.classList.remove("open"); - if (label) label.textContent = "More details"; - } else { - wrapper.classList.add("open"); - wrapper.style.maxHeight = wrapper.scrollHeight + "px"; - btn.classList.add("open"); - if (label) label.textContent = "Less"; - wrapper.addEventListener( - "transitionend", - () => { - if (wrapper.classList.contains("open")) { - wrapper.style.maxHeight = "none"; - } - }, - { once: true } - ); - } -} - -// --- Copy to clipboard --- -function copyInstall() { - const text = document.getElementById("install-command").textContent; - navigator.clipboard.writeText(text).then(() => { - const btn = document.querySelector(".install-widget-body .copy-btn"); - const original = btn.querySelector(".copy-text").textContent; - btn.querySelector(".copy-text").textContent = "Copied!"; - btn.style.color = "var(--primary-light)"; - setTimeout(() => { - btn.querySelector(".copy-text").textContent = original; - btn.style.color = ""; - }, 2000); - }); -} - -function copyText(btn) { - const text = btn.getAttribute("data-text"); - navigator.clipboard.writeText(text).then(() => { - const original = btn.textContent; - btn.textContent = "Copied!"; - btn.style.color = "var(--primary-light)"; - setTimeout(() => { - btn.textContent = original; - btn.style.color = ""; - }, 2000); - }); -} - -// --- Scroll-triggered fade-in --- -function initScrollAnimations() { - const elements = document.querySelectorAll( - ".feature-card, .install-step, " + - ".section-header, .terminal-window", - ); - - elements.forEach((el) => el.classList.add("fade-in")); - - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - // Stagger children within grids - const parent = entry.target.parentElement; - if (parent) { - const siblings = parent.querySelectorAll(".fade-in"); - let idx = Array.from(siblings).indexOf(entry.target); - if (idx < 0) idx = 0; - setTimeout(() => { - entry.target.classList.add("visible"); - }, idx * 60); - } else { - entry.target.classList.add("visible"); - } - observer.unobserve(entry.target); - } - }); - }, - { threshold: 0.1, rootMargin: "0px 0px -40px 0px" }, - ); - - elements.forEach((el) => observer.observe(el)); -} - -// --- Terminal Demo --- -const CURSOR = ''; - -const demoSequence = [ - { type: "prompt", text: "❯ " }, - { - type: "type", - text: "Research the latest approaches to GRPO training and write a summary", - delay: 30, - }, - { type: "pause", ms: 600 }, - { - type: "output", - lines: [ - "", - ' web_search "GRPO reinforcement learning 2026" 1.2s', - ], - }, - { type: "pause", ms: 400 }, - { - type: "output", - lines: [ - ' web_extract arxiv.org/abs/2402.03300 3.1s', - ], - }, - { type: "pause", ms: 400 }, - { - type: "output", - lines: [ - ' web_search "GRPO vs PPO ablation results" 0.9s', - ], - }, - { type: "pause", ms: 400 }, - { - type: "output", - lines: [ - ' web_extract huggingface.co/blog/grpo 2.8s', - ], - }, - { type: "pause", ms: 400 }, - { - type: "output", - lines: [ - ' write_file ~/research/grpo-summary.md 0.1s', - ], - }, - { type: "pause", ms: 500 }, - { - type: "output", - lines: [ - "", - 'Done! I\'ve written a summary covering:', - "", - ' GRPO\'s group-relative advantage (no critic model needed)', - ' Comparison with PPO/DPO on reasoning benchmarks', - ' Implementation notes for Axolotl and TRL', - "", - 'Saved to ~/research/grpo-summary.md', - ], - }, - { type: "pause", ms: 2500 }, - - { type: "clear" }, - { type: "prompt", text: "❯ " }, - { - type: "type", - text: "Review the PR at NousResearch/hermes-agent#42 and fix any issues", - delay: 30, - }, - { type: "pause", ms: 600 }, - { - type: "output", - lines: [ - "", - ' delegate_task "review PR #42 changes" 2.1s', - ], - }, - { type: "pause", ms: 500 }, - { - type: "output", - lines: [ - ' git diff main..pr-42 0.4s', - ], - }, - { type: "pause", ms: 400 }, - { - type: "output", - lines: [ - ' patch tools/registry.py 0.1s', - ], - }, - { type: "pause", ms: 400 }, - { - type: "output", - lines: [ - ' python -m pytest tests/ -x 3.2s', - ], - }, - { type: "pause", ms: 400 }, - { - type: "output", - lines: [ - ' git commit -m "fix: handle empty tool schemas" 0.3s', - ], - }, - { type: "pause", ms: 500 }, - { - type: "output", - lines: [ - "", - 'Found 2 issues in the PR and fixed both:', - "", - ' Empty tool schema crash in registry.py — added guard', - ' Missing error handling in delegate_tool.py — added try/except', - "", - 'Tests pass. Committed the fix and pushed to the PR branch.', - 'I also saved a skill for this PR review pattern.', - ], - }, - { type: "pause", ms: 2500 }, - - { type: "clear" }, - { type: "prompt", text: "❯ " }, - { - type: "type", - text: "How did we fix that Docker networking issue?", - delay: 35, - }, - { type: "pause", ms: 500 }, - { - type: "output", - lines: [ - "", - ' session_search "Docker networking" 1.4s', - ], - }, - { type: "pause", ms: 500 }, - { - type: "output", - lines: [ - "", - 'Found it — from a session on February 12th:', - "", - 'The containers couldn\'t reach each other because the compose', - 'file was using the default bridge network. We switched to a', - 'custom network with driver: overlay, added explicit', - 'aliases, and set dns: 8.8.8.8 as a fallback.', - "", - 'The fix was committed in docker-compose.prod.yml.', - ], - }, - { type: "pause", ms: 3000 }, -]; - -class TerminalDemo { - constructor(container) { - this.container = container; - this.running = false; - this.content = ""; - } - - async start() { - if (this.running) return; - this.running = true; - - while (this.running) { - for (const step of demoSequence) { - if (!this.running) return; - await this.execute(step); - } - this.clear(); - await this.sleep(1000); - } - } - - stop() { - this.running = false; - } - - async execute(step) { - switch (step.type) { - case "prompt": - this.append(`${step.text}`); - break; - case "type": - for (const char of step.text) { - if (!this.running) return; - this.append(`${char}`); - await this.sleep(step.delay || 30); - } - break; - case "output": - for (const line of step.lines) { - if (!this.running) return; - this.append("\n" + line); - await this.sleep(50); - } - break; - case "pause": - await this.sleep(step.ms); - break; - case "clear": - this.clear(); - break; - } - } - - append(html) { - this.content += html; - this.render(); - } - - render() { - this.container.innerHTML = this.content + CURSOR; - this.container.scrollTop = this.container.scrollHeight; - } - - clear() { - this.content = ""; - this.container.innerHTML = ""; - } - - sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } -} - -// --- Noise Overlay (ported from hermes-chat NoiseOverlay) --- -function initNoiseOverlay() { - if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; - if (typeof THREE === "undefined") return; - - const canvas = document.getElementById("noise-overlay"); - if (!canvas) return; - - const vertexShader = ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `; - - const fragmentShader = ` - uniform vec2 uRes; - uniform float uDpr, uSize, uDensity, uOpacity; - uniform vec3 uColor; - varying vec2 vUv; - - float hash(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * 0.1031); - p3 += dot(p3, p3.yzx + 33.33); - return fract((p3.x + p3.y) * p3.z); - } - - void main() { - float n = hash(floor(vUv * uRes / (uSize * uDpr))); - gl_FragColor = vec4(uColor, step(1.0 - uDensity, n)) * uOpacity; - } - `; - - function hexToVec3(hex) { - const c = hex.replace("#", ""); - return new THREE.Vector3( - parseInt(c.substring(0, 2), 16) / 255, - parseInt(c.substring(2, 4), 16) / 255, - parseInt(c.substring(4, 6), 16) / 255, - ); - } - - const renderer = new THREE.WebGLRenderer({ - alpha: true, - canvas, - premultipliedAlpha: false, - }); - renderer.setClearColor(0x000000, 0); - - const scene = new THREE.Scene(); - const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); - const geo = new THREE.PlaneGeometry(2, 2); - - const mat = new THREE.ShaderMaterial({ - vertexShader, - fragmentShader, - transparent: true, - uniforms: { - uColor: { value: hexToVec3("#8090BB") }, - uDensity: { value: 0.1 }, - uDpr: { value: 1 }, - uOpacity: { value: 0.4 }, - uRes: { value: new THREE.Vector2() }, - uSize: { value: 1.0 }, - }, - }); - - scene.add(new THREE.Mesh(geo, mat)); - - function resize() { - const dpr = window.devicePixelRatio; - const w = window.innerWidth; - const h = window.innerHeight; - renderer.setSize(w, h); - renderer.setPixelRatio(dpr); - mat.uniforms.uRes.value.set(w * dpr, h * dpr); - mat.uniforms.uDpr.value = dpr; - } - - resize(); - window.addEventListener("resize", resize); - - function loop() { - requestAnimationFrame(loop); - renderer.render(scene, camera); - } - loop(); -} - -// --- Initialize --- -document.addEventListener("DOMContentLoaded", () => { - const detectedPlatform = detectPlatform(); - switchPlatform(detectedPlatform); - - initScrollAnimations(); - initNoiseOverlay(); - - const terminalEl = document.getElementById("terminal-demo"); - - if (terminalEl) { - const demo = new TerminalDemo(terminalEl); - - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - demo.start(); - } else { - demo.stop(); - } - }); - }, - { threshold: 0.3 }, - ); - - observer.observe(document.querySelector(".terminal-window")); - } - - const nav = document.querySelector(".nav"); - let ticking = false; - window.addEventListener("scroll", () => { - if (!ticking) { - requestAnimationFrame(() => { - if (window.scrollY > 50) { - nav.style.borderBottomColor = "rgba(48, 80, 255, 0.15)"; - } else { - nav.style.borderBottomColor = ""; - } - ticking = false; - }); - ticking = true; - } - }); -}); diff --git a/landingpage/style.css b/landingpage/style.css deleted file mode 100644 index 30334df0d..000000000 --- a/landingpage/style.css +++ /dev/null @@ -1,1178 +0,0 @@ -/* ========================================================================= - Hermes Agent Landing Page - Colors: Nous Blue (#3050FF) palette - ========================================================================= */ - -/* --- Reset & Base --- */ -*, *::before, *::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -:root { - --primary: #3050FF; - --primary-light: #5070FF; - --primary-dim: #2040CC; - --primary-dark: #1E30AA; - --bg: #0A0E1A; - --bg-card: #12182A; - --bg-card-hover: #1A2240; - --border: rgba(48, 80, 255, 0.1); - --border-hover: rgba(48, 80, 255, 0.22); - --text: #E8ECFF; - --text-dim: #8090BB; - --text-muted: #506090; - --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; - --container: 1080px; - --radius: 12px; - --radius-sm: 8px; - - --ease-in-quad: cubic-bezier(.55, .085, .68, .53); - --ease-in-cubic: cubic-bezier(.550, .055, .675, .19); - --ease-in-quart: cubic-bezier(.895, .03, .685, .22); - --ease-in-quint: cubic-bezier(.755, .05, .855, .06); - --ease-in-expo: cubic-bezier(.95, .05, .795, .035); - --ease-in-circ: cubic-bezier(.6, .04, .98, .335); - - --ease-out-quad: cubic-bezier(.25, .46, .45, .94); - --ease-out-cubic: cubic-bezier(.215, .61, .355, 1); - --ease-out-quart: cubic-bezier(.165, .84, .44, 1); - --ease-out-quint: cubic-bezier(.23, 1, .32, 1); - --ease-out-expo: cubic-bezier(.19, 1, .22, 1); - --ease-out-circ: cubic-bezier(.075, .82, .165, 1); - - --ease-in-out-quad: cubic-bezier(.455, .03, .515, .955); - --ease-in-out-cubic: cubic-bezier(.645, .045, .355, 1); - --ease-in-out-quart: cubic-bezier(.77, 0, .175, 1); - --ease-in-out-quint: cubic-bezier(.86, 0, .07, 1); - --ease-in-out-expo: cubic-bezier(1, 0, 0, 1); - --ease-in-out-circ: cubic-bezier(.785, .135, .15, .86); -} - -html { - scroll-behavior: smooth; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - overflow-x: hidden; -} - -body { - font-family: var(--font-sans); - background: var(--bg); - color: var(--text); - line-height: 1.6; - overflow-x: hidden; - width: 100%; - max-width: 100vw; - background-image: radial-gradient(rgba(48, 80, 255, 0.04) 1px, transparent 1px); - background-size: 32px 32px; -} - -a { - color: var(--primary); - text-decoration: none; - transition: color 0.2s var(--ease-out-quad); -} -a:hover { - color: var(--primary-light); -} - -strong { - color: #fff; - font-weight: 600; -} - -/* --- Noise Overlay --- */ -#noise-overlay { - position: fixed; - inset: 0; - width: 100%; - height: 100%; - z-index: 50; - pointer-events: none; - mix-blend-mode: soft-light; -} - -/* --- Ambient Glow --- */ -.ambient-glow { - position: fixed; - pointer-events: none; - z-index: 0; - border-radius: 50%; - filter: blur(120px); - opacity: 0.15; -} -.glow-1 { - width: 600px; - height: 600px; - background: var(--primary); - top: -200px; - left: -200px; - opacity: 0.08; -} -.glow-2 { - width: 500px; - height: 500px; - background: var(--primary-dim); - bottom: 20%; - right: -150px; - opacity: 0.06; -} - -/* --- Container --- */ -.container { - max-width: var(--container); - margin: 0 auto; - padding: 0 24px; -} - -/* --- Navigation --- */ -.nav { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 100; - background: rgba(7, 7, 13, 0.8); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-bottom: 1px solid var(--border); - transition: border-bottom-color 0.3s var(--ease-out-quad); -} - -.nav-inner { - max-width: var(--container); - margin: 0 auto; - padding: 0 24px; - height: 60px; - display: flex; - align-items: center; - justify-content: space-between; -} - -.nav-logo { - display: flex; - align-items: center; - gap: 10px; - color: var(--text); - font-weight: 600; - font-size: 15px; - transition: color 0.2s var(--ease-out-quad); -} -.nav-logo:hover { color: var(--primary-light); } - -.nav-nous-logo { - width: 22px; - height: 22px; - border-radius: 4px; -} - -.nav-by { - font-weight: 400; - color: var(--text-muted); - font-size: 13px; -} - -.nav-links { - display: flex; - align-items: center; - gap: 28px; -} - -.nav-links a { - color: var(--text-dim); - font-size: 14px; - font-weight: 500; - display: flex; - align-items: center; - gap: 4px; - transition: color 0.2s var(--ease-out-quad); -} -.nav-links a:hover { color: #fff; } - -.external-icon { opacity: 0.4; } - -/* --- Hamburger & Mobile Nav --- */ -.nav-hamburger { - display: none; - background: none; - border: none; - cursor: pointer; - padding: 6px; - width: 34px; - height: 34px; - flex-direction: column; - justify-content: center; - gap: 5px; -} - -.hamburger-bar { - display: block; - width: 20px; - height: 2px; - background: var(--text-dim); - border-radius: 1px; - transition: transform 0.25s var(--ease-out-quint), opacity 0.2s var(--ease-out-quad); - transform-origin: center; -} - -.nav-hamburger.open .hamburger-bar:nth-child(1) { - transform: translateY(7px) rotate(45deg); -} - -.nav-hamburger.open .hamburger-bar:nth-child(2) { - opacity: 0; -} - -.nav-hamburger.open .hamburger-bar:nth-child(3) { - transform: translateY(-7px) rotate(-45deg); -} - -.nav-mobile { - display: none; -} - -.nav-mobile.open { - display: flex; - flex-direction: column; - position: absolute; - top: 60px; - left: 0; - right: 0; - background: rgba(7, 7, 13, 0.95); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-bottom: 1px solid var(--border); - padding: 16px 24px; - gap: 16px; -} - -.nav-mobile a { - color: var(--text-dim); - font-size: 15px; - font-weight: 500; - padding: 4px 0; - transition: color 0.2s var(--ease-out-quad); -} - -.nav-mobile a:hover { - color: #fff; -} - -/* --- Hero --- */ -.hero { - position: relative; - z-index: 1; - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - padding: 120px 24px 80px; - text-align: center; -} - -.hero-content { - max-width: 760px; -} - -.hero-badge { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 6px 16px; - background: rgba(48, 80, 255, 0.08); - border: 1px solid rgba(48, 80, 255, 0.18); - border-radius: 100px; - font-size: 13px; - color: var(--text-dim); - margin-bottom: 32px; - font-weight: 450; -} - -.badge-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--primary); - display: inline-block; - animation: pulse-dot 2s var(--ease-in-out-quad) infinite; -} - -@keyframes pulse-dot { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.3; } -} - -.hero-ascii { - margin-bottom: 28px; - font-family: 'JetBrains Mono', monospace; - font-variant-ligatures: none; - font-size: clamp(4px, 0.95vw, 11px); - line-height: 1.15; - color: var(--primary-light); - text-align: center; - text-shadow: 0 0 20px rgba(48, 80, 255, 0.3); - opacity: 0.85; - transition: opacity 0.3s var(--ease-out-cubic); - overflow-x: auto; - white-space: pre; -} - -.hero-ascii:hover { - opacity: 1; -} - -.hero-title { - font-size: clamp(36px, 6vw, 56px); - font-weight: 700; - line-height: 1.15; - letter-spacing: -0.03em; - margin-bottom: 20px; - color: #fff; -} - -.hero-gradient { - background: linear-gradient(135deg, var(--primary), var(--primary-light), #90B0FF); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.hero-subtitle { - font-size: 17px; - line-height: 1.7; - color: var(--text-dim); - max-width: 620px; - margin: 0 auto 36px; -} - -.hero-install { - margin-bottom: 32px; -} - -/* --- Install Widget (hero tabbed installer) --- */ -.install-widget { - max-width: 740px; - margin: 0 auto; - background: var(--bg-card); - border: 1px solid var(--border); - border-radius: var(--radius); - overflow: hidden; - transition: border-color 0.3s var(--ease-out-quad); -} - -.install-widget:hover { - border-color: var(--border-hover); -} - -.install-widget-header { - display: flex; - align-items: center; - gap: 16px; - padding: 10px 16px; - background: rgba(255, 255, 255, 0.02); - border-bottom: 1px solid var(--border); -} - -.install-dots { - display: flex; - gap: 6px; - flex-shrink: 0; -} - -.install-dots .dot { - width: 10px; - height: 10px; - border-radius: 50%; -} - -.install-tabs { - display: flex; - gap: 4px; - flex-wrap: wrap; -} - -.install-tab { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 5px 14px; - border: none; - border-radius: 6px; - font-family: var(--font-sans); - font-size: 12px; - font-weight: 500; - cursor: pointer; - transition: color 0.2s var(--ease-out-quad), background 0.2s var(--ease-out-quad); - background: transparent; - color: var(--text-muted); -} - -.install-tab:hover { - color: var(--text-dim); - background: rgba(255, 255, 255, 0.04); -} - -.install-tab.active { - background: rgba(48, 80, 255, 0.14); - color: var(--primary-light); -} - -.install-tab svg { - flex-shrink: 0; -} - -.install-widget-body { - display: flex; - align-items: center; - gap: 10px; - padding: 14px 16px; - font-family: var(--font-mono); - font-size: 13px; - color: var(--text); - overflow-x: auto; -} - -.install-prompt { - color: var(--primary-light); - font-weight: 600; - flex-shrink: 0; - opacity: 0.7; -} - -.install-widget-body code { - flex: 1; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - text-align: left; - transition: opacity 0.15s var(--ease-out-quad); -} - -/* --- Code block tabs (install step section) --- */ -.code-tabs { - display: flex; - gap: 2px; -} - -.code-tab { - padding: 3px 10px; - border: none; - border-radius: 4px; - font-family: var(--font-mono); - font-size: 11px; - font-weight: 500; - cursor: pointer; - transition: color 0.2s var(--ease-out-quad), background 0.2s var(--ease-out-quad); - background: transparent; - color: var(--text-muted); -} - -.code-tab:hover { - color: var(--text-dim); - background: rgba(255, 255, 255, 0.04); -} - -.code-tab.active { - background: rgba(48, 80, 255, 0.12); - color: var(--primary-light); -} - -.copy-btn { - flex-shrink: 0; - display: flex; - align-items: center; - gap: 6px; - background: none; - border: none; - color: var(--text-dim); - cursor: pointer; - padding: 4px 8px; - border-radius: 6px; - font-family: var(--font-sans); - font-size: 12px; - transition: color 0.2s var(--ease-out-quad), background 0.2s var(--ease-out-quad); -} -.copy-btn:hover { - color: var(--primary-light); - background: rgba(48, 80, 255, 0.1); -} -.copy-btn:active { - transform: scale(0.95); -} - -.install-note { - font-size: 13px; - color: var(--text-muted); - margin-top: 12px; -} - -.hero-links { - display: flex; - gap: 12px; - justify-content: center; - flex-wrap: wrap; -} - -.btn { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 11px 24px; - border-radius: var(--radius); - font-size: 14px; - font-weight: 550; - transition: background 0.25s var(--ease-out-quint), border-color 0.25s var(--ease-out-quad), color 0.2s var(--ease-out-quad), transform 0.25s var(--ease-out-quint); - border: 1px solid transparent; - will-change: transform; -} - -.btn-primary { - background: rgba(48, 80, 255, 0.12); - color: var(--primary-light); - border-color: rgba(48, 80, 255, 0.25); -} -.btn-primary:hover { - background: rgba(48, 80, 255, 0.22); - border-color: rgba(48, 80, 255, 0.4); - color: #fff; -} - -@media (hover: hover) and (pointer: fine) { - .btn-primary:hover { - transform: translateY(-1px); - } -} -.btn:active { - transform: scale(0.97); -} - -/* --- Sections --- */ -.section { - position: relative; - z-index: 1; - padding: 80px 0; -} - -.section-header { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; - margin-bottom: 48px; -} - -.section-header h2 { - font-size: 28px; - font-weight: 650; - color: #fff; - letter-spacing: -0.02em; -} - -.section-desc { - color: var(--text-dim); - font-size: 16px; - line-height: 1.7; - max-width: 640px; - margin: 0 auto 40px; - text-align: center; -} - -/* --- Features Grid --- */ -.features-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 16px; -} - -.feature-card { - background: var(--bg-card); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 20px; - transition: border-color 0.3s var(--ease-out-quad), background 0.3s var(--ease-out-quad), transform 0.3s var(--ease-out-quint); - will-change: transform; -} - -.feature-card:hover { - border-color: var(--border-hover); - background: var(--bg-card-hover); -} - -@media (hover: hover) and (pointer: fine) { - .feature-card:hover { - transform: translateY(-2px); - } -} - -.feature-header { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 10px; -} - -.feature-icon { - color: var(--primary-light); - opacity: 0.85; - flex-shrink: 0; - display: flex; - line-height: 0; -} - -.feature-card h3 { - font-size: 15px; - font-weight: 600; - color: #fff; - letter-spacing: -0.01em; -} - -.feature-card p { - font-size: 14px; - color: var(--text-dim); - line-height: 1.65; -} - -/* --- Terminal Demo --- */ -.section-demo { - padding-bottom: 60px; - border-top: 1px solid var(--border); - border-bottom: 1px solid var(--border); -} - -.terminal-window { - background: #0c0c14; - border: 1px solid var(--border); - border-radius: var(--radius); - overflow: hidden; - max-width: 800px; - margin: 0 auto; -} - -.terminal-header { - display: flex; - align-items: center; - padding: 12px 16px; - background: rgba(255, 255, 255, 0.02); - border-bottom: 1px solid var(--border); - gap: 12px; -} - -.terminal-dots { - display: flex; - gap: 6px; -} - -.dot { - width: 10px; - height: 10px; - border-radius: 50%; -} -.dot-red { background: #ff5f57; } -.dot-yellow { background: #febc2e; } -.dot-green { background: #28c840; } - -.terminal-title { - font-family: var(--font-mono); - font-size: 12px; - color: var(--text-muted); -} - -.terminal-body { - padding: 20px 24px; - height: 340px; - font-family: var(--font-mono); - font-size: 13px; - line-height: 1.7; - white-space: pre-wrap; - overflow-y: auto; - overflow-x: hidden; -} - -.terminal-cursor { - animation: blink 1s step-end infinite; - color: var(--primary-light); - opacity: 0.8; -} - -@keyframes blink { - 0%, 100% { opacity: 0.8; } - 50% { opacity: 0; } -} - -/* Terminal demo colors */ -.t-prompt { color: var(--primary-light); } -.t-cmd { color: #fff; } -.t-dim { color: var(--text-muted); } -.t-text { color: var(--text-dim); } -.t-green { color: #4ade80; } -.t-blue { color: #60a5fa; } -.t-accent { color: var(--primary-light); } -.t-highlight { color: #90B0FF; } -.t-tool { color: var(--text-muted); } - -/* --- Specs Toggle --- */ -.features-more { - text-align: center; - margin-top: 32px; -} - -.more-toggle { - background: none; - border: 1px solid var(--border); - color: var(--text-dim); - font-size: 14px; - font-family: inherit; - padding: 8px 20px; - border-radius: 6px; - cursor: pointer; - display: inline-flex; - align-items: center; - gap: 6px; - transition: color 0.2s var(--ease-out-quad), border-color 0.2s var(--ease-out-quad); -} - -.more-toggle:hover { - color: var(--primary-light); - border-color: var(--primary-light); -} -.more-toggle:active { - transform: scale(0.97); -} - -.more-chevron { - transition: transform 0.3s var(--ease-in-out-cubic); -} - -.more-toggle.open .more-chevron { - transform: rotate(180deg); -} - -.specs-wrapper { - max-height: 0; - overflow: hidden; - transition: max-height 0.4s var(--ease-out-quart), opacity 0.3s var(--ease-out-quad); - opacity: 0; -} - -.specs-wrapper.open { - opacity: 1; -} - -/* --- Specs --- */ -.section-specs { -} - -.specs-list { - max-width: 720px; - margin: 0 auto; - padding-top: 24px; -} - -.spec-row { - display: grid; - grid-template-columns: 120px 1fr; - gap: 24px; - padding: 24px 0; - border-bottom: 1px solid var(--border); -} - -.spec-row:last-child { - border-bottom: none; -} - -.spec-label { - font-size: 14px; - font-weight: 600; - color: var(--primary-light); - padding-top: 2px; -} - -.spec-value { - font-size: 15px; - color: var(--text-dim); - line-height: 1.7; -} - -.spec-value a { - color: var(--text); - border-bottom: 1px solid var(--border-hover); - transition: border-color 0.2s var(--ease-out-quad), color 0.2s var(--ease-out-quad); -} - -.spec-value a:hover { - color: var(--primary-light); - border-color: var(--primary-light); -} - -/* --- Install Section --- */ -.section-install { - border-top: 1px solid var(--border); -} - -.install-steps { - display: grid; - gap: 28px; - max-width: 640px; - margin: 0 auto; -} - -.install-step { - display: flex; - gap: 20px; -} - -.step-number { - flex-shrink: 0; - width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - background: rgba(48, 80, 255, 0.1); - border: 1px solid rgba(48, 80, 255, 0.2); - border-radius: 50%; - font-size: 14px; - font-weight: 600; - color: var(--primary-light); - margin-top: 2px; -} - -.step-content { - flex: 1; - min-width: 0; -} - -.step-content h4 { - font-size: 16px; - font-weight: 600; - color: #fff; - margin-bottom: 10px; -} - -.step-optional { - font-size: 12px; - font-weight: 400; - color: var(--text-muted); -} - -.step-note { - font-size: 13px; - color: var(--text-muted); - margin-top: 8px; -} - -.code-block { - background: #0c0c14; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - overflow: hidden; -} - -.code-block-sm { - max-width: 640px; -} - -.code-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 14px; - background: rgba(255, 255, 255, 0.02); - border-bottom: 1px solid var(--border); - font-family: var(--font-mono); - font-size: 11px; - color: var(--text-muted); -} - -.code-block pre { - padding: 14px 16px; - font-family: var(--font-mono); - font-size: 13px; - line-height: 1.6; - color: var(--text); - overflow-x: auto; - white-space: pre-wrap; - word-break: break-all; -} - -.code-comment { - color: var(--text-muted); -} - -.install-windows { - margin-top: 48px; - padding-top: 32px; - border-top: 1px solid var(--border); - max-width: 640px; - margin-left: auto; - margin-right: auto; -} - -.install-windows p { - font-size: 14px; - color: var(--text-dim); - margin-bottom: 12px; -} - -/* --- Footer --- */ -.footer { - position: relative; - z-index: 1; - padding: 40px 0 32px; - border-top: 1px solid var(--border); -} - -.footer-copy { - text-align: center; - font-size: 13px; - color: var(--text-muted); -} - -.footer-copy a { - color: var(--text-dim); - transition: color 0.2s var(--ease-out-quad); -} - -.footer-copy a:hover { - color: var(--primary-light); -} - -/* --- Scroll Animations --- */ -.fade-in { - opacity: 0; - transform: translateY(20px); - transition: opacity 0.6s var(--ease-out-quart), transform 0.6s var(--ease-out-quart); - will-change: transform, opacity; -} - -.fade-in.visible { - opacity: 1; - transform: translateY(0); -} - -/* --- Responsive --- */ - -/* Clamp ambient glows so they can't cause horizontal scroll */ -@media (max-width: 900px) { - .ambient-glow { display: none; } - - .features-grid { - grid-template-columns: repeat(2, 1fr); - } - -} - -@media (max-width: 640px) { - /* --- Global mobile --- */ - .container { - padding: 0 16px; - } - - .section { - padding: 50px 0; - } - - .section-header { - margin-bottom: 32px; - } - - .section-header h2 { - font-size: 20px; - } - - .section-desc { - font-size: 14px; - } - - /* --- Nav --- */ - .nav-inner { - padding: 0 16px; - } - - .nav-links { - display: none; - } - - .nav-hamburger { - display: flex; - } - - /* --- Hero --- */ - .hero { - padding: 90px 16px 50px; - min-height: auto; - } - - .hero-content { - max-width: 100%; - } - - .hero-badge { - font-size: 11px; - padding: 5px 12px; - margin-bottom: 24px; - } - - .hero-ascii { - font-size: 3.5px; - } - - .hero-title { - font-size: 26px; - margin-bottom: 14px; - } - - .hero-subtitle { - font-size: 14px; - line-height: 1.6; - margin: 0 auto 28px; - } - - .install-widget-body { - font-size: 10px; - padding: 10px 12px; - } - - .install-widget-body code { - overflow: hidden; - text-overflow: ellipsis; - display: block; - } - - .install-widget-header { - padding: 8px 12px; - gap: 10px; - } - - .install-tabs { - gap: 2px; - } - - .install-tab { - padding: 4px 10px; - font-size: 11px; - } - - .install-tab svg { - display: none; - } - - .copy-btn { - padding: 3px 6px; - } - - .copy-btn .copy-text { display: none; } - - .install-note { - font-size: 11px; - } - - .hero-links { - flex-direction: column; - align-items: stretch; - } - - .hero-links .btn { - justify-content: center; - } - - /* --- Grids → single column --- */ - .features-grid { - grid-template-columns: 1fr; - } - - .spec-row { - grid-template-columns: 1fr; - gap: 6px; - padding: 18px 0; - } - - .feature-card { - padding: 16px 18px; - } - - .feature-card p { - font-size: 13px; - line-height: 1.5; - } - - /* --- Terminal demo --- */ - .terminal-body { - font-size: 11px; - padding: 14px; - height: 260px; - } - - /* --- Install steps --- */ - .install-steps { - max-width: 100%; - } - - .install-step { - gap: 14px; - } - - .step-number { - width: 28px; - height: 28px; - font-size: 13px; - } - - .code-block pre { - font-size: 11px; - word-break: break-all; - } - - .install-windows { - max-width: 100%; - } - - /* --- Footer --- */ - .footer { - padding: 32px 0 24px; - } - -} - -/* --- Reduced Motion --- */ -@media (prefers-reduced-motion: reduce) { - *, *::before, *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } - - .fade-in { - opacity: 1; - transform: none; - } - - .hero-ascii { - opacity: 0.85; - } -} - -/* --- Selection --- */ -::selection { - background: rgba(48, 80, 255, 0.25); - color: #fff; -} - -/* --- Scrollbar --- */ -::-webkit-scrollbar { - width: 6px; - height: 6px; -} -::-webkit-scrollbar-track { - background: var(--bg); -} -::-webkit-scrollbar-thumb { - background: var(--border-hover); - border-radius: 3px; -} -::-webkit-scrollbar-thumb:hover { - background: var(--primary-dim); -} From 9f759d177125404f8123bcd9b54fc072eb1225b1 Mon Sep 17 00:00:00 2001 From: Austin Pickett Date: Wed, 15 Apr 2026 23:33:03 -0400 Subject: [PATCH 02/19] fix: match the url as prev --- .github/workflows/deploy-site.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 44da745b9..3e78bc61b 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -69,10 +69,15 @@ jobs: run: npm run build working-directory: website + - name: Stage deployment + run: | + mkdir -p _site/docs + cp -r website/build/* _site/docs/ + - name: Upload artifact uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 with: - path: website/build + path: _site - name: Deploy to GitHub Pages id: deploy From 465193b7eb2630546419a9804c7a16624fc4375c Mon Sep 17 00:00:00 2001 From: LeonSGP43 Date: Thu, 16 Apr 2026 18:41:31 +0530 Subject: [PATCH 03/19] fix(gateway): close temporary agents after one-off tasks Add shared _cleanup_agent_resources() for temporary gateway AIAgent instances. Apply cleanup to memory flush, background tasks, /btw, manual /compress, and session-hygiene auto-compression. Prevents unclosed aiohttp client session leaks. Cherry-picked from #10899 by @LeonSGP43. Consolidates #10945 by @Lubrsy706. Fixes #10865. Co-authored-by: Lubrsy706 --- gateway/run.py | 350 +++++++++--------- tests/gateway/test_background_command.py | 33 ++ tests/gateway/test_compress_command.py | 8 + .../gateway/test_flush_memory_stale_guard.py | 16 + tests/gateway/test_session_hygiene.py | 8 + 5 files changed, 241 insertions(+), 174 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index b7e44d91e..956369a3d 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -762,69 +762,72 @@ class GatewayRunner: enabled_toolsets=["memory", "skills"], session_id=old_session_id, ) - # Fully silence the flush agent — quiet_mode only suppresses init - # messages; tool call output still leaks to the terminal through - # _safe_print → _print_fn. Set a no-op to prevent that. - tmp_agent._print_fn = lambda *a, **kw: None - - # Build conversation history from transcript - msgs = [ - {"role": m.get("role"), "content": m.get("content")} - for m in history - if m.get("role") in ("user", "assistant") and m.get("content") - ] - - # Read live memory state from disk so the flush agent can see - # what's already saved and avoid overwriting newer entries. - _current_memory = "" try: - from tools.memory_tool import get_memory_dir - _mem_dir = get_memory_dir() - for fname, label in [ - ("MEMORY.md", "MEMORY (your personal notes)"), - ("USER.md", "USER PROFILE (who the user is)"), - ]: - fpath = _mem_dir / fname - if fpath.exists(): - content = fpath.read_text(encoding="utf-8").strip() - if content: - _current_memory += f"\n\n## Current {label}:\n{content}" - except Exception: - pass # Non-fatal — flush still works, just without the guard + # Fully silence the flush agent — quiet_mode only suppresses init + # messages; tool call output still leaks to the terminal through + # _safe_print → _print_fn. Set a no-op to prevent that. + tmp_agent._print_fn = lambda *a, **kw: None - # Give the agent a real turn to think about what to save - flush_prompt = ( - "[System: This session is about to be automatically reset due to " - "inactivity or a scheduled daily reset. The conversation context " - "will be cleared after this turn.\n\n" - "Review the conversation above and:\n" - "1. Save any important facts, preferences, or decisions to memory " - "(user profile or your notes) that would be useful in future sessions.\n" - "2. If you discovered a reusable workflow or solved a non-trivial " - "problem, consider saving it as a skill.\n" - "3. If nothing is worth saving, that's fine — just skip.\n\n" - ) + # Build conversation history from transcript + msgs = [ + {"role": m.get("role"), "content": m.get("content")} + for m in history + if m.get("role") in ("user", "assistant") and m.get("content") + ] - if _current_memory: - flush_prompt += ( - "IMPORTANT — here is the current live state of memory. Other " - "sessions, cron jobs, or the user may have updated it since this " - "conversation ended. Do NOT overwrite or remove entries unless " - "the conversation above reveals something that genuinely " - "supersedes them. Only add new information that is not already " - "captured below." - f"{_current_memory}\n\n" + # Read live memory state from disk so the flush agent can see + # what's already saved and avoid overwriting newer entries. + _current_memory = "" + try: + from tools.memory_tool import get_memory_dir + _mem_dir = get_memory_dir() + for fname, label in [ + ("MEMORY.md", "MEMORY (your personal notes)"), + ("USER.md", "USER PROFILE (who the user is)"), + ]: + fpath = _mem_dir / fname + if fpath.exists(): + content = fpath.read_text(encoding="utf-8").strip() + if content: + _current_memory += f"\n\n## Current {label}:\n{content}" + except Exception: + pass # Non-fatal — flush still works, just without the guard + + # Give the agent a real turn to think about what to save + flush_prompt = ( + "[System: This session is about to be automatically reset due to " + "inactivity or a scheduled daily reset. The conversation context " + "will be cleared after this turn.\n\n" + "Review the conversation above and:\n" + "1. Save any important facts, preferences, or decisions to memory " + "(user profile or your notes) that would be useful in future sessions.\n" + "2. If you discovered a reusable workflow or solved a non-trivial " + "problem, consider saving it as a skill.\n" + "3. If nothing is worth saving, that's fine — just skip.\n\n" ) - flush_prompt += ( - "Do NOT respond to the user. Just use the memory and skill_manage " - "tools if needed, then stop.]" - ) + if _current_memory: + flush_prompt += ( + "IMPORTANT — here is the current live state of memory. Other " + "sessions, cron jobs, or the user may have updated it since this " + "conversation ended. Do NOT overwrite or remove entries unless " + "the conversation above reveals something that genuinely " + "supersedes them. Only add new information that is not already " + "captured below." + f"{_current_memory}\n\n" + ) - tmp_agent.run_conversation( - user_message=flush_prompt, - conversation_history=msgs, - ) + flush_prompt += ( + "Do NOT respond to the user. Just use the memory and skill_manage " + "tools if needed, then stop.]" + ) + + tmp_agent.run_conversation( + user_message=flush_prompt, + conversation_history=msgs, + ) + finally: + self._cleanup_agent_resources(tmp_agent) logger.info("Pre-reset memory flush completed for session %s", old_session_id) except Exception as e: logger.debug("Pre-reset memory flush failed for session %s: %s", old_session_id, e) @@ -1562,19 +1565,25 @@ class GatewayRunner: ) except Exception: pass - try: - if hasattr(agent, "shutdown_memory_provider"): - agent.shutdown_memory_provider() - except Exception: - pass - # Close tool resources (terminal sandboxes, browser daemons, - # background processes, httpx clients) to prevent zombie - # process accumulation. - try: - if hasattr(agent, 'close'): - agent.close() - except Exception: - pass + self._cleanup_agent_resources(agent) + + def _cleanup_agent_resources(self, agent: Any) -> None: + """Best-effort cleanup for temporary or cached agent instances.""" + if agent is None: + return + try: + if hasattr(agent, "shutdown_memory_provider"): + agent.shutdown_memory_provider() + except Exception: + pass + # Close tool resources (terminal sandboxes, browser daemons, + # background processes, httpx clients) to prevent zombie + # process accumulation. + try: + if hasattr(agent, "close"): + agent.close() + except Exception: + pass _STUCK_LOOP_THRESHOLD = 3 # restarts while active before auto-suspend _STUCK_LOOP_FILE = ".restart_failure_counts" @@ -2077,16 +2086,7 @@ class GatewayRunner: if _cached_agent is None: _cached_agent = self._running_agents.get(key) if _cached_agent and _cached_agent is not _AGENT_PENDING_SENTINEL: - try: - if hasattr(_cached_agent, 'shutdown_memory_provider'): - _cached_agent.shutdown_memory_provider() - except Exception: - pass - try: - if hasattr(_cached_agent, 'close'): - _cached_agent.close() - except Exception: - pass + self._cleanup_agent_resources(_cached_agent) # Mark as flushed and persist to disk so the flag # survives gateway restarts. with self.session_store._lock: @@ -3775,51 +3775,54 @@ class GatewayRunner: enabled_toolsets=["memory"], session_id=session_entry.session_id, ) - _hyg_agent._print_fn = lambda *a, **kw: None + try: + _hyg_agent._print_fn = lambda *a, **kw: None - loop = asyncio.get_running_loop() - _compressed, _ = await loop.run_in_executor( - None, - lambda: _hyg_agent._compress_context( - _hyg_msgs, "", - approx_tokens=_approx_tokens, - ), - ) - - # _compress_context ends the old session and creates - # a new session_id. Write compressed messages into - # the NEW session so the old transcript stays intact - # and searchable via session_search. - _hyg_new_sid = _hyg_agent.session_id - if _hyg_new_sid != session_entry.session_id: - session_entry.session_id = _hyg_new_sid - self.session_store._save() - - self.session_store.rewrite_transcript( - session_entry.session_id, _compressed - ) - # Reset stored token count — transcript was rewritten - session_entry.last_prompt_tokens = 0 - history = _compressed - _new_count = len(_compressed) - _new_tokens = estimate_messages_tokens_rough( - _compressed - ) - - logger.info( - "Session hygiene: compressed %s → %s msgs, " - "~%s → ~%s tokens", - _msg_count, _new_count, - f"{_approx_tokens:,}", f"{_new_tokens:,}", - ) - - if _new_tokens >= _warn_token_threshold: - logger.warning( - "Session hygiene: still ~%s tokens after " - "compression", - f"{_new_tokens:,}", + loop = asyncio.get_running_loop() + _compressed, _ = await loop.run_in_executor( + None, + lambda: _hyg_agent._compress_context( + _hyg_msgs, "", + approx_tokens=_approx_tokens, + ), ) + # _compress_context ends the old session and creates + # a new session_id. Write compressed messages into + # the NEW session so the old transcript stays intact + # and searchable via session_search. + _hyg_new_sid = _hyg_agent.session_id + if _hyg_new_sid != session_entry.session_id: + session_entry.session_id = _hyg_new_sid + self.session_store._save() + + self.session_store.rewrite_transcript( + session_entry.session_id, _compressed + ) + # Reset stored token count — transcript was rewritten + session_entry.last_prompt_tokens = 0 + history = _compressed + _new_count = len(_compressed) + _new_tokens = estimate_messages_tokens_rough( + _compressed + ) + + logger.info( + "Session hygiene: compressed %s → %s msgs, " + "~%s → ~%s tokens", + _msg_count, _new_count, + f"{_approx_tokens:,}", f"{_new_tokens:,}", + ) + + if _new_tokens >= _warn_token_threshold: + logger.warning( + "Session hygiene: still ~%s tokens after " + "compression", + f"{_new_tokens:,}", + ) + finally: + self._cleanup_agent_resources(_hyg_agent) + except Exception as e: logger.warning( "Session hygiene auto-compress failed: %s", e @@ -4337,16 +4340,7 @@ class GatewayRunner: _cached = self._agent_cache.get(session_key) _old_agent = _cached[0] if isinstance(_cached, tuple) else _cached if _cached else None if _old_agent is not None: - try: - if hasattr(_old_agent, "shutdown_memory_provider"): - _old_agent.shutdown_memory_provider() - except Exception: - pass - try: - if hasattr(_old_agent, "close"): - _old_agent.close() - except Exception: - pass + self._cleanup_agent_resources(_old_agent) self._evict_cached_agent(session_key) try: @@ -5741,11 +5735,13 @@ class GatewayRunner: session_db=self._session_db, fallback_model=self._fallback_model, ) - - return agent.run_conversation( - user_message=prompt, - task_id=task_id, - ) + try: + return agent.run_conversation( + user_message=prompt, + task_id=task_id, + ) + finally: + self._cleanup_agent_resources(agent) result = await self._run_in_executor_with_context(run_sync) @@ -5923,11 +5919,14 @@ class GatewayRunner: skip_context_files=True, persist_session=False, ) - return agent.run_conversation( - user_message=btw_prompt, - conversation_history=history_snapshot, - task_id=task_id, - ) + try: + return agent.run_conversation( + user_message=btw_prompt, + conversation_history=history_snapshot, + task_id=task_id, + ) + finally: + self._cleanup_agent_resources(agent) result = await self._run_in_executor_with_context(run_sync) @@ -6256,42 +6255,45 @@ class GatewayRunner: enabled_toolsets=["memory"], session_id=session_entry.session_id, ) - tmp_agent._print_fn = lambda *a, **kw: None + try: + tmp_agent._print_fn = lambda *a, **kw: None - compressor = tmp_agent.context_compressor - compress_start = compressor.protect_first_n - compress_start = compressor._align_boundary_forward(msgs, compress_start) - compress_end = compressor._find_tail_cut_by_tokens(msgs, compress_start) - if compress_start >= compress_end: - return "Nothing to compress yet (the transcript is still all protected context)." + compressor = tmp_agent.context_compressor + compress_start = compressor.protect_first_n + compress_start = compressor._align_boundary_forward(msgs, compress_start) + compress_end = compressor._find_tail_cut_by_tokens(msgs, compress_start) + if compress_start >= compress_end: + return "Nothing to compress yet (the transcript is still all protected context)." - loop = asyncio.get_running_loop() - compressed, _ = await loop.run_in_executor( - None, - lambda: tmp_agent._compress_context(msgs, "", approx_tokens=approx_tokens, focus_topic=focus_topic) - ) + loop = asyncio.get_running_loop() + compressed, _ = await loop.run_in_executor( + None, + lambda: tmp_agent._compress_context(msgs, "", approx_tokens=approx_tokens, focus_topic=focus_topic) + ) - # _compress_context already calls end_session() on the old session - # (preserving its full transcript in SQLite) and creates a new - # session_id for the continuation. Write the compressed messages - # into the NEW session so the original history stays searchable. - new_session_id = tmp_agent.session_id - if new_session_id != session_entry.session_id: - session_entry.session_id = new_session_id - self.session_store._save() + # _compress_context already calls end_session() on the old session + # (preserving its full transcript in SQLite) and creates a new + # session_id for the continuation. Write the compressed messages + # into the NEW session so the original history stays searchable. + new_session_id = tmp_agent.session_id + if new_session_id != session_entry.session_id: + session_entry.session_id = new_session_id + self.session_store._save() - self.session_store.rewrite_transcript(new_session_id, compressed) - # Reset stored token count — transcript changed, old value is stale - self.session_store.update_session( - session_entry.session_key, last_prompt_tokens=0 - ) - new_tokens = estimate_messages_tokens_rough(compressed) - summary = summarize_manual_compression( - msgs, - compressed, - approx_tokens, - new_tokens, - ) + self.session_store.rewrite_transcript(new_session_id, compressed) + # Reset stored token count — transcript changed, old value is stale + self.session_store.update_session( + session_entry.session_key, last_prompt_tokens=0 + ) + new_tokens = estimate_messages_tokens_rough(compressed) + summary = summarize_manual_compression( + msgs, + compressed, + approx_tokens, + new_tokens, + ) + finally: + self._cleanup_agent_resources(tmp_agent) lines = [f"🗜️ {summary['headline']}"] if focus_topic: lines.append(f"Focus: \"{focus_topic}\"") diff --git a/tests/gateway/test_background_command.py b/tests/gateway/test_background_command.py index 90303c41c..559c04ea7 100644 --- a/tests/gateway/test_background_command.py +++ b/tests/gateway/test_background_command.py @@ -220,6 +220,8 @@ class TestRunBackgroundTask: with patch("gateway.run._resolve_runtime_agent_kwargs", return_value={"api_key": "test-key"}), \ patch("run_agent.AIAgent") as MockAgent: mock_agent_instance = MagicMock() + mock_agent_instance.shutdown_memory_provider = MagicMock() + mock_agent_instance.close = MagicMock() mock_agent_instance.run_conversation.return_value = mock_result MockAgent.return_value = mock_agent_instance @@ -231,6 +233,37 @@ class TestRunBackgroundTask: content = call_args[1].get("content", call_args[0][1] if len(call_args[0]) > 1 else "") assert "Background task complete" in content assert "Hello from background!" in content + mock_agent_instance.shutdown_memory_provider.assert_called_once() + mock_agent_instance.close.assert_called_once() + + @pytest.mark.asyncio + async def test_agent_cleanup_runs_when_background_agent_raises(self): + """Temporary background agents must be cleaned up on error paths too.""" + runner = _make_runner() + mock_adapter = AsyncMock() + mock_adapter.send = AsyncMock() + runner.adapters[Platform.TELEGRAM] = mock_adapter + + source = SessionSource( + platform=Platform.TELEGRAM, + user_id="12345", + chat_id="67890", + user_name="testuser", + ) + + with patch("gateway.run._resolve_runtime_agent_kwargs", return_value={"api_key": "test-key"}), \ + patch("run_agent.AIAgent") as MockAgent: + mock_agent_instance = MagicMock() + mock_agent_instance.shutdown_memory_provider = MagicMock() + mock_agent_instance.close = MagicMock() + mock_agent_instance.run_conversation.side_effect = RuntimeError("boom") + MockAgent.return_value = mock_agent_instance + + await runner._run_background_task("say hello", source, "bg_test") + + mock_adapter.send.assert_called_once() + mock_agent_instance.shutdown_memory_provider.assert_called_once() + mock_agent_instance.close.assert_called_once() @pytest.mark.asyncio async def test_exception_sends_error_message(self): diff --git a/tests/gateway/test_compress_command.py b/tests/gateway/test_compress_command.py index edeb1f47c..021e98773 100644 --- a/tests/gateway/test_compress_command.py +++ b/tests/gateway/test_compress_command.py @@ -62,6 +62,8 @@ async def test_compress_command_reports_noop_without_success_banner(): history = _make_history() runner = _make_runner(history) agent_instance = MagicMock() + agent_instance.shutdown_memory_provider = MagicMock() + agent_instance.close = MagicMock() agent_instance.context_compressor.protect_first_n = 0 agent_instance.context_compressor._align_boundary_forward.return_value = 0 agent_instance.context_compressor._find_tail_cut_by_tokens.return_value = 2 @@ -83,6 +85,8 @@ async def test_compress_command_reports_noop_without_success_banner(): assert "No changes from compression" in result assert "Compressed:" not in result assert "Rough transcript estimate: ~100 tokens (unchanged)" in result + agent_instance.shutdown_memory_provider.assert_called_once() + agent_instance.close.assert_called_once() @pytest.mark.asyncio @@ -95,6 +99,8 @@ async def test_compress_command_explains_when_token_estimate_rises(): ] runner = _make_runner(history) agent_instance = MagicMock() + agent_instance.shutdown_memory_provider = MagicMock() + agent_instance.close = MagicMock() agent_instance.context_compressor.protect_first_n = 0 agent_instance.context_compressor._align_boundary_forward.return_value = 0 agent_instance.context_compressor._find_tail_cut_by_tokens.return_value = 2 @@ -119,3 +125,5 @@ async def test_compress_command_explains_when_token_estimate_rises(): assert "Compressed: 4 → 3 messages" in result assert "Rough transcript estimate: ~100 → ~120 tokens" in result assert "denser summaries" in result + agent_instance.shutdown_memory_provider.assert_called_once() + agent_instance.close.assert_called_once() diff --git a/tests/gateway/test_flush_memory_stale_guard.py b/tests/gateway/test_flush_memory_stale_guard.py index 6a43817ce..c4e4e1fb6 100644 --- a/tests/gateway/test_flush_memory_stale_guard.py +++ b/tests/gateway/test_flush_memory_stale_guard.py @@ -202,6 +202,22 @@ class TestFlushAgentSilenced: sys.stdout = old_stdout assert buf.getvalue() == "", "no-op print_fn spinner must not write to stdout" + def test_flush_agent_closes_resources_after_run(self, monkeypatch): + """Memory flush should close temporary agent resources after the turn.""" + runner, tmp_agent, _ = _make_flush_context(monkeypatch) + tmp_agent.shutdown_memory_provider = MagicMock() + tmp_agent.close = MagicMock() + + with ( + patch("gateway.run._resolve_runtime_agent_kwargs", return_value={"api_key": "k"}), + patch("gateway.run._resolve_gateway_model", return_value="test-model"), + patch.dict("sys.modules", {"tools.memory_tool": MagicMock(get_memory_dir=lambda: Path("/nonexistent"))}), + ): + runner._flush_memories_for_session("session_cleanup") + + tmp_agent.shutdown_memory_provider.assert_called_once() + tmp_agent.close.assert_called_once() + class TestFlushPromptStructure: """Verify the flush prompt retains its core instructions.""" diff --git a/tests/gateway/test_session_hygiene.py b/tests/gateway/test_session_hygiene.py index 325c24fac..f2e343441 100644 --- a/tests/gateway/test_session_hygiene.py +++ b/tests/gateway/test_session_hygiene.py @@ -305,10 +305,15 @@ async def test_session_hygiene_messages_stay_in_originating_topic(monkeypatch, t monkeypatch.setitem(sys.modules, "dotenv", fake_dotenv) class FakeCompressAgent: + last_instance = None + def __init__(self, **kwargs): self.model = kwargs.get("model") self.session_id = kwargs.get("session_id", "fake-session") self._print_fn = None + self.shutdown_memory_provider = MagicMock() + self.close = MagicMock() + type(self).last_instance = self def _compress_context(self, messages, *_args, **_kwargs): # Simulate real _compress_context: create a new session_id @@ -385,3 +390,6 @@ async def test_session_hygiene_messages_stay_in_originating_topic(monkeypatch, t # Compression warnings are no longer sent to users — compression # happens silently with server-side logging only. assert len(adapter.sent) == 0 + assert FakeCompressAgent.last_instance is not None + FakeCompressAgent.last_instance.shutdown_memory_provider.assert_called_once() + FakeCompressAgent.last_instance.close.assert_called_once() From 73befa505d510dc96dc445337a12d81b496407fb Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Thu, 16 Apr 2026 18:40:04 +0530 Subject: [PATCH 04/19] fix(cli): handle null/non-dict display config in skin initialization display: null or display: in config.yaml crashed skin init with AttributeError. Now falls back to default skin gracefully. Cherry-picked from #10867 by @Bartok9. Consolidates #10876 by @cola-runner. Co-authored-by: cola-runner --- hermes_cli/skin_engine.py | 4 +++- tests/hermes_cli/test_skin_engine.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/hermes_cli/skin_engine.py b/hermes_cli/skin_engine.py index b992ada06..160e7c119 100644 --- a/hermes_cli/skin_engine.py +++ b/hermes_cli/skin_engine.py @@ -708,7 +708,9 @@ def init_skin_from_config(config: dict) -> None: Call this once during CLI init with the loaded config dict. """ - display = config.get("display", {}) + display = config.get("display") or {} + if not isinstance(display, dict): + display = {} skin_name = display.get("skin", "default") if isinstance(skin_name, str) and skin_name.strip(): set_active_skin(skin_name.strip()) diff --git a/tests/hermes_cli/test_skin_engine.py b/tests/hermes_cli/test_skin_engine.py index aadcde3a6..3ce185b82 100644 --- a/tests/hermes_cli/test_skin_engine.py +++ b/tests/hermes_cli/test_skin_engine.py @@ -152,6 +152,24 @@ class TestSkinManagement: init_skin_from_config({}) assert get_active_skin_name() == "default" + def test_init_skin_from_null_display(self): + """display: null should fall back to default, not crash.""" + from hermes_cli.skin_engine import init_skin_from_config, get_active_skin_name + init_skin_from_config({"display": None}) + assert get_active_skin_name() == "default" + + def test_init_skin_from_non_dict_display(self): + """display: should fall back to default.""" + from hermes_cli.skin_engine import init_skin_from_config, get_active_skin_name + init_skin_from_config({"display": "invalid"}) + assert get_active_skin_name() == "default" + + init_skin_from_config({"display": 42}) + assert get_active_skin_name() == "default" + + init_skin_from_config({"display": []}) + assert get_active_skin_name() == "default" + class TestUserSkins: def test_load_user_skin_from_yaml(self, tmp_path, monkeypatch): From 3e3ec35a5e0235184bb5c11e3404dd3d51458af0 Mon Sep 17 00:00:00 2001 From: konsisumer Date: Thu, 16 Apr 2026 09:17:24 +0200 Subject: [PATCH 05/19] fix: surface execute_code timeout to user instead of silently dropping (#10807) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When execute_code times out, the result JSON had status="timeout" and an error field, but the output field was empty. Many models treat empty output as "nothing happened" and produce an empty/minimal response. The gateway stream consumer then considers the response "already sent" (from pre-tool streaming) and silently drops it — leaving the user staring at silence. Three changes: 1. Include the timeout message in the output field (both local and remote paths) so the model always has visible content to relay to the user. 2. Add periodic activity callbacks to the local execution polling loop so the gateway's inactivity monitor knows execute_code is alive during long runs. 3. Fix stream_consumer._send_fallback_final to not silently drop content when the continuation appears empty but the final text differs from what was previously streamed (e.g. after a tool boundary reset). --- gateway/stream_consumer.py | 14 +++++++++--- tools/code_execution_tool.py | 43 ++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/gateway/stream_consumer.py b/gateway/stream_consumer.py index a644547e6..853b15903 100644 --- a/gateway/stream_consumer.py +++ b/gateway/stream_consumer.py @@ -515,9 +515,17 @@ class GatewayStreamConsumer: self._fallback_final_send = False if not continuation.strip(): # Nothing new to send — the visible partial already matches final text. - self._already_sent = True - self._final_response_sent = True - return + # BUT: if final_text itself has meaningful content (e.g. a timeout + # message after a long tool call), the prefix-based continuation + # calculation may wrongly conclude "already shown" because the + # streamed prefix was from a *previous* segment (before the tool + # boundary). In that case, send the full final_text as-is (#10807). + if final_text.strip() and final_text != self._visible_prefix(): + continuation = final_text + else: + self._already_sent = True + self._final_response_sent = True + return raw_limit = getattr(self.adapter, "MAX_MESSAGE_LENGTH", 4096) safe_limit = max(500, raw_limit - 100) diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index 8cffeda80..d61164bca 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -871,7 +871,18 @@ def _execute_remote( } if status == "timeout": - result["error"] = f"Script timed out after {timeout}s and was killed." + timeout_msg = f"Script timed out after {timeout}s and was killed." + result["error"] = timeout_msg + # Include timeout message in output so the LLM always surfaces it + # to the user (see local path comment — same reasoning, #10807). + if stdout_text: + result["output"] = stdout_text + f"\n\n⏰ {timeout_msg}" + else: + result["output"] = f"⏰ {timeout_msg}" + logger.warning( + "execute_code (remote) timed out after %ss (limit %ss) with %d tool calls", + duration, timeout, tool_call_counter[0], + ) elif status == "interrupted": result["output"] = ( stdout_text + "\n[execution interrupted — user sent a new message]" @@ -1117,6 +1128,8 @@ def execute_code( stderr_reader.start() status = "success" + _last_activity_touch = time.monotonic() + _ACTIVITY_INTERVAL = 10.0 while proc.poll() is None: if _is_interrupted(): _kill_process_group(proc) @@ -1126,6 +1139,19 @@ def execute_code( _kill_process_group(proc, escalate=True) status = "timeout" break + # Periodic activity touch so the gateway's inactivity timeout + # doesn't kill the agent during long code execution (#10807). + _now = time.monotonic() + if _now - _last_activity_touch >= _ACTIVITY_INTERVAL: + _last_activity_touch = _now + try: + from tools.environments.base import _get_activity_callback + _cb = _get_activity_callback() + if _cb: + _elapsed = int(_now - exec_start) + _cb(f"execute_code running ({_elapsed}s elapsed)") + except Exception: + pass time.sleep(0.2) # Wait for readers to finish draining @@ -1179,7 +1205,20 @@ def execute_code( } if status == "timeout": - result["error"] = f"Script timed out after {timeout}s and was killed." + timeout_msg = f"Script timed out after {timeout}s and was killed." + result["error"] = timeout_msg + # Include timeout message in output so the LLM always surfaces it + # to the user. When output is empty, models often treat the result + # as "nothing happened" and produce an empty response, which the + # gateway stream consumer silently drops (#10807). + if stdout_text: + result["output"] = stdout_text + f"\n\n⏰ {timeout_msg}" + else: + result["output"] = f"⏰ {timeout_msg}" + logger.warning( + "execute_code timed out after %ss (limit %ss) with %d tool calls", + duration, timeout, tool_call_counter[0], + ) elif status == "interrupted": result["output"] = stdout_text + "\n[execution interrupted — user sent a new message]" elif exit_code != 0: From a6142a8e087bcdb1e098657f4128b10b827059fd Mon Sep 17 00:00:00 2001 From: kshitijk4poor Date: Thu, 16 Apr 2026 19:01:56 +0530 Subject: [PATCH 06/19] fix: follow-up for salvaged PR #10854 - Extract duplicated activity-callback polling into shared touch_activity_if_due() helper in tools/environments/base.py - Use helper from both base.py _wait_for_process and code_execution_tool.py local polling loop (DRY) - Add test assertion that timeout output field contains the timeout message and emoji (#10807) - Add stream_consumer test for tool-boundary fallback scenario where continuation is empty but final_text differs from visible prefix (#10807) --- tests/gateway/test_stream_consumer.py | 50 +++++++++++++++++++++++++++ tests/tools/test_code_execution.py | 4 +++ tools/code_execution_tool.py | 22 +++++------- tools/environments/base.py | 44 ++++++++++++++++------- tools/environments/modal_utils.py | 27 ++++++--------- 5 files changed, 105 insertions(+), 42 deletions(-) diff --git a/tests/gateway/test_stream_consumer.py b/tests/gateway/test_stream_consumer.py index 38532e66b..cdba5f60e 100644 --- a/tests/gateway/test_stream_consumer.py +++ b/tests/gateway/test_stream_consumer.py @@ -606,6 +606,56 @@ class TestSegmentBreakOnToolBoundary: assert sent_texts[0].startswith(prefix) assert sum(len(t) for t in sent_texts[1:]) == len(tail) + @pytest.mark.asyncio + async def test_fallback_final_sends_full_text_at_tool_boundary(self): + """After a tool call, the streamed prefix is stale (from the pre-tool + segment). _send_fallback_final must still send the post-tool response + even when continuation_text calculates as empty (#10807).""" + adapter = MagicMock() + adapter.send = AsyncMock( + return_value=SimpleNamespace(success=True, message_id="msg_1"), + ) + adapter.edit_message = AsyncMock( + return_value=SimpleNamespace(success=True), + ) + adapter.MAX_MESSAGE_LENGTH = 4096 + + config = StreamConsumerConfig(edit_interval=0.01, buffer_threshold=5) + consumer = GatewayStreamConsumer(adapter, "chat_123", config) + + # Simulate a pre-tool streamed segment that becomes the visible prefix + pre_tool_text = "I'll run that code now." + consumer.on_delta(pre_tool_text) + task = asyncio.create_task(consumer.run()) + await asyncio.sleep(0.05) + + # After the tool call, the model returns a SHORT final response that + # does NOT start with the pre-tool prefix. The continuation calculator + # would return empty (no prefix match → full text returned, but if the + # streaming edit already showed pre_tool_text, the prefix-based logic + # wrongly matches). Simulate this by setting _last_sent_text to the + # pre-tool content, then finishing with different post-tool content. + consumer._last_sent_text = pre_tool_text + post_tool_response = "⏰ Script timed out after 30s and was killed." + consumer.finish() + await task + + # The fallback should send the post-tool response via + # _send_fallback_final. + await consumer._send_fallback_final(post_tool_response) + + # Verify the final text was sent (not silently dropped) + sent = False + for call in adapter.send.call_args_list: + content = call[1].get("content", call[0][0] if call[0] else "") + if "timed out" in str(content): + sent = True + break + assert sent, ( + "Post-tool timeout response was silently dropped by " + "_send_fallback_final — the #10807 fix should prevent this" + ) + class TestInterimCommentaryMessages: @pytest.mark.asyncio diff --git a/tests/tools/test_code_execution.py b/tests/tools/test_code_execution.py index d2fbc7c10..15f8faa9b 100644 --- a/tests/tools/test_code_execution.py +++ b/tests/tools/test_code_execution.py @@ -279,6 +279,10 @@ raise RuntimeError("deliberate crash") )) self.assertEqual(result["status"], "timeout") self.assertIn("timed out", result.get("error", "")) + # The timeout message must also appear in output so the LLM always + # surfaces it to the user (#10807). + self.assertIn("timed out", result.get("output", "")) + self.assertIn("\u23f0", result.get("output", "")) def test_web_search_tool(self): """Script calls web_search and processes results.""" diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index d61164bca..3e7e3f925 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -1128,8 +1128,10 @@ def execute_code( stderr_reader.start() status = "success" - _last_activity_touch = time.monotonic() - _ACTIVITY_INTERVAL = 10.0 + _activity_state = { + "last_touch": time.monotonic(), + "start": exec_start, + } while proc.poll() is None: if _is_interrupted(): _kill_process_group(proc) @@ -1141,17 +1143,11 @@ def execute_code( break # Periodic activity touch so the gateway's inactivity timeout # doesn't kill the agent during long code execution (#10807). - _now = time.monotonic() - if _now - _last_activity_touch >= _ACTIVITY_INTERVAL: - _last_activity_touch = _now - try: - from tools.environments.base import _get_activity_callback - _cb = _get_activity_callback() - if _cb: - _elapsed = int(_now - exec_start) - _cb(f"execute_code running ({_elapsed}s elapsed)") - except Exception: - pass + try: + from tools.environments.base import touch_activity_if_due + touch_activity_if_due(_activity_state, "execute_code running") + except Exception: + pass time.sleep(0.2) # Wait for readers to finish draining diff --git a/tools/environments/base.py b/tools/environments/base.py index 19c3bf024..8e9907923 100644 --- a/tools/environments/base.py +++ b/tools/environments/base.py @@ -37,6 +37,32 @@ def _get_activity_callback() -> Callable[[str], None] | None: return getattr(_activity_callback_local, "callback", None) +def touch_activity_if_due( + state: dict, + label: str, +) -> None: + """Fire the activity callback at most once every ``state['interval']`` seconds. + + *state* must contain ``last_touch`` (monotonic timestamp) and ``start`` + (monotonic timestamp of the operation start). An optional ``interval`` + key overrides the default 10 s cadence. + + Swallows all exceptions so callers don't need their own try/except. + """ + now = time.monotonic() + interval = state.get("interval", 10.0) + if now - state["last_touch"] < interval: + return + state["last_touch"] = now + try: + cb = _get_activity_callback() + if cb: + elapsed = int(now - state["start"]) + cb(f"{label} ({elapsed}s elapsed)") + except Exception: + pass + + def get_sandbox_dir() -> Path: """Return the host-side root for all sandbox storage (Docker workspaces, Singularity overlays/SIF cache, etc.). @@ -405,8 +431,11 @@ class BaseEnvironment(ABC): drain_thread = threading.Thread(target=_drain, daemon=True) drain_thread.start() deadline = time.monotonic() + timeout - _last_activity_touch = time.monotonic() - _ACTIVITY_INTERVAL = 10.0 # seconds between activity touches + _now = time.monotonic() + _activity_state = { + "last_touch": _now, + "start": _now, + } while proc.poll() is None: if is_interrupted(): @@ -428,16 +457,7 @@ class BaseEnvironment(ABC): "returncode": 124, } # Periodic activity touch so the gateway knows we're alive - _now = time.monotonic() - if _now - _last_activity_touch >= _ACTIVITY_INTERVAL: - _last_activity_touch = _now - _cb = _get_activity_callback() - if _cb: - try: - _elapsed = int(_now - (deadline - timeout)) - _cb(f"terminal command running ({_elapsed}s elapsed)") - except Exception: - pass + touch_activity_if_due(_activity_state, "terminal command running") time.sleep(0.2) drain_thread.join(timeout=5) diff --git a/tools/environments/modal_utils.py b/tools/environments/modal_utils.py index 161aad261..4d68399e4 100644 --- a/tools/environments/modal_utils.py +++ b/tools/environments/modal_utils.py @@ -105,9 +105,11 @@ class BaseModalExecutionEnvironment(BaseEnvironment): if self._client_timeout_grace_seconds is not None: deadline = time.monotonic() + prepared.timeout + self._client_timeout_grace_seconds - _last_activity_touch = time.monotonic() - _modal_exec_start = time.monotonic() - _ACTIVITY_INTERVAL = 10.0 # match _wait_for_process cadence + _now = time.monotonic() + _activity_state = { + "last_touch": _now, + "start": _now, + } while True: if is_interrupted(): @@ -133,20 +135,11 @@ class BaseModalExecutionEnvironment(BaseEnvironment): return self._timeout_result_for_modal(prepared.timeout) # Periodic activity touch so the gateway knows we're alive - _now = time.monotonic() - if _now - _last_activity_touch >= _ACTIVITY_INTERVAL: - _last_activity_touch = _now - try: - from tools.environments.base import _get_activity_callback - _cb = _get_activity_callback() - except Exception: - _cb = None - if _cb: - try: - _elapsed = int(_now - _modal_exec_start) - _cb(f"modal command running ({_elapsed}s elapsed)") - except Exception: - pass + try: + from tools.environments.base import touch_activity_if_due + touch_activity_if_due(_activity_state, "modal command running") + except Exception: + pass time.sleep(self._poll_interval_seconds) From fe12042e50c5a9187463d3646172b6212e122b7d Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:44:23 -0700 Subject: [PATCH 07/19] fix: remove context pressure warnings entirely (#11039) The gateway compression notifications were already removed in commit cc63b2d1 (PR #4139), but the agent-level context pressure warnings (85%/95% tiered alerts via _emit_context_pressure) were still firing on both CLI and gateway. Removed: - _emit_context_pressure method and all call sites in run_conversation() - Class-level dedup state (_context_pressure_last_warned, _CONTEXT_PRESSURE_COOLDOWN) - Instance attribute _context_pressure_warned_at - Pressure reset logic in _compress_context - format_context_pressure and format_context_pressure_gateway from agent/display.py - Orphaned ANSI constants that only served these functions - tests/run_agent/test_context_pressure.py (all 361 lines) Compression itself continues to run silently in the background. Closes #3784 --- agent/display.py | 80 ----- run_agent.py | 98 ------ tests/run_agent/test_context_pressure.py | 361 ----------------------- 3 files changed, 539 deletions(-) delete mode 100644 tests/run_agent/test_context_pressure.py diff --git a/agent/display.py b/agent/display.py index a7f3cbaa2..3f1341485 100644 --- a/agent/display.py +++ b/agent/display.py @@ -993,84 +993,4 @@ def get_cute_tool_message( # Honcho session line (one-liner with clickable OSC 8 hyperlink) # ========================================================================= -_DIM = "\033[2m" -_SKY_BLUE = "\033[38;5;117m" -_ANSI_RESET = "\033[0m" - -# ========================================================================= -# Context pressure display (CLI user-facing warnings) -# ========================================================================= - -# ANSI color codes for context pressure tiers -_CYAN = "\033[36m" -_YELLOW = "\033[33m" -_BOLD = "\033[1m" -_DIM_ANSI = "\033[2m" - -# Bar characters -_BAR_FILLED = "▰" -_BAR_EMPTY = "▱" -_BAR_WIDTH = 20 - - -def format_context_pressure( - compaction_progress: float, - threshold_tokens: int, - threshold_percent: float, - compression_enabled: bool = True, -) -> str: - """Build a formatted context pressure line for CLI display. - - The bar and percentage show progress toward the compaction threshold, - NOT the raw context window. 100% = compaction fires. - - Args: - compaction_progress: How close to compaction (0.0–1.0, 1.0 = fires). - threshold_tokens: Compaction threshold in tokens. - threshold_percent: Compaction threshold as a fraction of context window. - compression_enabled: Whether auto-compression is active. - """ - pct_int = min(int(compaction_progress * 100), 100) - filled = min(int(compaction_progress * _BAR_WIDTH), _BAR_WIDTH) - bar = _BAR_FILLED * filled + _BAR_EMPTY * (_BAR_WIDTH - filled) - - threshold_k = f"{threshold_tokens // 1000}k" if threshold_tokens >= 1000 else str(threshold_tokens) - threshold_pct_int = int(threshold_percent * 100) - - color = f"{_BOLD}{_YELLOW}" - icon = "⚠" - if compression_enabled: - hint = "compaction approaching" - else: - hint = "no auto-compaction" - - return ( - f" {color}{icon} context {bar} {pct_int}% to compaction{_ANSI_RESET}" - f" {_DIM_ANSI}{threshold_k} threshold ({threshold_pct_int}%) · {hint}{_ANSI_RESET}" - ) - - -def format_context_pressure_gateway( - compaction_progress: float, - threshold_percent: float, - compression_enabled: bool = True, -) -> str: - """Build a plain-text context pressure notification for messaging platforms. - - No ANSI — just Unicode and plain text suitable for Telegram/Discord/etc. - The percentage shows progress toward the compaction threshold. - """ - pct_int = min(int(compaction_progress * 100), 100) - filled = min(int(compaction_progress * _BAR_WIDTH), _BAR_WIDTH) - bar = _BAR_FILLED * filled + _BAR_EMPTY * (_BAR_WIDTH - filled) - - threshold_pct_int = int(threshold_percent * 100) - - icon = "⚠️" - if compression_enabled: - hint = f"Context compaction approaching (threshold: {threshold_pct_int}% of window)." - else: - hint = "Auto-compaction is disabled — context may be truncated." - - return f"{icon} Context: {bar} {pct_int}% to compaction\n{hint}" diff --git a/run_agent.py b/run_agent.py index d2612c346..3707e943e 100644 --- a/run_agent.py +++ b/run_agent.py @@ -540,13 +540,6 @@ class AIAgent: for AI models that support function calling. """ - # ── Class-level context pressure dedup (survives across instances) ── - # The gateway creates a new AIAgent per message, so instance-level flags - # reset every time. This dict tracks {session_id: (warn_level, timestamp)} - # to suppress duplicate warnings within a cooldown window. - _context_pressure_last_warned: dict = {} - _CONTEXT_PRESSURE_COOLDOWN = 300 # seconds between re-warning same session - @property def base_url(self) -> str: return self._base_url @@ -826,12 +819,6 @@ class AIAgent: self._budget_exhausted_injected = False self._budget_grace_call = False - # Context pressure warnings: notify the USER (not the LLM) as context - # fills up. Purely informational — displayed in CLI output and sent via - # status_callback for gateway platforms. Does NOT inject into messages. - # Tiered: fires at 85% and again at 95% of compaction threshold. - self._context_pressure_warned_at = 0.0 # highest tier already shown - # Activity tracking — updated on each API call, tool execution, and # stream chunk. Used by the gateway timeout handler to report what the # agent was doing when it was killed, and by the "still working" @@ -7220,20 +7207,6 @@ class AIAgent: self.context_compressor.last_prompt_tokens = _compressed_est self.context_compressor.last_completion_tokens = 0 - # Only reset the pressure warning if compression actually brought - # us below the warning level (85% of threshold). When compression - # can't reduce enough (e.g. threshold is very low, or system prompt - # alone exceeds the warning level), keep the tier set to prevent - # spamming the user with repeated warnings every loop iteration. - if self.context_compressor.threshold_tokens > 0: - _post_progress = _compressed_est / self.context_compressor.threshold_tokens - if _post_progress < 0.85: - self._context_pressure_warned_at = 0.0 - # Clear class-level dedup for this session so a fresh - # warning cycle can start if context grows again. - _sid = self.session_id or "default" - AIAgent._context_pressure_last_warned.pop(_sid, None) - # Clear the file-read dedup cache. After compression the original # read content is summarised away — if the model re-reads the same # file it needs the full content, not a "file unchanged" stub. @@ -8033,45 +8006,6 @@ class AIAgent: - def _emit_context_pressure(self, compaction_progress: float, compressor) -> None: - """Notify the user that context is approaching the compaction threshold. - - Args: - compaction_progress: How close to compaction (0.0–1.0, where 1.0 = fires). - compressor: The ContextCompressor instance (for threshold/context info). - - Purely user-facing — does NOT modify the message stream. - For CLI: prints a formatted line with a progress bar. - For gateway: fires status_callback so the platform can send a chat message. - """ - from agent.display import format_context_pressure, format_context_pressure_gateway - - threshold_pct = compressor.threshold_tokens / compressor.context_length if compressor.context_length else 0.5 - - # CLI output — always shown (these are user-facing status notifications, - # not verbose debug output, so they bypass quiet_mode). - # Gateway users also get the callback below. - if self.platform in (None, "cli"): - line = format_context_pressure( - compaction_progress=compaction_progress, - threshold_tokens=compressor.threshold_tokens, - threshold_percent=threshold_pct, - compression_enabled=self.compression_enabled, - ) - self._safe_print(line) - - # Gateway / external consumers - if self.status_callback: - try: - msg = format_context_pressure_gateway( - compaction_progress=compaction_progress, - threshold_percent=threshold_pct, - compression_enabled=self.compression_enabled, - ) - self.status_callback("context_pressure", msg) - except Exception: - logger.debug("status_callback error in context pressure", exc_info=True) - def _handle_max_iterations(self, messages: list, api_call_count: int) -> str: """Request a summary when max iterations are reached. Returns the final response text.""" print(f"⚠️ Reached maximum iterations ({self.max_iterations}). Requesting summary...") @@ -10792,38 +10726,6 @@ class AIAgent: else: _real_tokens = estimate_messages_tokens_rough(messages) - # ── Context pressure warnings (user-facing only) ────────── - # Notify the user (NOT the LLM) as context approaches the - # compaction threshold. Thresholds are relative to where - # compaction fires, not the raw context window. - # Does not inject into messages — just prints to CLI output - # and fires status_callback for gateway platforms. - # Tiered: 85% (orange) and 95% (red/critical). - if _compressor.threshold_tokens > 0: - _compaction_progress = _real_tokens / _compressor.threshold_tokens - # Determine the warning tier for this progress level - _warn_tier = 0.0 - if _compaction_progress >= 0.95: - _warn_tier = 0.95 - elif _compaction_progress >= 0.85: - _warn_tier = 0.85 - if _warn_tier > self._context_pressure_warned_at: - # Class-level dedup: check if this session was already - # warned at this tier within the cooldown window. - _sid = self.session_id or "default" - _last = AIAgent._context_pressure_last_warned.get(_sid) - _now = time.time() - if _last is None or _last[0] < _warn_tier or (_now - _last[1]) >= self._CONTEXT_PRESSURE_COOLDOWN: - self._context_pressure_warned_at = _warn_tier - AIAgent._context_pressure_last_warned[_sid] = (_warn_tier, _now) - self._emit_context_pressure(_compaction_progress, _compressor) - # Evict stale entries (older than 2x cooldown) - _cutoff = _now - self._CONTEXT_PRESSURE_COOLDOWN * 2 - AIAgent._context_pressure_last_warned = { - k: v for k, v in AIAgent._context_pressure_last_warned.items() - if v[1] > _cutoff - } - if self.compression_enabled and _compressor.should_compress(_real_tokens): self._safe_print(" ⟳ compacting context…") messages, active_system_prompt = self._compress_context( diff --git a/tests/run_agent/test_context_pressure.py b/tests/run_agent/test_context_pressure.py deleted file mode 100644 index 4140749c5..000000000 --- a/tests/run_agent/test_context_pressure.py +++ /dev/null @@ -1,361 +0,0 @@ -"""Tests for context pressure warnings (user-facing, not injected into messages). - -Covers: -- Display formatting (CLI and gateway variants) -- Flag tracking and threshold logic on AIAgent -- Flag reset after compression -- status_callback invocation -""" - -import json -from types import SimpleNamespace -from unittest.mock import MagicMock, patch - -import pytest - -from agent.display import format_context_pressure, format_context_pressure_gateway -from run_agent import AIAgent - - -# --------------------------------------------------------------------------- -# Display formatting tests -# --------------------------------------------------------------------------- - - -class TestFormatContextPressure: - """CLI context pressure display (agent/display.py). - - The bar shows progress toward the compaction threshold, not the - raw context window. 60% = 60% of the way to compaction. - """ - - def test_80_percent_uses_warning_icon(self): - line = format_context_pressure(0.80, 100_000, 0.50) - assert "⚠" in line - assert "80% to compaction" in line - - def test_90_percent_uses_warning_icon(self): - line = format_context_pressure(0.90, 100_000, 0.50) - assert "⚠" in line - assert "90% to compaction" in line - - def test_bar_length_scales_with_progress(self): - line_80 = format_context_pressure(0.80, 100_000, 0.50) - line_95 = format_context_pressure(0.95, 100_000, 0.50) - assert line_95.count("▰") > line_80.count("▰") - - def test_shows_threshold_tokens(self): - line = format_context_pressure(0.80, 100_000, 0.50) - assert "100k" in line - - def test_small_threshold(self): - line = format_context_pressure(0.80, 500, 0.50) - assert "500" in line - - def test_shows_threshold_percent(self): - line = format_context_pressure(0.80, 100_000, 0.50) - assert "50%" in line - - def test_approaching_hint(self): - line = format_context_pressure(0.80, 100_000, 0.50) - assert "compaction approaching" in line - - def test_no_compaction_when_disabled(self): - line = format_context_pressure(0.85, 100_000, 0.50, compression_enabled=False) - assert "no auto-compaction" in line - - def test_returns_string(self): - result = format_context_pressure(0.65, 128_000, 0.50) - assert isinstance(result, str) - - def test_over_100_percent_capped(self): - """Progress > 1.0 should cap both bar and percentage text at 100%.""" - line = format_context_pressure(1.05, 100_000, 0.50) - assert "▰" in line - assert line.count("▰") == 20 - assert "100%" in line - assert "105%" not in line - - -class TestFormatContextPressureGateway: - """Gateway (plain text) context pressure display.""" - - def test_80_percent_warning(self): - msg = format_context_pressure_gateway(0.80, 0.50) - assert "80% to compaction" in msg - assert "50%" in msg - - def test_90_percent_warning(self): - msg = format_context_pressure_gateway(0.90, 0.50) - assert "90% to compaction" in msg - assert "approaching" in msg - - def test_no_compaction_warning(self): - msg = format_context_pressure_gateway(0.85, 0.50, compression_enabled=False) - assert "disabled" in msg - - def test_no_ansi_codes(self): - msg = format_context_pressure_gateway(0.80, 0.50) - assert "\033[" not in msg - - def test_has_progress_bar(self): - msg = format_context_pressure_gateway(0.80, 0.50) - assert "▰" in msg - - def test_over_100_percent_capped(self): - """Progress > 1.0 should cap percentage text at 100%.""" - msg = format_context_pressure_gateway(1.09, 0.50) - assert "100% to compaction" in msg - assert "109%" not in msg - assert msg.count("▰") == 20 - - -# --------------------------------------------------------------------------- -# AIAgent context pressure flag tests -# --------------------------------------------------------------------------- - - -def _make_tool_defs(*names): - return [ - { - "type": "function", - "function": { - "name": n, - "description": f"{n} tool", - "parameters": {"type": "object", "properties": {}}, - }, - } - for n in names - ] - - -@pytest.fixture() -def agent(): - """Minimal AIAgent with mocked internals.""" - with ( - patch("run_agent.get_tool_definitions", return_value=_make_tool_defs("web_search")), - patch("run_agent.check_toolset_requirements", return_value={}), - patch("run_agent.OpenAI"), - ): - a = AIAgent( - api_key="test-key-1234567890", - quiet_mode=True, - skip_context_files=True, - skip_memory=True, - ) - a.client = MagicMock() - return a - - -class TestContextPressureFlags: - """Context pressure warning flag tracking on AIAgent.""" - - def test_flag_initialized_zero(self, agent): - assert agent._context_pressure_warned_at == 0.0 - - def test_emit_calls_status_callback(self, agent): - """status_callback should be invoked with event type and message.""" - cb = MagicMock() - agent.status_callback = cb - - compressor = MagicMock() - compressor.context_length = 200_000 - compressor.threshold_tokens = 100_000 # 50% - - agent._emit_context_pressure(0.85, compressor) - - cb.assert_called_once() - args = cb.call_args[0] - assert args[0] == "context_pressure" - assert "85% to compaction" in args[1] - - def test_emit_no_callback_no_crash(self, agent): - """No status_callback set — should not crash.""" - agent.status_callback = None - - compressor = MagicMock() - compressor.context_length = 200_000 - compressor.threshold_tokens = 100_000 - - # Should not raise - agent._emit_context_pressure(0.60, compressor) - - def test_emit_prints_for_cli_platform(self, agent, capsys): - """CLI platform should always print context pressure, even in quiet_mode.""" - agent.quiet_mode = True - agent.platform = "cli" - agent.status_callback = None - - compressor = MagicMock() - compressor.context_length = 200_000 - compressor.threshold_tokens = 100_000 - - agent._emit_context_pressure(0.85, compressor) - captured = capsys.readouterr() - assert "▰" in captured.out - assert "to compaction" in captured.out - - def test_emit_skips_print_for_gateway_platform(self, agent, capsys): - """Gateway platforms get the callback, not CLI print.""" - agent.platform = "telegram" - agent.status_callback = None - - compressor = MagicMock() - compressor.context_length = 200_000 - compressor.threshold_tokens = 100_000 - - agent._emit_context_pressure(0.85, compressor) - captured = capsys.readouterr() - assert "▰" not in captured.out - - def test_flag_reset_on_compression(self, agent): - """After _compress_context, context pressure flag should reset.""" - agent._context_pressure_warned_at = 0.85 - agent.compression_enabled = True - - agent.context_compressor = MagicMock() - agent.context_compressor.compress.return_value = [ - {"role": "user", "content": "Summary of conversation so far."} - ] - agent.context_compressor.context_length = 200_000 - agent.context_compressor.threshold_tokens = 100_000 - agent.context_compressor.compression_count = 1 - - agent._todo_store = MagicMock() - agent._todo_store.format_for_injection.return_value = None - - agent._build_system_prompt = MagicMock(return_value="system prompt") - agent._cached_system_prompt = "old system prompt" - agent._session_db = None - - messages = [ - {"role": "user", "content": "hello"}, - {"role": "assistant", "content": "hi there"}, - ] - agent._compress_context(messages, "system prompt") - - assert agent._context_pressure_warned_at == 0.0 - - def test_emit_callback_error_handled(self, agent): - """If status_callback raises, it should be caught gracefully.""" - cb = MagicMock(side_effect=RuntimeError("callback boom")) - agent.status_callback = cb - - compressor = MagicMock() - compressor.context_length = 200_000 - compressor.threshold_tokens = 100_000 - - # Should not raise - agent._emit_context_pressure(0.85, compressor) - - def test_tiered_reemits_at_95(self, agent): - """Warning fires at 85%, then fires again when crossing 95%.""" - agent._context_pressure_warned_at = 0.85 - # Simulate crossing 95%: the tier (0.95) > warned_at (0.85) - assert 0.95 > agent._context_pressure_warned_at - # After emission at 95%, the tier should update - agent._context_pressure_warned_at = 0.95 - assert agent._context_pressure_warned_at == 0.95 - - def test_tiered_no_double_emit_at_same_level(self, agent): - """Once warned at 85%, further 85%+ readings don't re-warn.""" - agent._context_pressure_warned_at = 0.85 - # At 88%, tier is 0.85, which is NOT > warned_at (0.85) - _warn_tier = 0.85 if 0.88 >= 0.85 else 0.0 - assert not (_warn_tier > agent._context_pressure_warned_at) - - def test_flag_not_reset_when_compression_insufficient(self, agent): - """When compression can't drop below 85%, keep the flag set.""" - agent._context_pressure_warned_at = 0.85 - agent.compression_enabled = True - - agent.context_compressor = MagicMock() - agent.context_compressor.compress.return_value = [ - {"role": "user", "content": "Summary of conversation so far."} - ] - agent.context_compressor.context_length = 200 - # Use a small threshold so the tiny compressed output still - # represents >= 85% of it (prevents flag reset). - agent.context_compressor.threshold_tokens = 10 - agent.context_compressor.compression_count = 1 - agent.context_compressor.last_prompt_tokens = 0 - - agent._todo_store = MagicMock() - agent._todo_store.format_for_injection.return_value = None - agent._build_system_prompt = MagicMock(return_value="system prompt") - agent._cached_system_prompt = "old system prompt" - agent._session_db = None - - messages = [ - {"role": "user", "content": "hello"}, - {"role": "assistant", "content": "hi there"}, - ] - agent._compress_context(messages, "system prompt") - - # Post-compression is ~90% of threshold — flag should NOT reset - assert agent._context_pressure_warned_at == 0.85 - - -class TestContextPressureGatewayDedup: - """Class-level dedup prevents warning spam across AIAgent instances.""" - - def setup_method(self): - """Clear class-level dedup state between tests.""" - AIAgent._context_pressure_last_warned.clear() - - def test_second_instance_within_cooldown_suppressed(self): - """Same session, same tier, within cooldown — should be suppressed.""" - import time - sid = "test_session_dedup" - # Simulate first warning - AIAgent._context_pressure_last_warned[sid] = (0.85, time.time()) - # Second instance checking same tier within cooldown - _last = AIAgent._context_pressure_last_warned.get(sid) - _should_warn = _last is None or _last[0] < 0.85 or (time.time() - _last[1]) >= AIAgent._CONTEXT_PRESSURE_COOLDOWN - assert not _should_warn - - def test_higher_tier_fires_despite_cooldown(self): - """Same session, higher tier — should fire even within cooldown.""" - import time - sid = "test_session_tier" - AIAgent._context_pressure_last_warned[sid] = (0.85, time.time()) - _last = AIAgent._context_pressure_last_warned.get(sid) - # 0.95 > 0.85 stored tier → should warn - _should_warn = _last is None or _last[0] < 0.95 or (time.time() - _last[1]) >= AIAgent._CONTEXT_PRESSURE_COOLDOWN - assert _should_warn - - def test_warning_fires_after_cooldown_expires(self): - """Same session, same tier, after cooldown — should fire again.""" - import time - sid = "test_session_expired" - # Set a timestamp far in the past - AIAgent._context_pressure_last_warned[sid] = (0.85, time.time() - AIAgent._CONTEXT_PRESSURE_COOLDOWN - 1) - _last = AIAgent._context_pressure_last_warned.get(sid) - _should_warn = _last is None or _last[0] < 0.85 or (time.time() - _last[1]) >= AIAgent._CONTEXT_PRESSURE_COOLDOWN - assert _should_warn - - def test_compression_clears_dedup(self): - """After compression drops below 85%, dedup entry should be cleared.""" - import time - sid = "test_session_clear" - AIAgent._context_pressure_last_warned[sid] = (0.85, time.time()) - assert sid in AIAgent._context_pressure_last_warned - # Simulate what _compress_context does on reset - AIAgent._context_pressure_last_warned.pop(sid, None) - assert sid not in AIAgent._context_pressure_last_warned - - def test_eviction_removes_stale_entries(self): - """Stale entries older than 2x cooldown should be evicted.""" - import time - _now = time.time() - AIAgent._context_pressure_last_warned = { - "fresh": (0.85, _now), - "stale": (0.85, _now - AIAgent._CONTEXT_PRESSURE_COOLDOWN * 3), - } - _cutoff = _now - AIAgent._CONTEXT_PRESSURE_COOLDOWN * 2 - AIAgent._context_pressure_last_warned = { - k: v for k, v in AIAgent._context_pressure_last_warned.items() - if v[1] > _cutoff - } - assert "fresh" in AIAgent._context_pressure_last_warned - assert "stale" not in AIAgent._context_pressure_last_warned From 3c42064efcd031971224e6aaea19fcdce9a97cfb Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:48:33 -0700 Subject: [PATCH 08/19] fix: enforce config.yaml as sole CWD source + deprecate .env CWD vars + add hermes memory reset (#11029) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit config.yaml terminal.cwd is now the single source of truth for working directory. MESSAGING_CWD and TERMINAL_CWD in .env are deprecated with a migration warning. Changes: 1. config.py: Remove MESSAGING_CWD from OPTIONAL_ENV_VARS (setup wizard no longer prompts for it). Add warn_deprecated_cwd_env_vars() that prints a migration hint when deprecated env vars are detected. 2. gateway/run.py: Replace all MESSAGING_CWD reads with TERMINAL_CWD (which is bridged from config.yaml terminal.cwd). MESSAGING_CWD is still accepted as a backward-compat fallback with deprecation warning. Config bridge skips cwd placeholder values so they don't clobber the resolved TERMINAL_CWD. 3. cli.py: Guard against lazy-import clobbering — when cli.py is imported lazily during gateway runtime (via delegate_tool), don't let load_cli_config() overwrite an already-resolved TERMINAL_CWD with os.getcwd() of the service's working directory. (#10817) 4. hermes_cli/main.py: Add 'hermes memory reset' command with --target all/memory/user and --yes flags. Profile-scoped via HERMES_HOME. Migration path for users with .env settings: Remove MESSAGING_CWD / TERMINAL_CWD from .env Add to config.yaml: terminal: cwd: /your/project/path Addresses: #10225, #4672, #10817, #7663 --- cli.py | 27 ++- gateway/run.py | 27 ++- hermes_cli/config.py | 55 +++++- hermes_cli/main.py | 50 ++++++ tests/cli/test_cwd_env_respect.py | 107 ++++++++++++ tests/gateway/test_config_cwd_bridge.py | 59 +++++++ .../hermes_cli/test_deprecated_cwd_warning.py | 64 +++++++ tests/hermes_cli/test_memory_reset.py | 157 ++++++++++++++++++ 8 files changed, 526 insertions(+), 20 deletions(-) create mode 100644 tests/cli/test_cwd_env_respect.py create mode 100644 tests/hermes_cli/test_deprecated_cwd_warning.py create mode 100644 tests/hermes_cli/test_memory_reset.py diff --git a/cli.py b/cli.py index aaed32e15..12cb72014 100644 --- a/cli.py +++ b/cli.py @@ -401,14 +401,27 @@ def load_cli_config() -> Dict[str, Any]: # filesystem is directly accessible. For ALL remote/container backends # (ssh, docker, modal, singularity), the host path doesn't exist on the # target -- remove the key so terminal_tool.py uses its per-backend default. - if terminal_config.get("cwd") in (".", "auto", "cwd"): - effective_backend = terminal_config.get("env_type", "local") - if effective_backend == "local": - terminal_config["cwd"] = os.getcwd() - defaults["terminal"]["cwd"] = terminal_config["cwd"] + # + # GUARD: If TERMINAL_CWD is already set to a real absolute path (by the + # gateway's config bridge earlier in the process), don't clobber it. + # This prevents a lazy import of cli.py during gateway runtime from + # rewriting TERMINAL_CWD to the service's working directory. + # See issue #10817. + _CWD_PLACEHOLDERS = (".", "auto", "cwd") + if terminal_config.get("cwd") in _CWD_PLACEHOLDERS: + _existing_cwd = os.environ.get("TERMINAL_CWD", "") + if _existing_cwd and _existing_cwd not in _CWD_PLACEHOLDERS and os.path.isabs(_existing_cwd): + # Gateway (or earlier startup) already resolved a real path — keep it + terminal_config["cwd"] = _existing_cwd + defaults["terminal"]["cwd"] = _existing_cwd else: - # Remove so TERMINAL_CWD stays unset → tool picks backend default - terminal_config.pop("cwd", None) + effective_backend = terminal_config.get("env_type", "local") + if effective_backend == "local": + terminal_config["cwd"] = os.getcwd() + defaults["terminal"]["cwd"] = terminal_config["cwd"] + else: + # Remove so TERMINAL_CWD stays unset → tool picks backend default + terminal_config.pop("cwd", None) env_mappings = { "env_type": "TERMINAL_ENV", diff --git a/gateway/run.py b/gateway/run.py index 956369a3d..7517fdabd 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -131,6 +131,12 @@ if _config_path.exists(): for _cfg_key, _env_var in _terminal_env_map.items(): if _cfg_key in _terminal_cfg: _val = _terminal_cfg[_cfg_key] + # Skip cwd placeholder values (".", "auto", "cwd") — the + # gateway resolves these to Path.home() later (line ~255). + # Writing the raw placeholder here would just be noise. + # Only bridge explicit absolute paths from config.yaml. + if _cfg_key == "cwd" and str(_val) in (".", "auto", "cwd"): + continue if isinstance(_val, list): os.environ[_env_var] = json.dumps(_val) else: @@ -225,6 +231,13 @@ try: except Exception: pass +# Warn if user has deprecated MESSAGING_CWD / TERMINAL_CWD in .env +try: + from hermes_cli.config import warn_deprecated_cwd_env_vars + warn_deprecated_cwd_env_vars() +except Exception: + pass + # Gateway runs in quiet mode - suppress debug output and use cwd directly (no temp dirs) os.environ["HERMES_QUIET"] = "1" @@ -232,12 +245,14 @@ os.environ["HERMES_QUIET"] = "1" os.environ["HERMES_EXEC_ASK"] = "1" # Set terminal working directory for messaging platforms. -# If the user set an explicit path in config.yaml (not "." or "auto"), -# respect it. Otherwise use MESSAGING_CWD or default to home directory. +# config.yaml terminal.cwd is the canonical source (bridged to TERMINAL_CWD +# by the config bridge above). When it's unset or a placeholder, default +# to home directory. MESSAGING_CWD is accepted as a backward-compat +# fallback (deprecated — the warning above tells users to migrate). _configured_cwd = os.environ.get("TERMINAL_CWD", "") if not _configured_cwd or _configured_cwd in (".", "auto", "cwd"): - messaging_cwd = os.getenv("MESSAGING_CWD") or str(Path.home()) - os.environ["TERMINAL_CWD"] = messaging_cwd + _fallback = os.getenv("MESSAGING_CWD") or str(Path.home()) + os.environ["TERMINAL_CWD"] = _fallback from gateway.config import ( Platform, @@ -3403,7 +3418,7 @@ class GatewayRunner: from agent.context_references import preprocess_context_references_async from agent.model_metadata import get_model_context_length - _msg_cwd = os.environ.get("MESSAGING_CWD", os.path.expanduser("~")) + _msg_cwd = os.environ.get("TERMINAL_CWD", os.path.expanduser("~")) _msg_ctx_len = get_model_context_length( self._model, base_url=self._base_url or "", @@ -5614,7 +5629,7 @@ class GatewayRunner: max_snapshots=cp_cfg.get("max_snapshots", 50), ) - cwd = os.getenv("MESSAGING_CWD", str(Path.home())) + cwd = os.getenv("TERMINAL_CWD", str(Path.home())) arg = event.get_command_args().strip() if not arg: diff --git a/hermes_cli/config.py b/hermes_cli/config.py index a9f55f4c5..33bc325ee 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -1597,13 +1597,8 @@ OPTIONAL_ENV_VARS = { }, # ── Agent settings ── - "MESSAGING_CWD": { - "description": "Working directory for terminal commands via messaging", - "prompt": "Messaging working directory (default: home)", - "url": None, - "password": False, - "category": "setting", - }, + # NOTE: MESSAGING_CWD was removed here — use terminal.cwd in config.yaml + # instead. The gateway reads TERMINAL_CWD (bridged from terminal.cwd). "SUDO_PASSWORD": { "description": "Sudo password for terminal commands requiring root access; set to an explicit empty string to try empty without prompting", "prompt": "Sudo password", @@ -2082,6 +2077,52 @@ def print_config_warnings(config: Optional[Dict[str, Any]] = None) -> None: sys.stderr.write("\n".join(lines) + "\n\n") +def warn_deprecated_cwd_env_vars(config: Optional[Dict[str, Any]] = None) -> None: + """Warn if MESSAGING_CWD or TERMINAL_CWD is set in .env instead of config.yaml. + + These env vars are deprecated — the canonical setting is terminal.cwd + in config.yaml. Prints a migration hint to stderr. + """ + import os, sys + messaging_cwd = os.environ.get("MESSAGING_CWD") + terminal_cwd_env = os.environ.get("TERMINAL_CWD") + + if config is None: + try: + config = load_config() + except Exception: + return + + terminal_cfg = config.get("terminal", {}) + config_cwd = terminal_cfg.get("cwd", ".") if isinstance(terminal_cfg, dict) else "." + # Only warn if config.yaml doesn't have an explicit path + config_has_explicit_cwd = config_cwd not in (".", "auto", "cwd", "") + + lines: list[str] = [] + if messaging_cwd: + lines.append( + f" \033[33m⚠\033[0m MESSAGING_CWD={messaging_cwd} found in .env — " + f"this is deprecated." + ) + if terminal_cwd_env and not config_has_explicit_cwd: + # TERMINAL_CWD in env but not from config bridge — likely from .env + lines.append( + f" \033[33m⚠\033[0m TERMINAL_CWD={terminal_cwd_env} found in .env — " + f"this is deprecated." + ) + if lines: + hint_path = os.environ.get("HERMES_HOME", "~/.hermes") + lines.insert(0, "\033[33m⚠ Deprecated .env settings detected:\033[0m") + lines.append( + f" \033[2mMove to config.yaml instead: " + f"terminal:\\n cwd: /your/project/path\033[0m" + ) + lines.append( + f" \033[2mThen remove the old entries from {hint_path}/.env\033[0m" + ) + sys.stderr.write("\n".join(lines) + "\n\n") + + def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, Any]: """ Migrate config to latest version, prompting for new required fields. diff --git a/hermes_cli/main.py b/hermes_cli/main.py index d1ee08c49..3eedcf7fc 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -5659,6 +5659,18 @@ Examples: memory_sub.add_parser("setup", help="Interactive provider selection and configuration") memory_sub.add_parser("status", help="Show current memory provider config") memory_sub.add_parser("off", help="Disable external provider (built-in only)") + _reset_parser = memory_sub.add_parser( + "reset", + help="Erase all built-in memory (MEMORY.md and USER.md)", + ) + _reset_parser.add_argument( + "--yes", "-y", action="store_true", + help="Skip confirmation prompt", + ) + _reset_parser.add_argument( + "--target", choices=["all", "memory", "user"], default="all", + help="Which store to reset: 'all' (default), 'memory', or 'user'", + ) def cmd_memory(args): sub = getattr(args, "memory_command", None) @@ -5671,6 +5683,44 @@ Examples: save_config(config) print("\n ✓ Memory provider: built-in only") print(" Saved to config.yaml\n") + elif sub == "reset": + from hermes_constants import get_hermes_home, display_hermes_home + mem_dir = get_hermes_home() / "memories" + target = getattr(args, "target", "all") + files_to_reset = [] + if target in ("all", "memory"): + files_to_reset.append(("MEMORY.md", "agent notes")) + if target in ("all", "user"): + files_to_reset.append(("USER.md", "user profile")) + + # Check what exists + existing = [(f, desc) for f, desc in files_to_reset if (mem_dir / f).exists()] + if not existing: + print(f"\n Nothing to reset — no memory files found in {display_hermes_home()}/memories/\n") + return + + print(f"\n This will permanently erase the following memory files:") + for f, desc in existing: + path = mem_dir / f + size = path.stat().st_size + print(f" ◆ {f} ({desc}) — {size:,} bytes") + + if not getattr(args, "yes", False): + try: + answer = input("\n Type 'yes' to confirm: ").strip().lower() + except (EOFError, KeyboardInterrupt): + print("\n Cancelled.\n") + return + if answer != "yes": + print(" Cancelled.\n") + return + + for f, desc in existing: + (mem_dir / f).unlink() + print(f" ✓ Deleted {f} ({desc})") + + print(f"\n Memory reset complete. New sessions will start with a blank slate.") + print(f" Files were in: {display_hermes_home()}/memories/\n") else: from hermes_cli.memory_setup import memory_command memory_command(args) diff --git a/tests/cli/test_cwd_env_respect.py b/tests/cli/test_cwd_env_respect.py new file mode 100644 index 000000000..e9f3341d2 --- /dev/null +++ b/tests/cli/test_cwd_env_respect.py @@ -0,0 +1,107 @@ +"""Tests that load_cli_config() guards against lazy-import TERMINAL_CWD clobbering. + +When the gateway resolves TERMINAL_CWD at startup and cli.py is later +imported lazily (via delegate_tool → CLI_CONFIG), load_cli_config() must +not overwrite the already-resolved value with os.getcwd(). + +config.yaml terminal.cwd is the canonical source of truth. +.env TERMINAL_CWD and MESSAGING_CWD are deprecated. +See issue #10817. +""" + +import os +import pytest + + +# The sentinel values that mean "resolve at runtime" +_CWD_PLACEHOLDERS = (".", "auto", "cwd") + + +def _resolve_terminal_cwd(terminal_config: dict, defaults: dict, env: dict): + """Simulate the CWD resolution logic from load_cli_config(). + + This mirrors the code in cli.py that checks for a pre-resolved + TERMINAL_CWD before falling back to os.getcwd(). + """ + if terminal_config.get("cwd") in _CWD_PLACEHOLDERS: + _existing_cwd = env.get("TERMINAL_CWD", "") + if _existing_cwd and _existing_cwd not in _CWD_PLACEHOLDERS and os.path.isabs(_existing_cwd): + terminal_config["cwd"] = _existing_cwd + defaults["terminal"]["cwd"] = _existing_cwd + else: + effective_backend = terminal_config.get("env_type", "local") + if effective_backend == "local": + terminal_config["cwd"] = "/fake/getcwd" # stand-in for os.getcwd() + defaults["terminal"]["cwd"] = terminal_config["cwd"] + else: + terminal_config.pop("cwd", None) + + # Simulate the bridging loop: write terminal_config["cwd"] to env + _file_has_terminal = defaults.get("_file_has_terminal", False) + if "cwd" in terminal_config: + if _file_has_terminal or "TERMINAL_CWD" not in env: + env["TERMINAL_CWD"] = str(terminal_config["cwd"]) + + return env.get("TERMINAL_CWD", "") + + +class TestLazyImportGuard: + """TERMINAL_CWD resolved by gateway must survive a lazy cli.py import.""" + + def test_gateway_resolved_cwd_survives(self): + """Gateway set TERMINAL_CWD → lazy cli import must not clobber.""" + env = {"TERMINAL_CWD": "/home/user/workspace"} + terminal_config = {"cwd": ".", "env_type": "local"} + defaults = {"terminal": {"cwd": "."}, "_file_has_terminal": False} + + result = _resolve_terminal_cwd(terminal_config, defaults, env) + assert result == "/home/user/workspace" + + def test_gateway_resolved_cwd_survives_with_file_terminal(self): + """Even when config.yaml has a terminal: section, resolved CWD survives.""" + env = {"TERMINAL_CWD": "/home/user/workspace"} + terminal_config = {"cwd": ".", "env_type": "local"} + defaults = {"terminal": {"cwd": "."}, "_file_has_terminal": True} + + result = _resolve_terminal_cwd(terminal_config, defaults, env) + assert result == "/home/user/workspace" + + +class TestConfigCwdResolution: + """config.yaml terminal.cwd is the canonical source of truth.""" + + def test_explicit_config_cwd_wins(self): + """terminal.cwd: /explicit/path always wins.""" + env = {"TERMINAL_CWD": "/old/gateway/value"} + terminal_config = {"cwd": "/explicit/path"} + defaults = {"terminal": {"cwd": "/explicit/path"}, "_file_has_terminal": True} + + result = _resolve_terminal_cwd(terminal_config, defaults, env) + assert result == "/explicit/path" + + def test_dot_cwd_resolves_to_getcwd_when_no_prior(self): + """With no pre-set TERMINAL_CWD, "." resolves to os.getcwd().""" + env = {} + terminal_config = {"cwd": "."} + defaults = {"terminal": {"cwd": "."}, "_file_has_terminal": False} + + result = _resolve_terminal_cwd(terminal_config, defaults, env) + assert result == "/fake/getcwd" + + def test_remote_backend_pops_cwd(self): + """Remote backend + placeholder cwd → popped for backend default.""" + env = {} + terminal_config = {"cwd": ".", "env_type": "docker"} + defaults = {"terminal": {"cwd": "."}, "_file_has_terminal": False} + + result = _resolve_terminal_cwd(terminal_config, defaults, env) + assert result == "" # cwd popped, no env var set + + def test_remote_backend_with_prior_cwd_preserves(self): + """Remote backend + pre-resolved TERMINAL_CWD → adopted.""" + env = {"TERMINAL_CWD": "/project"} + terminal_config = {"cwd": ".", "env_type": "docker"} + defaults = {"terminal": {"cwd": "."}, "_file_has_terminal": False} + + result = _resolve_terminal_cwd(terminal_config, defaults, env) + assert result == "/project" diff --git a/tests/gateway/test_config_cwd_bridge.py b/tests/gateway/test_config_cwd_bridge.py index 1b7a1d78b..7f6a75750 100644 --- a/tests/gateway/test_config_cwd_bridge.py +++ b/tests/gateway/test_config_cwd_bridge.py @@ -37,6 +37,10 @@ def _simulate_config_bridge(cfg: dict, initial_env: dict | None = None): for cfg_key, env_var in terminal_env_map.items(): if cfg_key in terminal_cfg: val = terminal_cfg[cfg_key] + # Skip cwd placeholder values — don't overwrite already-resolved + # TERMINAL_CWD. Mirrors the fix in gateway/run.py. + if cfg_key == "cwd" and str(val) in (".", "auto", "cwd"): + continue if isinstance(val, list): env[env_var] = json.dumps(val) else: @@ -146,3 +150,58 @@ class TestTopLevelCwdAlias: cfg = {"cwd": "/from/config"} result = _simulate_config_bridge(cfg, {"MESSAGING_CWD": "/from/env"}) assert result["TERMINAL_CWD"] == "/from/config" + + +class TestNestedTerminalCwdPlaceholderSkip: + """terminal.cwd placeholder values must not clobber TERMINAL_CWD. + + When config.yaml has terminal.cwd: "." (or "auto"/"cwd"), the gateway + config bridge should NOT write that placeholder to TERMINAL_CWD. + This prevents .env or MESSAGING_CWD values from being overwritten. + See issues #10225, #4672, #10817. + """ + + def test_terminal_dot_cwd_does_not_clobber_env(self): + """terminal.cwd: '.' should not overwrite a pre-set TERMINAL_CWD.""" + cfg = {"terminal": {"cwd": "."}} + result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/my/project"}) + assert result["TERMINAL_CWD"] == "/my/project" + + def test_terminal_auto_cwd_does_not_clobber_env(self): + cfg = {"terminal": {"cwd": "auto"}} + result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/my/project"}) + assert result["TERMINAL_CWD"] == "/my/project" + + def test_terminal_cwd_keyword_does_not_clobber_env(self): + cfg = {"terminal": {"cwd": "cwd"}} + result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/my/project"}) + assert result["TERMINAL_CWD"] == "/my/project" + + def test_terminal_explicit_cwd_does_override(self): + """terminal.cwd: '/explicit/path' SHOULD override TERMINAL_CWD.""" + cfg = {"terminal": {"cwd": "/explicit/path"}} + result = _simulate_config_bridge(cfg, {"TERMINAL_CWD": "/old/value"}) + assert result["TERMINAL_CWD"] == "/explicit/path" + + def test_terminal_dot_cwd_falls_back_to_messaging_cwd(self): + """terminal.cwd: '.' with no TERMINAL_CWD should fall to MESSAGING_CWD.""" + cfg = {"terminal": {"cwd": "."}} + result = _simulate_config_bridge(cfg, {"MESSAGING_CWD": "/from/env"}) + assert result["TERMINAL_CWD"] == "/from/env" + + def test_terminal_dot_cwd_and_messaging_cwd_both_set(self): + """Pre-set TERMINAL_CWD from .env wins over terminal.cwd: '.'.""" + cfg = {"terminal": {"cwd": ".", "backend": "local"}} + result = _simulate_config_bridge(cfg, { + "TERMINAL_CWD": "/my/project", + "MESSAGING_CWD": "/fallback", + }) + assert result["TERMINAL_CWD"] == "/my/project" + + def test_non_cwd_terminal_keys_still_bridge(self): + """Other terminal config keys (backend, timeout) should still bridge normally.""" + cfg = {"terminal": {"cwd": ".", "backend": "docker", "timeout": "300"}} + result = _simulate_config_bridge(cfg, {"MESSAGING_CWD": "/from/env"}) + assert result["TERMINAL_ENV"] == "docker" + assert result["TERMINAL_TIMEOUT"] == "300" + assert result["TERMINAL_CWD"] == "/from/env" diff --git a/tests/hermes_cli/test_deprecated_cwd_warning.py b/tests/hermes_cli/test_deprecated_cwd_warning.py new file mode 100644 index 000000000..4b438e7eb --- /dev/null +++ b/tests/hermes_cli/test_deprecated_cwd_warning.py @@ -0,0 +1,64 @@ +"""Tests for warn_deprecated_cwd_env_vars() migration warning.""" + +import os +import pytest + + +class TestDeprecatedCwdWarning: + """Warn when MESSAGING_CWD or TERMINAL_CWD is set in .env.""" + + def test_messaging_cwd_triggers_warning(self, monkeypatch, capsys): + monkeypatch.setenv("MESSAGING_CWD", "/some/path") + monkeypatch.delenv("TERMINAL_CWD", raising=False) + + from hermes_cli.config import warn_deprecated_cwd_env_vars + warn_deprecated_cwd_env_vars(config={}) + + captured = capsys.readouterr() + assert "MESSAGING_CWD" in captured.err + assert "deprecated" in captured.err.lower() + assert "config.yaml" in captured.err + + def test_terminal_cwd_triggers_warning_when_config_placeholder(self, monkeypatch, capsys): + monkeypatch.setenv("TERMINAL_CWD", "/project") + monkeypatch.delenv("MESSAGING_CWD", raising=False) + + from hermes_cli.config import warn_deprecated_cwd_env_vars + # config has placeholder cwd → TERMINAL_CWD likely from .env + warn_deprecated_cwd_env_vars(config={"terminal": {"cwd": "."}}) + + captured = capsys.readouterr() + assert "TERMINAL_CWD" in captured.err + assert "deprecated" in captured.err.lower() + + def test_no_warning_when_config_has_explicit_cwd(self, monkeypatch, capsys): + monkeypatch.setenv("TERMINAL_CWD", "/project") + monkeypatch.delenv("MESSAGING_CWD", raising=False) + + from hermes_cli.config import warn_deprecated_cwd_env_vars + # config has explicit cwd → TERMINAL_CWD could be from config bridge + warn_deprecated_cwd_env_vars(config={"terminal": {"cwd": "/project"}}) + + captured = capsys.readouterr() + assert "TERMINAL_CWD" not in captured.err + + def test_no_warning_when_env_clean(self, monkeypatch, capsys): + monkeypatch.delenv("MESSAGING_CWD", raising=False) + monkeypatch.delenv("TERMINAL_CWD", raising=False) + + from hermes_cli.config import warn_deprecated_cwd_env_vars + warn_deprecated_cwd_env_vars(config={}) + + captured = capsys.readouterr() + assert captured.err == "" + + def test_both_deprecated_vars_warn(self, monkeypatch, capsys): + monkeypatch.setenv("MESSAGING_CWD", "/msg/path") + monkeypatch.setenv("TERMINAL_CWD", "/term/path") + + from hermes_cli.config import warn_deprecated_cwd_env_vars + warn_deprecated_cwd_env_vars(config={}) + + captured = capsys.readouterr() + assert "MESSAGING_CWD" in captured.err + assert "TERMINAL_CWD" in captured.err diff --git a/tests/hermes_cli/test_memory_reset.py b/tests/hermes_cli/test_memory_reset.py new file mode 100644 index 000000000..3b91326de --- /dev/null +++ b/tests/hermes_cli/test_memory_reset.py @@ -0,0 +1,157 @@ +"""Tests for the `hermes memory reset` CLI command. + +Covers: +- Reset both stores (MEMORY.md + USER.md) +- Reset individual stores (--target memory / --target user) +- Skip confirmation with --yes +- Graceful handling when no memory files exist +- Profile-scoped reset (uses HERMES_HOME) +""" + +import os +import pytest +from argparse import Namespace +from pathlib import Path + + +@pytest.fixture +def memory_env(tmp_path, monkeypatch): + """Set up a fake HERMES_HOME with memory files.""" + hermes_home = tmp_path / ".hermes" + memories = hermes_home / "memories" + memories.mkdir(parents=True) + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + + # Create sample memory files + (memories / "MEMORY.md").write_text( + "§\nHermes repo is at ~/.hermes/hermes-agent\n§\nUser prefers dark themes", + encoding="utf-8", + ) + (memories / "USER.md").write_text( + "§\nUser is Teknium\n§\nTimezone: US Pacific", + encoding="utf-8", + ) + return hermes_home, memories + + +def _run_memory_reset(target="all", yes=False, monkeypatch=None, confirm_input="no"): + """Invoke the memory reset logic from cmd_memory in main.py. + + Simulates what happens when `hermes memory reset` is run. + """ + from hermes_constants import get_hermes_home, display_hermes_home + + mem_dir = get_hermes_home() / "memories" + files_to_reset = [] + if target in ("all", "memory"): + files_to_reset.append(("MEMORY.md", "agent notes")) + if target in ("all", "user"): + files_to_reset.append(("USER.md", "user profile")) + + existing = [(f, desc) for f, desc in files_to_reset if (mem_dir / f).exists()] + if not existing: + return "nothing" + + if not yes: + if confirm_input != "yes": + return "cancelled" + + for f, desc in existing: + (mem_dir / f).unlink() + + return "deleted" + + +class TestMemoryReset: + """Tests for `hermes memory reset` subcommand.""" + + def test_reset_all_with_yes_flag(self, memory_env): + """--yes flag should skip confirmation and delete both files.""" + hermes_home, memories = memory_env + assert (memories / "MEMORY.md").exists() + assert (memories / "USER.md").exists() + + result = _run_memory_reset(target="all", yes=True) + assert result == "deleted" + assert not (memories / "MEMORY.md").exists() + assert not (memories / "USER.md").exists() + + def test_reset_memory_only(self, memory_env): + """--target memory should only delete MEMORY.md.""" + hermes_home, memories = memory_env + + result = _run_memory_reset(target="memory", yes=True) + assert result == "deleted" + assert not (memories / "MEMORY.md").exists() + assert (memories / "USER.md").exists() + + def test_reset_user_only(self, memory_env): + """--target user should only delete USER.md.""" + hermes_home, memories = memory_env + + result = _run_memory_reset(target="user", yes=True) + assert result == "deleted" + assert (memories / "MEMORY.md").exists() + assert not (memories / "USER.md").exists() + + def test_reset_no_files_exist(self, tmp_path, monkeypatch): + """Should return 'nothing' when no memory files exist.""" + hermes_home = tmp_path / ".hermes" + (hermes_home / "memories").mkdir(parents=True) + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + + result = _run_memory_reset(target="all", yes=True) + assert result == "nothing" + + def test_reset_confirmation_denied(self, memory_env): + """Without --yes and without typing 'yes', should be cancelled.""" + hermes_home, memories = memory_env + + result = _run_memory_reset(target="all", yes=False, confirm_input="no") + assert result == "cancelled" + # Files should still exist + assert (memories / "MEMORY.md").exists() + assert (memories / "USER.md").exists() + + def test_reset_confirmation_accepted(self, memory_env): + """Typing 'yes' should proceed with deletion.""" + hermes_home, memories = memory_env + + result = _run_memory_reset(target="all", yes=False, confirm_input="yes") + assert result == "deleted" + assert not (memories / "MEMORY.md").exists() + assert not (memories / "USER.md").exists() + + def test_reset_profile_scoped(self, tmp_path, monkeypatch): + """Reset should work on the active profile's HERMES_HOME.""" + profile_home = tmp_path / "profiles" / "myprofile" + memories = profile_home / "memories" + memories.mkdir(parents=True) + (memories / "MEMORY.md").write_text("profile memory", encoding="utf-8") + (memories / "USER.md").write_text("profile user", encoding="utf-8") + monkeypatch.setenv("HERMES_HOME", str(profile_home)) + + result = _run_memory_reset(target="all", yes=True) + assert result == "deleted" + assert not (memories / "MEMORY.md").exists() + assert not (memories / "USER.md").exists() + + def test_reset_partial_files(self, memory_env): + """Reset should work when only one memory file exists.""" + hermes_home, memories = memory_env + (memories / "USER.md").unlink() + + result = _run_memory_reset(target="all", yes=True) + assert result == "deleted" + assert not (memories / "MEMORY.md").exists() + + def test_reset_empty_memories_dir(self, tmp_path, monkeypatch): + """No memories dir at all should report nothing.""" + hermes_home = tmp_path / ".hermes" + hermes_home.mkdir(parents=True) + # No memories dir + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + + # The memories dir won't exist; get_hermes_home() / "memories" won't have files + result = _run_memory_reset(target="all", yes=True) + assert result == "nothing" From 77bdad5b02eca395a17118738103399c558f7017 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:49:36 -0700 Subject: [PATCH 09/19] fix(tests): resolve 12 CI failures + 10 errors across 6 root causes (#11040) Group A (3 tests): 'No LLM provider configured' RuntimeError - test_user_message_surrogates_sanitized, test_counters_initialized_in_init, test_openai_prompt_tokens_unchanged - Root cause: AIAgent.__init__ now requires base_url alongside api_key to skip resolve_provider_client() (which returns None when API keys are blanked in CI). Added base_url='http://localhost:1234/v1' to test agent construction. Group B (5 tests): Discord slash command auto-registration - test_auto_registers_missing_gateway_commands, test_auto_registered_command_*, test_register_skill_group_* - Root cause: xdist workers that loaded a discord mock WITHOUT app_commands.Command/Group caused _register_slash_commands() to fail silently. Added comprehensive shared discord mock in tests/gateway/conftest.py (same pattern as existing telegram mock). Group C (5 errors): Discord reply mode 'NoneType has no DMChannel' - All TestReplyToText tests - Root cause: FakeDMChannel was not a subclass of real discord.DMChannel, so isinstance() checks in _handle_message failed when running in full suite (real discord installed). Made FakeDMChannel inherit from discord.DMChannel when available. Removed fragile monkeypatch approach. Group D (2 tests): detect_provider_for_model wrong provider - test_openrouter_slug_match (got 'ai-gateway'), test_bare_name_gets_ openrouter_slug (got 'copilot') - Root cause: ai-gateway, copilot, and kilocode are multi-vendor aggregators that list other providers' models (OpenRouter-style slugs). They were being matched in Step 1 before OpenRouter. Added all three to _AGGREGATORS set so they're skipped like nous/openrouter. Group E (1 test): model_flow_custom StopIteration - test_model_flow_custom_saves_verified_v1_base_url - Root cause: 'Display name' prompt was added after the test was written. The input iterator had 5 answers but the flow now asks 6 questions. Added 6th empty string answer. Group F (1 test): Telegram proxy env assertion - test_uses_proxy_env_for_primary_and_fallback_transports - Root cause: _resolve_proxy_url() now checks TELEGRAM_PROXY first (via resolve_proxy_url('TELEGRAM_PROXY')). Test didn't clear this env var, allowing potential leakage from other tests in xdist workers. Added TELEGRAM_PROXY to the cleanup list. --- hermes_cli/models.py | 2 +- tests/cli/test_cli_provider_resolution.py | 2 +- tests/cli/test_surrogate_sanitization.py | 2 +- tests/gateway/conftest.py | 81 +++++++++++++++++++ tests/gateway/test_discord_reply_mode.py | 17 ++-- tests/gateway/test_telegram_network.py | 2 +- .../run_agent/test_context_token_tracking.py | 2 +- tests/run_agent/test_run_agent.py | 4 +- 8 files changed, 100 insertions(+), 12 deletions(-) diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 360055487..2129788be 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -1044,7 +1044,7 @@ def detect_provider_for_model( return (resolved_provider, default_models[0]) # Aggregators list other providers' models — never auto-switch TO them - _AGGREGATORS = {"nous", "openrouter"} + _AGGREGATORS = {"nous", "openrouter", "ai-gateway", "copilot", "kilocode"} # If the model belongs to the current provider's catalog, don't suggest switching current_models = _PROVIDER_MODELS.get(current_provider, []) diff --git a/tests/cli/test_cli_provider_resolution.py b/tests/cli/test_cli_provider_resolution.py index 9c5bf0cca..624e166a8 100644 --- a/tests/cli/test_cli_provider_resolution.py +++ b/tests/cli/test_cli_provider_resolution.py @@ -578,7 +578,7 @@ def test_model_flow_custom_saves_verified_v1_base_url(monkeypatch, capsys): # After the probe detects a single model ("llm"), the flow asks # "Use this model? [Y/n]:" — confirm with Enter, then context length, # then display name. - answers = iter(["http://localhost:8000", "local-key", "", "", ""]) + answers = iter(["http://localhost:8000", "local-key", "", "", "", ""]) monkeypatch.setattr("builtins.input", lambda _prompt="": next(answers)) monkeypatch.setattr("getpass.getpass", lambda _prompt="": next(answers)) diff --git a/tests/cli/test_surrogate_sanitization.py b/tests/cli/test_surrogate_sanitization.py index defad587e..43af7fe16 100644 --- a/tests/cli/test_surrogate_sanitization.py +++ b/tests/cli/test_surrogate_sanitization.py @@ -138,7 +138,7 @@ class TestRunConversationSurrogateSanitization: mock_stream.return_value = mock_response mock_api.return_value = mock_response - agent = AIAgent(model="test/model", quiet_mode=True, skip_memory=True, skip_context_files=True) + agent = AIAgent(model="test/model", api_key="test-key", base_url="http://localhost:1234/v1", quiet_mode=True, skip_memory=True, skip_context_files=True) agent.client = MagicMock() # Pass a message with surrogates diff --git a/tests/gateway/conftest.py b/tests/gateway/conftest.py index 5fd8d86fe..d2f55ff9f 100644 --- a/tests/gateway/conftest.py +++ b/tests/gateway/conftest.py @@ -62,5 +62,86 @@ def _ensure_telegram_mock() -> None: sys.modules["telegram.error"] = mod.error +def _ensure_discord_mock() -> None: + """Install a comprehensive discord mock in sys.modules. + + Idempotent — skips when the real library is already imported. + Uses ``sys.modules[name] = mod`` (overwrite) instead of + ``setdefault`` so it wins even if a partial/broken import already + cached the module. + + This mock is comprehensive — it includes **all** attributes needed by + every gateway discord test file. Individual test files should call + this function (it short-circuits when already present) rather than + maintaining their own mock setup. + """ + if "discord" in sys.modules and hasattr(sys.modules["discord"], "__file__"): + return # Real library is installed — nothing to mock + + from types import SimpleNamespace + + discord_mod = MagicMock() + discord_mod.Intents.default.return_value = MagicMock() + discord_mod.Client = MagicMock + discord_mod.File = MagicMock + discord_mod.DMChannel = type("DMChannel", (), {}) + discord_mod.Thread = type("Thread", (), {}) + discord_mod.ForumChannel = type("ForumChannel", (), {}) + discord_mod.Interaction = object + discord_mod.Embed = MagicMock + discord_mod.ui = SimpleNamespace( + View=object, + button=lambda *a, **k: (lambda fn: fn), + Button=object, + ) + discord_mod.ButtonStyle = SimpleNamespace( + success=1, primary=2, secondary=2, danger=3, + green=1, grey=2, blurple=2, red=3, + ) + discord_mod.Color = SimpleNamespace( + orange=lambda: 1, green=lambda: 2, blue=lambda: 3, + red=lambda: 4, purple=lambda: 5, + ) + + # app_commands — needed by _register_slash_commands auto-registration + class _FakeGroup: + def __init__(self, *, name, description, parent=None): + self.name = name + self.description = description + self.parent = parent + self._children: dict = {} + if parent is not None: + parent.add_command(self) + + def add_command(self, cmd): + self._children[cmd.name] = cmd + + class _FakeCommand: + def __init__(self, *, name, description, callback, parent=None): + self.name = name + self.description = description + self.callback = callback + self.parent = parent + + discord_mod.app_commands = SimpleNamespace( + describe=lambda **kwargs: (lambda fn: fn), + choices=lambda **kwargs: (lambda fn: fn), + Choice=lambda **kwargs: SimpleNamespace(**kwargs), + Group=_FakeGroup, + Command=_FakeCommand, + ) + + ext_mod = MagicMock() + commands_mod = MagicMock() + commands_mod.Bot = MagicMock + ext_mod.commands = commands_mod + + for name in ("discord", "discord.ext", "discord.ext.commands"): + sys.modules[name] = discord_mod + sys.modules["discord.ext"] = ext_mod + sys.modules["discord.ext.commands"] = commands_mod + + # Run at collection time — before any test file's module-level imports. _ensure_telegram_mock() +_ensure_discord_mock() diff --git a/tests/gateway/test_discord_reply_mode.py b/tests/gateway/test_discord_reply_mode.py index 8a3b440bb..0203bfab6 100644 --- a/tests/gateway/test_discord_reply_mode.py +++ b/tests/gateway/test_discord_reply_mode.py @@ -284,9 +284,20 @@ class TestEnvVarOverride: # Tests for reply_to_text extraction in _handle_message # ------------------------------------------------------------------ -class FakeDMChannel: +# Build FakeDMChannel as a subclass of the real discord.DMChannel when the +# library is installed — this guarantees isinstance() checks pass in +# production code regardless of test ordering or monkeypatch state. +try: + import discord as _discord_lib + _DMChannelBase = _discord_lib.DMChannel +except (ImportError, AttributeError): + _DMChannelBase = object + + +class FakeDMChannel(_DMChannelBase): """Minimal DM channel stub (skips mention / channel-allow checks).""" def __init__(self, channel_id: int = 100, name: str = "dm"): + # Do NOT call super().__init__() — real DMChannel requires State self.id = channel_id self.name = name @@ -309,10 +320,6 @@ def _make_message(*, content: str = "hi", reference=None): @pytest.fixture def reply_text_adapter(monkeypatch): """DiscordAdapter wired for _handle_message → handle_message capture.""" - import gateway.platforms.discord as discord_platform - - monkeypatch.setattr(discord_platform.discord, "DMChannel", FakeDMChannel, raising=False) - config = PlatformConfig(enabled=True, token="fake-token") adapter = DiscordAdapter(config) adapter._client = SimpleNamespace(user=SimpleNamespace(id=999)) diff --git a/tests/gateway/test_telegram_network.py b/tests/gateway/test_telegram_network.py index 2770211f3..ff74d4c66 100644 --- a/tests/gateway/test_telegram_network.py +++ b/tests/gateway/test_telegram_network.py @@ -322,7 +322,7 @@ class TestFallbackTransportInit: seen_kwargs.append(kwargs.copy()) return FakeTransport([], {}) - for key in ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", "https_proxy", "http_proxy", "all_proxy"): + for key in ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", "https_proxy", "http_proxy", "all_proxy", "TELEGRAM_PROXY"): monkeypatch.delenv(key, raising=False) monkeypatch.setenv("HTTPS_PROXY", "http://proxy.example:8080") monkeypatch.setattr(tnet.httpx, "AsyncHTTPTransport", factory) diff --git a/tests/run_agent/test_context_token_tracking.py b/tests/run_agent/test_context_token_tracking.py index b924448b6..6800a2b49 100644 --- a/tests/run_agent/test_context_token_tracking.py +++ b/tests/run_agent/test_context_token_tracking.py @@ -59,7 +59,7 @@ def _make_agent(monkeypatch, api_mode, provider, response_fn): self._disable_streaming = True return super().run_conversation(msg, conversation_history=conversation_history, task_id=task_id) - return _A(model="test-model", api_key="test-key", provider=provider, api_mode=api_mode) + return _A(model="test-model", api_key="test-key", base_url="http://localhost:1234/v1", provider=provider, api_mode=api_mode) def _anthropic_resp(input_tok, output_tok, cache_read=0, cache_creation=0): diff --git a/tests/run_agent/test_run_agent.py b/tests/run_agent/test_run_agent.py index 49ef1dc8f..46eec2cf7 100644 --- a/tests/run_agent/test_run_agent.py +++ b/tests/run_agent/test_run_agent.py @@ -4115,8 +4115,8 @@ class TestMemoryNudgeCounterPersistence: """Counters must exist on the agent after __init__.""" with patch("run_agent.get_tool_definitions", return_value=[]): a = AIAgent( - model="test", api_key="test-key", provider="openrouter", - skip_context_files=True, skip_memory=True, + model="test", api_key="test-key", base_url="http://localhost:1234/v1", + provider="openrouter", skip_context_files=True, skip_memory=True, ) assert hasattr(a, "_turns_since_memory") assert hasattr(a, "_iters_since_skill") From e9b3b8e820b1bbd37362a5bd38fc5dbd374fb37a Mon Sep 17 00:00:00 2001 From: Billard <82095453+iacker@users.noreply.github.com> Date: Sun, 12 Apr 2026 23:53:38 +0200 Subject: [PATCH 10/19] fix(cron): treat empty agent response as error in last_status (fixes #8585) When a cron job's agent run completes but produces an empty final_response (e.g. API 404 from invalid model name), the scheduler now marks last_status as "error" instead of "ok", so the failure is visible in job listings. Previously, any run that didn't raise an exception was marked "ok" regardless of whether the agent actually produced output. --- cron/scheduler.py | 7 ++++++ tests/cron/test_scheduler.py | 41 ++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/cron/scheduler.py b/cron/scheduler.py index 4bd5724a6..9a0f561b0 100644 --- a/cron/scheduler.py +++ b/cron/scheduler.py @@ -979,6 +979,13 @@ def tick(verbose: bool = True, adapters=None, loop=None) -> int: delivery_error = str(de) logger.error("Delivery failed for job %s: %s", job["id"], de) + # Treat empty final_response as a soft failure so last_status + # is not "ok" — the agent ran but produced nothing useful. + # (issue #8585) + if success and not final_response: + success = False + error = "Agent completed but produced empty response (model error, timeout, or misconfiguration)" + mark_job_run(job["id"], success, error, delivery_error=delivery_error) executed += 1 diff --git a/tests/cron/test_scheduler.py b/tests/cron/test_scheduler.py index a1cc2e127..160b55efc 100644 --- a/tests/cron/test_scheduler.py +++ b/tests/cron/test_scheduler.py @@ -675,7 +675,7 @@ class TestRunJobSessionPersistence: def test_run_job_empty_response_returns_empty_not_placeholder(self, tmp_path): """Empty final_response should stay empty for delivery logic (issue #2234). - + The placeholder '(No response generated)' should only appear in the output log, not in the returned final_response that's used for delivery. """ @@ -693,7 +693,7 @@ class TestRunJobSessionPersistence: patch( "hermes_cli.runtime_provider.resolve_runtime_provider", return_value={ - "api_key": "test-key", + "api_key": "***", "base_url": "https://example.invalid/v1", "provider": "openrouter", "api_mode": "chat_completions", @@ -714,6 +714,43 @@ class TestRunJobSessionPersistence: # But the output log should show the placeholder assert "(No response generated)" in output + def test_tick_marks_empty_response_as_error(self, tmp_path): + """When run_job returns success=True but final_response is empty, + tick() should mark the job as error so last_status != 'ok'. + (issue #8585) + """ + from cron.scheduler import tick + from cron.jobs import load_jobs, save_jobs + + job = { + "id": "empty-job", + "name": "empty-test", + "prompt": "do something", + "schedule": "every 1h", + "enabled": True, + "next_run_at": "2020-01-01T00:00:00", + "deliver": "local", + "last_status": None, + } + + fake_db = MagicMock() + + with patch("cron.scheduler._hermes_home", tmp_path), \ + patch("cron.scheduler.get_due_jobs", return_value=[job]), \ + patch("cron.scheduler.advance_next_run"), \ + patch("cron.scheduler.mark_job_run") as mock_mark, \ + patch("cron.scheduler.save_job_output", return_value="/tmp/out.md"), \ + patch("cron.scheduler._resolve_origin", return_value=None), \ + patch("cron.scheduler.run_job", return_value=(True, "output", "", None)): + tick(verbose=False) + + # Should be called with success=False because final_response is empty + mock_mark.assert_called_once() + call_args = mock_mark.call_args + assert call_args[0][0] == "empty-job" + assert call_args[0][1] is False # success should be False + assert "empty" in call_args[0][2].lower() # error should mention empty + def test_run_job_sets_auto_delivery_env_from_dotenv_home_channel(self, tmp_path, monkeypatch): job = { "id": "test-job", From f938fe460c228ac74c528a834f2051a41cf3dd89 Mon Sep 17 00:00:00 2001 From: Teknium Date: Thu, 16 Apr 2026 06:47:42 -0700 Subject: [PATCH 11/19] chore: add iacker to AUTHOR_MAP --- scripts/release.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/release.py b/scripts/release.py index 4d3a7ac3c..09786362d 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -180,6 +180,7 @@ AUTHOR_MAP = { "lisicheng168@gmail.com": "lesterli", "mingjwan@microsoft.com": "MagicRay1217", "orangeko@gmail.com": "GenKoKo", + "82095453+iacker@users.noreply.github.com": "iacker", "niyant@spicefi.xyz": "spniyant", "olafthiele@gmail.com": "olafthiele", "oncuevtv@gmail.com": "sprmn24", From 45fc0bd83af2cf8ac624f72f8ddf167c63b94a63 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:53:44 -0700 Subject: [PATCH 12/19] fix: UnboundLocalError on 'entry' in parallel subagent polling loop (#11050) The completion-line printing block (idx = entry['task_index'] etc.) was outside the 'for future in done:' loop but referenced 'entry' which is only assigned inside that loop. When concurrent.futures.wait() returns with an empty 'done' set (timeout expired, no futures finished), the loop body never executes and 'entry' is unbound. Moved the completion-line printing and spinner-update code inside the for loop so each completed future gets its own status line, and empty poll cycles simply loop back without accessing 'entry'. --- tools/delegate_tool.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index 87218b1ba..1b29150fb 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -806,28 +806,28 @@ def delegate_task( results.append(entry) completed_count += 1 - # Print per-task completion line above the spinner - idx = entry["task_index"] - label = task_labels[idx] if idx < len(task_labels) else f"Task {idx}" - dur = entry.get("duration_seconds", 0) - status = entry.get("status", "?") - icon = "✓" if status == "completed" else "✗" - remaining = n_tasks - completed_count - completion_line = f"{icon} [{idx+1}/{n_tasks}] {label} ({dur}s)" - if spinner_ref: - try: - spinner_ref.print_above(completion_line) - except Exception: + # Print per-task completion line above the spinner + idx = entry["task_index"] + label = task_labels[idx] if idx < len(task_labels) else f"Task {idx}" + dur = entry.get("duration_seconds", 0) + status = entry.get("status", "?") + icon = "✓" if status == "completed" else "✗" + remaining = n_tasks - completed_count + completion_line = f"{icon} [{idx+1}/{n_tasks}] {label} ({dur}s)" + if spinner_ref: + try: + spinner_ref.print_above(completion_line) + except Exception: + print(f" {completion_line}") + else: print(f" {completion_line}") - else: - print(f" {completion_line}") - # Update spinner text to show remaining count - if spinner_ref and remaining > 0: - try: - spinner_ref.update_text(f"🔀 {remaining} task{'s' if remaining != 1 else ''} remaining") - except Exception as e: - logger.debug("Spinner update_text failed: %s", e) + # Update spinner text to show remaining count + if spinner_ref and remaining > 0: + try: + spinner_ref.update_text(f"🔀 {remaining} task{'s' if remaining != 1 else ''} remaining") + except Exception as e: + logger.debug("Spinner update_text failed: %s", e) # Sort by task_index so results match input order results.sort(key=lambda r: r["task_index"]) From 5b4773fc20e844e784d1b710adaa0cfcc5e14d87 Mon Sep 17 00:00:00 2001 From: Jorge Date: Thu, 16 Apr 2026 20:03:31 +0930 Subject: [PATCH 13/19] fix: wire up Ollama Cloud dynamic model discovery in /model TUI picker provider_model_ids() and list_authenticated_providers() had no case for "ollama-cloud", so the /model slash command showed 0 models despite fetch_ollama_cloud_models() being fully implemented. The CLI subcommand worked because it called fetch_ollama_cloud_models() directly. - Add ollama-cloud case to provider_model_ids() in models.py - Populate curated dict for ollama-cloud in list_authenticated_providers() - Add tests for both code paths --- hermes_cli/model_switch.py | 4 ++ hermes_cli/models.py | 4 ++ .../hermes_cli/test_ollama_cloud_provider.py | 59 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index 5a494dc19..3395ea70d 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -807,6 +807,10 @@ def list_authenticated_providers( # "nous" shares OpenRouter's curated list if not separately defined if "nous" not in curated: curated["nous"] = curated["openrouter"] + # Ollama Cloud uses dynamic discovery (no static curated list) + if "ollama-cloud" not in curated: + from hermes_cli.models import fetch_ollama_cloud_models + curated["ollama-cloud"] = fetch_ollama_cloud_models() # --- 1. Check Hermes-mapped providers --- for hermes_id, mdev_id in PROVIDER_TO_MODELS_DEV.items(): diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 2129788be..309840aea 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -1286,6 +1286,10 @@ def provider_model_ids(provider: Optional[str], *, force_refresh: bool = False) live = _fetch_ai_gateway_models() if live: return live + if normalized == "ollama-cloud": + live = fetch_ollama_cloud_models(force_refresh=force_refresh) + if live: + return live if normalized == "custom": base_url = _get_custom_base_url() if base_url: diff --git a/tests/hermes_cli/test_ollama_cloud_provider.py b/tests/hermes_cli/test_ollama_cloud_provider.py index 9dad26092..f3702a417 100644 --- a/tests/hermes_cli/test_ollama_cloud_provider.py +++ b/tests/hermes_cli/test_ollama_cloud_provider.py @@ -114,6 +114,65 @@ class TestOllamaCloudModelCatalog: assert "ollama-cloud" in _PROVIDER_LABELS assert _PROVIDER_LABELS["ollama-cloud"] == "Ollama Cloud" + def test_provider_model_ids_returns_dynamic_models(self, tmp_path, monkeypatch): + """provider_model_ids('ollama-cloud') should call fetch_ollama_cloud_models().""" + from hermes_cli.models import provider_model_ids + + monkeypatch.setenv("HERMES_HOME", str(tmp_path)) + monkeypatch.setenv("OLLAMA_API_KEY", "test-key") + + mock_mdev = { + "ollama-cloud": { + "models": { + "qwen3.5:397b": {"tool_call": True}, + "glm-5": {"tool_call": True}, + } + } + } + with patch("hermes_cli.models.fetch_api_models", return_value=["qwen3.5:397b"]), \ + patch("agent.models_dev.fetch_models_dev", return_value=mock_mdev): + result = provider_model_ids("ollama-cloud", force_refresh=True) + + assert len(result) > 0 + assert "qwen3.5:397b" in result + + +# ── Model Picker (list_authenticated_providers) ── + +class TestOllamaCloudModelPicker: + def test_ollama_cloud_shows_model_count(self, tmp_path, monkeypatch): + """Ollama Cloud should show non-zero model count in provider picker.""" + from hermes_cli.model_switch import list_authenticated_providers + + monkeypatch.setenv("HERMES_HOME", str(tmp_path)) + monkeypatch.setenv("OLLAMA_API_KEY", "test-key") + + mock_mdev = { + "ollama-cloud": { + "models": { + "qwen3.5:397b": {"tool_call": True}, + "glm-5": {"tool_call": True}, + } + } + } + with patch("hermes_cli.models.fetch_api_models", return_value=["qwen3.5:397b"]), \ + patch("agent.models_dev.fetch_models_dev", return_value=mock_mdev): + providers = list_authenticated_providers(current_provider="ollama-cloud") + + ollama = next((p for p in providers if p["slug"] == "ollama-cloud"), None) + assert ollama is not None, "ollama-cloud should appear when OLLAMA_API_KEY is set" + assert ollama["total_models"] > 0, "ollama-cloud should show non-zero model count" + + def test_ollama_cloud_not_shown_without_creds(self, monkeypatch): + """Ollama Cloud should not appear without credentials.""" + from hermes_cli.model_switch import list_authenticated_providers + + monkeypatch.delenv("OLLAMA_API_KEY", raising=False) + + providers = list_authenticated_providers(current_provider="openrouter") + ollama = next((p for p in providers if p["slug"] == "ollama-cloud"), None) + assert ollama is None, "ollama-cloud should not appear without OLLAMA_API_KEY" + # ── Merged Model Discovery ── From 0a9229c8c68a95fe93d8498ede7ab9e5d37e7455 Mon Sep 17 00:00:00 2001 From: kshitij <82637225+kshitijk4poor@users.noreply.github.com> Date: Thu, 16 Apr 2026 07:44:41 -0700 Subject: [PATCH 14/19] chore: add salvage PR contributors to AUTHOR_MAP (#11076) Add 11 community contributors whose work was cherry-picked via salvage PRs during the April 16 triage session. Without these entries, contributor_audit strict mode fails for release attribution. Contributors: sontianye, jackjin1997, danieldoderlein, lrawnsley, taeuk178, ogzerber, cola-runner, ygd58, vominh1919, LeonSGP43, Lubrsy706 Co-authored-by: kshitijk4poor --- scripts/release.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/release.py b/scripts/release.py index 09786362d..30b72a9c7 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -181,6 +181,17 @@ AUTHOR_MAP = { "mingjwan@microsoft.com": "MagicRay1217", "orangeko@gmail.com": "GenKoKo", "82095453+iacker@users.noreply.github.com": "iacker", + "sontianye@users.noreply.github.com": "sontianye", + "jackjin1997@users.noreply.github.com": "jackjin1997", + "danieldoderlein@users.noreply.github.com": "danieldoderlein", + "lrawnsley@users.noreply.github.com": "lrawnsley", + "taeuk178@users.noreply.github.com": "taeuk178", + "ogzerber@users.noreply.github.com": "ogzerber", + "cola-runner@users.noreply.github.com": "cola-runner", + "ygd58@users.noreply.github.com": "ygd58", + "vominh1919@users.noreply.github.com": "vominh1919", + "LeonSGP43@users.noreply.github.com": "LeonSGP43", + "Lubrsy706@users.noreply.github.com": "Lubrsy706", "niyant@spicefi.xyz": "spniyant", "olafthiele@gmail.com": "olafthiele", "oncuevtv@gmail.com": "sprmn24", From 8c1276c0bf3b41503ae34386ed6d1f3c79eb8a60 Mon Sep 17 00:00:00 2001 From: lrawnsley Date: Thu, 16 Apr 2026 19:24:21 +0530 Subject: [PATCH 15/19] fix: pass resolved args to resolve_vision_provider_client() resolve_vision_provider_client() was receiving the raw call_llm parameters instead of the resolved provider/model/key/url from _resolve_task_provider_model(). This caused config overrides (auxiliary.vision.provider, etc.) to be silently discarded. Cherry-picked from #10901 by @lrawnsley. --- agent/auxiliary_client.py | 16 +++++----- tests/agent/test_vision_resolved_args.py | 40 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 tests/agent/test_vision_resolved_args.py diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index bc6b1efbe..c31ff55f9 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -2391,10 +2391,10 @@ def call_llm( if task == "vision": effective_provider, client, final_model = resolve_vision_provider_client( - provider=provider, - model=model, - base_url=base_url, - api_key=api_key, + provider=resolved_provider if resolved_provider != "auto" else provider, + model=resolved_model or model, + base_url=resolved_base_url or base_url, + api_key=resolved_api_key or api_key, async_mode=False, ) if client is None and resolved_provider != "auto" and not resolved_base_url: @@ -2599,10 +2599,10 @@ async def async_call_llm( if task == "vision": effective_provider, client, final_model = resolve_vision_provider_client( - provider=provider, - model=model, - base_url=base_url, - api_key=api_key, + provider=resolved_provider if resolved_provider != "auto" else provider, + model=resolved_model or model, + base_url=resolved_base_url or base_url, + api_key=resolved_api_key or api_key, async_mode=True, ) if client is None and resolved_provider != "auto" and not resolved_base_url: diff --git a/tests/agent/test_vision_resolved_args.py b/tests/agent/test_vision_resolved_args.py new file mode 100644 index 000000000..aace43578 --- /dev/null +++ b/tests/agent/test_vision_resolved_args.py @@ -0,0 +1,40 @@ +"""Test that call_llm vision path passes resolved provider args, not raw ones.""" + +from unittest.mock import patch, MagicMock + + +def test_vision_call_uses_resolved_provider_args(): + """Resolved provider/model/key/url from config must reach resolve_vision_provider_client.""" + from agent.auxiliary_client import call_llm + + fake_client = MagicMock() + fake_client.chat.completions.create.return_value = MagicMock( + choices=[MagicMock(message=MagicMock(content="description"))], + usage=MagicMock(prompt_tokens=10, completion_tokens=5), + ) + + with ( + patch( + "agent.auxiliary_client._resolve_task_provider_model", + return_value=("my-resolved-provider", "my-resolved-model", "http://resolved", "resolved-key", "chat_completions"), + ), + patch( + "agent.auxiliary_client.resolve_vision_provider_client", + return_value=("my-resolved-provider", fake_client, "my-resolved-model"), + ) as mock_vision, + ): + call_llm( + "vision", + provider="raw-provider", + model="raw-model", + base_url="http://raw", + api_key="raw-key", + messages=[{"role": "user", "content": "describe this"}], + ) + + # The resolved values must be passed, not the raw call_llm arguments + call_args = mock_vision.call_args + assert call_args.kwargs["provider"] == "my-resolved-provider" + assert call_args.kwargs["model"] == "my-resolved-model" + assert call_args.kwargs["base_url"] == "http://resolved" + assert call_args.kwargs["api_key"] == "resolved-key" From 31a72bdbf24ded9fe0eb854d8d389c94ae2e642e Mon Sep 17 00:00:00 2001 From: danieldoderlein Date: Thu, 16 Apr 2026 19:22:30 +0530 Subject: [PATCH 16/19] fix: escape command content in Telegram exec approval prompt Switch from fragile Markdown V1 to HTML parse mode with html.escape() for exec approval messages. Add fallback to text-based approval when the formatted send fails. Cherry-picked from #10999 by @danieldoderlein. --- gateway/platforms/telegram.py | 12 +++++------- gateway/run.py | 9 +++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index d5578961c..2f4ec9329 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -11,6 +11,7 @@ import asyncio import json import logging import os +import html as _html import re from typing import Dict, List, Optional, Any @@ -1129,13 +1130,10 @@ class TelegramAdapter(BasePlatformAdapter): try: cmd_preview = command[:3800] + "..." if len(command) > 3800 else command - # Escape backticks that would break Markdown v1 inline code parsing - safe_cmd = cmd_preview.replace("`", "'") - safe_desc = description.replace("`", "'").replace("*", "∗") text = ( - f"⚠️ *Command Approval Required*\n\n" - f"`{safe_cmd}`\n\n" - f"Reason: {safe_desc}" + f"⚠️ Command Approval Required\n\n" + f"
{_html.escape(cmd_preview)}
\n\n" + f"Reason: {_html.escape(description)}" ) # Resolve thread context for thread replies @@ -1163,7 +1161,7 @@ class TelegramAdapter(BasePlatformAdapter): kwargs: Dict[str, Any] = { "chat_id": int(chat_id), "text": text, - "parse_mode": ParseMode.MARKDOWN, + "parse_mode": ParseMode.HTML, "reply_markup": keyboard, **self._link_preview_kwargs(), } diff --git a/gateway/run.py b/gateway/run.py index 7517fdabd..7e4cc26a7 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -8816,7 +8816,7 @@ class GatewayRunner: # false positives from MagicMock auto-attribute creation in tests. if getattr(type(_status_adapter), "send_exec_approval", None) is not None: try: - asyncio.run_coroutine_threadsafe( + _approval_result = asyncio.run_coroutine_threadsafe( _status_adapter.send_exec_approval( chat_id=_status_chat_id, command=cmd, @@ -8826,7 +8826,12 @@ class GatewayRunner: ), _loop_for_step, ).result(timeout=15) - return + if _approval_result.success: + return + logger.warning( + "Button-based approval failed (send returned error), falling back to text: %s", + _approval_result.error, + ) except Exception as _e: logger.warning( "Button-based approval failed, falling back to text: %s", _e From 896e7b03e8b745a5380fda5bc2e9377e0cc95e9c Mon Sep 17 00:00:00 2001 From: taeuk178 Date: Thu, 16 Apr 2026 19:24:22 +0530 Subject: [PATCH 17/19] fix(run_agent): prevent _create_openai_client from mutating caller kwargs Shallow-copy client_kwargs at the top of _create_openai_client() to prevent in-place mutation from leaking back into self._client_kwargs. Defensive fix that locks the contract for future httpx/transport work. Cherry-picked from #10978 by @taeuk178. --- run_agent.py | 9 +++++ ...t_create_openai_client_kwargs_isolation.py | 35 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/run_agent/test_create_openai_client_kwargs_isolation.py diff --git a/run_agent.py b/run_agent.py index 3707e943e..f6c67b109 100644 --- a/run_agent.py +++ b/run_agent.py @@ -4340,6 +4340,15 @@ class AIAgent: def _create_openai_client(self, client_kwargs: dict, *, reason: str, shared: bool) -> Any: from agent.auxiliary_client import _validate_base_url, _validate_proxy_env_urls + # Treat client_kwargs as read-only. Callers pass self._client_kwargs (or shallow + # copies of it) in; any in-place mutation leaks back into the stored dict and is + # reused on subsequent requests. #10933 hit this by injecting an httpx.Client + # transport that was torn down after the first request, so the next request + # wrapped a closed transport and raised "Cannot send a request, as the client + # has been closed" on every retry. The revert resolved that specific path; this + # copy locks the contract so future transport/keepalive work can't reintroduce + # the same class of bug. + client_kwargs = dict(client_kwargs) _validate_proxy_env_urls() _validate_base_url(client_kwargs.get("base_url")) if self.provider == "copilot-acp" or str(client_kwargs.get("base_url", "")).startswith("acp://copilot"): diff --git a/tests/run_agent/test_create_openai_client_kwargs_isolation.py b/tests/run_agent/test_create_openai_client_kwargs_isolation.py new file mode 100644 index 000000000..506e1486c --- /dev/null +++ b/tests/run_agent/test_create_openai_client_kwargs_isolation.py @@ -0,0 +1,35 @@ +"""Guardrail: _create_openai_client must not mutate its input kwargs. + +#10933 injected an httpx.Client directly into the caller's ``client_kwargs``. +When the dict was ``self._client_kwargs``, the shared transport was torn down +after the first request_complete close and subsequent request-scoped clients +wrapped a closed transport, raising ``APIConnectionError('Connection error.')`` +with cause ``RuntimeError: Cannot send a request, as the client has been closed`` +on every retry. That PR has since been reverted, but the underlying issue +(#10324, connections hanging in CLOSE-WAIT) is still open, so another transport +tweak inside this function is likely. This test pins the contract that the +function must treat its input dict as read-only. +""" +from unittest.mock import MagicMock, patch + +from run_agent import AIAgent + + +@patch("run_agent.OpenAI") +def test_create_openai_client_does_not_mutate_input_kwargs(mock_openai): + mock_openai.return_value = MagicMock() + agent = AIAgent( + model="test/model", + quiet_mode=True, + skip_context_files=True, + skip_memory=True, + ) + + kwargs = {"api_key": "test-key", "base_url": "https://api.example.com/v1"} + snapshot = dict(kwargs) + + agent._create_openai_client(kwargs, reason="test", shared=False) + + assert kwargs == snapshot, ( + f"_create_openai_client mutated input kwargs; expected {snapshot}, got {kwargs}" + ) From f5ac025714aac956b47feae07fbc75ec399b0fb7 Mon Sep 17 00:00:00 2001 From: jackjin1997 Date: Thu, 16 Apr 2026 19:22:38 +0530 Subject: [PATCH 18/19] fix(gateway): guard pending_event.channel_prompt against None in recursive _run_agent Initialize next_channel_prompt before the pending_event check and use getattr with None default, matching the existing pattern for next_source/next_message/next_message_id. Prevents AttributeError when pending_event is None (interrupt path). Cherry-picked from #10953 by @jackjin1997. --- gateway/run.py | 4 ++- tests/gateway/test_pending_event_none.py | 42 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/gateway/test_pending_event_none.py diff --git a/gateway/run.py b/gateway/run.py index 7e4cc26a7..68efed918 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -9461,6 +9461,7 @@ class GatewayRunner: next_source = source next_message = pending next_message_id = None + next_channel_prompt = None if pending_event is not None: next_source = getattr(pending_event, "source", None) or source next_message = await self._prepare_inbound_message_text( @@ -9471,6 +9472,7 @@ class GatewayRunner: if next_message is None: return result next_message_id = getattr(pending_event, "message_id", None) + next_channel_prompt = getattr(pending_event, "channel_prompt", None) # Restart typing indicator so the user sees activity while # the follow-up turn runs. The outer _process_message_background @@ -9494,7 +9496,7 @@ class GatewayRunner: session_key=session_key, _interrupt_depth=_interrupt_depth + 1, event_message_id=next_message_id, - channel_prompt=pending_event.channel_prompt, + channel_prompt=next_channel_prompt, ) finally: # Stop progress sender, interrupt monitor, and notification task diff --git a/tests/gateway/test_pending_event_none.py b/tests/gateway/test_pending_event_none.py new file mode 100644 index 000000000..b2e1356fa --- /dev/null +++ b/tests/gateway/test_pending_event_none.py @@ -0,0 +1,42 @@ +"""Tests for the pending_event None guard in recursive _run_agent calls. + +When pending_event is None (Path B: pending comes from interrupt_message), +accessing pending_event.channel_prompt previously raised AttributeError. +This verifies the fix: channel_prompt is captured inside the +`if pending_event is not None:` block and falls back to None otherwise. +""" + +from types import SimpleNamespace + + +def _extract_channel_prompt(pending_event): + """Reproduce the fixed logic from gateway/run.py. + + Mirrors the variable-capture pattern used before the recursive + _run_agent call so we can test both paths without a full runner. + """ + next_channel_prompt = None + if pending_event is not None: + next_channel_prompt = getattr(pending_event, "channel_prompt", None) + return next_channel_prompt + + +class TestPendingEventNoneChannelPrompt: + """Guard against AttributeError when pending_event is None.""" + + def test_none_pending_event_returns_none_channel_prompt(self): + """Path B: pending_event is None — must not raise AttributeError.""" + result = _extract_channel_prompt(None) + assert result is None + + def test_pending_event_with_channel_prompt_passes_through(self): + """Path A: pending_event present — channel_prompt is forwarded.""" + event = SimpleNamespace(channel_prompt="You are a helpful bot.") + result = _extract_channel_prompt(event) + assert result == "You are a helpful bot." + + def test_pending_event_without_channel_prompt_returns_none(self): + """Path A: pending_event present but has no channel_prompt attribute.""" + event = SimpleNamespace() + result = _extract_channel_prompt(event) + assert result is None From f19ca50cd9f605387f3dc5a0fc1ef601c0c37a1f Mon Sep 17 00:00:00 2001 From: sontianye Date: Thu, 16 Apr 2026 19:22:22 +0530 Subject: [PATCH 19/19] fix(context_compressor): always keep last user message in tail to prevent active-task loss Ensure _align_boundary_backward never pushes the last user message into the compressed region. Without this, compression could delete the user active task instruction mid-session. Cherry-picked from #10969 by @sontianye. Fixes #10896. --- agent/context_compressor.py | 82 ++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/agent/context_compressor.py b/agent/context_compressor.py index ac5db7762..34ec5091b 100644 --- a/agent/context_compressor.py +++ b/agent/context_compressor.py @@ -39,7 +39,10 @@ SUMMARY_PREFIX = ( "into the summary below. This is a handoff from a previous context " "window — treat it as background reference, NOT as active instructions. " "Do NOT answer questions or fulfill requests mentioned in this summary; " - "they were already addressed. Respond ONLY to the latest user message " + "they were already addressed. " + "Your current task is identified in the '## Active Task' section of the " + "summary — resume exactly from there. " + "Respond ONLY to the latest user message " "that appears AFTER this summary. The current session state (files, " "config, etc.) may reflect work described here — avoid repeating it:" ) @@ -581,8 +584,16 @@ class ContextCompressor(ContextEngine): ) # Shared structured template (used by both paths). - _template_sections = f"""## Goal -[What the user is trying to accomplish] + _template_sections = f"""## Active Task +[THE SINGLE MOST IMPORTANT FIELD. Copy the user's most recent request or +task assignment verbatim — the exact words they used. If multiple tasks +were requested and only some are done, list only the ones NOT yet completed. +The next assistant must pick up exactly here. Example: +"User asked: 'Now refactor the auth module to use JWT instead of sessions'" +If no outstanding task exists, write "None."] + +## Goal +[What the user is trying to accomplish overall] ## Constraints & Preferences [User preferences, coding style, constraints, important decisions] @@ -644,7 +655,7 @@ PREVIOUS SUMMARY: NEW TURNS TO INCORPORATE: {content_to_summarize} -Update the summary using this exact structure. PRESERVE all existing information that is still relevant. ADD new completed actions to the numbered list (continue numbering). Move items from "In Progress" to "Completed Actions" when done. Move answered questions to "Resolved Questions". Update "Active State" to reflect current state. Remove information only if it is clearly obsolete. +Update the summary using this exact structure. PRESERVE all existing information that is still relevant. ADD new completed actions to the numbered list (continue numbering). Move items from "In Progress" to "Completed Actions" when done. Move answered questions to "Resolved Questions". Update "Active State" to reflect current state. Remove information only if it is clearly obsolete. CRITICAL: Update "## Active Task" to reflect the user's most recent unfulfilled request — this is the most important field for task continuity. {_template_sections}""" else: @@ -862,6 +873,62 @@ The user has requested that this compaction PRIORITISE preserving all informatio # Tail protection by token budget # ------------------------------------------------------------------ + def _find_last_user_message_idx( + self, messages: List[Dict[str, Any]], head_end: int + ) -> int: + """Return the index of the last user-role message at or after *head_end*, or -1.""" + for i in range(len(messages) - 1, head_end - 1, -1): + if messages[i].get("role") == "user": + return i + return -1 + + def _ensure_last_user_message_in_tail( + self, + messages: List[Dict[str, Any]], + cut_idx: int, + head_end: int, + ) -> int: + """Guarantee the most recent user message is in the protected tail. + + Context compressor bug (#10896): ``_align_boundary_backward`` can pull + ``cut_idx`` past a user message when it tries to keep tool_call/result + groups together. If the last user message ends up in the *compressed* + middle region the LLM summariser writes it into "Pending User Asks", + but ``SUMMARY_PREFIX`` tells the next model to respond only to user + messages *after* the summary — so the task effectively disappears from + the active context, causing the agent to stall, repeat completed work, + or silently drop the user's latest request. + + Fix: if the last user-role message is not already in the tail + (``messages[cut_idx:]``), walk ``cut_idx`` back to include it. We + then re-align backward one more time to avoid splitting any + tool_call/result group that immediately precedes the user message. + """ + last_user_idx = self._find_last_user_message_idx(messages, head_end) + if last_user_idx < 0: + # No user message found beyond head — nothing to anchor. + return cut_idx + + if last_user_idx >= cut_idx: + # Already in the tail; nothing to do. + return cut_idx + + # The last user message is in the middle (compressed) region. + # Pull cut_idx back to it directly — a user message is already a + # clean boundary (no tool_call/result splitting risk), so there is no + # need to call _align_boundary_backward here; doing so would + # unnecessarily pull the cut further back into the preceding + # assistant + tool_calls group. + if not self.quiet_mode: + logger.debug( + "Anchoring tail cut to last user message at index %d " + "(was %d) to prevent active-task loss after compression", + last_user_idx, + cut_idx, + ) + # Safety: never go back into the head region. + return max(last_user_idx, head_end + 1) + def _find_tail_cut_by_tokens( self, messages: List[Dict[str, Any]], head_end: int, token_budget: int | None = None, @@ -879,7 +946,8 @@ The user has requested that this compaction PRIORITISE preserving all informatio read, etc.). If even the minimum 3 messages exceed 1.5x the budget the cut is placed right after the head so compression still runs. - Never cuts inside a tool_call/result group. + Never cuts inside a tool_call/result group. Always ensures the most + recent user message is in the tail (see ``_ensure_last_user_message_in_tail``). """ if token_budget is None: token_budget = self.tail_token_budget @@ -918,6 +986,10 @@ The user has requested that this compaction PRIORITISE preserving all informatio # Align to avoid splitting tool groups cut_idx = self._align_boundary_backward(messages, cut_idx) + # Ensure the most recent user message is always in the tail so the + # active task is never lost to compression (fixes #10896). + cut_idx = self._ensure_last_user_message_in_tail(messages, cut_idx, head_end) + return max(cut_idx, head_end + 1) # ------------------------------------------------------------------