Skip to content

【新支持】Add native Hermes runtime support#1

Open
shiro123444 wants to merge 1 commit intokitephp:mainfrom
shiro123444:shiro/hermes-runtime-support
Open

【新支持】Add native Hermes runtime support#1
shiro123444 wants to merge 1 commit intokitephp:mainfrom
shiro123444:shiro/hermes-runtime-support

Conversation

@shiro123444
Copy link
Copy Markdown

概述

新增支持了Hermes,请审查。

主要修改如下:

  • 增加运行时兼容层,区分 hermes / openclaw
  • 为初始化脚本增加 --runtime--sync-root-files
  • Hermes 模式下默认不再强制写入 SOUL.mdHEARTBEAT.mdAGENTS.md
  • 重组 SKILL.md 和 references,使 Hermes / OpenClaw 的使用方式分开说明
  • Hermes 模式下,llm_client.py 优先复用 Hermes 自身的 provider / model 解析,而不是固定走 Anthropic
  • 补上原仓库缺失但初始化脚本实际依赖的 assets/templates/profile/base.md

一些个人改进和优势:

  • 对 Hermes 更原生,减少额外持久化副作用
  • 保留 OpenClaw 兼容,不破坏原有主流程
  • 让运行时差异、持久化边界和模型调用边界更清晰,便于审核和后续维护

核心代码

1. 运行时兼容层

新增:

  • scripts/runtime_compat.py

用于统一处理:

  • runtime 自动识别
  • 不同 runtime 下 root file sync 的默认策略
  • 不同 runtime 下脚本调用和 managed block 模板

2. 初始化与诊断

修改:

  • scripts/init_soul.py
  • scripts/doctor_soul.py

主要变化:

  • 初始化支持显式选择 --runtime hermes|openclaw
  • 支持 --sync-root-files auto|always|never
  • Hermes 下默认允许只生成 soul/ 目录,不要求根文件托管块
  • OpenClaw 下保留原有 root bootstrap 逻辑

3. Hermes provider 适配

修改:

  • scripts/llm_client.py

主要变化:

  • Hermes 模式下优先复用 Hermes 的 provider / model 解析
  • 支持跟随 Hermes 当前配置,也支持 SOUL_LLM_PROVIDER / SOUL_LLM_MODEL 显式覆盖
  • OpenClaw 路径暂时保留原有 Anthropic-first 逻辑

4. 文档结构调整

修改 / 新增:

  • SKILL.md
  • references/runtime-hermes.md
  • references/runtime-openclaw.md
  • references/security-review.md
  • references/managed-blocks.md

主要是把两种宿主框架的行为拆开描述,减少混用和误解。

后续建议

为了更好适配未来不同 Agent 框架,后续维护的话可以继续做三件事:

  1. 进一步把“宿主运行时差异”和“skill 自身业务逻辑”解耦,尽量集中到单独兼容层
  2. 把 LLM 调用继续抽象成统一接口,避免 skill 内部绑定单一 provider
  3. 把持久化行为分级,例如区分 soul/ 内部状态写入、managed block 写入、外部投递行为,方便不同框架做更细粒度的安全控制

个人也挺喜欢这种后陪伴感的skills希望有更多的维护者

Copilot AI review requested due to automatic review settings April 13, 2026 11:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds native Hermes runtime support to the soul-agent skill while retaining OpenClaw compatibility, mainly by introducing a runtime compatibility layer, gating root-file managed-block persistence, and adapting LLM provider/model resolution for Hermes.

Changes:

  • Added runtime_compat.py to centralize runtime detection, root-file sync policy, and managed-block templates per runtime.
  • Updated init/doctor scripts to accept --runtime and root-sync policy flags, and to skip root-file writes by default in Hermes mode.
  • Updated LLM client to prefer Hermes provider/model resolution in Hermes mode, while keeping an OpenClaw/Anthropic compatibility path.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
skills/soul-agent/SKILL.md Restructures usage guidance and splits Hermes vs OpenClaw workflows/references.
skills/soul-agent/scripts/runtime_compat.py New runtime compatibility layer (detection, root-sync policy, managed-block templates, env-file discovery).
skills/soul-agent/scripts/llm_client.py Adds Hermes-native provider/model routing and retains OpenClaw Anthropic path.
skills/soul-agent/scripts/init_soul.py Adds runtime + root-sync flags and gates managed-block writes based on runtime/policy.
skills/soul-agent/scripts/doctor_soul.py Adds runtime + managed-block expectation flags and runtime-aware block checks.
skills/soul-agent/references/security-review.md Documents security/persistence boundary changes for Hermes vs OpenClaw.
skills/soul-agent/references/runtime-hermes.md Adds Hermes-specific operational guidance (cron, delivery, root-sync off by default).
skills/soul-agent/references/runtime-openclaw.md Adds OpenClaw-specific guidance emphasizing bootstrap/root-file expectations.
skills/soul-agent/references/managed-blocks.md Updates managed-block policy documentation and runtime notes.
skills/soul-agent/assets/templates/profile/base.md Extends persona template with provider/model fields and reorganizes sections.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +56
def detect_runtime(explicit: str = "auto") -> str:
if explicit in RUNTIME_PROFILES:
return explicit

