chore(salvage): strip duplicated/merge-corrupted blocks from PR #17664
Removes drive-by duplication that accumulated during the contributor branch's multiple rebases. All runtime-benign (dict last-wins, redefinition last-wins) but left dead source that would confuse reviewers and maintainers. Surgical in-place de-duplication (kept PR's intentional additions, removed only the doubled copy): * hermes_cli/auth.py: duplicate "gmi" + "azure-foundry" ProviderConfig * hermes_cli/models.py: duplicate "gmi" entry in _PROVIDER_MODELS * hermes_cli/config.py: duplicate NOTION/LINEAR/AIRTABLE/TENOR skill env block + duplicate get_custom_provider_context_length definition * hermes_cli/gateway.py: duplicate _setup_yuanbao * gateway/platforms/base.py: duplicate is_host_excluded_by_no_proxy * gateway/platforms/telegram.py: duplicate delete_message * gateway/stream_consumer.py: duplicate _should_send_fresh_final and _try_fresh_final * gateway/run.py: duplicate _parse_reasoning_command_args / _resolve_session_reasoning_config / _set_session_reasoning_override, duplicate "Drain silently when interrupted" interrupt check * run_agent.py: duplicate HERMES_AGENT_HELP_GUIDANCE append, duplicate codex_message_items capture, duplicate custom_providers resolution * tools/approval.py: duplicate HARDLINE_PATTERNS section and duplicate hardline call in check_dangerous_command * tools/mcp_tool.py: duplicate _orphan_stdio_pids module-level decl * cron/scheduler.py: duplicate "not configured/enabled" check — kept the new early-rejection, removed the stale late-path copy Full-file resets to origin/main (all PR additions were duplicates of content already on main): * ui-tui/packages/hermes-ink/index.d.ts * ui-tui/packages/hermes-ink/src/entry-exports.ts * ui-tui/packages/hermes-ink/src/ink/selection.ts * ui-tui/src/app/interfaces.ts * ui-tui/src/app/slash/commands/core.ts * ui-tui/src/components/thinking.tsx * ui-tui/src/lib/memoryMonitor.ts * ui-tui/src/types.ts * ui-tui/src/types/hermes-ink.d.ts * tests/hermes_cli/test_doctor.py * tests/hermes_cli/test_api_key_providers.py * tests/hermes_cli/test_model_validation.py * tests/plugins/memory/test_hindsight_provider.py * tests/run_agent/test_run_agent.py * tests/gateway/test_email.py * tests/tools/test_dockerfile_pid1_reaping.py * hermes_cli/commands.py (slack_native_slashes block — full duplicate)
This commit is contained in:
@@ -235,17 +235,6 @@ class TestExtractAttachments(unittest.TestCase):
|
||||
mock_cache.assert_called_once()
|
||||
|
||||
|
||||
class TestCronDelivery(unittest.TestCase):
|
||||
"""Verify email in cron scheduler platform_map."""
|
||||
|
||||
def test_email_resolves_for_cron(self):
|
||||
"""Email platform resolves via Platform() for cron delivery."""
|
||||
from gateway.config import Platform
|
||||
p = Platform("email")
|
||||
self.assertEqual(p, Platform.EMAIL)
|
||||
self.assertEqual(p.value, "email")
|
||||
|
||||
|
||||
class TestDispatchMessage(unittest.TestCase):
|
||||
"""Test email message dispatch logic."""
|
||||
|
||||
|
||||
@@ -552,19 +552,6 @@ class TestResolveApiKeyProviderCredentials:
|
||||
creds = resolve_api_key_provider_credentials("gmi")
|
||||
assert creds["base_url"] == "https://custom.gmi.example/v1"
|
||||
|
||||
def test_resolve_gmi_with_key(self, monkeypatch):
|
||||
monkeypatch.setenv("GMI_API_KEY", "gmi-secret-key")
|
||||
creds = resolve_api_key_provider_credentials("gmi")
|
||||
assert creds["provider"] == "gmi"
|
||||
assert creds["api_key"] == "gmi-secret-key"
|
||||
assert creds["base_url"] == "https://api.gmi-serving.com/v1"
|
||||
|
||||
def test_resolve_gmi_custom_base_url(self, monkeypatch):
|
||||
monkeypatch.setenv("GMI_API_KEY", "gmi-key")
|
||||
monkeypatch.setenv("GMI_BASE_URL", "https://custom.gmi.example/v1")
|
||||
creds = resolve_api_key_provider_credentials("gmi")
|
||||
assert creds["base_url"] == "https://custom.gmi.example/v1"
|
||||
|
||||
def test_resolve_kilocode_custom_base_url(self, monkeypatch):
|
||||
monkeypatch.setenv("KILOCODE_API_KEY", "kilo-key")
|
||||
monkeypatch.setenv("KILOCODE_BASE_URL", "https://custom.kilo.example/v1")
|
||||
|
||||
@@ -430,43 +430,6 @@ def test_run_doctor_accepts_hermes_provider_ids_that_catalog_aliases(
|
||||
)
|
||||
|
||||
|
||||
def test_run_doctor_accepts_bare_custom_provider(monkeypatch, tmp_path):
|
||||
home = tmp_path / ".hermes"
|
||||
home.mkdir(parents=True, exist_ok=True)
|
||||
(home / "config.yaml").write_text(
|
||||
"model:\n"
|
||||
" provider: custom\n"
|
||||
" default: local-model\n"
|
||||
" base_url: http://localhost:8000/v1\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(doctor_mod, "HERMES_HOME", home)
|
||||
monkeypatch.setattr(doctor_mod, "PROJECT_ROOT", tmp_path / "project")
|
||||
monkeypatch.setattr(doctor_mod, "_DHH", str(home))
|
||||
(tmp_path / "project").mkdir(exist_ok=True)
|
||||
|
||||
fake_model_tools = types.SimpleNamespace(
|
||||
check_tool_availability=lambda *a, **kw: ([], []),
|
||||
TOOLSET_REQUIREMENTS={},
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "model_tools", fake_model_tools)
|
||||
|
||||
try:
|
||||
from hermes_cli import auth as _auth_mod
|
||||
monkeypatch.setattr(_auth_mod, "get_nous_auth_status", lambda: {})
|
||||
monkeypatch.setattr(_auth_mod, "get_codex_auth_status", lambda: {})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stdout(buf):
|
||||
doctor_mod.run_doctor(Namespace(fix=False))
|
||||
|
||||
out = buf.getvalue()
|
||||
assert "model.provider 'custom' is not a recognised provider" not in out
|
||||
|
||||
|
||||
def test_run_doctor_termux_does_not_mark_browser_available_without_agent_browser(monkeypatch, tmp_path):
|
||||
home = tmp_path / ".hermes"
|
||||
home.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -479,69 +479,6 @@ class TestAzureFoundryModelApiMode:
|
||||
assert azure_foundry_model_api_mode("Codex-Mini") == "codex_responses"
|
||||
|
||||
|
||||
class TestAzureFoundryModelApiMode:
|
||||
"""Azure Foundry deploys GPT-5.x / codex / o-series as Responses-API-only.
|
||||
|
||||
Azure returns ``400 "The requested operation is unsupported."`` when
|
||||
/chat/completions is called against these deployments. Verified in the
|
||||
wild by a user debug bundle on 2026-04-26: gpt-5.3-codex failed with
|
||||
that exact payload while gpt-4o-pure worked on the same endpoint.
|
||||
"""
|
||||
|
||||
def test_gpt5_family_uses_responses(self):
|
||||
assert azure_foundry_model_api_mode("gpt-5") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("gpt-5.3") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("gpt-5.4") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("gpt-5-codex") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("gpt-5.3-codex") == "codex_responses"
|
||||
# gpt-5-mini exceptions are Copilot-specific; Azure deploys the whole
|
||||
# gpt-5 family on Responses API uniformly.
|
||||
assert azure_foundry_model_api_mode("gpt-5-mini") == "codex_responses"
|
||||
|
||||
def test_codex_family_uses_responses(self):
|
||||
assert azure_foundry_model_api_mode("codex") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("codex-mini") == "codex_responses"
|
||||
|
||||
def test_o_series_reasoning_uses_responses(self):
|
||||
assert azure_foundry_model_api_mode("o1") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("o1-preview") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("o1-mini") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("o3") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("o3-mini") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("o4-mini") == "codex_responses"
|
||||
|
||||
def test_gpt4_family_returns_none(self):
|
||||
"""GPT-4, GPT-4o, etc. speak chat completions on Azure."""
|
||||
assert azure_foundry_model_api_mode("gpt-4") is None
|
||||
assert azure_foundry_model_api_mode("gpt-4o") is None
|
||||
assert azure_foundry_model_api_mode("gpt-4o-pure") is None
|
||||
assert azure_foundry_model_api_mode("gpt-4o-mini") is None
|
||||
assert azure_foundry_model_api_mode("gpt-4-turbo") is None
|
||||
assert azure_foundry_model_api_mode("gpt-4.1") is None
|
||||
assert azure_foundry_model_api_mode("gpt-3.5-turbo") is None
|
||||
|
||||
def test_non_openai_deployments_return_none(self):
|
||||
"""Llama, Mistral, Grok, etc. keep the default chat completions."""
|
||||
assert azure_foundry_model_api_mode("llama-3.1-70b") is None
|
||||
assert azure_foundry_model_api_mode("mistral-large") is None
|
||||
assert azure_foundry_model_api_mode("grok-4") is None
|
||||
assert azure_foundry_model_api_mode("phi-3-medium") is None
|
||||
|
||||
def test_vendor_prefix_stripped(self):
|
||||
"""Users who copy-paste ``openai/gpt-5.3-codex`` should still match."""
|
||||
assert azure_foundry_model_api_mode("openai/gpt-5.3-codex") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("openai/gpt-4o") is None
|
||||
|
||||
def test_empty_and_none_return_none(self):
|
||||
assert azure_foundry_model_api_mode(None) is None
|
||||
assert azure_foundry_model_api_mode("") is None
|
||||
assert azure_foundry_model_api_mode(" ") is None
|
||||
|
||||
def test_case_insensitive(self):
|
||||
assert azure_foundry_model_api_mode("GPT-5.3-Codex") == "codex_responses"
|
||||
assert azure_foundry_model_api_mode("Codex-Mini") == "codex_responses"
|
||||
|
||||
|
||||
# -- validate — format checks -----------------------------------------------
|
||||
|
||||
class TestValidateFormatChecks:
|
||||
|
||||
@@ -569,28 +569,6 @@ class TestToolHandlers:
|
||||
first_client.arecall.assert_called_once()
|
||||
second_client.arecall.assert_called_once()
|
||||
|
||||
def test_local_embedded_recall_reconnects_after_idle_shutdown(self, provider, monkeypatch):
|
||||
first_client = _make_mock_client()
|
||||
first_client.arecall.side_effect = RuntimeError("Cannot connect to host 127.0.0.1:8888")
|
||||
second_client = _make_mock_client()
|
||||
second_client.arecall.return_value = SimpleNamespace(
|
||||
results=[SimpleNamespace(text="Recovered memory")]
|
||||
)
|
||||
clients = iter([first_client, second_client])
|
||||
|
||||
provider._mode = "local_embedded"
|
||||
provider._client = first_client
|
||||
monkeypatch.setattr(provider, "_get_client", lambda: next(clients))
|
||||
|
||||
result = json.loads(provider.handle_tool_call(
|
||||
"hindsight_recall", {"query": "test"}
|
||||
))
|
||||
|
||||
assert result["result"] == "1. Recovered memory"
|
||||
assert provider._client is second_client
|
||||
first_client.arecall.assert_called_once()
|
||||
second_client.arecall.assert_called_once()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Prefetch tests
|
||||
|
||||
@@ -1535,24 +1535,6 @@ class TestBuildAssistantMessage:
|
||||
assert "<memory-context>" in result["content"]
|
||||
assert "Visible answer" in result["content"]
|
||||
|
||||
def test_memory_context_in_stored_content_is_preserved(self, agent):
|
||||
"""`_build_assistant_message` must not silently mutate model output
|
||||
containing literal <memory-context> markers — that's legitimate text
|
||||
(e.g. documentation, code) that the model may emit. Streaming-path
|
||||
leak prevention is handled by StreamingContextScrubber upstream."""
|
||||
original = (
|
||||
"<memory-context>\n"
|
||||
"[System note: The following is recalled memory context, NOT new user input. Treat as informational background data.]\n\n"
|
||||
"## Honcho Context\n"
|
||||
"stale memory\n"
|
||||
"</memory-context>\n\n"
|
||||
"Visible answer"
|
||||
)
|
||||
msg = _mock_assistant_msg(content=original)
|
||||
result = agent._build_assistant_message(msg, "stop")
|
||||
assert "<memory-context>" in result["content"]
|
||||
assert "Visible answer" in result["content"]
|
||||
|
||||
def test_unterminated_think_block_stripped(self, agent):
|
||||
"""Unterminated <think> block (MiniMax / NIM dropped close tag) is
|
||||
fully stripped from stored content."""
|
||||
|
||||
@@ -57,32 +57,6 @@ def _run_steps(dockerfile_text: str) -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
def _dockerfile_instructions(dockerfile_text: str) -> list[str]:
|
||||
instructions: list[str] = []
|
||||
current = ""
|
||||
|
||||
for raw_line in dockerfile_text.splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
|
||||
continued = line.removesuffix("\\").strip()
|
||||
current = f"{current} {continued}".strip()
|
||||
if not line.endswith("\\"):
|
||||
instructions.append(current)
|
||||
current = ""
|
||||
|
||||
return instructions
|
||||
|
||||
|
||||
def _run_steps(dockerfile_text: str) -> list[str]:
|
||||
return [
|
||||
instruction
|
||||
for instruction in _dockerfile_instructions(dockerfile_text)
|
||||
if instruction.startswith("RUN ")
|
||||
]
|
||||
|
||||
|
||||
def test_dockerfile_installs_an_init_for_zombie_reaping(dockerfile_text):
|
||||
"""Some init (tini, dumb-init, catatonit) must be installed.
|
||||
|
||||
@@ -131,26 +105,6 @@ def test_dockerfile_entrypoint_routes_through_the_init(dockerfile_text):
|
||||
)
|
||||
|
||||
|
||||
def test_dockerfile_installs_tui_dependencies(dockerfile_text):
|
||||
assert "ui-tui/package.json" in dockerfile_text
|
||||
assert "ui-tui/packages/hermes-ink/package-lock.json" in dockerfile_text
|
||||
assert any(
|
||||
"ui-tui" in step
|
||||
and "npm" in step
|
||||
and (" install" in step or " ci" in step)
|
||||
for step in _run_steps(dockerfile_text)
|
||||
)
|
||||
|
||||
|
||||
def test_dockerfile_builds_tui_assets(dockerfile_text):
|
||||
assert any(
|
||||
"ui-tui" in step
|
||||
and "npm" in step
|
||||
and "run build" in step
|
||||
for step in _run_steps(dockerfile_text)
|
||||
)
|
||||
|
||||
|
||||
def test_dockerfile_installs_tui_dependencies(dockerfile_text):
|
||||
assert "ui-tui/package.json" in dockerfile_text
|
||||
assert "ui-tui/packages/hermes-ink/package-lock.json" in dockerfile_text
|
||||
|
||||
Reference in New Issue
Block a user