Skip to content
Merged
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
3 changes: 3 additions & 0 deletions agent/credential_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ def __getattr__(self, name: str):
def from_dict(cls, provider: str, payload: Dict[str, Any]) -> "PooledCredential":
field_names = {f.name for f in fields(cls) if f.name != "provider"}
data = {k: payload.get(k) for k in field_names if k in payload}
# Rehydrated last_status_at may be an ISO string from to_dict() — normalize to float epoch
if "last_status_at" in data and isinstance(data["last_status_at"], str):
data["last_status_at"] = _parse_absolute_timestamp(data["last_status_at"])
extra = {k: payload[k] for k in _EXTRA_KEYS if k in payload and payload[k] is not None}
data["extra"] = extra
data.setdefault("id", uuid.uuid4().hex[:6])
Expand Down
2 changes: 2 additions & 0 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12604,6 +12604,7 @@ def handle_paste(event):
paste_dir.mkdir(parents=True, exist_ok=True)
paste_file = paste_dir / f"paste_{_paste_counter[0]}_{datetime.now().strftime('%H%M%S')}.txt"
paste_file.write_text(pasted_text, encoding="utf-8")
logger.info("Collapsed paste #%d: %d lines, %d chars -> %s", _paste_counter[0], line_count + 1, len(pasted_text), paste_file)
placeholder = f"[Pasted text #{_paste_counter[0]}: {line_count + 1} lines \u2192 {paste_file}]"
prefix = ""
if buf.cursor_position > 0 and buf.text[buf.cursor_position - 1] != '\n':
Expand Down Expand Up @@ -12771,6 +12772,7 @@ def _on_text_changed(buf):
paste_dir.mkdir(parents=True, exist_ok=True)
paste_file = paste_dir / f"paste_{_paste_counter[0]}_{datetime.now().strftime('%H%M%S')}.txt"
paste_file.write_text(text, encoding="utf-8")
logger.info("Collapsed paste #%d: %d lines, %d chars -> %s (fallback)", _paste_counter[0], line_count + 1, len(text), paste_file)
_paste_just_collapsed[0] = True
buf.text = f"[Pasted text #{_paste_counter[0]}: {line_count + 1} lines \u2192 {paste_file}]"
buf.cursor_position = len(buf.text)
Expand Down
3 changes: 3 additions & 0 deletions gateway/platforms/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,9 @@ def cache_video_from_bytes(data: bytes, ext: str = ".mp4") -> str:
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
".ts": "text/plain",
".py": "text/plain",
".sh": "text/plain",
}


Expand Down
2 changes: 1 addition & 1 deletion hermes_cli/kanban.py
Original file line number Diff line number Diff line change
Expand Up @@ -1403,7 +1403,7 @@ def _cmd_diagnostics(args: argparse.Namespace) -> int:
sev = getattr(args, "severity", None)
if sev:
for tid in list(diags_by_task.keys()):
kept = [d for d in diags_by_task[tid] if d.severity == sev]
kept = [d for d in diags_by_task[tid] if kd.SEVERITY_ORDER.index(d.severity) >= kd.SEVERITY_ORDER.index(sev)]
if kept:
diags_by_task[tid] = kept
else:
Expand Down
1 change: 1 addition & 0 deletions plugins/model-providers/deepseek/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def build_api_kwargs_extras(
"deepseek-reasoner",
),
base_url="https://api.deepseek.com/v1",
default_aux_model="deepseek-chat",
)

register_provider(deepseek)
6 changes: 5 additions & 1 deletion run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2061,7 +2061,7 @@ def __init__(
try:
_mem_provider_name = mem_config.get("provider", "") if mem_config else ""

if _mem_provider_name:
if _mem_provider_name and _mem_provider_name.strip():
from agent.memory_manager import MemoryManager as _MemoryManager
from plugins.memory import load_memory_provider as _load_mem
self._memory_manager = _MemoryManager()
Expand Down Expand Up @@ -10437,12 +10437,16 @@ def _needs_kimi_tool_reasoning(self) -> bool:
Kimi ``/coding`` and Moonshot thinking mode both require
``reasoning_content`` on every assistant tool-call message; omitting
it causes the next replay to fail with HTTP 400.

Also detects Kimi models served through third-party providers (e.g.
ollama-cloud) by matching ``kimi`` in the model name.
"""
return (
self.provider in {"kimi-coding", "kimi-coding-cn"}
or base_url_host_matches(self.base_url, "api.kimi.com")
or base_url_host_matches(self.base_url, "moonshot.ai")
or base_url_host_matches(self.base_url, "moonshot.cn")
or "kimi" in (self.model or "").lower()
)

def _needs_deepseek_tool_reasoning(self) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ============================================================================
# ============================================================================
# Hermes Agent Installer for Windows
# ============================================================================
# Installation script for Windows (PowerShell).
Expand Down
17 changes: 17 additions & 0 deletions scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,23 @@
"4296245+matthewlai@users.noreply.github.com": "matthewlai",
"109617724+0xchainer@users.noreply.github.com": "0xchainer", # PR #27154/27138/27147 salvage
"201800237+kronexoi@users.noreply.github.com": "kronexoi", # PR #27167 salvage (Teams port fallback)
# batch salvage (May 2026 LHF run, group 2)
"shellybotmoyer@example.com": "shellybotmoyer", # PR #26661 (kanban --severity >=)
"coulson@shellybotmoyer.com": "shellybotmoyer", # PR #25576 (credential_pool ISO rehydrate)
"258858106+shellybotmoyer@users.noreply.github.com": "shellybotmoyer",
"33156212+ether-btc@users.noreply.github.com": "ether-btc", # PR #26632 (memory provider whitespace guard)
"Bloomtonjovish@gmail.com": "LifeJiggy", # PR #26516 (paste collapse logging)
"141562589+LifeJiggy@users.noreply.github.com": "LifeJiggy",
"beastant1@gmail.com": "nekwo", # PR #26481 (PS5.1 UTF-8 BOM)
"43717185+nekwo@users.noreply.github.com": "nekwo",
"67979730+flooryyyy@users.noreply.github.com": "flooryyyy", # PR #26374 (tool_trace error detection)
"188585318+dgians@users.noreply.github.com": "dgians", # PR #26034 (.ts/.py/.sh docs types)
"zealy@tz.co": "dgians", # PR #26034 (bot-committed by zealy-tzco under dgians' PR)
"mottei.survive@gmail.com": "flanny7", # PR #27030 (setup_open_webui python var)
"20530505+flanny7@users.noreply.github.com": "flanny7",
"hermesagent26@gmail.com": "hermesagent26", # PR #26438 (kimi model-name reasoning pad)
"276067471+hermesagent26@users.noreply.github.com": "hermesagent26",
"71590782+kriscolab@users.noreply.github.com": "kriscolab", # PR #26926 (deepseek default_aux_model)
}


Expand Down
4 changes: 2 additions & 2 deletions scripts/setup_open_webui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ install_open_webui() {
"$py" -m venv "$OPEN_WEBUI_VENV"
# shellcheck disable=SC1090
source "$OPEN_WEBUI_VENV/bin/activate"
python -m pip install --upgrade pip setuptools wheel
python -m pip install open-webui
"$py" -m pip install --upgrade pip setuptools wheel
"$py" -m pip install open-webui
}

write_launcher() {
Expand Down
23 changes: 23 additions & 0 deletions tests/plugins/model_providers/test_deepseek_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,26 @@ def test_v3_chat_full_kwargs_omit_thinking(self, deepseek_profile):
)
assert "reasoning_effort" not in kwargs
assert "extra_body" not in kwargs or "thinking" not in kwargs.get("extra_body", {})


class TestDeepSeekAuxModel:
"""DeepSeek aux model is set on the profile so users stop seeing the
bogus 'No auxiliary LLM provider configured' warning (#26924).

Pinned at the profile layer rather than the legacy
`_API_KEY_PROVIDER_AUX_MODELS_FALLBACK` dict — new providers are
expected to set `default_aux_model` on `ProviderProfile`, and the
fallback dict only exists for providers that predate the profiles
system.
"""

def test_profile_advertises_deepseek_chat(self, deepseek_profile):
assert deepseek_profile.default_aux_model == "deepseek-chat"

def test_consumer_api_returns_deepseek_chat(self):
from agent.auxiliary_client import _get_aux_model_for_provider
assert _get_aux_model_for_provider("deepseek") == "deepseek-chat"

def test_consumer_api_returns_non_empty(self):
from agent.auxiliary_client import _get_aux_model_for_provider
assert _get_aux_model_for_provider("deepseek") != ""
2 changes: 1 addition & 1 deletion tools/delegate_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -1649,7 +1649,7 @@ def _run_with_thread_capture():
trace_by_id[tc_id] = entry_t
elif msg.get("role") == "tool":
content = msg.get("content", "")
is_error = bool(content and "error" in content[:80].lower())
is_error = _looks_like_error_output(content)
result_meta = {
"result_bytes": len(content),
"status": "error" if is_error else "ok",
Expand Down
Loading