Conversation
|
Hermes automated repair (pr-repair-steward) Resolved the pinned-base merge conflicts in agent/egress_source_annotations.py and tests/agent/test_llm_egress_firewall.py, preserving annotation comment visibility and credential-default scanning. Focused check: ./scripts/run_tests.sh tests/agent/test_llm_egress_firewall.py (143 passed). Repair commit: 9b4259a. |
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
Hermes automated repair (ci-general-fixer) Removed the trailing blank line that caused Commit: Merge remains gated and no CI or safety gate was relaxed. |
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Lowering the session trigger must not replace the window-relative lean selection budget with threshold times target_ratio. Invalidate the lean cache through the existing property while preserving explicit legacy and external-engine fallback behavior. Narrow adaptation of the aux-sync diagnosis and invariants in NousResearch#93576, without adding a required recalibration method to context engines. Related: NousResearch#95681, NousResearch#93576 Co-authored-by: Turgut Kural <58116817+TurgutKural@users.noreply.github.com>
Add Move up/down controls for actual rooms without changing bot or folder ordering. Preserve default pin/activity ordering until an explicit move, retain hidden room slots, and persist Desktop-local order through room updates, mirror merges, and hydration. No membership or routing writes. Adapted narrowly from the group ordering idea in archived NousResearch/Hermes-Bot-Mode#105 by @onuraycicek; rename already exists. Co-authored-by: Onur Aycicek <onur.m.aycicek@gmail.com>
Port Adolanium's focused-turn pose from Hermes-Bot-Mode#101 and hermes-agent#88134 to the current typed Bot Mode implementation. Match the busy signal's connection-qualified focused owner rather than the gateway socket, retain worker activity, and ease transitions in elapsed time on the existing shared face clock. Includes owner-isolation and animated-pose invariants, both proven red on origin/main, and native Electron before/after verification against a real temporary Hermes backend with held loopback inference. Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
…tention-salvage fix(bot-mode): clear resolved Group Chat attention and bind prompts across rename
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Bumps the uv group with 1 update in the / directory: [httpx2](https://github.com/pydantic/httpx2). Updates `httpx2` from 2.7.0 to 2.12.0 - [Release notes](https://github.com/pydantic/httpx2/releases) - [Changelog](https://github.com/pydantic/httpx2/blob/main/src/httpx2/CHANGELOG.md) - [Commits](pydantic/httpx2@v2.7.0...v2.12.0) --- updated-dependencies: - dependency-name: httpx2 dependency-version: 2.12.0 dependency-type: direct:production dependency-group: uv ... Signed-off-by: dependabot[bot] <support@github.com>
…e full supported Python range test_encode_fingerprint_has_a_stable_literal_value() hardcoded a single expected hex digest, but ast.dump(annotate_fields=True) started omitting default-valued fields (e.g. arguments(posonlyargs=[], ...) -> arguments()) starting in Python 3.12, changing the normalized string encode_fingerprint() hashes. requires-python allows 3.11-3.13, straddling that cutover: CI runs 3.11 (needing one literal), this dev machine runs 3.13 (needing the other) -- a single hardcoded value could only ever be correct on one side. Verified both literals directly (uv run --python 3.11 / 3.12 / 3.13) and select the expected value by sys.version_info, so the test passes on the whole supported range instead of only the interpreter whoever last edited it happened to run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e at call time _media_delivery_allowed_roots() only ever returned cache roots from MEDIA_DELIVERY_SAFE_ROOTS, a module-level constant frozen at import time from _HERMES_HOME = get_hermes_home() -- a profile switch after this module's import left the PREVIOUS profile's cache dirs as the only allowlisted media-delivery destination, silently failing delivery for anything the new active profile generated. Added _active_profile_cache_roots(), calling get_hermes_home() at request time instead of relying on the import-time snapshot, and included it in _media_delivery_allowed_roots()'s return list alongside the existing static/per-profile/kanban/operator sources. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r bootstrap step hermes_bootstrap.py's own trailing comment says "entry points only need import hermes_bootstrap first", and every other bootstrap function (apply_windows_utf8_bootstrap, suppress_platform_ver_console, activate_durable_lazy_target) is applied there -- but harden_import_path() was left out, so a foreign utils.py in the launch cwd could still shadow Hermes's own utils module for any importer that didn't explicitly call it (only 3 of the 4+ entry points did). Idempotent (repositions the same root each call), so the 3 existing explicit call sites are harmless now-redundant belt-and-braces. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…telegram topic test The test's switch_session mock fixture and assertion used task_owned_cwd=/conversation_worktree= -- a signature switch_session() never has on this branch (gateway/session.py's actual signature is conversation_kind=/persisted_cwd=). The production call site (GatewayRunner._process_handoff) already calls it correctly; only the test's stale kwargs needed updating. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…id, not 0 stream_events() defaulted an absent `since` query param to 0 via _int_param(), so a fresh Desktop WebSocket connection replayed the ENTIRE task_events ledger on connect instead of streaming only new events. Added _EventTail.baseline() (SELECT MAX(id)) and use it as the starting cursor when `since` is absent; an explicit `since` (including "0", a client deliberately resuming from the start) is still honored. Moved the cursor resolution inside the existing try/except so a cancellation during baselining is handled the same way as cancellation during a normal poll. The test's own connect mock patched the wrong module attribute (kanban_db.connect, a deprecated compat forwarding shim, instead of kbc.connect -- the reference _EventTail actually calls, matching every other test in this file); fixed to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
hermes_state_holders.py's module split (sqlite_sidecar_identity(), deleted_sqlite_sidecar_holders()) duplicated hermes_state_dbfile.py's older _stat_sqlite_sidecar_identity()/iter_deleted_sqlite_sidecar_holders() -- the same /proc/<pid>/fd deleted-WAL scan and os.stat-based identity snapshot, reimplemented rather than reused, with the newer functions never wired into SessionDB's actual open path (refuse_deleted_wal_generation(), called before every sqlite3.connect() specifically to stop a second opener from minting a replacement WAL inode over a still-held deleted generation). Verified equivalence before delegating: hermes_state_holders. sqlite_sidecar_identity()'s inline os.stat + dev/ino-truthy check is the same logic as hermes_state_common.stat_db_file_identity() (which the old function called per-suffix), and deleted_sqlite_sidecar_holders(include_self=True) (the default) matches iter_deleted_sqlite_sidecar_holders()'s always-include-self contract exactly. Made the two hermes_state_dbfile.py functions (kept, since iter_deleted_sqlite_sidecar_holders is an external plugin-compat pointer target) delegate to hermes_state_holders instead of reimplementing the scan, so SessionDB's real open path now exercises the newer module and there's one scan implementation instead of two that could silently drift apart. No production callers changed their observable behavior; verified against the full hermes_state and zeroed_state_db suites plus real-SessionDB-opening smoke tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…to ready block_task() always parked a needs_input block behind the human-only blocked/triage lanes, even for legacy github-pr-feedback intake cards using needs_input to mean "start validation" -- now role-owned machine work per recompute_ready's auto_triage docstring. That recovery path only fires once a task is already stuck in triage (after hitting the block-loop recurrence limit), so a first-time block never reached it. Wire the same _is_machine_recoverable_pr_feedback_triage() check into block_task() itself: when it matches, skip normal block/triage routing and return the task straight to ready with a machine_handoff event, so the dispatcher can retry it immediately instead of waiting on a human. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…eason _should_skip_fallback_candidate() and try_activate_fallback() accepted a FailoverReason but never special-cased the two reasons that need it: - egress_policy_blocked: the local privacy firewall already rejected the outbound payload, so retrying the same request against another remote provider cannot succeed and only produces noisy false provider failures. _fallback_destination_class()/classify_destination() already existed to answer "is this candidate local?" but nothing called them; now egress-blocked failover skips any non-local (non-loopback, non-local-process) candidate and only activates a local fallback. - unsupported_thinking: the selected model itself lacks thinking capability, a property of the model config a remote fallback chain can't fix, so no candidate is walked at all (matches the classifier's should_fallback=False intent for this specific reason). Also fills in the missing "egress_policy_blocked" -> "local egress policy blocked the request" label in _FALLBACK_REASON_LABELS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nt default test_worktree_add_without_base_uses_remote_default failed only in CI, not locally. The fixture's bare `origin.git` was created with plain `git init --bare`, so its HEAD symref followed the runner's ambient init.defaultBranch -- unset on some CI images, where git's legacy built-in default is "master". Since the fixture only ever pushes main/feature, a "master"-pointing HEAD symref is dangling, so `git remote show origin`'s "HEAD branch:" line resolves to "(unknown)" and resolve_worktree_base() falls through to using local HEAD (the just-checked-out parked-feature branch) instead of origin's default branch -- exactly the mismatch the test caught. Pin the bare repo's initial branch to "main" explicitly (matching the clone's own `-b main`) so the fixture no longer depends on the runner's git config. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ckup writes cron/jobs.py: _epoch_file_age() clamped any negative age (stamp in the future) to 0.0, the same value a genuinely fresh heartbeat produces. A restored/replayed ticker_heartbeat file or gross clock skew therefore read as "just ticked" instead of "cannot determine liveness" -- the exact false-positive a liveness check must not produce. Now an age more than 1s in the future (small skew still tolerated) returns None instead of a clamped 0.0. hermes_cli/backup.py: _write_full_zip_backup_locked() (the unattended automatic-backup path) called `list(_iter_backup_files(...))` before opening the archive, materializing the entire home directory tree in memory before writing a single zip entry. Stream the walk generator straight into _write_zip_entries() instead (peeking one entry up front to preserve the empty/no-files-to-back-up short circuit), so each directory's files are archived before the walk descends into the next. The interactive `hermes backup` CLI path (_run_backup_locked) keeps its eager list — it prints an upfront file count for progress UX, which the unattended path has no use for. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ersation
_handle_voice_channel_input() routed every transcript from an allowlisted
fast-lane speaker into the tool-free private lane, with no check on what
the transcript actually said -- an explicit work request spoken into a
fast-lane voice channel ("fix the voice delay", "run the tests and
commit the patch") would silently lose task capability instead of being
acted on, indistinguishable from idle chat ("can you hear me?").
Add _voice_fast_lane_requests_work() (a verb-based heuristic matching
inspect/fix/run/commit/write/edit/... operation verbs) and gate the
fast-lane branch on it: casual conversation still gets a private,
tool-free turn, but an explicit operation keeps full task capability
even from a fast-lane speaker.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…er call
agent/kanban_stop.py defined successful_kanban_terminal_transition() --
matching the current tool batch against a durable {"ok": true}
kanban_complete/kanban_block result -- but nothing in the conversation
loop ever called it. A kanban worker that finished its task therefore
kept looping: run_tool_round() had no way to recognize the terminal
transition, so it fell through to "continue" and burned another API
call after the task was already durably complete.
Wire the check into run_tool_round() right after tool execution/
persistence succeeds (matching the function's own contract: call only
after the executor has persisted every tool-result row). On a match,
end the turn with turn_exit_reason="kanban_terminal_transition" instead
of continuing to the next iteration. final_response is normalized to ""
(not left None) so the finalizer's `completed` computation reads this as
a clean completion rather than a turn stuck mid-flight on a pending tool
result.
tests/run_agent/test_tool_call_incremental_persistence.py: the target
test's fabricated HERMES_KANBAN_TASK/RUN_ID have no real board row, so
_touch_activity's auto-heartbeat/comment-injection bridges
(heartbeat_current_worker_from_env / inject_new_comments_from_env) would
reach for a real kanban board -- either false-positive "lease lost" and
hard-interrupt the turn, or block waiting on a real DB connection.
Stub both bridges to no-ops; they are unrelated to what this test
exercises (kanban_complete tool-call persistence ordering).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ride ee39240 bumped website/package.json's js-yaml override from 4.3.1 to 4.3.2 (GHSA-2883-xcg3-v3hh) but only refreshed the root/apps/desktop lockfiles -- website/package-lock.json still pinned js-yaml@4.3.1, so `npm ci` failed closed with EUSAGE ("lock file's js-yaml@4.3.1 does not satisfy js-yaml@4.3.2") in the Docs Site CI job. Refreshed via `npm install --package-lock-only` inside website/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nts shutdown test_stream_events_closes_connection_when_cancelled failed intermittently in CI (never locally) with an unhandled asyncio.CancelledError surfacing from `await task`, traced to plugin_api.py's `finally: await tail.shutdown()`. A `finally` block's own exceptions are never caught by its enclosing try's `except` clauses -- so when cancelling a task that's mid-await on a `run_in_executor()` future (baseline/poll) races with that future's underlying thread completing, asyncio can deliver a second CancelledError at the very next await checkpoint, landing unguarded inside the `finally`. The existing `except asyncio.CancelledError: return` sibling already treats cancellation as normal shutdown; extend that same treatment to the cleanup call itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The prior fix (0478d5e) stopped the crash but left the connection leaking: CI showed close_calls==0 after a cancelled stream. Once task.cancel() has landed on stream_events()'s task (the common path -- shutdown() runs from its `finally`), an internal `await run_in_executor(...)` inside shutdown() can be skipped by asyncio before _close() ever runs, even though the executor thread itself can't actually be interrupted. Replace the await with a blocking `executor.submit(...).result(timeout)` call: a coroutine with no internal await point can't be interrupted by task cancellation mid-body, so the connection is guaranteed to close exactly once regardless of any pending cancellation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… suite apps/desktop :: check:test:ui was crashing outright in CI four consecutive runs (445-650s, never producing a single test-file result or assertion failure -- just silence, then a bare FAIL from the task runner). Vitest's default pool sizing (one thread per CPU) has no memory cap, and each thread pays its own jsdom environment for this suite; on a memory-constrained CI runner that plausibly exceeds the available budget and the whole process is killed before anything can report. Capped poolOptions.threads.maxThreads to 4 for the ui project, trading some wall-clock time for staying inside the runner's memory budget. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…olOptions
The prior fix used poolOptions.threads.{max,min}Threads, deprecated and
inert in Vitest 4.1.10 (installed here) -- CI's deprecation warning
confirmed the value was silently ignored, so the crash persisted. Use
the current top-level maxWorkers/minWorkers, which replaced
poolOptions across all pool types in the 4.x migration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The test docblock was updated to say "The bot row's only activation side
effect is opening" and the warm tests were flipped to assert warmProfile /
warmAgent are NOT called on pointerEnter, but the implementation was never
updated to match.
Root cause: the warm() function and onPointerEnter={warm} were left in
bot-row.tsx after the tests were updated to document the new no-warm
contract. A roster can contain hundreds of rows and can reflow under a
stationary pointer, so pointer-entry warming behaves like roster-wide
warming in practice and must not start profile backends.
Fix: delete the warm() function and the onPointerEnter prop entirely.
Verified: bot-row.test.tsx 6/6 pass locally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Thread 1 (plugins_discovery): exclude github-pr-feedback from bundled auto-discovery per plugins/AGENTS.md vendor SaaS connector policy. Thread 2 (egress split): extract terminal classifier functions and constants from llm_egress_runtime.py into new llm_egress_terminal.py before adding github-pr-feedback behavior. Reduces the god-file by ~2,150 lines; runtime re-imports all moved symbols. Thread 3 (session.py _supports_conversation_worktree): broaden the platform allowlist to accept any non-None SessionSource, not just the two originally hard-coded values. Thread 4 (session.py _conversation_worktree_manager): route to the profile-specific database via _db_for_key(session_key) instead of always using the ambient self._db. Thread 5 (session-sweep.ts): extend the Bot Chat hide sweep to cover profile-wide sessions (bot-to-bot/CLI flows) on load/reconnect via reconcileAllBotProfileSessions(), in addition to the existing group-chat-known-id sweep. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Thread PRRT_kwDOT_eOos6gzdSH (dispatcher-readiness.ts): - Treat HTTP 404 from disabled Kanban plugin as non-blocking (returns status: 'disabled' instead of throwing DispatcherReadinessError). Thread PRRT_kwDOT_eOos6gzdSU (dispatcher-readiness.test.ts): - Replace text-scan test (reads main.ts as raw string) with a behavioral test for runDispatcherReadinessGate() that verifies advancePhase fires before the readiness check, and a 404 test case. Thread PRRT_kwDOT_eOos6g03Mi (secure_worker.py): - Include symlinks in rglob inventory: path.is_file() or path.is_symlink(). Thread PRRT_kwDOT_eOos6g03Mb (secure_worker.py): - Deny 'hooks' and 'hooks_auto_accept' keys in secure-worker audit profiles. Thread PRRT_kwDOT_eOos6g03M0 (supply-chain-audit.yml): - Detect changed lock-file directories and run npm audit per directory. Thread PRRT_kwDOT_eOos6g2OkE (federation.py): - Write toolsets to platform_toolsets["cli"] in addition to toolsets. Thread PRRT_kwDOT_eOos6g03Ms (federation.py): - Snapshot config/identity files before refresh; roll back on exception. Thread PRRT_kwDOT_eOos6g2OkK (federation.py): - Use canonical manifest _role_to_groups map to replace stale memberships. Thread PRRT_kwDOT_eOos6g2OkG (specialist_routing.py): - Wire CapabilityRegistry.is_profile_declared() into route decisions. Thread PRRT_kwDOT_eOos6g4YKr (worktree_environment.py + test): - Add _platform kwarg to _venv_python_path; use it in tests instead of monkeypatching sys.platform, making both branches testable on any host. Thread PRRT_kwDOT_eOos6g03MJ (world-sync.ts): - publishEvents now merges incoming events into the current projection instead of clobbering it with emptyProjection(). Add getProjection() to WorldSyncSink; store it in storeWorldSyncSink(). Thread PRRT_kwDOT_eOos6g03MS (index.tsx + world-sync.ts): - Guard refreshWorldProjection with optional isCancelled predicate; index.tsx effect sets cancelled=true on cleanup to discard stale fetches. Thread PRRT_kwDOT_eOos6g03MX (world-actions.ts): - Route inspect/inspect_blocker/show_source through context.inspect door when provided; report as unavailable otherwise instead of silently succeeding. Thread PRRT_kwDOT_eOos6g03Mn (discord/adapter.py): - Check remaining configured users before disconnecting from voice channel. Thread PRRT_kwDOT_eOos6g4YKu (model_performance_router + llm_egress_runtime): - Add install_performance_route_table() to llm_egress_runtime; _route_for_agent consults the compiled table when agent.performance_surface is set. - GatewayRunner._init_runtime_settings loads artifact from agent.performance_route_artifact config key at startup. - End-to-end tests verify _route_for_agent uses the installed table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Body was at class-method def level (8 spaces) rather than inside the def (12 spaces), causing ruff to raise invalid-syntax and blocking CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gistry The method was indented at 8 spaces (inside is_profile_declared()) rather than 4 spaces (class level), so it was unreachable dead code. The previous commit re-indented the body but left the def at the wrong level, causing AttributeError in all resolve() call-sites. Dedent the entire method by 4 spaces so it is a proper CapabilityRegistry instance method. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: P1 review findings from the PR-feedback stack merges
…tation-delivery-20260905
૮ >ﻌ< ა ci reviewran on d80f69a — Merge branch 'codex/fork-upstream-sync-20260901' into codex/ ❌ Job failuresCheck contributors / check-attribution · View jobJob Check contributors / check-attribution failed. OSV scan / Scan lockfiles / osv-scan · View jobJob OSV scan / Scan lockfiles / osv-scan failed. Python lints / Windows footguns (blocking) · View jobJob Python lints / Windows footguns (blocking) failed.
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
💡 Codex ReviewWhen verified Python source contains AGENTS.md reference: AGENTS.md:L66-L67 In the desktop Vitest suite, this reads AGENTS.md reference: AGENTS.md:L407-L413 These real process/ AGENTS.md reference: AGENTS.md:L379-L384 This assertion can fail whenever a loaded CI worker pauses the event loop for 80 ms after the foreground request has already been granted correctly; it measures scheduler availability rather than queue ordering. The surrounding AGENTS.md reference: AGENTS.md:L345-L348 hermes-agent/plugins/web/perplexity/plugin.yaml Lines 1 to 3 in a0d2387 This adds a connector for the third-party Perplexity Search SaaS directly under the bundled AGENTS.md reference: plugins/AGENTS.md:L23-L28 hermes-agent/plugins/image_gen/meta-ai/plugin.yaml Lines 1 to 3 in a0d2387 The new Meta Model API image backend is another vendor SaaS integration bundled into the core repository, so maintaining changes to Meta's endpoint and authentication becomes part of Hermes releases. The scoped plugin policy requires new third-party-product integrations to live in standalone plugin repositories rather than under AGENTS.md reference: plugins/AGENTS.md:L23-L28 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
PR #22 still rejects verified source containing
api_key: str | None = Noneas a secret. This companion fix recognizes built-in Python annotations and literalNonedefaults only in grant-bound source, while retaining credential values, quoted strings, presentation metadata, and the existing policy for unbound text. Outbound source is unchanged; only the scan copy is transformed.Based on #22 at
c68ecb99a785cdbf311ada73c389989ecaf16717. The reported function-argument annotation alone already passes at that head; the empty annotated credential default was reproduced assecret_detectedbefore this fix.Validation:
scripts/run_tests.sh tests/agent/test_llm_egress_firewall.py— 140 passed. Raw and numbered source presentation accept empty defaults and reject real credential defaults.git diff --checkpasses. No hosted-CI or merge-readiness claim: fork Actions are disabled. This draft targets #22's branch and does not modify its existing head.