Commit Graph

15 Commits

Author SHA1 Message Date
Teknium 50a13d53eb fix(goal): the judge can WAIT on delegated subagents (4 of 5 nudges in one run re-poked an agent that was waiting on workers)
JUDGE_SYSTEM_PROMPT allowed WAIT only for "a background process listed
below" or a stated backoff. A fan-out orchestrator's in-flight work is
delegated subagents, which are not registry processes, so when the agent
said "waiting on 4 worker batches, nothing to dispatch" the judge answered
CONTINUE ("no background process is listed to gate on"). In the
1,393-agent run 4 of 5 nudges fired 6-153 s after exactly such a turn and
each bought a status recap: 19 API calls at ~450K context, ~$4.9, and no
work a batch-complete notification would not have produced.

The judge now receives an "Active delegations: N batch(es) still running"
line (count of live async_delegation records whose parent_session_id is
this session, via the existing _session_records selector) and a WAIT
branch for it: wait_for_seconds 600-1800 when the response says it is
waiting on them with nothing dispatchable. Both goal-loop callers (CLI,
gateway) pass the count.

Live A/B with the real auxiliary judge on the run's response shape
("4 worker batches still running ... nothing else is dispatchable until
these return"): main 3/3 CONTINUE, branch 3/3 WAIT 1200 s.

Tests (2): the prompt carries the delegations line only when N > 0 and
a wait verdict parses through; count_active_delegations is scoped to the
spawning session and ignores completed records.
2026-09-05 06:24:06 -07:00
Teknium 1f3912d277 fix(goal): the TUI judge also sees only its own session's processes; a lifted barrier resumes the loop from the idle hook
Independent review found two gaps in the first fix. The TUI/Desktop/dashboard
goal path (tui_gateway/prompt_turn.py) still passed every agent's processes to
the judge; it now filters by the session's own key like the CLI and gateway
loops. And the 30-minute barrier cap (and a pid/timer barrier lifting at all)
was only checked lazily, on the next turn, so a parked session with nothing
else arriving stayed parked. The CLI idle hook now checks the barrier every
5 s while idle and queues the goal's continuation the moment it lifts.

Tests (2 new): an elapsed timed barrier queues the continuation from the idle
hook and clears the barrier; an unparked or inactive goal is a no-op.
2026-09-05 06:23:55 -07:00
Teknium f8b87f5637 fix(goal): the judge only sees the goal's own background processes, and a pid/session wait barrier expires after 30 min
In a fan-out run the /goal loop parked for 3 h 22 min at the end on a
grandchild's poller (waiting_on_session=proc_21a6fe2369a1, 00:47 -> 04:09)
while the root itself had nothing running. Every one of the root's 7 logged
judge verdicts was WAIT on a child-owned process.

Two causes. gather_background_processes() was called with no task_id from
both goal-loop callers (CLI cli_loops_mixin, gateway run_goals), and the
registry's task_id is the CONTAINER key, which collapses to one value for
every agent in the process, so the judge's process list was every one of
~1,300 subagents' pollers. And a pid/session barrier had no ceiling: once
the judge said WAIT on a session that never exits, nothing resumed judging;
waiting_since was recorded and never read.

Now list_sessions() reports owner_task_id (the RAW spawning id the
registry already keeps for ownership checks), gather_background_processes
takes owner_task_id and both callers pass their own session id (CLI turns
register processes under self.session_id; gateway turns under
turn_ctx.session_id), and is_waiting() ages out a pid/session barrier
after _MAX_BARRIER_WAIT_S (30 min). Timed barriers keep their own deadline.

Tests: only the owning session's running processes are returned when
owner_task_id is given (unfiltered behaviour unchanged); a live barrier
older than the ceiling clears and judging resumes.
2026-09-05 00:30:19 -07:00
kshitijk4poor 872be057e1 refactor(cli): class-level disabled_toolsets default instead of per-site getattr
Inspection surfaces run on partially built HermesCLI instances (tests use
HermesCLI.__new__), so the attribute needs a default; declare it once on
the class next to _seeded_first_message rather than getattr at five sites.
2026-09-05 12:21:28 +05:30
aydnOktay e2d738f89d fix(cli): guard disabled_toolsets on partial CLI instances
show_banner/_show_status call get_tool_definitions with disabled_toolsets,
but test and early-init paths build HermesCLI via __new__ without running
__init__, so self.disabled_toolsets was missing and CI failed with
AttributeError in test_cli_context_warning.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-05 12:21:28 +05:30
aydnOktay 2494eadb39 fix(tools): align inspection surfaces with disabled composite toolsets
hermes tools --summary and CLI /tools used name-level disabled_toolsets subtraction, so disabling debugging still showed terminal/web/file as enabled while runtime stripped those tools. Prune platform toolsets after tool-level subtraction and pass disabled_toolsets into CLI get_tool_definitions calls.

Fixes #97015

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-05 12:21:28 +05:30
Teknium eeb7671e69 simplify(compat): hermes_cli small facades — drop 7 re-exports/aliases (+relay_runtime alias module), repoint 12 callers/tests 2026-09-03 13:05:57 -07:00
Teknium e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium c6f75a32ec refactor(hermes_cli): unify resume state restore, fold trivial locals and module docstrings in g6 files 2026-09-02 22:42:56 -07:00
Teknium 48ef059a6c refactor(hermes_cli): dedupe decision-message printing, width-probe loop, plugin dedupe via dict 2026-09-02 22:35:44 -07:00
Teknium da6991f8f5 refactor(hermes_cli): extract recap-entry collection, compact docstrings, squeeze intra-function blanks across g6 files 2026-09-02 22:27:53 -07:00
Teknium baa22490c5 refactor(hermes_cli): fold hanging import/call spans in cli_loops_mixin and cli_terminal_mixin 2026-09-02 20:44:42 -07:00
Teknium d60e8ef6e8 refactor(hermes_cli): unify session-bound manager getters, flatten /title and /clear branches in cli_loops_mixin 2026-09-02 20:22:23 -07:00
Teknium 79aeb22886 refactor(cli): unify duplicated model-switch staging and last-assistant-text blocks
cli_model_switch_mixin: the identical snapshot->stage->agent.switch_model->rollback
block in _apply_model_switch_result and _confirm_and_apply_cli_model_switch (2x47
lines) -> _stage_and_swap_model(result, old_model) -> bool. cli_loops_mixin: the
identical reversed-history assistant-text extraction in the loop-tick and goal
post-turn hooks -> _last_assistant_response_text(). Four _StubCLI test doubles bind
the new helper to the real implementation.
2026-09-02 16:17:31 -07:00
Teknium eb74a00c71 refactor(cli): split HermesCLI into 10 cohesive mixins (cli.py 22,284 -> 9,150)
326 methods lifted by AST (bodies identical; ast.dump-verified) into
hermes_cli/cli_{tui,status_bar,voice,model_switch,session,stream,modal,
terminal,info,loops}_mixin.py. cli.py-internal symbols resolve via lazy
'from cli import ...' inside each method (no import cycle; patch('cli.X')
keeps working). The three 'global' writers (_skill_commands, _cli_wake_owner)
now write the cli module attribute explicitly so the origin's readers still
see them. Dropped imports left unused in cli.py; kept display_hermes_home /
build_welcome_banner as re-exports (mixins + tests resolve them via cli).
Repointed two AST change-detector tests to cli_tui_mixin.py; one test
fixture now keeps 'cli' in sys.modules across its patch.dict scope.
2026-09-02 15:42:24 -07:00