Skip to content

evanstinger/omp-coding-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OMP Coding Agent

CI

Durable Hermes Agent delegation to Oh My Pi. Serious coding missions run through a detached OMP RPC worker, survive Hermes process loss, expose bounded live progress when supported, and remain recoverable from owner-only state.

Status: community release v1.2.1. Independent project; not affiliated with, endorsed by, or officially supported by Nous Research, Hermes Agent, or Oh My Pi. No license has been selected for this repository.

Why detached jobs

A normal synchronous child process inherits host lifetime and open pipes. If Hermes exits, a long coding mission can die with it or become impossible to identify safely. This plugin moves ownership to a durable runner:

  1. omp_rpc_coding_task validates request and atomically creates job state.
  2. Hermes launches job_runner.py with stdin=DEVNULL, owner-only stdout/stderr files, start_new_session=True, and closed inherited descriptors.
  3. Detached runner acquires per-job lock, records actual runner identity, starts OMP in its own process group, and owns all RPC pipes.
  4. Runner persists bounded progress and terminal result independently of Hermes.
  5. Same synchronous tool call waits when host remains alive. After host loss, later call attaches by persisted job_id.

New missions deduplicate only against identical active full-request fingerprints. Fingerprint covers task, resolved cwd, timeout, thinking level, resolved session_file or null, OMP executable, and system prompt. Changed field means distinct work. Terminal jobs never deduplicate new submissions.

Runner requires RPC ready, prompt acknowledgement, agent_end, get_last_assistant_text, and get_state before success. It then gathers bounded Git status/diff-stat, terminates remaining OMP process-group members, and reaps leader. Interactive extension UI requests are cancelled because detached execution has no headless answer channel.

Live progress

One tool call remains synchronous, but progress updates replace current Hermes tool row instead of creating conversation messages. Typical sequence:

OMP job <job-id> · queued
OMP process started · awaiting RPC ready
OMP · read — Inspecting (1 tool)
OMP · edit complete (2/2 tools)
OMP process alive · no RPC activity for 42s
OMP job succeeded

Progress includes lifecycle state, allowlisted tool name, optional allowlisted generic action word, bounded counters, timestamps, and process-liveness observations. It excludes mission text, tool arguments/output, assistant/model text, and chain-of-thought. OMP process alive reports process observation only; it does not claim model health or forward progress.

Durable execution and recovery still work when Hermes lacks progress-reporter support. Plugin falls back cleanly and omits live UI updates. Live in-place progress currently requires evanstinger/hermes-agent branch feat/omp-tool-progress until equivalent support is upstream.

Prerequisites

  • macOS with Homebrew OMP executable at /opt/homebrew/bin/omp.
  • OMP configured and able to start RPC mode using default profile.
  • Hermes Agent version with user-plugin discovery and enablement.
  • Python 3.10 or newer.
  • Git available for optional repository summary.

Verify fixed OMP path before enabling:

test -x /opt/homebrew/bin/omp
/opt/homebrew/bin/omp --version

Plugin does not create or modify OMP profile, provider, credential, memory, or Second Brain configuration. Optional durable-memory integration is used only when already configured and available.

Install, enable, reload

HERMES_HOME defaults to ~/.hermes. Recommended public install:

export HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
hermes plugins install evanstinger/omp-coding-agent --enable

Manual equivalent:

export HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
mkdir -p "$HERMES_HOME/plugins"
git clone https://github.com/evanstinger/omp-coding-agent.git \
  "$HERMES_HOME/plugins/omp-coding-agent"
hermes plugins enable omp-coding-agent

Update later:

hermes plugins update omp-coding-agent

Plugin code loads at process start. Reload every long-lived consumer after install, enable, or update:

hermes gateway restart
hermes

Second command starts fresh CLI/TUI process. Fully quit and reopen Hermes Desktop separately. Do not expect already-running gateway, desktop, or TUI process to load changed Python code.

Tool contract and examples

  • Toolset: omp-coding
  • Tool: omp_rpc_coding_task

New mission

{
  "task": "Implement requested change end to end, run relevant checks, and return exact handoff.",
  "cwd": "/absolute/path/to/repository",
  "timeout_seconds": 1800,
  "thinking": "xhigh"
}

