Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 20 additions & 64 deletions agent/agent_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,50 +722,10 @@ def init_agent(
elif agent.provider == "moa":
from agent.moa_loop import MoAClient
agent.api_mode = "chat_completions"

# Route reference-model outputs to the agent's tool_progress_callback so
# every surface that already consumes it (CLI spinner/scrollback, TUI,
# desktop, gateway) can show each reference's answer as a labelled block
# before the aggregator acts. The facade emits "moa.reference" and
# "moa.aggregating" events; we forward them through the same callback
# the tool lifecycle uses. Best-effort and cache-safe — these are
# display-only events, they never touch the message history.
def _moa_reference_relay(event: str, **kwargs: Any) -> None:
cb = getattr(agent, "tool_progress_callback", None)
if cb is None:
return
try:
if event == "moa.reference":
label = str(kwargs.get("label") or "")
text = str(kwargs.get("text") or "")
idx = kwargs.get("index")
count = kwargs.get("count")
cb(
"moa.reference",
label,
text,
None,
moa_index=idx,
moa_count=count,
)
elif event == "moa.aggregating":
cb(
"moa.aggregating",
str(kwargs.get("aggregator") or ""),
None,
None,
moa_ref_count=kwargs.get("ref_count"),
)
except Exception:
pass

agent.client = MoAClient(
agent.model or "default",
reference_callback=_moa_reference_relay,
)
agent.client = MoAClient(agent.model or "default")
agent._client_kwargs = {}
agent.api_key = api_key or "moa-virtual-provider"
agent.base_url = "moa://local"
agent.base_url = base_url or "moa://local"
if not agent.quiet_mode:
print(f"🤖 AI Agent initialized with MoA preset: {agent.model}")
elif agent.api_mode == "bedrock_converse":
Expand Down Expand Up @@ -828,7 +788,7 @@ def _moa_reference_relay(event: str, **kwargs: Any) -> None:
client_kwargs["default_headers"] = build_nvidia_nim_headers(effective_base)
elif base_url_host_matches(effective_base, "api.routermint.com"):
client_kwargs["default_headers"] = _ra()._routermint_headers()
elif base_url_host_matches(effective_base, "githubcopilot.com"):
elif base_url_host_matches(effective_base, "api.githubcopilot.com"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 GitHub Copilot endpoint detection narrowed — enterprise subdomains not detected for header injection (bug)

The patch changed hostname matching for GitHub Copilot from githubcopilot.com to api.githubcopilot.com in agent_init.py:791, run_agent.py, and agent_runtime_helpers.py. Since base_url_host_matches does suffix-based matching, githubcopilot.com matches all subdomains including api.enterprise.githubcopilot.com and api.business.githubcopilot.com, but api.githubcopilot.com only matches the api subdomain. Enterprise Copilot users will not get copilot-specific headers. Additionally, many other files (auxiliary_client.py, chat_completion_helpers.py) still use the original githubcopilot.com pattern, creating divergent behavior across code paths.

💡 Suggestion: Use githubcopilot.com (without the api. prefix) throughout, or add explicit enterprise subdomain checks alongside api.githubcopilot.com.

📋 Prompt for AI Agents

In agent_init.py line 791, run_agent.py, and agent_runtime_helpers.py create_openai_client, revert the api.githubcopilot.com strings back to githubcopilot.com. In run_agent.py _is_github_copilot_url, restore: return hostname == "api.githubcopilot.com" or hostname.endswith(".githubcopilot.com").

from hermes_cli.models import copilot_default_headers

client_kwargs["default_headers"] = copilot_default_headers()
Expand Down Expand Up @@ -974,6 +934,21 @@ def _moa_reference_relay(event: str, **kwargs: Any) -> None:
# this mutation is reflected in the client built just below.
agent._apply_user_default_headers()

try:
from hermes_cli.config import (
apply_custom_provider_tls_to_client_kwargs,
get_compatible_custom_providers,
load_config,
)

apply_custom_provider_tls_to_client_kwargs(
client_kwargs,
str(client_kwargs.get("base_url") or agent.base_url or ""),
get_compatible_custom_providers(load_config()),
)
except Exception:
pass

agent.api_key = client_kwargs.get("api_key", "")
agent.base_url = client_kwargs.get("base_url", agent.base_url)
try:
Expand Down Expand Up @@ -1167,11 +1142,6 @@ def _moa_reference_relay(event: str, **kwargs: Any) -> None:
# continuation row that must remain open after the helper is torn down;
# those callers explicitly set this flag to False.
agent._end_session_on_close = True
# When True, this agent NEVER persists to the canonical session store
# (state.db) or the JSON snapshot, regardless of session_id. Set on the
# background skill/memory review fork so its harness turn can't leak into
# the user's real session and hijack the next live turn. Default False.
agent._persist_disabled = False
agent._session_init_model_config = {
"max_iterations": agent.max_iterations,
"reasoning_config": reasoning_config,
Expand Down Expand Up @@ -1312,12 +1282,6 @@ def _moa_reference_relay(event: str, **kwargs: Any) -> None:
_agent_section = {}
agent._tool_use_enforcement = _agent_section.get("tool_use_enforcement", "auto")

# Intent-ack continuation config: "auto" (default — codex_responses only,
# the historical gate), true (all api_modes), false (never), or a list of
# model-name substrings. Resolved against the active api_mode/model in the
# conversation loop's intent-ack block.
agent._intent_ack_continuation = _agent_section.get("intent_ack_continuation", "auto")

# Universal task-completion guidance toggle. Default True. Surfaced
# as a separate flag from tool_use_enforcement because the guidance
# applies to ALL models, not just the model families enforcement
Expand Down Expand Up @@ -1670,12 +1634,6 @@ def _moa_reference_relay(event: str, **kwargs: Any) -> None:
abort_on_summary_failure=compression_abort_on_summary_failure,
max_tokens=agent.max_tokens,
)
_bind_session_state = getattr(agent.context_compressor, "bind_session_state", None)
if callable(_bind_session_state):
try:
_bind_session_state(session_db=session_db, session_id=agent.session_id)
except Exception:
pass
agent.compression_enabled = compression_enabled
agent.compression_in_place = compression_in_place

Expand All @@ -1687,10 +1645,8 @@ def _moa_reference_relay(event: str, **kwargs: Any) -> None:
f"Model {agent.model} has a context window of {_ctx:,} tokens, "
f"which is below the minimum {MINIMUM_CONTEXT_LENGTH:,} required "
f"by Hermes Agent. Choose a model with at least "
f"{MINIMUM_CONTEXT_LENGTH // 1000}K context. If your server "
f"reports a window smaller than the model's true window, set "
f"model.context_length in config.yaml to the real value "
f"(this must be at least {MINIMUM_CONTEXT_LENGTH // 1000}K)."
f"{MINIMUM_CONTEXT_LENGTH // 1000}K context, or set "
f"model.context_length in config.yaml to override."
)

# Inject context engine tool schemas (e.g. lcm_grep, lcm_describe, lcm_expand).
Expand Down
Loading
Loading