Skip to content

fix(toolsets): restore fork-custom messaging + moa toolsets dropped by parity merge - #121

Merged
Kyzcreig merged 3 commits into
mainfrom
fix/restore-fork-messaging-moa-toolsets
Jun 30, 2026
Merged

Kyzcreig merged 3 commits into
mainfrom
fix/restore-fork-messaging-moa-toolsets

Conversation

@Kyzcreig

Copy link
Copy Markdown
Collaborator

Problem

The 2026-06-29 upstream parity merge (PR #119) took upstream's toolsets.py structure, which silently dropped two fork-only toolsets:

Post-merge, resolve_toolset('messaging') and ('moa') returned [] with no error, so on deploy Apollo would silently lose both tools across cli/telegram/discord. Caught by the config-migration dry-run's post-migration toolset validation (not by the test suite — hence the new guard below).

Fix — restore the fork features

  • toolsets.py: re-add messaging + moa (marked FORK-CUSTOM with a comment so a future merge preserves them)
  • tools/mixture_of_agents_tool.py: restored (self-registers moa via registry.register)
  • agent/display.py: mixture_of_agents formatter + arg-key
  • model_tools.py: moa_tools legacy alias
  • acp_adapter/tools.py: mixture_of_agents in polished ACP tools
  • hermes_cli/tools_config.py: messaging+moa UI rows, moa default-off + OPENROUTER_API_KEY requirement
  • hermes_cli/config.py: OPENROUTER_API_KEY tools list (+ mixture_of_agents)
  • hermes_cli/tips.py: mixture_of_agents tip

E2E regression guard (the "catch it next time" ask)

tests/agent/test_fork_custom_toolsets.py — a hard CI gate that fails if either fork toolset is dropped again. Proven: 6/6 pass on this branch, 6/6 fail on the broken pre-restore tree. Hardened against the editable-install live-tree leak (TRAP 1 from docs/sync/review/ci-gate-traps.md) so a deleted module can't false-pass via a sibling install.

Verification

  • resolve_toolset parity restored: messaging→1, moa→1 (matches pre-merge live tree exactly)
  • Re-ran the config-migration dry-run against all real fleet configs with this code: messaging/moa warnings gone, both resolve >0 post-migration
  • Neighbor suites green: 156 (toolsets/tools_config/validation) + 234 (display/acp) + 6 (new guard)
  • gitleaks 8.18.4 clean; all changed files py_compile clean

Unblocks the staged fleet deploy of the parity merge.

…y upstream parity merge

The 2026-06-29 upstream parity merge (PR #119) took upstream's toolsets.py
structure, which silently dropped two fork-only toolsets:

  * messaging -> send_message (agent-callable; the fork's origin-routing work
    #71/#83 is built on it). Upstream deliberately omits an agent-callable
    send_message; this fork re-enables it.
  * moa -> mixture_of_agents (tools/mixture_of_agents_tool.py, fully deleted).

Post-merge resolve_toolset('messaging')/('moa') returned [] with no error, so
on deploy Apollo would silently lose both tools across cli/telegram/discord.
Caught by the config-migration dry-run's post-migration toolset validation.

Restores:
  - toolsets.py: messaging + moa entries (marked FORK-CUSTOM so a future merge
    preserves them)
  - tools/mixture_of_agents_tool.py (self-registers moa via registry.register)
  - agent/display.py: mixture_of_agents formatter + arg-key
  - model_tools.py: moa_tools legacy alias
  - acp_adapter/tools.py: mixture_of_agents in polished ACP tools
  - hermes_cli/tools_config.py: messaging+moa UI rows, moa default-off + env req
  - hermes_cli/config.py: OPENROUTER_API_KEY tools list (+ mixture_of_agents)
  - hermes_cli/tips.py: mixture_of_agents tip

Adds tests/agent/test_fork_custom_toolsets.py — a HARD regression gate that fails
CI if either fork toolset is dropped again (proven: 6/6 pass restored, 6/6 fail
on the broken pre-restore tree). Hardened against the editable-install live-tree
leak so a deleted module can't false-pass via a sibling install.
@Kyzcreig
Kyzcreig enabled auto-merge June 30, 2026 06:39
@github-actions

github-actions Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

🔎 Lint report: fix/restore-fork-messaging-moa-toolsets vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 12207 on HEAD, 12205 on base (🆕 +2)

🆕 New issues (2):

Rule Count
unresolved-import 1
invalid-return-type 1
First entries
tests/agent/test_fork_custom_toolsets.py:23: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tools/mixture_of_agents_tool.py:111: [invalid-return-type] invalid-return-type: Function can implicitly return `None`, which is not assignable to return type `tuple[str, str, bool]`

✅ Fixed issues: none

Unchanged: 6393 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@greptile-apps

greptile-apps Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

Greptile Summary

Restores two fork-custom toolsets (messaging → send_message and moa → mixture_of_agents) that were silently dropped by the upstream parity merge in PR #119. The fix re-adds the toolset entries, the mixture_of_agents_tool.py module, display/UI wiring across six files, and a hard CI gate that will catch the same regression in future merges.

  • toolsets.py + tools/mixture_of_agents_tool.py: Both toolsets are restored with prominent FORK-CUSTOM comment blocks; the MoA module self-registers via registry.register at import time and the four issues flagged in the previous review round (empty-content false-success, ignored aggregator_model, wrong GPT temperature prefix, aggregator empty-success) are all addressed in this restored version.
  • CI gate (tests/agent/test_fork_custom_toolsets.py): Six tests covering toolset resolution, module self-registration, schema shape, UI surface, env-requirement declaration, and the headline zero-tool invariant; the editable-install leak trap is well-designed.

Confidence Score: 5/5

Safe to merge — the restored code is a faithful recovery of the pre-merge state with all four previously flagged bugs corrected, and the new CI gate makes future regressions detectable at build time.

The change is a targeted restore of two removed toolsets across nine files. The logic paths in mixture_of_agents_tool.py are well-bounded: empty-content and aggregator-failure cases both now surface explicit errors rather than silent successes. The CI gate's editable-install leak trap is solid. The only fresh gaps are a stale docstring entry and a test coupling to a private attribute, neither of which affects runtime behavior.

No files require special attention. tools/mixture_of_agents_tool.py has the most new logic but it is self-contained, and the CI gate in tests/agent/test_fork_custom_toolsets.py exercises all its key invariants.

Important Files Changed

Filename Overview
toolsets.py Restores messaging and moa fork-custom toolset entries with clear FORK-CUSTOM comment block to prevent future merge accidents.
tools/mixture_of_agents_tool.py Full restore of the MoA tool module; all four issues from prior review threads are properly addressed. Docstring documents processing_time in the return shape but the implementation intentionally omits it (only in debug logs).
tests/agent/test_fork_custom_toolsets.py New hard CI gate for both fork-custom toolsets; editable-install leak trap is well-designed, though tests access the private registry._tools attribute directly.
hermes_cli/tools_config.py Adds messaging/moa UI rows, marks moa default-off, and declares OPENROUTER_API_KEY requirement — all consistent with the rest of the file.
hermes_cli/config.py Adds mixture_of_agents to the OPENROUTER_API_KEY tools list — straightforward one-line addition.
hermes_cli/tips.py Adds a tool-level tip for mixture_of_agents alongside the existing /moa slash-command tip.
model_tools.py Adds moa_tools legacy alias mapping to mixture_of_agents — simple one-liner consistent with other legacy aliases.
agent/display.py Wires mixture_of_agents into build_tool_preview arg-key map and get_cute_tool_message — clean addition matching the existing pattern.
acp_adapter/tools.py Adds mixture_of_agents to the _POLISHED_TOOLS set — one-word addition matching the format of every other entry.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Agent
    participant MoA as mixture_of_agents_tool
    participant OR as OpenRouter API
    participant R1 as Ref Model 1 (claude-opus-4.6)
    participant R2 as Ref Model 2 (gemini-2.5-pro)
    participant R3 as Ref Model 3 (gpt-5.4-pro)
    participant R4 as Ref Model 4 (deepseek-v3.2)
    participant Agg as Aggregator (claude-opus-4.6)

    Agent->>MoA: mixture_of_agents(user_prompt)
    MoA->>MoA: check OPENROUTER_API_KEY

    par Layer 1 — parallel reference calls
        MoA->>OR: "chat.completions R1 reasoning=xhigh"
        OR-->>MoA: response / empty → retry x6
        MoA->>OR: "chat.completions R2 reasoning=xhigh"
        OR-->>MoA: response / empty → retry x6
        MoA->>OR: chat.completions R3 no temp
        OR-->>MoA: response / empty → retry x6
        MoA->>OR: "chat.completions R4 reasoning=xhigh"
        OR-->>MoA: response / empty → retry x6
    end

    MoA->>MoA: "filter successes >= MIN_SUCCESSFUL_REFERENCES"

    Note over MoA: Layer 2 — aggregation
    MoA->>OR: "chat.completions Agg system=all_responses"
    OR-->>MoA: aggregated content / empty → retry x1
    alt still empty after retry
        MoA-->>Agent: RuntimeError not silent success
    else content present
        MoA-->>Agent: JSON success response models_used
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Agent
    participant MoA as mixture_of_agents_tool
    participant OR as OpenRouter API
    participant R1 as Ref Model 1 (claude-opus-4.6)
    participant R2 as Ref Model 2 (gemini-2.5-pro)
    participant R3 as Ref Model 3 (gpt-5.4-pro)
    participant R4 as Ref Model 4 (deepseek-v3.2)
    participant Agg as Aggregator (claude-opus-4.6)

    Agent->>MoA: mixture_of_agents(user_prompt)
    MoA->>MoA: check OPENROUTER_API_KEY

    par Layer 1 — parallel reference calls
        MoA->>OR: "chat.completions R1 reasoning=xhigh"
        OR-->>MoA: response / empty → retry x6
        MoA->>OR: "chat.completions R2 reasoning=xhigh"
        OR-->>MoA: response / empty → retry x6
        MoA->>OR: chat.completions R3 no temp
        OR-->>MoA: response / empty → retry x6
        MoA->>OR: "chat.completions R4 reasoning=xhigh"
        OR-->>MoA: response / empty → retry x6
    end

    MoA->>MoA: "filter successes >= MIN_SUCCESSFUL_REFERENCES"

    Note over MoA: Layer 2 — aggregation
    MoA->>OR: "chat.completions Agg system=all_responses"
    OR-->>MoA: aggregated content / empty → retry x1
    alt still empty after retry
        MoA-->>Agent: RuntimeError not silent success
    else content present
        MoA-->>Agent: JSON success response models_used
    end
Loading

Reviews (3): Last reviewed commit: "fix(moa): aggregator empty-content raise..." | Re-trigger Greptile

Comment thread tools/mixture_of_agents_tool.py
Comment thread tools/mixture_of_agents_tool.py Outdated
Comment thread tools/mixture_of_agents_tool.py Outdated
…ter gpt- temperature guard

Addresses Greptile P1/P1/P2 on the restored mixture_of_agents_tool.py (pre-existing
fork bugs, fixed while restoring):
- empty final-retry content now returns success=False instead of forwarding an
  empty string to the aggregator
- _run_aggregator_model now honors the computed agg_model (aggregator_model param
  was silently ignored)
- temperature guard matches OpenRouter-namespaced 'openai/gpt-*' IDs, not just 'gpt-*'
@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

Addressed all three Greptile findings in 091b3f9 (these were pre-existing bugs in the fork's mixture_of_agents_tool.py, fixed while restoring it):

  • P1 (empty content as success): the final-retry path now returns success=False with an error marker instead of forwarding an empty string to the aggregator.
  • P1 (aggregator_model ignored): _run_aggregator_model now takes a model param and the call site passes the computed agg_model, so a custom aggregator is actually honored.
  • P2 (gpt- temperature guard): the guard now matches OpenRouter-namespaced openai/gpt-* IDs in both the reference and aggregator paths, not just bare gpt-*.

Resolving the threads.

Comment thread tools/mixture_of_agents_tool.py
…success

Greptile P1 follow-up: _run_aggregator_model returned "" after both retries
failed, which the caller wrapped as {success: True, response: ""}. Now raises
RuntimeError so the caller's failure path reports it — symmetric with the
reference-model final-attempt guard.
@Kyzcreig
Kyzcreig merged commit 30255a6 into main Jun 30, 2026
32 checks passed
@Kyzcreig
Kyzcreig deleted the fix/restore-fork-messaging-moa-toolsets branch June 30, 2026 07:04
@Kyzcreig
Kyzcreig restored the fix/restore-fork-messaging-moa-toolsets branch September 21, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant