Connectors

OmniGent

The OmniGent connector installs a custom Python policy that maps six policy phases to DefenseClaw ALLOW, ASK, and DENY decisions, with optional native OTLP telemetry.

The OmniGent connector uses OmniGent's documented custom Python policy API. It does not proxy OmniGent's model traffic. A small in-process policy bridge sends each policy event to the local DefenseClaw gateway, then maps the result back to OmniGent ALLOW, ASK, or DENY.

Platform support

PlatformStatusNotes
macOS and LinuxSupportedThe reviewed OmniGent (>=0.7.0,<0.14.0) custom-policy bridge maps all six awaited phases to DefenseClaw decisions; native OTLP remains an explicit launch-time opt-in.
Native Windows x64Supported — native degradedSupport is limited to the reviewed OmniGent range (>=0.7.0,<0.14.0) server and SDK-based harnesses using the official same-user uv tool layout. Native terminal wrappers, filesystem/network sandboxing, and L7 egress proxy parity are unavailable; DefenseClaw does not emulate them through WSL or a shell compatibility layer.

Setup

uv tool install --python 3.12 omnigent==0.13.0
export OMNIGENT_CONFIG_HOME="${HOME}/.omnigent"
defenseclaw setup omnigent                # observe (default)
defenseclaw setup omnigent --mode action  # enforce ALLOW / ASK / DENY
omnigent server --config "${OMNIGENT_CONFIG_HOME}/config.yaml"
uv tool install --python 3.12 omnigent==0.13.0
$env:OMNIGENT_CONFIG_HOME = Join-Path $HOME '.omnigent'
defenseclaw setup omnigent                # observe (default)
defenseclaw setup omnigent --mode action  # enforce ALLOW / ASK / DENY
omnigent server --config (Join-Path $env:OMNIGENT_CONFIG_HOME 'config.yaml')

Restart OmniGent after setup so it reloads the policy registry. DefenseClaw:

  • writes an owner-only defenseclaw_omnigent_policy.py bridge under its data directory;
  • adds that directory to OmniGent's Python environment with defenseclaw_omnigent.pth;
  • registers the module in OmniGent's effective config.yaml under policy_modules; and
  • enables the defenseclaw_guardrail server-wide policy.

setup omnigent is the dedicated OmniGent alias. See the quick-alias reference for its common setup options and full guardrail setup for advanced scanner and judge configuration.

DefenseClaw patches the explicit OMNIGENT_CONFIG path when set, then $OMNIGENT_CONFIG_HOME/config.yaml, and finally ~/.omnigent/config.yaml. OmniGent's CLI server loads an empty configuration when --config is omitted, so always pass the same patched file to omnigent server --config. Hosted server entrypoints can instead consume OMNIGENT_CONFIG. Doctor reports PASS only when the recorded live server process proves the same effective path via --config or readable process-environment evidence; a valid file without that binding is reported as unverified rather than healthy.

The connector must write the .pth file into the Python environment that owns the omnigent executable. Use an isolated environment that your user can write; setup stops with a clear error instead of attempting a privileged system-Python install. Native Windows setup requires the official uv tool layout and a fresh protected executable selection; discovery, setup, repair, and later reconciliation bind the exact omnigent.exe digest to the connector lock. Direct uv.exe and tool-Python metadata probes have connector-owned deadlines; setup does not fall back to a shell or an unbounded child process.

The native Windows Setup executable accepts the explicit CONNECTOR=omnigent selection. Its transaction ledger records the exact previous and current OMNIGENT_CONFIG_HOME, carries that binding through repair and upgrade, reconciles all three managed backup identities, and retains failed teardown evidence for retry or deferred uninstall. This implemented lifecycle remains supported while validation metadata stays empty and live=false. Connector setup and teardown share a process-local plus owned cross-process lifecycle lock; scoped credentials are refreshed inside that transaction and revoked only after clean teardown.

All three managed files are backed up. Teardown restores unchanged files byte-for-byte and removes only DefenseClaw-owned YAML entries when the operator has edited the config.

Policy phases and decisions

