Skip to content

feat(acp): generic ACP harness + Omnigent-tool MCP bridge for all ACP harnesses - #2152

Merged
dhruv0811 merged 4 commits into
mainfrom
acp-harness
Jul 8, 2026
Merged

feat(acp): generic ACP harness + Omnigent-tool MCP bridge for all ACP harnesses#2152
dhruv0811 merged 4 commits into
mainfrom
acp-harness

Conversation

@dhruv0811

Copy link
Copy Markdown
Member

What

Adds a generic acp harness that connects Omnigent to any agent speaking the Agent Client Protocolgemini --experimental-acp, npx @zed-industries/claude-code-acp, goose acp, qwen --acp, or a custom in-house agent — plus exposes Omnigent's builtin tools to all three ACP harnesses (acp, goose, qwen) over ACP's native session/new.mcpServers.

Omnigent already shipped two hand-rolled ACP clients (goose, qwen) that duplicated the entire protocol. This generalizes that pattern into one well-tested client so every other ACP agent is reachable with zero new code — no new dependency (the in-repo ACP client is reused, not the PyPI SDK).

UX

Register named agents once (omnigent setup → configure harnesses → Custom ACP agent), stored in a new top-level acp: config block:

acp:
  agents:
    - {name: Gemini CLI,  command: gemini --experimental-acp}
    - {name: Claude Code, command: npx -y @zed-industries/claude-code-acp}
    - {name: Goose,       command: goose acp, model: gpt-5.3}

Each configured agent appears as its own harness-picker row (acp:<slug>) and chats like goose: streaming, tool-call cards, and mid-turn approvals as web-elicitation cards. Auth is each agent's own (OWN_AUTH) — Omnigent stores no credential. The command is resolved from config at spawn, so editing it updates every agent.

How it works

  • Routing: the registry stays one acp harness; a configured agent is addressed as acp:<slug> which canonicalize_harness maps to acp for validity/module resolution, while _build_acp_spawn_env resolves the slug → command from config. No per-agent registry entries.
  • Omnigent MCP bridge: reuses the same serve-mcp stdio relay the native harnesses use. The agent spawns serve-mcp, which proxies each Omnigent tool call back through the adapter-injected _tool_executorctx.dispatch_tool → the Omnigent server (TOOL_CALL / TOOL_RESULT policy enforced). Shared helper omnigent/inner/_acp_omnigent_mcp.py. Global kill switch OMNIGENT_ACP_MCP=0; the generic acp also has a per-agent omnigent_mcp flag. A new allow-listed bridge root ($TMPDIR/omnigent-<uid>/acp-mcp) was added to claude_native_bridge.
  • Improvements over the goose path baked into the generic client: tool-call cards (tool_callToolCallRequest/Complete), reasoning (agent_thought_chunkReasoningChunk), and a real interrupt via ACP session/cancel.

Files

New: omnigent/inner/acp_executor.py (generic AcpExecutor + AcpAgentConfig), acp_harness.py (wrap), onboarding/acp_auth.py (the acp: config block), inner/_acp_omnigent_mcp.py (shared MCP relay helper), and two test modules.

Edited (small/additive): harness_plugins.py (register + dynamic acp:<slug> catalog rows), harness_aliases.py + runtime/harnesses/process_manager.py (the acp:*acp shim), runtime/workflow.py (_build_acp_spawn_env), runner/app.py (dispatch), onboarding/harness_readiness.py, cli.py (_manage_acp_harness), claude_native_bridge.py (ACP-MCP bridge root helpers), and inner/goose_executor.py + inner/qwen_executor.py (~6 lines each to opt into the shared MCP relay). The web picker needs no change — its label fetch already merges /v1/harnesses catalog rows.

Testing

  • New: unit coverage (argv, both session/new shapes, tool-call → event mapping, permission outcome, interrupt, MCP-server construction) + a hermetic fake-ACP-agent e2e that drives a real subprocess through handshake → streaming → tool card → permission (approve/deny) → completion with no vendor binary, plus a real relay start/teardown.
  • Regression green: test_acp_executor (42), test_acp_spawn_env (8), test_goose_executor + test_qwen_executor, test_harness_capabilities, and test_claude_native_bridge (153, since the shared bridge file changed).
  • ruff clean; no uv.lock / package-lock.json changes.

Caveats / follow-ups

  • An Omnigent-MCP tool call may currently render twice (the agent's own tool_call card + the dispatch's function_call); to be verified/refined against real agent tool-naming during live testing.
  • With os_env.sandbox != none, serve-mcp (spawned by the sandboxed agent) may not reach the loopback relay; default is none.
  • Follow-ups: fold goose/qwen fully onto AcpExecutor (only the MCP feature is shared today), picker presentation polish, and ACP-registry auto-download.

