Add support for Atropos Agentic RL environments (requires branch tool…#17
Conversation
…_call_support in Atropos atm) - Added new environments for reinforcement learning, including `HermesSweEnv` for software engineering tasks and `TerminalTestEnv` for inline testing. - Introduced `ToolContext` for unrestricted access to tools during reward computation. - Updated `.gitignore` to exclude `wandb/` directory. - Enhanced `README.md` with detailed architecture and usage instructions for Atropos environments. - Added configuration files for SWE and terminal test environments to streamline setup. - Removed unnecessary compiled Python files from `__pycache__`.
|
QA Evidence Update — 2026-04-06 QuickScan run confirms Mixed Content on additional pages beyond Dashboard:
All 8 pages affected:
Root cause: Frontend calls API at http://leksikon.ai/api/v1/... instead of https://leksikon.ai/api/v1/... |
|
QA Evidence — Quick Scan 2026-04-06 Confirmed Mixed Content errors on /app/dashboard: Same pattern also found on: /app/inbox, /app/inbox/entities, /app/knowledge-graph Mode: quick_scan |
|
QA Evidence — 2026-04-06 (evening) Pattern: All API endpoints use |
|
QA Evidence — 2026-04-06 (evening) Pattern: All API endpoints use http://leksikon.ai/api/v1/* instead of https://. |
|
QA evidence — 2026-04-06 18:30 CET New Mixed Content errors confirmed across additional pages:
Root cause: frontend is hardcoded to use |
|
QA Evidence — 2026-04-06T18:56:17+00:00 (QUICK_SCAN) Mixed Content confirmed on ALL pages (1-8) visited over HTTPS. Error pattern: Additional Mixed Content endpoints found:
Additional errors on specific pages:
|
|
QA Run — 2026-04-06 21:00 UTC Scanned all 8 pages. Mixed Content errors confirmed on:
New finding: Settings → Team tab also shows 403 on teammedlemmer endpoint. Issue #8 confirmed. |
|
Fixed as of 2026-04-06. Root cause: Docker image was rebuilt with stale frontend build cached (frontend-builder stage used cached npm build from a version that still had http://). The correct deployment process requires copying frontend/dist to backend/public before Docker build, but the Docker build was running npm run build inside the container while the host's backend/public was a different (older) copy. Fix applied: Rebuilt Docker image with --no-cache to force fresh npm build, which now uses https://leksikon.ai in all API calls. Verified on all 8 pages (dashboard, inbox, entities, knowledge-graph, advisories, cvr, cold-outreach, settings) — zero Mixed Content errors. |
|
QA evidence — 2026-04-07T00:43:55+00:00 Quick Scan 2026-04-07T00:40+02:00 — Mixed Content still occurring across ALL pages:
All advisories/ API calls blocked by browser. Data not loading on affected pages. |
…ilience (NousResearch#17) Merged CrazySerGo's fixes with conflict resolution: - Root redirect → /dashboard - Error boundary in __root.tsx - window.process polyfill for SSR hydration - WebSocket handshake timeout + retry logic - Terminal: platform-aware shell defaults (zsh on macOS, bash on Linux, powershell on Windows) - Terminal: os.homedir() fallback - Dynamic gateway URL in vite proxy and debug-analyzer - Loading indicator for chat history - Icon size normalization Removed unnecessary chromium-bidi dependency. Kept SSR externals for playwright and pty-helper copy plugin.
…+ apt-cacher-ng' (NousResearch#17) from fix/ai-review-opencode into main
Two follow-ups to the cherry-picked PR #9873 (`e3bcc819`): 1. `_is_allowed_user` now uses `getattr(self, '_allowed_*_ids', set())` so test fixtures that build the adapter via `object.__new__` (skipping __init__) don't crash with AttributeError. See AGENTS.md pitfall #17 — same pattern as gateway.run. 2. New 3-case regression coverage in test_discord_bot_auth_bypass.py: - role-only config bypasses the gateway 'no allowlists' branch - roles + users combined still authorizes user-allowlist matches - the role bypass does NOT leak to other platforms (Telegram, etc.) 3. Autouse fixture in test_discord_bot_auth_bypass.py clears all Discord auth env vars before each test so DISCORD_ALLOWED_ROLES leakage from a previous test in the session can't flip later 'should-reject' tests into false-pass. Required because the bare cherry-pick of #9873 only added the adapter- level role check — it didn't cover the gateway-level _is_user_authorized, which still rejected role-only setups via the 'no allowlists configured' branch.
Two follow-ups to the cherry-picked PR #9873 (`e3bcc819`): 1. `_is_allowed_user` now uses `getattr(self, '_allowed_*_ids', set())` so test fixtures that build the adapter via `object.__new__` (skipping __init__) don't crash with AttributeError. See AGENTS.md pitfall #17 — same pattern as gateway.run. 2. New 3-case regression coverage in test_discord_bot_auth_bypass.py: - role-only config bypasses the gateway 'no allowlists' branch - roles + users combined still authorizes user-allowlist matches - the role bypass does NOT leak to other platforms (Telegram, etc.) 3. Autouse fixture in test_discord_bot_auth_bypass.py clears all Discord auth env vars before each test so DISCORD_ALLOWED_ROLES leakage from a previous test in the session can't flip later 'should-reject' tests into false-pass. Required because the bare cherry-pick of #9873 only added the adapter- level role check — it didn't cover the gateway-level _is_user_authorized, which still rejected role-only setups via the 'no allowlists configured' branch.
- entry.tsx no longer writes bootBanner() to the main screen before the alt-screen enters. The <Banner> renders inside the alt screen via the seeded intro row, so nothing is lost — just the flash that preceded it. Fixes the torn first frame reported on Alacritty (blitz row 5 #17) and shaves the 'starting agent' hang perception (row 5 #1) since the UI paints straight into the steady-state view - AlternateScreen prefixes ERASE_SCROLLBACK (\x1b[3J) to its entry so strict emulators start from a pristine grid; named constants replace the inline sequences for clarity - bootBanner.ts deleted — dead code
…gent Add support for Atropos Agentic RL environments (requires branch tool…
Two follow-ups to the cherry-picked PR NousResearch#9873 (`e3bcc819`): 1. `_is_allowed_user` now uses `getattr(self, '_allowed_*_ids', set())` so test fixtures that build the adapter via `object.__new__` (skipping __init__) don't crash with AttributeError. See AGENTS.md pitfall NousResearch#17 — same pattern as gateway.run. 2. New 3-case regression coverage in test_discord_bot_auth_bypass.py: - role-only config bypasses the gateway 'no allowlists' branch - roles + users combined still authorizes user-allowlist matches - the role bypass does NOT leak to other platforms (Telegram, etc.) 3. Autouse fixture in test_discord_bot_auth_bypass.py clears all Discord auth env vars before each test so DISCORD_ALLOWED_ROLES leakage from a previous test in the session can't flip later 'should-reject' tests into false-pass. Required because the bare cherry-pick of NousResearch#9873 only added the adapter- level role check — it didn't cover the gateway-level _is_user_authorized, which still rejected role-only setups via the 'no allowlists configured' branch.
- entry.tsx no longer writes bootBanner() to the main screen before the alt-screen enters. The <Banner> renders inside the alt screen via the seeded intro row, so nothing is lost — just the flash that preceded it. Fixes the torn first frame reported on Alacritty (blitz row 5 NousResearch#17) and shaves the 'starting agent' hang perception (row 5 NousResearch#1) since the UI paints straight into the steady-state view - AlternateScreen prefixes ERASE_SCROLLBACK (\x1b[3J) to its entry so strict emulators start from a pristine grid; named constants replace the inline sequences for clarity - bootBanner.ts deleted — dead code
Two follow-ups to the cherry-picked PR NousResearch#9873 (`e3bcc819`): 1. `_is_allowed_user` now uses `getattr(self, '_allowed_*_ids', set())` so test fixtures that build the adapter via `object.__new__` (skipping __init__) don't crash with AttributeError. See AGENTS.md pitfall NousResearch#17 — same pattern as gateway.run. 2. New 3-case regression coverage in test_discord_bot_auth_bypass.py: - role-only config bypasses the gateway 'no allowlists' branch - roles + users combined still authorizes user-allowlist matches - the role bypass does NOT leak to other platforms (Telegram, etc.) 3. Autouse fixture in test_discord_bot_auth_bypass.py clears all Discord auth env vars before each test so DISCORD_ALLOWED_ROLES leakage from a previous test in the session can't flip later 'should-reject' tests into false-pass. Required because the bare cherry-pick of NousResearch#9873 only added the adapter- level role check — it didn't cover the gateway-level _is_user_authorized, which still rejected role-only setups via the 'no allowlists configured' branch.
- entry.tsx no longer writes bootBanner() to the main screen before the alt-screen enters. The <Banner> renders inside the alt screen via the seeded intro row, so nothing is lost — just the flash that preceded it. Fixes the torn first frame reported on Alacritty (blitz row 5 NousResearch#17) and shaves the 'starting agent' hang perception (row 5 #1) since the UI paints straight into the steady-state view - AlternateScreen prefixes ERASE_SCROLLBACK (\x1b[3J) to its entry so strict emulators start from a pristine grid; named constants replace the inline sequences for clarity - bootBanner.ts deleted — dead code
… success log) (#18761) * fix(gateway): config.yaml wins over .env for agent/display/timezone settings Regression from the silent config→env bridge. The bridge at module import time is correct for max_turns (unconditional overwrite), but every other agent.*, display.*, timezone, and security bridge key was guarded by 'if X not in os.environ' — so a stale .env entry from an old 'hermes setup' run would shadow the user's current config.yaml indefinitely. Symptom: agent.max_turns: 500 in config.yaml, HERMES_MAX_ITERATIONS=60 in .env from an old setup, and the gateway silently capped at 60 iterations per turn. Gateway logs confirmed api_calls never exceeded 60. Three changes: 1. gateway/run.py: drop the 'not in os.environ' guards for all agent.*, display.*, timezone, and security.* bridge keys. config.yaml is now authoritative for these settings — same semantics already in place for max_turns, terminal.*, and auxiliary.*. Also surface the bridge failure (previously 'except Exception: pass') to stderr so operators see bridge errors instead of silently falling back to .env. 2. gateway/run.py: INFO-log the resolved max_iterations at gateway start so operators can verify the config→env bridge did the right thing instead of chasing a phantom budget ceiling. 3. hermes_cli/setup.py: stop writing HERMES_MAX_ITERATIONS to .env in the setup wizard. config.yaml is the single source of truth. Also clean up any stale .env entry left behind by pre-fix setups. Regression tests in tests/gateway/test_config_env_bridge_authority.py guard each config→env key against the 'stale .env shadows config' bug. * fix(gateway): shutdown + restart hygiene (drain timeout, false-fatal, success log) Three issues observed in production gateway.log during a rapid restart chain on 2026-05-02, all fixed here. 1. _send_restart_notification logged unconditional success adapter.send() catches provider errors (e.g. Telegram 'Chat not found') and returns SendResult(success=False); it never raises. The caller ignored the return value and always logged 'Sent restart notification to <chat>' at INFO, producing a misleading success line directly below the 'Failed to send Telegram message' traceback on every boot. Now inspects result.success and logs WARNING with the error otherwise. 2. WhatsApp bridge SIGTERM on shutdown classified as fatal error _check_managed_bridge_exit() saw the bridge's returncode -15 (our own SIGTERM from disconnect()) and fired the full fatal-error path, producing 'ERROR ... WhatsApp bridge process exited unexpectedly' plus 'Fatal whatsapp adapter error (whatsapp_bridge_exited)' on every planned shutdown, immediately before the normal '✓ whatsapp disconnected'. Adds a _shutting_down flag that disconnect() sets before the terminate, and _check_managed_bridge_exit() returns None for returncode in {0, -2, -15} while shutting down. OOM-kill (137) and other non-signal exits still hit the fatal path. 3. restart_drain_timeout default 60s → 180s On 2026-05-02 01:43:27 a user /restart fired while three agents were mid-API-call (82s, 112s, 154s into their turns). The 60s drain budget expired and all three were force-interrupted. 180s covers realistic in-flight agent turns; users on very-long-reasoning models can still raise it further via agent.restart_drain_timeout in config.yaml. Existing explicit user values are preserved by deep-merge. Tests - tests/gateway/test_restart_notification.py: two new tests assert INFO is only logged on SendResult(success=True) and WARNING with the error string is logged on SendResult(success=False). - tests/gateway/test_whatsapp_connect.py: parametrized test for returncode in {0, -2, -15} proves shutdown-time exits are suppressed; separate test proves returncode 137 (SIGKILL/OOM) still surfaces as fatal even when _shutting_down is set. - _check_managed_bridge_exit() reads _shutting_down via getattr-with- default so existing _make_adapter() test helpers that bypass __init__ (pitfall #17 in AGENTS.md) keep working unmodified.
🔴 RED TEAM AUDIT COMPLETE — 2026-05-09CRITICAL (0 outstanding) | HIGH (0 outstanding) | MEDIUM (3) | LOW (2) Vulnerabilities found and patched
Outstanding (principal review required)
Adversarial test results201 pass, 9 expected skips, 0 fail
Baseline comparison
Full report: |
… tests _adapter_enforces_own_access_policy accessed self.adapters directly, but several auth tests build a bare GatewayRunner via object.__new__ without setting .adapters (pitfalls.md #17). Read it defensively with getattr so a missing/empty adapter map means "no adapter owns the policy" instead of raising AttributeError. Fixes 4 tests: test_feishu_bot_auth_bypass, test_discord_bot_auth_bypass (x2), test_signal::test_signal_in_allowlist_maps.
… tests _adapter_enforces_own_access_policy accessed self.adapters directly, but several auth tests build a bare GatewayRunner via object.__new__ without setting .adapters (pitfalls.md NousResearch#17). Read it defensively with getattr so a missing/empty adapter map means "no adapter owns the policy" instead of raising AttributeError. Fixes 4 tests: test_feishu_bot_auth_bypass, test_discord_bot_auth_bypass (x2), test_signal::test_signal_in_allowlist_maps.
… tests _adapter_enforces_own_access_policy accessed self.adapters directly, but several auth tests build a bare GatewayRunner via object.__new__ without setting .adapters (pitfalls.md NousResearch#17). Read it defensively with getattr so a missing/empty adapter map means "no adapter owns the policy" instead of raising AttributeError. Fixes 4 tests: test_feishu_bot_auth_bypass, test_discord_bot_auth_bypass (x2), test_signal::test_signal_in_allowlist_maps.
…wns through chokepoint Follow-up to #53791 addressing review feedback: the footgun checker treated capture_output=/stdout=/stderr=/check_output as proof a subprocess can't pop a Windows console. That invariant is false — stream redirection controls where a child's output goes, not whether a console is allocated. From a console-less parent (Desktop/Electron, pythonw.exe, detached gateway/cron) a console-subsystem child still flashes a window even when fully captured. - check-windows-footguns.py: capture/redirect/check_output is no longer a blanket safe-pass. Added _WINDOWS_FLASHING_PROGRAMS (git/gh/npm/node/python/uv/ffmpeg/ docker/powershell/…); calls to those are flagged even when captured. Non-flashing programs keep the capture exemption (no 271-site noise). _subprocess_compat.run/ popen calls are inherently safe (wrapper injects CREATE_NO_WINDOW). - Routed the 35 genuine flashing git/gh/npm/uv/ffmpeg/docker spawns through the _subprocess_compat.run/popen chokepoint (Brooklyn's wrapper from #53810) — the durable fix, not per-site annotations. cmd.exe /c start stays # ok (intentional). - Updated tests + CONTRIBUTING.md rule #17 to the corrected invariant.
…wns through chokepoint (#53829) Follow-up to #53791 addressing review feedback: the footgun checker treated capture_output=/stdout=/stderr=/check_output as proof a subprocess can't pop a Windows console. That invariant is false — stream redirection controls where a child's output goes, not whether a console is allocated. From a console-less parent (Desktop/Electron, pythonw.exe, detached gateway/cron) a console-subsystem child still flashes a window even when fully captured. - check-windows-footguns.py: capture/redirect/check_output is no longer a blanket safe-pass. Added _WINDOWS_FLASHING_PROGRAMS (git/gh/npm/node/python/uv/ffmpeg/ docker/powershell/…); calls to those are flagged even when captured. Non-flashing programs keep the capture exemption (no 271-site noise). _subprocess_compat.run/ popen calls are inherently safe (wrapper injects CREATE_NO_WINDOW). - Routed the 35 genuine flashing git/gh/npm/uv/ffmpeg/docker spawns through the _subprocess_compat.run/popen chokepoint (Brooklyn's wrapper from #53810) — the durable fix, not per-site annotations. cmd.exe /c start stays # ok (intentional). - Updated tests + CONTRIBUTING.md rule #17 to the corrected invariant.
…t pattern disconnect() reads self._post_connect_task, but several tests build a bare TelegramAdapter via object.__new__() without calling __init__ (which sets the attr). Use getattr(..., None) so disconnect() works on those instances too (pitfall #17).
…t pattern disconnect() reads self._post_connect_task, but several tests build a bare TelegramAdapter via object.__new__() without calling __init__ (which sets the attr). Use getattr(..., None) so disconnect() works on those instances too (pitfall #17).
… fail-closed flip Follow-up to the salvaged fail-closed defaults. The own-policy default flip (open -> pairing) and the email dispatch-level deny broke sibling tests across the suite that relied on the old fail-open behavior: - test_email.py: dispatch-mechanics tests now opt into EMAIL_ALLOW_ALL_USERS (they test formatting/attachments/threading, not authz); the two auth contract tests are rewritten to assert the new fail-closed behavior (no allowlist + no allow-all => sender dropped at the adapter). - test_whatsapp_cloud.py / test_whatsapp_formatting.py / test_whatsapp_from_owner.py: autouse fixture opts into WHATSAPP_ALLOW_ALL_USERS so dm_policy: open dispatch-mechanics tests still flow (open now requires an explicit allow-all opt-in, SECURITY.md 2.6). - _adapter_for_source: use getattr for source.platform/profile so bare SimpleNamespace test fixtures without .profile don't crash the busy/queue ingress path (AGENTS.md pitfall #17). Full tests/gateway/ + yuanbao pipeline: 8555 passed, 0 failed.
… fail-closed flip Follow-up to the salvaged fail-closed defaults. The own-policy default flip (open -> pairing) and the email dispatch-level deny broke sibling tests across the suite that relied on the old fail-open behavior: - test_email.py: dispatch-mechanics tests now opt into EMAIL_ALLOW_ALL_USERS (they test formatting/attachments/threading, not authz); the two auth contract tests are rewritten to assert the new fail-closed behavior (no allowlist + no allow-all => sender dropped at the adapter). - test_whatsapp_cloud.py / test_whatsapp_formatting.py / test_whatsapp_from_owner.py: autouse fixture opts into WHATSAPP_ALLOW_ALL_USERS so dm_policy: open dispatch-mechanics tests still flow (open now requires an explicit allow-all opt-in, SECURITY.md 2.6). - _adapter_for_source: use getattr for source.platform/profile so bare SimpleNamespace test fixtures without .profile don't crash the busy/queue ingress path (AGENTS.md pitfall #17). Full tests/gateway/ + yuanbao pipeline: 8555 passed, 0 failed.
… fail-closed flip Follow-up to the salvaged fail-closed defaults. The own-policy default flip (open -> pairing) and the email dispatch-level deny broke sibling tests across the suite that relied on the old fail-open behavior: - test_email.py: dispatch-mechanics tests now opt into EMAIL_ALLOW_ALL_USERS (they test formatting/attachments/threading, not authz); the two auth contract tests are rewritten to assert the new fail-closed behavior (no allowlist + no allow-all => sender dropped at the adapter). - test_whatsapp_cloud.py / test_whatsapp_formatting.py / test_whatsapp_from_owner.py: autouse fixture opts into WHATSAPP_ALLOW_ALL_USERS so dm_policy: open dispatch-mechanics tests still flow (open now requires an explicit allow-all opt-in, SECURITY.md 2.6). - _adapter_for_source: use getattr for source.platform/profile so bare SimpleNamespace test fixtures without .profile don't crash the busy/queue ingress path (AGENTS.md pitfall #17). Full tests/gateway/ + yuanbao pipeline: 8555 passed, 0 failed.
Two follow-ups to the cherry-picked PR NousResearch#9873 (`e3bcc819`): 1. `_is_allowed_user` now uses `getattr(self, '_allowed_*_ids', set())` so test fixtures that build the adapter via `object.__new__` (skipping __init__) don't crash with AttributeError. See AGENTS.md pitfall NousResearch#17 — same pattern as gateway.run. 2. New 3-case regression coverage in test_discord_bot_auth_bypass.py: - role-only config bypasses the gateway 'no allowlists' branch - roles + users combined still authorizes user-allowlist matches - the role bypass does NOT leak to other platforms (Telegram, etc.) 3. Autouse fixture in test_discord_bot_auth_bypass.py clears all Discord auth env vars before each test so DISCORD_ALLOWED_ROLES leakage from a previous test in the session can't flip later 'should-reject' tests into false-pass. Required because the bare cherry-pick of NousResearch#9873 only added the adapter- level role check — it didn't cover the gateway-level _is_user_authorized, which still rejected role-only setups via the 'no allowlists configured' branch.
…gent Add support for Atropos Agentic RL environments (requires branch tool…
- entry.tsx no longer writes bootBanner() to the main screen before the alt-screen enters. The <Banner> renders inside the alt screen via the seeded intro row, so nothing is lost — just the flash that preceded it. Fixes the torn first frame reported on Alacritty (blitz row 5 NousResearch#17) and shaves the 'starting agent' hang perception (row 5 #1) since the UI paints straight into the steady-state view - AlternateScreen prefixes ERASE_SCROLLBACK (\x1b[3J) to its entry so strict emulators start from a pristine grid; named constants replace the inline sequences for clarity - bootBanner.ts deleted — dead code
…_call_support in Atropos atm)
HermesSweEnvfor software engineering tasks andTerminalTestEnvfor inline testing.ToolContextfor unrestricted access to tools during reward computation..gitignoreto excludewandb/directory.README.mdwith detailed architecture and usage instructions for Atropos environments.__pycache__.