OmniGent phaseDefenseClaw eventEnforcement
requestUserPromptSubmitALLOW, native ASK, or DENY
tool_callPreToolUseALLOW, native ASK, or DENY before execution
tool_resultPostToolUseDENY suppresses the downstream result with OmniGent's denial text; execution is not rolled back
responseAfterAgentResponseDENY persists OmniGent's sentinel response; completed model work is not rolled back
llm_requestBeforeModelALLOW, native ASK, or DENY
llm_responseAfterModelDENY can suppress onward-visible content; completed model work is not rolled back

OmniGent parks only its pre-action request, tool_call, and llm_request phases for approval. DefenseClaw therefore advertises native human approval only for those events. A post-phase confirm finding is audited and continues without an approval pause. A post-phase DENY uses OmniGent's built-in denial/sentinel behavior; the bridge does not supply custom replacement data and cannot undo a tool call or model request that already completed. The bridge also honors fail-open or fail-closed behavior when the gateway is unavailable. Request events use OmniGent 0.7.0's official structured {user_content, attachments} shape. DefenseClaw forwards the typed prompt and a bounded list of attachment filename, content type, and decoded text fields; it retains the documented bare-string and content-block compatibility shapes. All ordinary normalization, trace propagation, serialization, transport, read, and response-parse exceptions pass through the configured bridge fail mode. The bridge connects directly to the configured gateway with ambient HTTP(S)/Windows proxies disabled and rejects redirects so its scoped credential and inspected content stay on that origin.

For llm_request, the bridge inspects both separately labeled system_prompt_preview and last_user_message fields within fixed bounds. It also forwards bounded cumulative token usage and cost, model, harness, actor client ID, and label metadata. Oversized or invalid label projections are marked partial. OmniGent v0.7 policy callbacks expose no session identifier, so the bridge records that field as unavailable rather than inventing correlation.

The official extension is an awaited in-process Python function. It does not read policy events from stdin, write verdicts to stdout, or communicate an enforcement result through a process exit code. A bridge exception is fail-closed by OmniGent's policy engine; DefenseClaw's configured fail mode governs transport and invalid-response handling inside the bridge.

Telemetry

Policy evaluations always produce DefenseClaw hook logs, counters, and spans. When OmniGent has an active OpenTelemetry span, the bridge forwards its W3C traceparent; otherwise the gateway starts a new trace.

OmniGent also supports native OTLP through standard process environment variables. This channel is not active after setup: DefenseClaw does not edit shell startup files or the OmniGent launcher. Export the variables in the process that starts OmniGent. Native OTLP uses its own connector-scoped token; it does not reuse the policy-hook token or the gateway master credential. Setup creates the owner-only token and successful teardown revokes it.

$tokenPath = Join-Path $env:USERPROFILE '.defenseclaw\hooks\.otlp-omnigent.token'
$otlpToken = [IO.File]::ReadAllText($tokenPath).Trim()
$env:OMNIGENT_TELEMETRY_ENABLED = 'true'
$env:OTEL_EXPORTER_OTLP_ENDPOINT = 'http://127.0.0.1:18970'
$env:OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'
$env:OTEL_EXPORTER_OTLP_HEADERS = "authorization=Bearer%20$otlpToken,x-defenseclaw-source=omnigent,x-defenseclaw-client=omnigent-otel%2F1.0"
$env:OTEL_LOGS_EXPORTER = 'otlp'
$env:OTEL_METRICS_EXPORTER = 'otlp'
$env:OTEL_TRACES_EXPORTER = 'otlp'
$env:OMNIGENT_OTEL_CAPTURE_CONTENT = 'false'
omnigent server --config (Join-Path $env:OMNIGENT_CONFIG_HOME 'config.yaml')

Logs, metrics, and traces use OpenTelemetry packages present in OmniGent's base/default dependency set; no separate tracing extra is required. The status API reports only channels configured by DefenseClaw, so it continues to show hooks; native OTLP remains a separately documented capability because setup cannot verify another process's environment.

OmniGent 0.7.0 policy events do not publish stable session, turn, tool-call, or model-request IDs. Native spans can carry OmniGent's session.id, but there is no source-proven field that joins that native session to a policy event. DefenseClaw therefore labels policy-event session correlation unavailable and does not infer missing cross-rail identities.

Local surfaces

config.yaml
defenseclaw_omnigent_policy.py