Note on the push

The pre-push secret scan flagged pre-existing postgres-connection-string false positives in an unrelated older commit (omnigent/db/utils.py, deploy/docker/entrypoint.py, existing cli.py lines) — none in this PR's diff — so it was pushed with SKIP_SECRET_SCAN=1.

This pull request and its description were written by Isaac.

… harnesses

Add a generic `acp` harness that connects Omnigent to ANY agent speaking the Agent Client Protocol (gemini --experimental-acp, @zed-industries/claude-code-acp, goose, qwen, custom in-house agents). Users register named agents in an `acp:` config block via `omnigent setup`; each surfaces as its own harness-picker row (`acp:<slug>`) and drives one well-tested ACP client. Generalized from the existing (duplicated) goose/qwen ACP executors; no new dependency.

Also expose Omnigent's builtin tools (sys_*, load_skill, web_fetch, policy tools) to ALL three ACP harnesses (acp, goose, qwen) via ACP's native session/new.mcpServers, reusing the shared serve-mcp stdio relay the native harnesses use — tool calls route through ctx.dispatch_tool so Omnigent policy is enforced. Shared helper omnigent/inner/_acp_omnigent_mcp.py; global kill switch OMNIGENT_ACP_MCP=0 (generic acp also has a per-agent omnigent_mcp flag).

Routing: the registry stays one `acp` harness; a configured agent is addressed as `acp:<slug>` (canonicalizes to `acp`), command resolved from config at spawn. Improvements over the goose path baked into the generic client: tool-call cards, reasoning (agent_thought_chunk), and a real interrupt via ACP session/cancel.

Tests: unit + a hermetic fake-ACP-agent e2e (handshake -> stream -> tool card -> permission -> completion, no vendor binary) + a real relay start/teardown; goose/qwen/claude_native_bridge/capabilities regressions green.

Co-authored-by: Isaac
@github-actions github-actions Bot added the size/XL Pull request size: XL label Jul 8, 2026
@omnigent-ci

omnigent-ci Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Polly AI Review

Missing visual demonstration

This PR is substantially user-visible: it adds a new omnigent setup → Custom ACP agent onboarding drill-in (_manage_acp_harness, add/remove/examples menus), new acp:<slug> rows in the harness picker, and a full chat surface (streaming text, tool-call cards, reasoning, and mid-turn approval/elicitation cards). The description narrates this UX in detail but the "Attached images/videos" scan found none. Please attach a short screen recording or screenshots showing (a) the setup flow registering an agent, (b) the new picker row, and (c) a live turn with a tool-call card + an approve/deny elicitation card, so reviewers can confirm the rendering without booting a vendor binary.

Blocking issues

None. I traced the protocol flow, permission path, MCP-bridge wiring, session/restart handling, and the acp:<slug> routing shims and did not find a correctness bug, broken contract, or data-loss risk introduced by the diff. The command is shlex.split + create_subprocess_exec (never a shell), so there is no command-injection surface; image blocks reject non-data: URIs (no SSRF); and the catalog/readiness readers swallow malformed acp: blocks rather than crashing.

Security vulnerabilities

No new vulnerability, but two boundary points worth confirming (both rely on existing enforcement being correct):

  • MCP bridge scope. prepare_acp_mcp_bridge_dir writes a token-only bridge.json so serve-mcp advertises only the relay tools (not raw sys_os_*), and all relayed calls go through ctx.dispatch_tool where TOOL_CALL/TOOL_RESULT policy is enforced. This is the same relay the native harnesses use and is gated by OMNIGENT_ACP_MCP/omnigent_mcp. Correct as written — just note that this now exposes sys_session_* / sys_agent_* / web_fetch to arbitrary third-party ACP agents by default; that is the intended design, and the policy layer is the control.
  • fs delegation vs. sandbox. _fs_delegation is enabled whenever os_env is not None and not fork, and fs/read_text_file / fs/write_text_file execute through OSEnvironment outside any bwrap/seatbelt wrapper applied to the agent binary. This is only safe if OSEnvironment.read/write independently enforce the spec's read/write roots (the docstring claims it does). If it does not, a sandboxed agent could read/write outside its confinement by delegating fs ops. Worth a targeted test with sandbox != none to confirm root enforcement on the delegated path.

Non-blocking notes

  • CLI add-agent can't reach Qwen-shaped agents. _add_acp_agent only prompts name/command/model; it never sets session_id_mode="client" or send_model=True. The examples list suggests qwen --acp, but an agent registered through the wizard always gets server mode with no model in session/new, which may not work for Qwen-style agents. Users must hand-edit config.yaml to reach those knobs — worth surfacing in the prompt or docs.
  • Timeout leaks a pending future. On the _PROMPT_TIMEOUT_SECONDS path run_turn yields ExecutorError(retryable=True) without popping self._pending[req_id]; the entry lingers until a late response resolves it. Harmless but untidy.
  • Restart retains _session_id. _start_process (on subprocess death/restart) resets _initialized but not _session_id/_system_prompt_sent, so the first post-restart turn reuses a session id the new process doesn't know and fails with "Session not found" before self-healing on the next turn. It recovers, but one turn is spent on the error; resetting session state in _start_process would avoid the wasted round-trip.
  • Idle-timeout semantics. The 300s deadline resets on every inbound message, so a chatty-but-stuck agent that keeps streaming never times out. Acceptable, but the docstring's "time-without-progress" framing is slightly optimistic since any chunk counts as progress.

Summary

Solid, well-scoped generalization: it collapses two hand-rolled ACP clients into one reusable executor, reuses the existing serve-mcp relay (no new dependency), routes the acp:<slug> id cleanly through canonicalization/module-resolution/spawn-env, and fails soft everywhere the bridge or config could be malformed. Test coverage (unit + hermetic fake-agent e2e) is strong. No blocking correctness or security defects found in the diff; the main asks are a visual demonstration for the new UI surfaces and confirmation that delegated fs ops honor sandbox roots when a sandbox is active.


Automated review by Polly · workflow run

Comment thread omnigent/inner/acp_executor.py Fixed
Comment thread omnigent/inner/acp_executor.py Fixed
if self._reader_task:
self._reader_task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await self._reader_task
if self._stderr_task:
self._stderr_task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await self._stderr_task
Comment thread omnigent/harness_plugins.py Fixed
Comment thread tests/inner/test_acp_executor.py Fixed
CI: ruff-format all touched files (pre-commit); move 'Custom ACP agent' to the end of the configure-harnesses list + update the position/priority tests; add 'acp' to the harness-readiness map expectations (config-gated, not CLI-gated); exclude the generic 'acp' harness from the no-agent live-binary matrix (it has no fixed binary).

AI review: comment the two expected-shutdown empty-except blocks in acp_executor; use module _logger instead of a redundant local 'import logging' in harness_plugins.harness_catalog; drop an unused fake_rpc in the acp tests.

Co-authored-by: Isaac
@dhruv0811

Copy link
Copy Markdown
Member Author

/review

@omnigent-ci

omnigent-ci Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Polly AI Review

Missing visual demonstration

This PR adds user-visible surfaces — a new Custom ACP agent onboarding drill-in (_manage_acp_harness add/list/remove flow, _print_acp_examples), new harness-picker rows (acp:<slug> catalog entries), and mid-turn approval / tool-call cards streamed from ACP agents. The PR description contains no screenshots or video (attachment scan: "(none found)"). Please attach a short screen capture or screenshots of the setup flow (add/remove an agent) and the picker row + a live ACP chat with a tool-call card / approval so reviewers can confirm the UX without checking out the branch.

Blocking issues

None. I traced the transport, handshake, permission, fs-delegation, MCP-relay, and spawn-env paths and did not find a correctness bug, broken contract, or data-loss risk that rises to blocking. Notably:

  • shlex.split(command) + create_subprocess_exec (no shell) — no command-injection surface even though the command is user config.
  • Permission flow fails closed on POLICY_ACTION_ASK with no elicitation handler; the fall-open-to-allow branch only triggers when neither policy nor elicitation is wired (standalone/unit-test use), matching the goose/qwen precedent.
  • Response/request disambiguation in _read_stdout (id present + "method" not in msg) correctly prevents an agent-initiated request id from mis-resolving a client future.
  • EOF wakes pending futures so run_turn fails fast; completion waits for fut.done() AND queue empty so trailing chunks aren't truncated.

Security vulnerabilities