task and cwd are required. thinking accepts high or xhigh; default xhigh. Timeout default is 1800 seconds.

Attach active or terminal durable job

{
  "job_id": "<persisted-job-id>"
}

Same call works for both states. Active attach waits on existing runner and forwards its progress; terminal attach returns persisted result immediately. job_id cannot be combined with any new-mission field.

Resume existing OMP session file

{
  "task": "Continue prior OMP session, address remaining issue, verify result, and return handoff.",
  "cwd": "/absolute/path/to/repository",
  "session_file": "/absolute/path/to/existing-session.jsonl",
  "timeout_seconds": 1800,
  "thinking": "xhigh"
}

session_file must be absolute existing file. Plugin passes it through OMP --resume. This is OMP conversation resume, not durable-job attachment; use job_id for latter.

Success JSON includes job_id, final_handoff, OMP session_file and session_id, elapsed seconds, unique OMP tool names, resolved cwd, and optional Git summary. Failures return structured JSON with success: false, stage, bounded error, available job/session context, and bounded stderr; handler does not raise into Hermes.

Recovery when Hermes dies before job ID delivery

Do not blindly resubmit. Job is created before runner launch, so registry may contain live or completed work even when original tool result never reached Hermes.

Default registry:

export HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
export OMP_JOB_REGISTRY="$HERMES_HOME/omp-coding-agent/jobs"

Inspect bounded summaries without printing raw task text:

python3 - <<'PY'
import json
import os
from pathlib import Path

root = Path(os.environ["OMP_JOB_REGISTRY"]).expanduser()
for state_file in sorted(root.glob("*/job.json")):
    try:
        state = json.loads(state_file.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        print(state_file.parent.name, "unreadable", type(exc).__name__)
        continue
    print(
        state_file.parent.name,
        state.get("state"),
        state.get("cwd"),
        state.get("created_at"),
        state.get("updated_at"),
        state.get("request_fingerprint"),
    )
PY

Match resolved cwd, state, timestamps, task fingerprint/request fingerprint, and local mission. For candidate only, inspect owner-local records:

python3 -m json.tool "$OMP_JOB_REGISTRY/<candidate-job-id>/job.json"
python3 -m json.tool "$OMP_JOB_REGISTRY/<candidate-job-id>/request.json"

request.json contains full mission text; keep output local. Once match is unambiguous, attach:

{
  "job_id": "<candidate-job-id>"
}

Corrupt, unreadable, symlinked, mismatched, or otherwise unclassifiable registry entry blocks new launch with bounded registry_corrupt or registry_conflict failure. This fail-closed behavior avoids duplicate work.

Security and privacy model

Treat plugin as privileged code-execution bridge:

  • OMP starts with --approval-mode yolo inside supplied cwd. Only delegate trusted repositories and missions.
  • Plugin itself does not add network transport, but OMP, its tools, model provider, Git, and configured integrations may access network or secrets under their own configuration.
  • Registry root and job directories are forced owner-only (0700); JSON, lock, and log files are created owner-only (0600). Ownership and symlink checks fail closed.
  • request.json stores full mission and resolved paths. job.json stores hashes, process metadata, progress, session identity, log paths, and terminal result. SHA-256 fingerprints are identifiers, not encryption.
  • Progress never copies raw mission, args, result, assistant/model text, or reasoning. Untrusted intent is stripped, secret/high-entropy patterns are rejected, and only fixed generic action word survives.
  • RPC tool-call IDs become fixed-size SHA-256 correlation keys. Malformed response IDs fail as protocol errors.
  • No automatic registry retention exists. Archive or remove terminal job directories only after results are no longer needed; never remove active directory.
  • Tests use inert fake OMP process and explicitly synthetic credential-shaped privacy fixtures. They never contact model or credential provider.

Resource bounds

Resource Bound
RPC NDJSON frame 4,194,304 bytes (4 MiB)
Raw frame queue 4 frames; 16,777,216-byte (16 MiB) envelope
Task input 100,000 characters and 262,144 UTF-8 bytes
cwd / session_file 4,096 characters and 8,192 UTF-8 bytes each
job_id 80 ASCII characters / 80 bytes
Timeout 60–7,200 seconds
Serialized tool result strictly below 48,000 UTF-8 bytes
Final handoff 28,000 UTF-8 bytes before whole-result compaction
Error / returned stderr 4,000 UTF-8 bytes each
Persisted OMP stderr 65,536 bytes; continues draining after truncation
Each Git stdout/stderr capture 65,536 bytes; both pipes continue draining
Returned Git status / diff-stat 6,000 UTF-8 bytes each
Unique OMP tool names 128
Progress intent / tool name / preview 160 / 96 / 320 UTF-8 bytes
Progress counters / sequence 1,000,000 / $2^{63}-1$

Runner heartbeat persists at most every 15 seconds. Host forwards changed sequences and 30-second silence fallback. Host wait adds fixed 60-second finalization allowance covering queued-start grace, bounded process cleanup/reap, five bounded Git commands, terminal persistence, and reader shutdown; successful calls do not sleep for unused allowance.

Compatibility and limitations

  • Runtime target currently macOS/Homebrew because executable path is fixed to /opt/homebrew/bin/omp.
  • CI exercises stdlib plugin and detached-runner semantics on Linux and macOS with fake OMP. This does not claim supported Linux OMP deployment with current fixed path.
  • POSIX process groups, signals, file locking, and owner permission checks are required. Native Windows runtime is not supported by this release.
  • Default OMP profile is used; plugin intentionally passes no --profile.
  • Interactive OMP extension UI is cancelled in detached mode. Passive UI frames are ignored.
  • One Hermes tool call waits synchronously while host lives; durability protects underlying work, not host call continuity.
  • Registry retention/cleanup is manual.
  • Live UI progress requires compatible Hermes core; durability does not.

Troubleshooting

runner_launch or OMP executable error

test -x /opt/homebrew/bin/omp
/opt/homebrew/bin/omp --version

Current release does not resolve OMP from PATH.

Plugin missing from Hermes

hermes plugins list
hermes plugins enable omp-coding-agent

Then restart CLI/TUI/Desktop or run hermes gateway restart.

Job appears stuck

Inspect job.json.progress.sequence, phase, updated_at, last_rpc_activity_at, runner_heartbeat_at, liveness booleans, bounded counters, and safe current-tool fields. Inspect owner-only runner/OMP stderr logs for failure details. Changing heartbeat proves runner observation only. Reattach by job_id; do not submit duplicate mission.

Registry failure blocks launch

Do not delete unknown active state. Inspect named entry ownership, symlink status, job.json, request.json, and runner lock. Repair/archive only after confirming no live runner owns directory.

Development

No real model is called. Suite uses tests/fake_omp.py as local NDJSON RPC executable. Select an explicit Python 3.10+ interpreter; macOS may still resolve bare /usr/bin/python3 to Python 3.9.

# Override on other platforms or when Homebrew Python lives elsewhere.
PYTHON_BIN="${PYTHON_BIN:-/opt/homebrew/bin/python3}"

"$PYTHON_BIN" -c \
  'import sys; assert sys.version_info >= (3, 10), "Python 3.10+ required"'
"$PYTHON_BIN" -m venv .venv
. .venv/bin/activate
python -m pip install --disable-pip-version-check --no-cache-dir ruff==0.15.22
python -m unittest discover -s tests -p 'test_*.py' -v
python -m py_compile __init__.py job_runner.py tests/*.py
python -m ruff check .

CI matrix covers Python 3.10–3.14 on Linux and macOS.

Repository layout

Path Purpose
__init__.py Hermes registration, validation, durable registry, progress, RPC, cleanup, and result bounds
job_runner.py Minimal detached entry point loading one persisted job
omp-system-prompt.txt Generic worker behavior and optional-memory handoff policy
plugin.yaml Hermes plugin manifest; version 1.2.1
tests/fake_omp.py Inert local OMP RPC fixture
tests/test_plugin.py Tool contract, lifecycle, registration, resume, and recovery tests
tests/test_durability.py Host-loss survival test
tests/test_fail_closed.py Bounds, process cleanup, registry, and protocol failure tests
tests/test_progress.py Progress privacy, heartbeat, attachment, and terminal-state tests
.github/workflows/ci.yml Least-privilege Linux/macOS verification matrix

About

Durable OMP RPC coding jobs for Hermes Agent, with crash recovery and bounded live progress.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages