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
551 changes: 551 additions & 0 deletions agent/codex_app_server_client.py

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions cli-config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -882,9 +882,20 @@ delegation:
# Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.
# inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.
# model: "google/gemini-3-flash-preview" # Override model for subagents (empty = inherit parent)
# reasoning_effort: "medium" # Override reasoning effort for subagents: none, minimal, low, medium, high, xhigh
# provider: "openrouter" # Override provider for subagents (empty = inherit parent)
# # Resolves full credentials (base_url, api_key) automatically.
# # Supported: openrouter, nous, zai, kimi-coding, minimax
# # Set provider: "codex-app-server" to run delegated
# # subagents inside Codex's native app-server harness
# # instead of Hermes' child AIAgent/tool loop. If model
# # or reasoning_effort is blank here, Codex app-server
# # delegation defaults to model "gpt-5.5" and low reasoning.
# # Requires Codex CLI: npm install -g @openai/codex
# # Codex app-server reuses stock Hermes openai-codex OAuth
# # by default; set CODEX_HOME only to force Codex CLI auth.
# # Optional for Codex app-server: command: "codex",
# # args: ["app-server", "--listen", "stdio://"]

# =============================================================================
# Honcho Integration (Cross-Session User Modeling)
Expand Down
55 changes: 55 additions & 0 deletions hermes_cli/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ def _has_provider_env_config(content: str) -> bool:
return any(key in content for key in _PROVIDER_ENV_HINTS)


def _delegation_uses_codex_app_server(config: dict) -> bool:
delegation = config.get("delegation") if isinstance(config, dict) else None
if not isinstance(delegation, dict):
return False
return str(delegation.get("provider") or "").strip().lower() == "codex-app-server"


def _delegation_codex_app_server_command(config: dict) -> str:
delegation = config.get("delegation") if isinstance(config, dict) else None
if not isinstance(delegation, dict):
return ""
return str(delegation.get("command") or "").strip()


def _honcho_is_configured_for_doctor() -> bool:
"""Return True when Honcho is configured, even if this process has no active session."""
try:
Expand Down Expand Up @@ -515,6 +529,9 @@ def run_doctor(args):
check_info("Run 'hermes setup' to create one")
issues.append("Run 'hermes setup' to create .env")

delegation_uses_codex_app_server = False
delegation_codex_app_server_command = ""

# Check ~/.hermes/config.yaml (primary) or project cli-config.yaml (fallback)
config_path = HERMES_HOME / 'config.yaml'
if config_path.exists():
Expand All @@ -524,6 +541,8 @@ def run_doctor(args):
try:
import yaml as _yaml
cfg = _yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
delegation_uses_codex_app_server = _delegation_uses_codex_app_server(cfg)
delegation_codex_app_server_command = _delegation_codex_app_server_command(cfg)
model_section = cfg.get("model") or {}
provider_raw = (model_section.get("provider") or "").strip()
provider = provider_raw.lower()
Expand Down Expand Up @@ -691,6 +710,13 @@ def run_doctor(args):
fallback_config = PROJECT_ROOT / 'cli-config.yaml'
if fallback_config.exists():
check_ok("cli-config.yaml exists (in project directory)")
try:
import yaml as _yaml
cfg = _yaml.safe_load(fallback_config.read_text(encoding="utf-8")) or {}
delegation_uses_codex_app_server = _delegation_uses_codex_app_server(cfg)
delegation_codex_app_server_command = _delegation_codex_app_server_command(cfg)
except Exception:
pass
else:
if should_fix:
config_path.parent.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -833,6 +859,35 @@ def run_doctor(args):
except Exception as e:
check_warn("Auth provider status", f"(could not check: {e})")

codex_command_override = (
os.getenv("HERMES_CODEX_APP_SERVER_COMMAND", "").strip()
or os.getenv("CODEX_APP_SERVER_COMMAND", "").strip()
or delegation_codex_app_server_command
)
if _safe_which("codex"):
suffix = "(required by delegation.provider: codex-app-server)" if delegation_uses_codex_app_server else ""
check_ok("codex CLI", suffix)
elif delegation_uses_codex_app_server and codex_command_override:
check_ok("Codex app-server command configured", f"({codex_command_override})")
elif delegation_uses_codex_app_server:
check_warn(
"codex CLI not installed",
"(required by delegation.provider: codex-app-server)",
)
check_info("Install with: npm install -g @openai/codex")
issues.append(
"delegation.provider is set to codex-app-server, but the codex CLI is not on PATH. "
"Install it with 'npm install -g @openai/codex' or set HERMES_CODEX_APP_SERVER_COMMAND."
)
else:
# Native OAuth uses Hermes' own device-code flow β€” the Codex CLI is
# only needed if you want to import existing tokens from
# ~/.codex/auth.json. Downgrade to info so users running
# `hermes auth openai-codex` aren't told they're missing something.
check_info(
"codex CLI not installed "
"(optional β€” only required to import tokens from an existing Codex CLI login)"
)
# xAI OAuth β€” separate try/except so an import failure here cannot
# disrupt the already-printed Nous/Codex/Gemini/MiniMax rows above.
try:
Expand Down
30 changes: 30 additions & 0 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ DETECTED_BROWSER_EXECUTABLE=""
# Options
USE_VENV=true
RUN_SETUP=true
INSTALL_CODEX=false
SKIP_BROWSER=false
BRANCH="main"
ENSURE_DEPS=""
Expand All @@ -94,6 +95,8 @@ while [[ $# -gt 0 ]]; do
RUN_SETUP=false
shift
;;
--install-codex)
INSTALL_CODEX=true
--skip-browser|--no-playwright)
SKIP_BROWSER=true
shift
Expand Down Expand Up @@ -127,6 +130,7 @@ while [[ $# -gt 0 ]]; do
echo "Options:"
echo " --no-venv Don't create virtual environment"
echo " --skip-setup Skip interactive setup wizard"
echo " --install-codex Install Codex CLI for codex-app-server delegation"
echo " --skip-browser Skip Playwright/Chromium install (browser tools won't work)"
echo " --branch NAME Git branch to install (default: main)"
echo " --dir PATH Installation directory"
Expand Down Expand Up @@ -1692,6 +1696,31 @@ install_node_deps() {

}

install_codex_cli() {
if [ "$INSTALL_CODEX" = false ]; then
return 0
fi

if [ "$HAS_NODE" = false ] || ! command -v npm &> /dev/null; then
log_warn "Cannot install Codex CLI because npm is not available"
log_info "After installing Node.js, run: npm install -g @openai/codex"
return 0
fi

if command -v codex &> /dev/null; then
log_success "Codex CLI already installed: $(command -v codex)"
return 0
fi

log_info "Installing Codex CLI for codex-app-server delegation..."
if npm install -g @openai/codex; then
log_success "Codex CLI installed"
else
log_warn "Codex CLI install failed"
log_info "Install manually: npm install -g @openai/codex"
fi
}

run_setup_wizard() {
if [ "$RUN_SETUP" = false ]; then
log_info "Skipping setup wizard (--skip-setup)"
Expand Down Expand Up @@ -2052,6 +2081,7 @@ main() {
setup_venv
install_deps
install_node_deps
install_codex_cli
setup_path
copy_config_templates
run_setup_wizard
Expand Down
Loading