No new weakness found.

  • The Omnigent-tool MCP relay reuses the same serve-mcp bridge the native harnesses use; exposed tool calls still route through ctx.dispatch_tool where TOOL_CALL / TOOL_RESULT policy is enforced, and prepare_acp_mcp_bridge_dir writes a token-only bridge.json so raw sys_os_* fs tools are not served to the agent.
  • New allow-listed bridge root ($TMPDIR/omnigent-<uid>/acp-mcp) follows the identical uid-scoped parent.parent trust shape as cursor/qwen/hermes-native — consistent, not a loosening.
  • Image handling only accepts inline image/* data URIs; external URLs are never fetched (_parse_image_data_uri returns None), avoiding SSRF.
  • No pyproject.toml / lockfile changes and no new dependency, so no package/extra concerns.

Non-blocking notes

  • Silent agent substitution in _build_acp_spawn_env. When a spec references acp:<slug> but that slug is no longer in config (e.g. the agent was renamed/removed), the builder falls back to acp_agents()[0] — i.e. it launches a different configured agent than the spec named, with no warning. This can run an unexpected command/agent under a spec that requested a specific one. Consider logging a warning (or leaving HARNESS_ACP_COMMAND unset to surface the clear request-time error) when a named slug can't be resolved, rather than silently picking the first agent. The bare-acp (no slug) → first-agent fallback is reasonable; the unknown-slug case is the surprising one.
  • _to_acp_mcp_servers assumes spec["command"] exists on each server entry; a malformed build_mcp_config entry would raise KeyError inside the try in session_new_servers, which is caught and degrades to no Omnigent tools — safe, but a spec.get("command") guard would be tidier.
  • _permission_outcome allow path falls back to allow_always when the agent offers no allow_once; the docstring's "never persist a blanket always allow" intent isn't fully guaranteed for such agents. Minor / best-effort as written.
  • The session_id_mode / send_model per-agent knobs exist in AcpAgentEntry and are read by _build_acp_spawn_env, but the interactive _add_acp_agent flow only prompts for name/command/model — the protocol knobs are config-file-only. Fine if intentional; worth a doc note.

Summary

A well-structured generalization: it collapses the duplicated goose/qwen ACP clients into one configurable executor, wires the shared serve-mcp relay into all three ACP harnesses without adding a dependency, and keeps security boundaries (policy-gated tool dispatch, token-only bridge, no-shell exec, uid-scoped bridge root) consistent with existing native harnesses. Error handling on the transport and permission paths is careful and fails closed where it matters. No blocking correctness or security issues; the main follow-ups are the silent unknown-slug agent substitution and adding a visual demonstration of the new onboarding/picker UX to the PR description.


Automated review by Polly · workflow run

dhruv0811 added 2 commits July 8, 2026 20:50
…ses row

Previously the setup 'configure harnesses' overview showed a single 'Custom ACP agent' row and the individual agents were buried in the drill-in. Now each configured ACP agent gets its own top-level row (alongside the built-in harnesses), plus an 'Add custom ACP agent' row — matching the web picker, which already lists each acp:<slug>. All rows route to the shared ACP manager (add/edit/remove); a per-agent edit drill-in is a follow-up. No agents configured → unchanged single 'Custom ACP agent' row.

Co-authored-by: Isaac
Addresses UX feedback on the ACP rows: (1) the Add row jumps straight into the add flow (prints examples, then prompts) instead of a second add/remove menu; (2) it renders with no ✗ glyph (new 'action' status kind); (3) Remove now lives on each agent's own row via a per-agent drill-in (_manage_acp_agent). Deletes the now-unused combined _manage_acp_harness / _remove_acp_agent.

Co-authored-by: Isaac
@dhruv0811
dhruv0811 enabled auto-merge (squash) July 8, 2026 21:33
@dhruv0811
dhruv0811 merged commit c2822b3 into main Jul 8, 2026
70 of 71 checks passed
@github-actions github-actions Bot added the needs-doc-update Merged PR needs a user-facing docs update label Jul 8, 2026
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🏷️ Doc impact: needs-doc-update

Adds a new generic "acp" harness (integration) plus its omnigent setup "Custom ACP agent" configuration flow and acp: config block, so the harness/integration and setup docs need updating.

Drafting a docs PR to omnigent-ai/omnigent-site (staged on 0.5-docs until release)…

Auto-classified on merge. Set the label manually before merging to override. · run

PattaraS added a commit that referenced this pull request Jul 9, 2026
…erved (#2280)

`omni run --harness acp:<slug>` (a configured ACP agent, e.g. acp:qwenacp)
failed at spec synthesis: _materialize_harness_launcher_file put the harness id
straight into the agent `name`, and the agent-name validator rejects the colon
("name must match [a-zA-Z0-9_-]+"). The generic ACP harness (#2152) intends
acp:<slug> as the run-time addressing form (canonicalizes to `acp`, command
resolved from the acp: config block at spawn), but this no-AGENT launcher path
was missed.

Fix: keep the FULL acp:<slug> in executor.harness (canonicalize_harness drops
the slug to bare `acp`, which would lose the agent selection), and sanitize the
colon (":" -> "-") for the agent NAME and temp filename only, which must be
[a-zA-Z0-9_-]+ / path-safe. Non-acp harnesses are unchanged: name still uses the
raw input (claude -> "claude"), executor/filename still canonicalize (claude ->
claude-sdk, kimi alias -> kimi). Added an acp:<slug> launcher test; existing
launcher tests green.
dosenr added a commit to dosenr/omnigent that referenced this pull request Jul 9, 2026
The batch hermes harness returns stdout only when a turn ends, so the
transcript gets one end-of-turn write. hermes-acp drives Hermes' ACP mode
(hermes acp, JSON-RPC over stdio) through the generic AcpExecutor (omnigent-ai#2152),
adding only the Hermes layer on top:

- per-session HERMES_HOME wiring the pre_tool_call hook (the complete policy
  gate: Hermes raises session/request_permission for only a subset of its
  tools, so permission requests are auto-allowed beneath the hook)
- self-executed vs MCP-bridge tool-call classification: bridge calls keep the
  generic dispatch pairing; self-executed calls are marked so the adapter
  renders the card without a dispatch-correlation enqueue and re-emits a
  completed function_call at completion so the card persists
- Hermes usage normalization (cache-inclusive inputTokens split) and
  session/new extras (model, skills filter) with reject-retry

Adds a _spawn_env seam to AcpExecutor so subclasses can scope the agent's
config HOME without re-implementing _start_process.

Signed-off-by: dosenr <robert.dosen@gmail.com>
dosenr added a commit to dosenr/omnigent that referenced this pull request Jul 9, 2026
The batch hermes harness returns stdout only when a turn ends, so the
transcript gets one end-of-turn write. hermes-acp drives Hermes' ACP mode
(hermes acp, JSON-RPC over stdio) through the generic AcpExecutor (omnigent-ai#2152),
adding only the Hermes layer on top:

- per-session HERMES_HOME wiring the pre_tool_call hook (the complete policy
  gate: Hermes raises session/request_permission for only a subset of its
  tools, so permission requests are auto-allowed beneath the hook)
- self-executed vs MCP-bridge tool-call classification: bridge calls keep the
  generic dispatch pairing; self-executed calls are marked so the adapter
  renders the card without a dispatch-correlation enqueue and re-emits a
  completed function_call at completion so the card persists
- Hermes usage normalization (cache-inclusive inputTokens split) and
  session/new extras (model, skills filter) with reject-retry

Adds a _spawn_env seam to AcpExecutor so subclasses can scope the agent's
config HOME without re-implementing _start_process.

Signed-off-by: dosenr <robert.dosen@gmail.com>
dosenr added a commit to dosenr/omnigent that referenced this pull request Jul 9, 2026
The batch hermes harness returns stdout only when a turn ends, so the
transcript gets one end-of-turn write. hermes-acp drives Hermes' ACP mode
(hermes acp, JSON-RPC over stdio) through the generic AcpExecutor (omnigent-ai#2152),
adding only the Hermes layer on top:

- per-session HERMES_HOME wiring the pre_tool_call hook (the complete policy
  gate: Hermes raises session/request_permission for only a subset of its
  tools, so permission requests are auto-allowed beneath the hook)
- self-executed vs MCP-bridge tool-call classification: bridge calls keep the
  generic dispatch pairing; self-executed calls are marked so the adapter
  renders the card without a dispatch-correlation enqueue and re-emits a
  completed function_call at completion so the card persists
- Hermes usage normalization (cache-inclusive inputTokens split) and
  session/new extras (model, skills filter) with reject-retry

Adds a _spawn_env seam to AcpExecutor so subclasses can scope the agent's
config HOME without re-implementing _start_process.

Signed-off-by: dosenr <robert.dosen@gmail.com>
jestyr27 added a commit to uhstray-io/huhhb that referenced this pull request Jul 9, 2026
One fix per CodeRabbit PR #25 finding:
- MODEL-MANIFEST: mark older Fable ORCHESTRATOR/ARCHITECT-ONLY entry as
  superseded by the escalation-only directive above it
- cross-review: add gemini-complex to the sys_session_send agent enum
- investigate: add gemini-lite/gemini-complex to the dispatch enum
  (args.model contradiction was already fixed in b90f85d)
- README + config.yaml: Gemini CLI tier callout - deployment runs a
  team/business subscription, live-verified 2026-07-09 via flash-lite
  round-trip; consumer-tier restriction reports documented
- README + config.yaml: upstream generic ACP harness confirmed MERGED
  2026-07-08 (omnigent-ai/omnigent#2152); runtime version requirement
  documented
- config.yaml + routing-guide: Pro-tier subscription wording corrected
  to team/business per operator
- gemini-* agent configs: keep-in-sync markers (bundles are
  self-contained; no cross-bundle YAML anchor/include mechanism)
- investigate/cross-review/routing-guide: backtick bare gemini-*
  tokens (markdownlint MD037 / stray-emphasis rendering)
dhruv0811 pushed a commit to omnigent-ai/omnigent-site that referenced this pull request Jul 10, 2026
Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
dosenr added a commit to dosenr/omnigent that referenced this pull request Jul 10, 2026
The batch hermes harness returns stdout only when a turn ends, so the
transcript gets one end-of-turn write. hermes-acp drives Hermes' ACP mode
(hermes acp, JSON-RPC over stdio) through the generic AcpExecutor (omnigent-ai#2152),
adding only the Hermes layer on top:

- per-session HERMES_HOME wiring the pre_tool_call hook (the complete policy
  gate: Hermes raises session/request_permission for only a subset of its
  tools, so permission requests are auto-allowed beneath the hook)
- self-executed vs MCP-bridge tool-call classification: bridge calls keep the
  generic dispatch pairing; self-executed calls are marked so the adapter
  renders the card without a dispatch-correlation enqueue and re-emits a
  completed function_call at completion so the card persists
- Hermes usage normalization (cache-inclusive inputTokens split) and
  session/new extras (model, skills filter) with reject-retry

Adds a _spawn_env seam to AcpExecutor so subclasses can scope the agent's
config HOME without re-implementing _start_process.

Signed-off-by: dosenr <robert.dosen@gmail.com>
Signed-off-by: rdosen <robert.dosen@gmail.com>
dosenr added a commit to dosenr/omnigent that referenced this pull request Jul 10, 2026
The batch hermes harness returns stdout only when a turn ends, so the
transcript gets one end-of-turn write. hermes-acp drives Hermes' ACP mode
(hermes acp, JSON-RPC over stdio) through the generic AcpExecutor (omnigent-ai#2152),
adding only the Hermes layer on top:

- per-session HERMES_HOME wiring the pre_tool_call hook (the complete policy
  gate: Hermes raises session/request_permission for only a subset of its
  tools, so permission requests are auto-allowed beneath the hook)
- self-executed vs MCP-bridge tool-call classification: bridge calls keep the
  generic dispatch pairing; self-executed calls are marked so the adapter
  renders the card without a dispatch-correlation enqueue and re-emits a
  completed function_call at completion so the card persists
- Hermes usage normalization (cache-inclusive inputTokens split) and
  session/new extras (model, skills filter) with reject-retry

Adds a _spawn_env seam to AcpExecutor so subclasses can scope the agent's
config HOME without re-implementing _start_process.

Signed-off-by: dosenr <robert.dosen@gmail.com>
Signed-off-by: rdosen <robert.dosen@gmail.com>
dhruv0811 added a commit to omnigent-ai/omnigent-site that referenced this pull request Jul 10, 2026
* docs: document omnigent-ai/omnigent#1722 (#261)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2018 (#265)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#1386 (#272)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2156 (#278)

* docs: document omnigent-ai/omnigent#2156

* Apply suggestion from @serena-ruan

* Apply suggestions from code review

Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2022 (#268)

* docs: document omnigent-ai/omnigent#2022

* docs: add steering gif and simplify message queue section

Co-authored-by: Isaac

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <serena.rxy@gmail.com>

* chore(api): sync openapi.json from omnigent@3c7a558 (#274)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#526 (#279)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: rename hindsight extra to memory (omnigent[memory]) (#282)

The memory tools ship under the `memory` extra (omnigent[memory]), not
`hindsight`. Update the install instruction and extra name to match.
The Hindsight product name and the hindsight_* tool names are unchanged.

* docs: document default base branch for new worktrees (#284)

* docs: document default base branch for new worktrees

* docs: condense worktree branches section and add setting demo gif

Co-authored-by: Isaac

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <serena.rxy@gmail.com>

* chore(api): sync openapi.json from omnigent@7fb779f (#281)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* chore(api): sync openapi.json from omnigent@60e775a (#288)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2152 (#280)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#1859 (#277)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2135 (#276)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>
Co-authored-by: Serena Ruan <serena.rxy@gmail.com>
Co-authored-by: Pat Sukprasert <pattara.sk127@gmail.com>
Co-authored-by: Dhruv Gupta <dhruv.gupta@databricks.com>
dosenr added a commit to dosenr/omnigent that referenced this pull request Jul 12, 2026
The batch hermes harness returns stdout only when a turn ends, so the
transcript gets one end-of-turn write. hermes-acp drives Hermes' ACP mode
(hermes acp, JSON-RPC over stdio) through the generic AcpExecutor (omnigent-ai#2152),
adding only the Hermes layer on top:

- per-session HERMES_HOME wiring the pre_tool_call hook (the complete policy
  gate: Hermes raises session/request_permission for only a subset of its
  tools, so permission requests are auto-allowed beneath the hook)
- self-executed vs MCP-bridge tool-call classification: bridge calls keep the
  generic dispatch pairing; self-executed calls are marked so the adapter
  renders the card without a dispatch-correlation enqueue and re-emits a
  completed function_call at completion so the card persists
- Hermes usage normalization (cache-inclusive inputTokens split) and
  session/new extras (model, skills filter) with reject-retry

Adds a _spawn_env seam to AcpExecutor so subclasses can scope the agent's
config HOME without re-implementing _start_process.

Signed-off-by: dosenr <robert.dosen@gmail.com>
Signed-off-by: rdosen <robert.dosen@gmail.com>
dosenr added a commit to dosenr/omnigent that referenced this pull request Jul 13, 2026
The batch hermes harness returns stdout only when a turn ends, so the
transcript gets one end-of-turn write. hermes-acp drives Hermes' ACP mode
(hermes acp, JSON-RPC over stdio) through the generic AcpExecutor (omnigent-ai#2152),
adding only the Hermes layer on top:

- per-session HERMES_HOME wiring the pre_tool_call hook (the complete policy
  gate: Hermes raises session/request_permission for only a subset of its
  tools, so permission requests are auto-allowed beneath the hook)
- self-executed vs MCP-bridge tool-call classification: bridge calls keep the
  generic dispatch pairing; self-executed calls are marked so the adapter
  renders the card without a dispatch-correlation enqueue and re-emits a
  completed function_call at completion so the card persists
- Hermes usage normalization (cache-inclusive inputTokens split) and
  session/new extras (model, skills filter) with reject-retry

Adds a _spawn_env seam to AcpExecutor so subclasses can scope the agent's
config HOME without re-implementing _start_process.

Signed-off-by: dosenr <robert.dosen@gmail.com>
Signed-off-by: rdosen <robert.dosen@gmail.com>
yours-aditya pushed a commit to yours-aditya/omnigent that referenced this pull request Jul 16, 2026
… harnesses (omnigent-ai#2152)

* feat(acp): generic ACP harness + Omnigent-tool MCP bridge for all ACP harnesses

Add a generic `acp` harness that connects Omnigent to ANY agent speaking the Agent Client Protocol (gemini --experimental-acp, @zed-industries/claude-code-acp, goose, qwen, custom in-house agents). Users register named agents in an `acp:` config block via `omnigent setup`; each surfaces as its own harness-picker row (`acp:<slug>`) and drives one well-tested ACP client. Generalized from the existing (duplicated) goose/qwen ACP executors; no new dependency.

Also expose Omnigent's builtin tools (sys_*, load_skill, web_fetch, policy tools) to ALL three ACP harnesses (acp, goose, qwen) via ACP's native session/new.mcpServers, reusing the shared serve-mcp stdio relay the native harnesses use — tool calls route through ctx.dispatch_tool so Omnigent policy is enforced. Shared helper omnigent/inner/_acp_omnigent_mcp.py; global kill switch OMNIGENT_ACP_MCP=0 (generic acp also has a per-agent omnigent_mcp flag).

Routing: the registry stays one `acp` harness; a configured agent is addressed as `acp:<slug>` (canonicalizes to `acp`), command resolved from config at spawn. Improvements over the goose path baked into the generic client: tool-call cards, reasoning (agent_thought_chunk), and a real interrupt via ACP session/cancel.

Tests: unit + a hermetic fake-ACP-agent e2e (handshake -> stream -> tool card -> permission -> completion, no vendor binary) + a real relay start/teardown; goose/qwen/claude_native_bridge/capabilities regressions green.

Co-authored-by: Isaac

* fix(acp): resolve CI failures + address AI-review comments

CI: ruff-format all touched files (pre-commit); move 'Custom ACP agent' to the end of the configure-harnesses list + update the position/priority tests; add 'acp' to the harness-readiness map expectations (config-gated, not CLI-gated); exclude the generic 'acp' harness from the no-agent live-binary matrix (it has no fixed binary).

AI review: comment the two expected-shutdown empty-except blocks in acp_executor; use module _logger instead of a redundant local 'import logging' in harness_plugins.harness_catalog; drop an unused fake_rpc in the acp tests.

Co-authored-by: Isaac

* feat(acp): list each configured ACP agent as its own configure-harnesses row

Previously the setup 'configure harnesses' overview showed a single 'Custom ACP agent' row and the individual agents were buried in the drill-in. Now each configured ACP agent gets its own top-level row (alongside the built-in harnesses), plus an 'Add custom ACP agent' row — matching the web picker, which already lists each acp:<slug>. All rows route to the shared ACP manager (add/edit/remove); a per-agent edit drill-in is a follow-up. No agents configured → unchanged single 'Custom ACP agent' row.

Co-authored-by: Isaac

* fix(acp): per-agent remove + straight-to-add in configure-harnesses

Addresses UX feedback on the ACP rows: (1) the Add row jumps straight into the add flow (prints examples, then prompts) instead of a second add/remove menu; (2) it renders with no ✗ glyph (new 'action' status kind); (3) Remove now lives on each agent's own row via a per-agent drill-in (_manage_acp_agent). Deletes the now-unused combined _manage_acp_harness / _remove_acp_agent.

Co-authored-by: Isaac
Signed-off-by: Aditya Devarapalli <adityareddyd2@gmail.com>
yours-aditya pushed a commit to yours-aditya/omnigent that referenced this pull request Jul 16, 2026
…erved (omnigent-ai#2280)

`omni run --harness acp:<slug>` (a configured ACP agent, e.g. acp:qwenacp)
failed at spec synthesis: _materialize_harness_launcher_file put the harness id
straight into the agent `name`, and the agent-name validator rejects the colon
("name must match [a-zA-Z0-9_-]+"). The generic ACP harness (omnigent-ai#2152) intends
acp:<slug> as the run-time addressing form (canonicalizes to `acp`, command
resolved from the acp: config block at spawn), but this no-AGENT launcher path
was missed.

Fix: keep the FULL acp:<slug> in executor.harness (canonicalize_harness drops
the slug to bare `acp`, which would lose the agent selection), and sanitize the
colon (":" -> "-") for the agent NAME and temp filename only, which must be
[a-zA-Z0-9_-]+ / path-safe. Non-acp harnesses are unchanged: name still uses the
raw input (claude -> "claude"), executor/filename still canonicalize (claude ->
claude-sdk, kimi alias -> kimi). Added an acp:<slug> launcher test; existing
launcher tests green.

Signed-off-by: Aditya Devarapalli <adityareddyd2@gmail.com>
daniellok-db added a commit to omnigent-ai/omnigent-site that referenced this pull request Jul 17, 2026
* docs: document omnigent-ai/omnigent#1722 (#261)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2018 (#265)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#1386 (#272)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2156 (#278)

* docs: document omnigent-ai/omnigent#2156

* Apply suggestion from @serena-ruan

* Apply suggestions from code review

Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2022 (#268)

* docs: document omnigent-ai/omnigent#2022

* docs: add steering gif and simplify message queue section

Co-authored-by: Isaac

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <serena.rxy@gmail.com>

* chore(api): sync openapi.json from omnigent@3c7a558 (#274)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#526 (#279)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: rename hindsight extra to memory (omnigent[memory]) (#282)

The memory tools ship under the `memory` extra (omnigent[memory]), not
`hindsight`. Update the install instruction and extra name to match.
The Hindsight product name and the hindsight_* tool names are unchanged.

* docs: document default base branch for new worktrees (#284)

* docs: document default base branch for new worktrees

* docs: condense worktree branches section and add setting demo gif

Co-authored-by: Isaac

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <serena.rxy@gmail.com>

* chore(api): sync openapi.json from omnigent@7fb779f (#281)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* chore(api): sync openapi.json from omnigent@60e775a (#288)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2152 (#280)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#1859 (#277)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document omnigent-ai/omnigent#2135 (#276)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

* docs: document official kubernetes server image variant (#285)

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>

---------

Co-authored-by: omnigent-ci[bot] <294685417+omnigent-ci[bot]@users.noreply.github.com>
Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>
Co-authored-by: Serena Ruan <serena.rxy@gmail.com>
Co-authored-by: Pat Sukprasert <pattara.sk127@gmail.com>
Co-authored-by: Dhruv Gupta <dhruv.gupta@databricks.com>
Co-authored-by: Daniel Lok <daniel.lok@databricks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-doc-update Merged PR needs a user-facing docs update size/XL Pull request size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant