Skip to content
Closed
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
2 changes: 2 additions & 0 deletions agent/anthropic_adapter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Anthropic Messages API adapter for Hermes Agent.

from __future__ import annotations

Translates between Hermes's internal OpenAI-style message format and
Anthropic's Messages API. Follows the same pattern as the codex_responses
adapter — all provider-specific logic is isolated here.
Expand Down
2 changes: 2 additions & 0 deletions agent/auxiliary_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Shared auxiliary client router for side tasks.

from __future__ import annotations

Provides a single resolution chain so every consumer (context compression,
session search, web extraction, vision analysis, browser vision) picks up
the best available backend without duplicating fallback logic.
Expand Down
2 changes: 2 additions & 0 deletions agent/bedrock_adapter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""AWS Bedrock Converse API adapter for Hermes Agent.

from __future__ import annotations

Provides native integration with Amazon Bedrock using the Converse API,
bypassing the OpenAI-compatible endpoint in favor of direct AWS SDK calls.
This enables full access to the Bedrock ecosystem:
Expand Down
2 changes: 2 additions & 0 deletions agent/context_compressor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Automatic context window compression for long conversations.

from __future__ import annotations

Self-contained class with its own OpenAI client for summarization.
Uses auxiliary model (cheap/fast) to summarize middle turns while
protecting head and tail context.
Expand Down
2 changes: 2 additions & 0 deletions agent/display.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""CLI presentation -- spinner, kawaii faces, tool preview formatting.

from __future__ import annotations

Pure display functions and classes with no AIAgent dependency.
Used by AIAgent._execute_tool_calls for CLI feedback.
"""
Expand Down
2 changes: 2 additions & 0 deletions agent/model_metadata.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Model metadata, context lengths, and token estimation utilities.

from __future__ import annotations

Pure utility functions with no AIAgent dependency. Used by ContextCompressor
and run_agent.py for pre-flight context checks.
"""
Expand Down
33 changes: 23 additions & 10 deletions agent/prompt_builder.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""System prompt assembly -- identity, platform hints, skills index, context files.

from __future__ import annotations

All functions are stateless. AIAgent._build_system_prompt() calls these to
assemble pieces, then combines them with memory and ephemeral prompts.
"""
Expand Down Expand Up @@ -152,7 +154,13 @@ def _strip_yaml_frontmatter(content: str) -> str:
"Do NOT save task progress, session outcomes, completed-work logs, or temporary TODO "
"state to memory; use session_search to recall those from past transcripts. "
"If you've discovered a new way to do something, solved a problem that could be "
"necessary later, save it as a skill with the skill tool."
"necessary later, save it as a skill with the skill tool.\n"
"Write memories as declarative facts, not instructions to yourself. "
"'User prefers concise responses' ✓ — 'Always respond concisely' ✗. "
"'Project uses pytest with xdist' ✓ — 'Run tests with pytest -n 4' ✗. "
"Imperative phrasing gets re-read as a directive in later sessions and can "
"cause repeated work or override the user's current request. Procedures and "
"workflows belong in skills, not memory."
)

SESSION_SEARCH_GUIDANCE = (
Expand Down Expand Up @@ -344,7 +352,11 @@ def _strip_yaml_frontmatter(content: str) -> str:
),
"cli": (
"You are a CLI AI Agent. Try not to use markdown but simple text "
"renderable inside a terminal."
"renderable inside a terminal. "
"IMPORTANT: There is NO attachment channel on the CLI. "
"Do NOT emit MEDIA:/path tags — they will appear as literal text. "
"Instead, just tell the user the absolute path to any generated file "
"so they can access it directly."
),
"sms": (
"You are communicating via SMS. Keep responses concise and use plain text "
Expand Down Expand Up @@ -613,21 +625,21 @@ def build_skills_system_prompt(
or get_session_env("HERMES_SESSION_PLATFORM")
or ""
)
disabled = get_disabled_skill_names()
cache_key = (
str(skills_dir.resolve()),
tuple(str(d) for d in external_dirs),
tuple(sorted(str(t) for t in (available_tools or set()))),
tuple(sorted(str(ts) for ts in (available_toolsets or set()))),
_platform_hint,
tuple(sorted(disabled)),
)
with _SKILLS_PROMPT_CACHE_LOCK:
cached = _SKILLS_PROMPT_CACHE.get(cache_key)
if cached is not None:
_SKILLS_PROMPT_CACHE.move_to_end(cache_key)
return cached

disabled = get_disabled_skill_names()

# ── Layer 2: disk snapshot ────────────────────────────────────────
snapshot = _load_skills_snapshot(skills_dir)

Expand All @@ -654,7 +666,7 @@ def build_skills_system_prompt(
):
continue
skills_by_category.setdefault(category, []).append(
(skill_name, entry.get("description", ""))
(frontmatter_name, entry.get("description", ""))
)
category_descriptions = {
str(k): str(v)
Expand All @@ -679,7 +691,7 @@ def build_skills_system_prompt(
):
continue
skills_by_category.setdefault(entry["category"], []).append(
(skill_name, entry["description"])
(entry["frontmatter_name"], entry["description"])
)

# Read category-level DESCRIPTION.md files
Expand Down Expand Up @@ -722,19 +734,20 @@ def build_skills_system_prompt(
continue
entry = _build_snapshot_entry(skill_file, ext_dir, frontmatter, desc)
skill_name = entry["skill_name"]
if skill_name in seen_skill_names:
frontmatter_name = entry["frontmatter_name"]
if frontmatter_name in seen_skill_names:
continue
if entry["frontmatter_name"] in disabled or skill_name in disabled:
if frontmatter_name in disabled or skill_name in disabled:
continue
if not _skill_should_show(
extract_skill_conditions(frontmatter),
available_tools,
available_toolsets,
):
continue
seen_skill_names.add(skill_name)
seen_skill_names.add(frontmatter_name)
skills_by_category.setdefault(entry["category"], []).append(
(skill_name, entry["description"])
(frontmatter_name, entry["description"])
)
except Exception as e:
logger.debug("Error reading external skill %s: %s", skill_file, e)
Expand Down
2 changes: 2 additions & 0 deletions agent/skill_commands.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Shared slash command helpers for skills and built-in prompt-style modes.

from __future__ import annotations

Shared between CLI (cli.py) and gateway (gateway/run.py) so both surfaces
can invoke skills via /skill-name commands and prompt-only built-ins like
/plan.
Expand Down
2 changes: 2 additions & 0 deletions agent/skill_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Lightweight skill metadata utilities shared by prompt_builder and skills_tool.

from __future__ import annotations

This module intentionally avoids importing the tool registry, CLI config, or any
heavy dependency chain. It is safe to import at module level without triggering
tool registration or provider resolution.
Expand Down
Loading