Official OmniGent agent YAML can declare local functions, MCP tools, and subagents. DefenseClaw v1 neither modifies nor scans those sources, so MCP, function, subagent, skill, rule, plugin, and agent-bundle inventory remains unsupported and unverified instead of appearing as an empty live inventory.

Hook capabilities

Block events

  • UserPromptSubmit
  • PreToolUse
  • PostToolUse
  • AfterAgentResponse
  • BeforeModel
  • AfterModel

Native ask events

  • UserPromptSubmit
  • PreToolUse
  • BeforeModel

OpenShell sandbox

DefenseClaw can build an OmniGent OpenShell overlay image. The files below exist only in the image; nothing on your host changes.

  • Policy in the managed tier. OmniGent evaluates policies in the server that omnigent run starts inside the sandbox. That server reads $OMNIGENT_CONFIG_HOME/config.yaml, and the image launcher points it at the root-owned /etc/omnigent/config.yaml. That file loads the DefenseClaw policy module and attaches the server-wide defenseclaw_guardrail policy. A user ~/.omnigent/config.yaml has no effect on it. The policy bridge itself is root-owned under /usr/local/lib/defenseclaw/omnigent/.

  • TUI theme. OmniGent's TUI keeps its theme in the same file, so the image sets the dark theme there: the first-launch theme picker would otherwise fail writing the root-owned file. /theme cannot change the theme inside a sandbox. It fails with OmniGent's own message, Failed to write TUI user config at /etc/omnigent/config.yaml: [Errno 13] Permission denied, and the top of that file says why. The file and its directory stay root-owned because the file carries DefenseClaw's policy.

  • Fail closed. The sandbox bridge presents only the per-sandbox binding token and retries a dropped request once. Every failure becomes a DENY: missing token, ingress down, a bad status or a bad reply.

  • Confirm denies. On a host, a DefenseClaw confirm verdict becomes OmniGent's native ASK. In a sandbox it becomes a DENY that says approval is not available. OmniGent parks an ASK on its local server, and that server's approval routes accept an answer from any process in the sandbox, so the agent could approve its own call.

  • Sandbox agent. OmniGent's own bubblewrap sandbox cannot run inside OpenShell. The image therefore ships an agent, /usr/local/lib/defenseclaw/omnigent/agent, whose shell runs in the caller process on the openai-agents harness. The managed configuration names it as default_agent, so omnigent run --model <model> starts it. Every request, model call and tool call still passes the DefenseClaw policy.

  • Blocked tool calls. OmniGent's TUI hides tool results until you press Ctrl+T, and a DefenseClaw block reaches it only as the blocked call's result (Denied by policy: <reason>). OmniGent's policy results have no field meant for the user. So the sandbox agent's instructions tell the model to say that DefenseClaw blocked the call and to quote the reason word for word. Ctrl+T shows the results of later calls. The session summary names the blocking rule, and defenseclaw sandbox activity --sandbox <name> lists every block.

  • Continuing a conversation. defenseclaw sandbox connect <name> starts a new OmniGent conversation. defenseclaw sandbox connect <name> -- --continue continues the sandbox agent's latest one, and a kept sandbox's session end names that command. The Resume: omnigent run … --resume <id> line OmniGent prints at /quit runs OmniGent on this machine, outside the sandbox, unless the omnigent shell wrapper is on (defenseclaw sandbox enable omnigent). The wrapper sends it into the folder's sandbox; the sandbox drops the run it forwards, since the launcher runs omnigent run already.

  • Ctrl-Z. OmniGent's TUI has no suspend, on a host as in a sandbox. It keeps prompt_toolkit's default binding, which types Ctrl-Z into the prompt as a ^Z character (Backspace removes it). OmniGent never tries to stop itself, so DefenseClaw's notice that a sandbox cannot suspend a harness does not appear.

  • One server. OmniGent reuses a running local server or host daemon that it finds in its records under ~/.omnigent. It does not check which configuration that process started with. Before every start the launcher checks each recorded process: it must be the pinned OmniGent, started with OMNIGENT_CONFIG_HOME=/etc/omnigent and nothing that moves the configuration or loads other code. For the server, it must also listen on the recorded port. If a recorded process fails this check, the launcher runs omnigent stop --force and drops the process's record, so OmniGent starts its own. The launcher also refuses --server with a URL, and a project .omnigent/config.yaml that sets server. Either would run the session on a server that does not load DefenseClaw's policy.

  • Known gaps. The policy binds through OMNIGENT_CONFIG_HOME, which the launcher sets. A second OmniGent that a tool call starts without it (the pinned binary run directly) runs without the policy, as does a client that a tool call points straight at such a server. DefenseClaw judges that tool call first. Command rules judge the sandbox agent's sys_os_shell calls.

  • Install pin. The image installs OmniGent 0.13.0 from PyPI as a root-owned uv tool on a private CPython 3.12.13. The build refuses a version outside >=0.13.0,<0.14.0, a narrower range than the host contract's (>=0.7.0,<0.14.0): before 0.13.0 OmniGent's server never runs the response policy phase for the sandbox agent, whose runner it relays, so an image of an older release would fail the hook-fire probe (hook AfterAgentResponse never fired). An older openshell.image.harness_versions.omnigent pin, such as "0.12.0", is refused before the build starts, with that reason.

  • Model access. The sandbox agent names no model, and OmniGent sends a run without one to Databricks. Pick one of these profiles:

    • the regional defenseclaw-bedrock-mantle-openai-<region>: the launcher copies the Bedrock key into OPENAI_API_KEY, the profile points OPENAI_BASE_URL at the Mantle route and defaults the model to openai.gpt-oss-20b.
    • defenseclaw-openai (OPENAI_API_KEY) needs --model <model>.
    • defenseclaw-anthropic (ANTHROPIC_API_KEY) needs --model anthropic/<model>. This profile is unverified: the sandbox agent runs on the openai-agents harness.

    defenseclaw sandbox run omnigent picks the profile from the first set of OPENAI_API_KEY, ANTHROPIC_API_KEY and AWS_BEARER_TOKEN_BEDROCK (--llm picks one). It refuses a profile without a default model unless you pass --model after the harness name, and it does so before a sandbox is created. OmniGent has no skip-permissions switch: approval pauses come from its policies.

  • Tool commands and the proxy. OmniGent runs the agent's tools in a runner that inherits only an allowlisted environment. The launcher lists the proxy settings in OMNIGENT_RUNNER_ENV_PASSTHROUGH, so tool commands also go through the DefenseClaw egress proxy. Loopback traffic between OmniGent's own processes stays off the proxy.

  • Other traffic. OmniGent's usage telemetry is off in a sandbox: the managed configuration sets telemetry: false, which every OmniGent process reads (the tool runner included), and the launcher sets OMNIGENT_DISABLE_TELEMETRY=1. So OmniGent does not contact config.omnigent-telemetry.io or api.omnigent-telemetry.io. The launcher also sets OMNIGENT_NO_UPDATE_CHECK=1. At start OmniGent still fetches its model catalog, which gives a model's context window and prices, from MLflow's GitHub releases (github.com, redirected to release-assets.githubusercontent.com). A session summary counts those two as new sites even when nothing was prompted. An earlier mock-model run also reached chatgpt.com and models.dev (model catalogs). That traffic goes through the DefenseClaw egress proxy, which logs it and applies the blocklist.

  • Status. Verified end to end. A run through the DefenseClaw daemon in an OpenShell sandbox, with the sandbox agent on a mock model behind a credential binding, showed:

    • policy events reaching the sandbox ingress, with OpenShell substituting the model key;
    • a command that a DefenseClaw rule blocks denied by the policy, with the rule's reason passed back to the model;
    • egress through the proxy from a tool command, and a blocklisted host that a sandbox-scoped unblock then let through.

    The image also passes the hook-fire probe with the request, llm_request, tool_call, tool_result and response policy events, both headless and with the TUI started on a terminal and a prompt typed into it. In that probe a hostile user ~/.omnigent/config.yaml and a planted sitecustomize module in the launch environment change nothing, and the launcher refuses a project configuration that names another server. The curated provider profiles have not carried a real model inside a sandbox; see the harness's verification note for what was run.

Disable

defenseclaw guardrail disable --connector omnigent --yes

Restart OmniGent after teardown so its running server drops the removed policy module.