Cardinal policy modules for omnigent's policy engine: agent-session telemetry + enforced spend limits.
Project description
cardinal-omnigent-policy
Cardinal policy modules for omnigent's policy engine. One
pip-installable package (cardinal-omnigent-policy) covers every harness
omnigent runs (Claude, Codex, Cursor, Hermes, Pi, OpenCode, custom YAML
agents) — unlike the four CLI adapters in this repo, cardinal-agent-core
is a real dependency here, not vendored, and the policies run inside the
omnigent server process.
Verified against omnigent commit 2b3b54a4
(cardinal_omnigent.OMNIGENT_VERIFIED_COMMIT); CI re-diffs the upstream
contract surface against that pin (build/omnigent_drift.py, the
"Omnigent contract drift" workflow). omnigent is alpha: every event-field
read goes through defensive accessors (_events.py), and the contract
must be re-verified on omnigent upgrades.
What it ships
| Module | Kind | Phases | Emits / enforces |
|---|---|---|---|
cardinal_omnigent.telemetry |
observe-only (never blocks; ALLOWs only to carry state_updates) |
request, llm_response, tool_call, tool_result, response | cardinal.git_state (labels convention + branch sniffing), api_request + cardinal.turn_usage (per-turn on the claude_sdk executor, usage_granularity="turn"), cardinal.turn_tool (core bashclass on shell tools), tool_result, cardinal.subagent_usage (sub-agent child conversations — see below) |
cardinal_omnigent.spend_limits |
enforcing | request | Cardinal server verdicts → DENY (block) / ALLOW + standing message with ack_band hysteresis (warn/notify); plus an omnigent-native per-session cap (session_cost_limit_usd) checked against context.usage.total_cost_usd |
Because request is in omnigent's FAIL_CLOSED_PHASES, spend limits here
are enforced, not suggested — if policy evaluation fails, the request
does not proceed. Our own internal errors deliberately abstain
(return None), never self-DENY: only a genuine server verdict or the
configured session cap denies (see the spend_limits module docstring).
Session identity (minted)
omnigent's policy contract carries no session or conversation id —
the event a callable receives is {type, target, data, context{actor, usage, user_daily_cost, model, harness, labels, subtree_usage}, session_state, llm_client, request_data}. Cardinal telemetry is
session-keyed, so the policies mint a cardinal.session_id into the
engine's per-conversation session_state via state_updates (durable
server-side; an in-process memo keyed by the engine-shared llm_client
keeps both policies on one id until it persists). See _identity.py.
Subagent mapping
cardinal.subagent_usage is emitted on the response phase of
conversations whose labels mark them as sub-agent children:
- claude-native / codex-native UI children carry
omnigent.wrapper(*-native-ui-subagent) plus id labels (omnigent.claude_native.subagent_id,omnigent.codex_native.subagent_thread_id/parent_thread_id/agent_nickname/agent_role) — stamped by the omnigent server. - native workflow / custom-YAML sub-agents get no upstream label:
stamp
cardinal.subagent: <name>in the sub-agent spec's guardrails labels (the Cardinal convention).
Emitted usage is the child conversation's cumulative context.usage
(usage_scope="session_cumulative" — downstream takes last-per-session,
not a sum) with model from the engine-injected context.model.
Cost attribution: cost_usd on usage events is the delta of the
server-maintained cumulative context.usage.total_cost_usd (anchored via
omnigent state_updates with an in-process mirror), falling back to
core pricing tables (anthropic / openai / gemini, core 0.3.0) keyed
on the model — platform-prefixed SKUs like databricks-claude-opus-4-8
re-resolve from the embedded claude- name. Unpriced models emit no
cost_usd rather than a misleading $0.
Registering with an omnigent server
The easy way — device-flow connect (mints the ingest credential, writes
the state dir, merges a cardinalManaged-tagged block into your server
config). Install into the interpreter that runs your omnigent server:
# venv
pip install cardinal-omnigent-policy
# pipx-installed omnigent (use --include-apps so the CLI lands on PATH)
pipx inject omnigent cardinal-omnigent-policy --include-apps
# then, from anywhere:
cardinal-omnigent-connect --config /etc/omnigent/config.yaml
cardinal-agent-core is resolved as a dependency. python3 -m cardinal_omnigent.connect still works as an alternative to the
cardinal-omnigent-connect console script.
Or by hand: policy_modules: names the module; omnigent scans it for
POLICY_REGISTRY (both policies take config by arity). The
cardinal: block is what the policies receive as config:
policy_modules:
- cardinal_omnigent
cardinal:
ingest_endpoint: "https://intake.us-east-2.aws.cardinalhq.io"
ingest_api_key: "..." # minted by connect / org admin
ingest_api_header: "x-cardinalhq-api-key"
deployment_environment: "prod"
org: "your-org-slug"
state_dir: "~/.omnigent" # server-writable; verdict/ack files
session_cost_limit_usd: 25.0 # optional per-session hard cap
service_name: "omnigent" # optional resource override
Factory registration (closure state) is also supported via omnigent's
function: {path, arguments} form with
cardinal_omnigent.make_telemetry_policy /
cardinal_omnigent.make_spend_limits_policy.
The labels convention (initiative attribution)
Policies never see the workspace — omnigent's policy contract carries no cwd, repo, or branch. Initiative attribution therefore rides session labels: have your agent specs / operator config stamp
labels:
cardinal.repo: "your-org/your-repo" # 'org/name'
cardinal.branch: "feat/outcomes-observability"
cardinal.branch runs through core resolve_initiative (one branch =
one initiative, <type-prefix>/<kebab-name>). Sessions without labels
attribute to initiative=None, type=research — same as protected-branch
sessions on the CLI adapters, so rollups stay honest. Consequently
cardinal.git_state here carries repo/branch/initiative facts but not
head_sha/cwd/remote_url (structurally unavailable server-side).
Branch sniffing (enrichment): creation-time labels can't see a
mid-session git checkout -b. Shell tool_call events are watched for
branch creation/switch commands (checkout -b/-B, switch/switch -c);
a detected branch emits a fresh cardinal.git_state at the boundary
(marked cardinal_branch_source="tool_sniff") and is used at request
time when labels carry no branch. Labels always win when present.
Identity is per-event: user.email comes from
event.context.actor.run_as; cardinal.omnigent_harness (resource
attribute) from event.context.harness so downstream can slice by
underlying harness.
Tests
cd adapters/omnigent
PYTHONPATH=../../core:. python3 -m unittest discover tests
(tests/fixtures.py also bootstraps sys.path for core and the adapter,
so a bare python3 -m unittest discover tests from this directory works
too.) The suite drives both policies against the core StubIngest harness
and asserts attribute coverage against the repo-root contract test's
REQUIRED_KEYS.
This adapter has no hooks/ directory on purpose: build/vendor.py
discovers vendoring targets by the presence of hooks/, and this package
must never be vendored — core is a pip dependency.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cardinal_omnigent_policy-0.2.0.tar.gz.
File metadata
- Download URL: cardinal_omnigent_policy-0.2.0.tar.gz
- Upload date:
- Size: 29.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08f78e81ae0e321683374e73a05870436686ab3378fa85d9ee01f60f3d667399
|
|
| MD5 |
50b95eaed228d83130d66fbc36ba797b
|
|
| BLAKE2b-256 |
ce1e599d9677edb4898e10c1a2c927f56727474a00939a89856362f9aeb3e1aa
|
Provenance
The following attestation bundles were made for cardinal_omnigent_policy-0.2.0.tar.gz:
Publisher:
release.yml on cardinalhq/cardinal-agent-plugins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cardinal_omnigent_policy-0.2.0.tar.gz -
Subject digest:
08f78e81ae0e321683374e73a05870436686ab3378fa85d9ee01f60f3d667399 - Sigstore transparency entry: 2171723897
- Sigstore integration time:
-
Permalink:
cardinalhq/cardinal-agent-plugins@20a6fd08692fe91ccf0434895776932c59324011 -
Branch / Tag:
refs/tags/omnigent-v0.2.0 - Owner: https://github.com/cardinalhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@20a6fd08692fe91ccf0434895776932c59324011 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cardinal_omnigent_policy-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cardinal_omnigent_policy-0.2.0-py3-none-any.whl
- Upload date:
- Size: 23.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ba657ca3a8ce4a8d8a6086d38060f50ec291b1491dc6aebd99f0ac2ada2ebbe
|
|
| MD5 |
867a7c8bbe4cba4af8eb4dc0aae2f4a7
|
|
| BLAKE2b-256 |
c2048d0bb691a10a4b34eea52a587325a1923c6f7cf8d2419e4d88831f1590b6
|
Provenance
The following attestation bundles were made for cardinal_omnigent_policy-0.2.0-py3-none-any.whl:
Publisher:
release.yml on cardinalhq/cardinal-agent-plugins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cardinal_omnigent_policy-0.2.0-py3-none-any.whl -
Subject digest:
5ba657ca3a8ce4a8d8a6086d38060f50ec291b1491dc6aebd99f0ac2ada2ebbe - Sigstore transparency entry: 2171723905
- Sigstore integration time:
-
Permalink:
cardinalhq/cardinal-agent-plugins@20a6fd08692fe91ccf0434895776932c59324011 -
Branch / Tag:
refs/tags/omnigent-v0.2.0 - Owner: https://github.com/cardinalhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@20a6fd08692fe91ccf0434895776932c59324011 -
Trigger Event:
push
-
Statement type: