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
| Platform | Status | Notes |
|---|---|---|
| macOS and Linux | Supported | The 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 x64 | Supported — native degraded | Support 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.pybridge under its data directory; - adds that directory to OmniGent's Python environment with
defenseclaw_omnigent.pth; - registers the module in OmniGent's effective
config.yamlunderpolicy_modules; and - enables the
defenseclaw_guardrailserver-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 phase | DefenseClaw event | Enforcement |
|---|---|---|
request | UserPromptSubmit | ALLOW, native ASK, or DENY |
tool_call | PreToolUse | ALLOW, native ASK, or DENY before execution |
tool_result | PostToolUse | DENY suppresses the downstream result with OmniGent's denial text; execution is not rolled back |
response | AfterAgentResponse | DENY persists OmniGent's sentinel response; completed model work is not rolled back |
llm_request | BeforeModel | ALLOW, native ASK, or DENY |
llm_response | AfterModel | DENY 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
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 runstarts 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-widedefenseclaw_guardrailpolicy. A user~/.omnigent/config.yamlhas 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.
/themecannot 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 theopenai-agentsharness. The managed configuration names it asdefault_agent, soomnigent 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, anddefenseclaw sandbox activity --sandbox <name>lists every block. -
Continuing a conversation.
defenseclaw sandbox connect <name>starts a new OmniGent conversation.defenseclaw sandbox connect <name> -- --continuecontinues the sandbox agent's latest one, and a kept sandbox's session end names that command. TheResume: omnigent run … --resume <id>line OmniGent prints at/quitruns OmniGent on this machine, outside the sandbox, unless theomnigentshell wrapper is on (defenseclaw sandbox enable omnigent). The wrapper sends it into the folder's sandbox; the sandbox drops therunit forwards, since the launcher runsomnigent runalready. -
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
^Zcharacter (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 withOMNIGENT_CONFIG_HOME=/etc/omnigentand 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 runsomnigent stop --forceand drops the process's record, so OmniGent starts its own. The launcher also refuses--serverwith a URL, and a project.omnigent/config.yamlthat setsserver. 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'ssys_os_shellcalls. -
Install pin. The image installs OmniGent 0.13.0 from PyPI as a root-owned
uv toolon 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 olderopenshell.image.harness_versions.omnigentpin, 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 intoOPENAI_API_KEY, the profile pointsOPENAI_BASE_URLat the Mantle route and defaults the model toopenai.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 theopenai-agentsharness.
defenseclaw sandbox run omnigentpicks the profile from the first set ofOPENAI_API_KEY,ANTHROPIC_API_KEYandAWS_BEARER_TOKEN_BEDROCK(--llmpicks one). It refuses a profile without a default model unless you pass--modelafter the harness name, and it does so before a sandbox is created. OmniGent has no skip-permissions switch: approval pauses come from its policies. - the regional
-
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 setsOMNIGENT_DISABLE_TELEMETRY=1. So OmniGent does not contactconfig.omnigent-telemetry.ioorapi.omnigent-telemetry.io. The launcher also setsOMNIGENT_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 torelease-assets.githubusercontent.com). A session summary counts those two as new sites even when nothing was prompted. An earlier mock-model run also reachedchatgpt.comandmodels.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.yamland a plantedsitecustomizemodule 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 --yesRestart OmniGent after teardown so its running server drops the removed policy module.
Amp
The Amp connector installs a system TypeScript policy plugin that gates tool execution and model-bound results, uses Amp's native confirmation UI, and records the five documented plugin lifecycle events.
GitHub Copilot CLI
Copilot CLI connector wires global ~/.copilot/hooks by default, with optional workspace .github/hooks. preToolUse supports native ask; four documented events can block.