if os.environ.get("HERMES_HOME"):
return "hermes"
if os.environ.get("OPENCLAW_HOME") or os.environ.get("CLAW_HOME"):
return "openclaw"

hermes_bin = shutil.which("hermes")
openclaw_bin = shutil.which("openclaw")
if hermes_bin and not openclaw_bin:
return "hermes"
if openclaw_bin and not hermes_bin:
return "openclaw"

# Default to Hermes because the local fork is intended to be Hermes-native.
return "hermes"
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

detect_runtime() defaults to returning hermes when runtime cannot be inferred (both/no binaries found and no env vars). This changes the previous OpenClaw-first behavior and can silently mis-detect OpenClaw workspaces (e.g., dev machines with both CLIs installed), which then breaks LLM usage because Hermes-only dependencies are used. Consider defaulting to openclaw for backward compatibility, or add a workspace-based heuristic / explicit env var (e.g., SOUL_RUNTIME) and treat ambiguous cases as an error that asks the operator to pass --runtime explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
else:
home = os.environ.get("HOME", "").strip()
if home:
_append(Path(home) / ".hermes" / ".env")
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

env_file_candidates() falls back to reading $HOME/.hermes/.env even when HERMES_HOME is not set. On the OpenClaw compatibility path this expands the secret-search surface beyond the workspace and can unintentionally pick up developer-local credentials. If the goal is to support Hermes deployments, consider only reading HERMES_HOME/.env when HERMES_HOME is explicitly set (and otherwise stick to workspace/.env), or gate home-directory probing behind an explicit opt-in flag/env var.

Suggested change
else:
home = os.environ.get("HOME", "").strip()
if home:
_append(Path(home) / ".hermes" / ".env")

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +63
self._initialize_hermes_runtime()
else:
self.api_key = self._resolve_api_key_openclaw()
self.model = self._resolve_model_openclaw()

Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

In Hermes mode, if Hermes dependencies are unavailable or runtime resolution fails, LLMClient keeps self.runtime == 'hermes', which makes available() false and prevents the OpenClaw/Anthropic fallback path from ever being used even if ANTHROPIC_API_KEY is present. Consider adding a fallback: if Hermes initialization or Hermes client resolution fails, downgrade to the OpenClaw client path (or try OpenClaw when agent.auxiliary_client cannot be imported).

Suggested change
self._initialize_hermes_runtime()
else:
self.api_key = self._resolve_api_key_openclaw()
self.model = self._resolve_model_openclaw()
try:
self._initialize_hermes_runtime()
except Exception:
self._initialize_openclaw_fallback()
else:
try:
hermes_available = self.available()
except Exception:
hermes_available = bool(self._client or self._runtime_info)
if not hermes_available:
self._initialize_openclaw_fallback()
else:
self._initialize_openclaw_fallback()
def _initialize_openclaw_fallback(self) -> None:
self.runtime = "openclaw"
self.api_key = self._resolve_api_key_openclaw()
self.model = self._resolve_model_openclaw()

Copilot uses AI. Check for mistakes.
else:
print("soul-agent diagnosis: FAIL")
print("Ask Claude to run soul-agent initialization (say: '帮我初始化 soul-agent').")
print("Ask Hermes to run soul-agent initialization (say: '帮我初始化 soul-agent').")
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

The failure hint is now hard-coded to Hermes ("Ask Hermes to run..."). This script is still used for OpenClaw mode as well, so the message becomes incorrect/misleading there. Consider using runtime_cfg.display_name (or a generic "Ask the host agent") so the guidance matches the selected runtime.

Suggested change
print("Ask Hermes to run soul-agent initialization (say: '帮我初始化 soul-agent').")
print(
f"Ask {runtime_cfg.display_name} to run soul-agent initialization "
"(say: '帮我初始化 soul-agent')."
)

Copilot uses AI. Check for mistakes.
Comment on lines +538 to +541
warnings.append(
f"[INFO] Root-file sync skipped for runtime={runtime}. "
"Hermes can attach soul-agent explicitly without touching SOUL.md / HEARTBEAT.md / AGENTS.md."
)
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

When root-file sync is disabled, the warning message always references Hermes attachment patterns. If an operator disables root sync in OpenClaw mode (e.g., --sync-root-files never), this guidance is inaccurate. Consider tailoring the message based on runtime (or making it runtime-neutral) so the output remains correct for both runtimes.

Copilot uses AI. Check for mistakes.
Comment on lines 49 to +53
```bash
python skills/soul-agent/scripts/init_soul.py \
python scripts/init_soul.py \
--workspace <workspace-root> \
--runtime <hermes|openclaw> \
--sync-root-files <auto|always|never> \
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

The commands here use python scripts/... while passing --workspace <workspace-root>, which reads like they should be runnable from the workspace root. In this repo the scripts live under skills/soul-agent/scripts/, so these examples are ambiguous and can easily fail depending on the current working directory. Consider either (1) using the full path (skills/soul-agent/scripts/init_soul.py), or (2) explicitly stating that these commands are run from the skill root (skills/soul-agent/).

Copilot uses AI. Check for mistakes.
Comment on lines 85 to +89
```bash
# Heartbeat every 10 minutes
openclaw cron add --name "soul-heartbeat" --cron "*/10 * * * *" \
--session isolated --agent main --light-context \
--message "[soul-heartbeat] Run heartbeat check and engine..."

# Daily distillation at 00:30
openclaw cron add --name "soul-memory-daily" --cron "30 0 * * *" \
--session isolated --agent main --no-deliver \
--message "[soul-memory-daily] Distill life logs..."
```

## Initialization Behavior

- `--mode auto`: init if missing, migrate if legacy, repair otherwise
- Interactive prompts for: name, age, city, life profile, occupation, hobbies
- Generates `soul/profile/*`, `soul/state/*`, `soul/log/*`, `soul/memory/*`
- Auto-syncs managed blocks in `SOUL.md`, `HEARTBEAT.md`, `AGENTS.md`

## Generative Architecture (v2)

Inspired by Smallville Generative Agents:

```
Morning planning (LLM)
Today's Plan (soul/plan/YYYY-MM-DD.json)
Each heartbeat:
Memory Stream (recent log entries) ←─┐
↓ │
LLM Narrative Generation │
↓ │
Life Log Entry ─────────────────────→┘
Daily Reflection (distill_life_log.py)
SOUL_MEMORY.md (long-term memory)
python scripts/heartbeat_check.py --workspace <workspace-root> --json
python scripts/heartbeat_engine.py --workspace <workspace-root> --json
python scripts/update_state.py --workspace <workspace-root> --action interaction --json
python scripts/distill_life_log.py --workspace <workspace-root> --archive
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

Same path ambiguity as initialization: python scripts/heartbeat_*.py implies a top-level scripts/ directory. In this repo these files are under skills/soul-agent/scripts/, so the example commands are only correct if the operator first cd's into the skill directory. Clarify the expected working directory or use explicit paths so operators don't run into "file not found" failures.

Copilot uses AI. Check for mistakes.
```
`soul-agent` runtime contract (default: `main`):
1. Follow OpenClaw's default bootstrap order for root files (including `SOUL.md` and `HEARTBEAT.md`).
1. Follow Hermes's root context files (including `SOUL.md`, `AGENTS.md`, and `HEARTBEAT.md` during heartbeat runs).
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

This shared managed-block reference now states to "Follow Hermes's root context files" in the generic AGENTS.md contract section, which conflicts with the OpenClaw runtime contract described elsewhere (and with runtime_compat.managed_block_templates() for OpenClaw). Consider making this section runtime-neutral ("Follow the host runtime's root bootstrap/context files") or splitting it into explicit Hermes vs OpenClaw variants to avoid contradictory guidance.

Suggested change
1. Follow Hermes's root context files (including `SOUL.md`, `AGENTS.md`, and `HEARTBEAT.md` during heartbeat runs).
1. Follow the host runtime's root bootstrap/context files (including `SOUL.md`, `AGENTS.md`, and `HEARTBEAT.md` when the runtime uses them during heartbeat runs).

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +30
```bash
python scripts/init_soul.py \
--workspace . \
--runtime hermes \
--sync-root-files never \
--non-interactive \
--profile-json '{"display_name":"<name>","city":"<city>","life_profile":"freelancer"}'
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

The init example uses python scripts/init_soul.py, which is only correct if the operator is running from the skill root directory. If the intended usage is from a workspace/repo root, consider using python skills/soul-agent/scripts/init_soul.py (or explicitly note the required working directory) to avoid path confusion.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +19
```bash
python scripts/init_soul.py \
--workspace . \
--runtime openclaw \
--sync-root-files auto \
--non-interactive \
--profile-json '{"display_name":"<name>","city":"<city>","life_profile":"freelancer"}'
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

Same path ambiguity as in other docs: python scripts/init_soul.py assumes the current working directory is the skill root. If operators are expected to run from the workspace root, use an explicit path (e.g., python skills/soul-agent/scripts/init_soul.py) or add a note about the required working directory.

Copilot uses AI. Check for mistakes.
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.

2 participants