API reference
Core — envelope, model, gate, registry, harness
halo_agent_harness.core.model
Core domain model — the agent I/O envelope and the two-axis authority/decision model.
Implements harness-protocol.md §2 (envelope) and §3 (authority + decision). This module is framework-free and dependency-free (ADR-0005, INV-5).
AuthorityLevel
Bases: IntEnum
An agent's static capability ceiling (spec §3.1). Ordered: higher = more authority.
MUST NOT change at runtime (no self-escalation, INV-3).
Source code in src/halo_agent_harness/core/model.py
15 16 17 18 19 20 21 22 23 24 25 | |
DecisionAction
Bases: Enum
The dynamic outcome of a single invocation (spec §3.2).
Source code in src/halo_agent_harness/core/model.py
28 29 30 31 32 33 34 35 | |
AgentInput
dataclass
One invocation's input (spec §2.1). tenantId/userId are the multi-tenancy boundary.
Source code in src/halo_agent_harness/core/model.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | |
correlation_id
property
correlation_id
Correlation/trace id to propagate into every port call (spec §7.5).
is_scoped
is_scoped()
True if tenant and user scope are both present and non-empty (spec §2.1).
Source code in src/halo_agent_harness/core/model.py
90 91 92 | |
Decision
dataclass
The outcome an agent proposes; auto_enforced is owned by the harness (spec §2.2, §4).
An agent MUST NOT set auto_enforced; the harness overwrites it via the confidence gate.
Source code in src/halo_agent_harness/core/model.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | |
validate
validate()
Protocol-level validation of the decision shape (spec §2.2).
Source code in src/halo_agent_harness/core/model.py
113 114 115 116 117 118 | |
AgentOutput
dataclass
One invocation's output envelope (spec §2.2).
Source code in src/halo_agent_harness/core/model.py
121 122 123 124 125 126 127 128 129 130 131 | |
min_authority_for
min_authority_for(action)
The minimum authority level required to emit action (spec §3.3).
Source code in src/halo_agent_harness/core/model.py
59 60 61 | |
action_within_authority
action_within_authority(action, authority)
True if an agent at authority is permitted to emit action (spec §3.3).
Source code in src/halo_agent_harness/core/model.py
64 65 66 | |
action_precedence
action_precedence(action)
The Decision-Hierarchy precedence of an action (§3.3): higher = safer/stricter.
Source code in src/halo_agent_harness/core/model.py
69 70 71 | |
reconcile
reconcile(actions)
Reconcile competing decisions toward the safer action (spec §3.3, §6.2).
Source code in src/halo_agent_harness/core/model.py
74 75 76 77 78 | |
halo_agent_harness.core.gate
The confidence gate (spec §4, ADR-0003).
Centralized, runs on every invocation, and cannot be disabled. It is the only place auto_enforced
is set. Agents cannot reach it.
ConfidenceGate
Decides auto_enforced for a decision. There is no 'disabled' state (G-4).
The gate never trusts an incoming auto_enforced flag — it always recomputes it.
Source code in src/halo_agent_harness/core/gate.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
threshold_for
threshold_for(authority)
Effective auto-enforce threshold for an authority level (never below BASE_THRESHOLD).
Source code in src/halo_agent_harness/core/gate.py
31 32 33 | |
evaluate
evaluate(decision, authority)
Compute whether decision may auto-enforce, and stamp it onto the decision.
Returns the resulting auto_enforced value (also written to decision.auto_enforced).
Source code in src/halo_agent_harness/core/gate.py
35 36 37 38 39 40 41 42 | |
is_bypass
is_bypass(decision, authority)
A bypass is an auto-enforced decision that should not have cleared the gate.
In a correct system this is always False (spec §4.2: confidence_gate_bypass_total == 0).
Used defensively by the harness to feed the bypass counter.
Source code in src/halo_agent_harness/core/gate.py
54 55 56 57 58 59 60 61 62 | |
halo_agent_harness.core.registry
The tool registry — the governance boundary for agent capabilities (spec §5, ADR-0004).
Default-deny: a tool not in an agent's allowlist is unreachable. No wildcards. Registration of a wildcard allowlist is rejected. The registry also holds the concrete tool implementations.
ToolRegistry
Holds tool implementations and per-agent allowlists; authorizes every call.
Source code in src/halo_agent_harness/core/registry.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
grant
grant(agent_name, tool_names)
Set an agent's explicit tool allowlist (spec §5 T-1, T-3). Wildcards are rejected.
Source code in src/halo_agent_harness/core/registry.py
57 58 59 60 61 62 63 | |
side_effect
side_effect(tool_name)
The declared side-effect class of a tool (spec §5 T-5), or None if unregistered.
Source code in src/halo_agent_harness/core/registry.py
72 73 74 75 | |
invoke
invoke(agent_name, tool_name, arguments)
Authorize (default-deny) then invoke. Unauthorized → ToolNotAuthorizedError (spec §5 T-1/T-2).
Source code in src/halo_agent_harness/core/registry.py
77 78 79 80 81 82 83 84 | |
halo_agent_harness.core.side_effect
Side-effect gating policy (spec §5 T-5, §5.3, ADR-0011).
Every registered tool declares a side-effect class: none | read | write | external. Before a tool
executes, the harness consults this policy. none/read calls are ungated (they observe, they do
not act). write/external calls are gated: they MUST clear a confidence threshold and MUST
NOT be issued by a read-only (OBSERVE-authority) agent. A gated call that fails the policy is
refused before execution and recorded as a security event — the side effect never happens.
This is the tool-call analogue of the confidence gate (§4): the gate decides auto_enforced for a
decision; this policy decides whether a side-effecting tool call may run. It is deliberately small;
the general PolicyPort (spec §7) is a later increment.
SideEffectPolicy
dataclass
Confidence thresholds for gated side-effect classes. Never below the gate's base floor.
Source code in src/halo_agent_harness/core/side_effect.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | |
permits
permits(side_effect, confidence, authority)
True if a call with this side-effect class may execute (spec §5.3).
Ungated classes always pass. For gated classes: a read-only (OBSERVE) agent is never
permitted, and the supplied confidence (absent → 0.0) MUST meet the class threshold.
Source code in src/halo_agent_harness/core/side_effect.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | |
halo_agent_harness.core.harness
The Harness — safe, governed invocation of a single agent (spec §2–§8).
Every agent invocation flows through here. The harness validates scope, honours the kill switch, runs the agent with a registry-enforced tool invoker, applies the confidence gate, routes non-enforcing decisions to human review, and records audit + observability. On any failure it resolves to a safe, non-enforcing decision (spec §8) — it never fails open.
Harness
Wraps agent execution with the harness's controls.
Source code in src/halo_agent_harness/core/harness.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | |
halo_agent_harness.core.failure
Failure-mode defaults (spec §8).
Every failure resolves to a safe decision: lowered confidence and auto_enforced=False.
An agent never fails open into an autonomous action.
default_for
default_for(mode, detail='')
Return the safe fallback decision for a failure mode (spec §8).
Source code in src/halo_agent_harness/core/failure.py
34 35 36 37 38 39 40 41 | |
Orchestration (spec §6)
halo_agent_harness.orchestration.pipeline
Sequential Pipeline orchestration (spec §6.1).
Agents run in order. Each stage receives the prior stage's decision in its context under the
pipeline key, so a later stage can react to what came before. Every stage is invoked through
Harness.invoke, so each passes the confidence gate and tool registry individually (O-1).
The pipeline short-circuits on the first BLOCK or DEFER (spec §6.1): once a stage refuses or
defers, later stages do not run — the safe action stands and no further side effects are risked.
PipelineResult
dataclass
The outcome of a sequential pipeline run.
Source code in src/halo_agent_harness/orchestration/pipeline.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
reconciled_action
property
reconciled_action
The safest action across every stage that actually ran (spec §3.3).
Pipeline
Runs agents sequentially, feeding each stage the prior stage's decision.
Source code in src/halo_agent_harness/orchestration/pipeline.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
halo_agent_harness.orchestration.fanout
Parallel Fan-out orchestration (spec §6.2).
Independent worker agents run concurrently over the same AgentInput; the harness reconciles
their decisions with the Decision Hierarchy (BLOCK wins, then ALERT, …). Every worker is
invoked through Harness.invoke, so each passes the confidence gate and tool registry individually
(O-1). The aggregation policy is collect-and-reconcile — every worker runs and the safest action
wins; no worker's controls are skipped.
Concurrency: workers run on a thread pool. The reference in-memory adapters are concurrency-safe (the
bypass counter is incremented under a lock), so confidence_gate_bypass_total accumulates correctly.
FanOutResult
dataclass
The aggregated outcome of a parallel fan-out.
Source code in src/halo_agent_harness/orchestration/fanout.py
23 24 25 26 27 28 29 30 31 32 | |
FanOut
Runs independent workers concurrently over one input, then reconciles their decisions.
Source code in src/halo_agent_harness/orchestration/fanout.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
halo_agent_harness.orchestration.debate
Debate / Consensus orchestration (spec §6.4).
Multiple agents produce competing decisions over the same AgentInput; a consensus rule
reconciles them. Every participant is invoked through Harness.invoke, so each passes the confidence
gate and tool registry individually (O-1).
Two consensus rules are provided:
SAFEST(default) — the strictest action wins, per the Decision Hierarchy (§3.3). Conservative: a singleBLOCKbeats any number ofALLOWs. This is the safe default for governance.MAJORITY— the most-proposed action wins; a tie resolves toDEFER(tie → human review).MAJORITYmay de-escalate below the strictest proposal (that is the point of a vote); useSAFESTwhen the strictest participant must always win.
Safety floor (invariant). Consensus can never raise authority above the strictest participant: the consensus action's severity never exceeds the strictest action any participant actually proposed. Since every rule chooses among proposed actions, this holds by construction; it is enforced defensively and asserted by tests.
DebateResult
dataclass
The reconciled outcome of a debate.
Source code in src/halo_agent_harness/orchestration/debate.py
43 44 45 46 47 48 49 50 51 52 53 54 | |
Debate
Runs competing agents and reconciles their decisions with a consensus rule.
Source code in src/halo_agent_harness/orchestration/debate.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
halo_agent_harness.orchestration.supervisor
Supervisor + Workers orchestration (spec §6.3, ADR-0007) — the primary multi-step pattern.
A supervisor coordinates worker agents. The supervisor holds NO tool permissions (T-4). The run has two phases:
- Planning turn. The supervisor is invoked through the harness (
Harness.invoke), so its planning decision passes the confidence gate, kill switch, and audit like any other invocation (O-1) — it just cannot touch tools. If the supervisor's decision isBLOCKorDEFERit halts delegation: no workers run. Otherwise the supervisor may select which workers to engage. - Delegation. Selected workers are each invoked through the harness (gate + registry individually), and their decisions are reconciled via the Decision Hierarchy (spec §3.3).
Worker selection is optional: a supervisor that also implements :class:Planner chooses a subset of
workers; a plain supervisor delegates to all of them (backwards-compatible).
Planner
Bases: Protocol
Optional supervisor capability: choose which workers to delegate to (spec §6.3).
Planning is a pure coordination step — it selects names, it does not act — so it needs no tools.
Source code in src/halo_agent_harness/orchestration/supervisor.py
31 32 33 34 35 36 37 38 39 40 | |
plan
plan(request, worker_names)
Return the subset of worker_names to engage for this request.
Source code in src/halo_agent_harness/orchestration/supervisor.py
38 39 40 | |
SupervisorWorkers
Coordinates worker agents under a tool-less supervisor with a real planning turn.
Source code in src/halo_agent_harness/orchestration/supervisor.py
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
Ports (spec §7)
halo_agent_harness.ports.governance
Governance ports — audit, human review, observability, memory, kill switch, policy (spec §7).
All are Protocols; concrete adapters live at the edge and depend on this core, never the reverse (ADR-0005). Data types carried across the ports are defined here.
AuditPort
Bases: Protocol
Append-only audit log. Implementations MUST NOT update or delete, and MUST redact PII.
Source code in src/halo_agent_harness/ports/governance.py
42 43 44 45 46 47 | |
ReviewItem
dataclass
Source code in src/halo_agent_harness/ports/governance.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
deadline
property
deadline
When this item breaches its SLA (spec §7.4).
OverrideRecord
dataclass
A human override of a queued decision — itself auditable (spec §7.4).
Source code in src/halo_agent_harness/ports/governance.py
71 72 73 74 75 76 77 78 79 80 | |
HumanReviewPort
Bases: Protocol
Queue for decisions that must reach a human, with an SLA per item.
Source code in src/halo_agent_harness/ports/governance.py
83 84 85 86 87 | |
ObservabilityPort
Bases: Protocol
Per-invocation metrics + counters. MUST expose confidence_gate_bypass_total (must stay 0).
Source code in src/halo_agent_harness/ports/governance.py
103 104 105 106 107 108 | |
KillSwitchPort
Bases: Protocol
System-wide stop without a code deploy. When engaged, nothing auto-enforces.
Source code in src/halo_agent_harness/ports/governance.py
114 115 116 117 118 | |
halo_agent_harness.ports.llm
LlmPort — provider-agnostic LLM access (spec §7).
The shape is adopted verbatim from the apex-sdlc provider abstraction
(apex-sdlc/platform/backend/app/integrations/llm/base.py) so existing adapters plug straight in.
No agent calls an SDK directly; all LLM access goes through this port (INV: Ollama/provider replaceable).
Message
dataclass
A single chat message.
Source code in src/halo_agent_harness/ports/llm.py
14 15 16 17 18 19 | |
ToolDefinition
dataclass
Describes a callable tool that can be offered to an LLM (spec §5.2).
Source code in src/halo_agent_harness/ports/llm.py
22 23 24 25 26 27 28 29 | |
ToolCall
dataclass
A tool invocation requested by the LLM.
Source code in src/halo_agent_harness/ports/llm.py
32 33 34 35 36 37 38 | |
CompletionResult
dataclass
Unified response from any LLM provider.
Source code in src/halo_agent_harness/ports/llm.py
41 42 43 44 45 46 47 48 49 50 | |
LlmPort
Bases: Protocol
Protocol every LLM provider adapter must satisfy.
Source code in src/halo_agent_harness/ports/llm.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
complete
async
complete(messages, system=None, tools=None, max_tokens=4096, temperature=0.7)
Generate a single completion and return the full result.
Source code in src/halo_agent_harness/ports/llm.py
60 61 62 63 64 65 66 67 68 69 | |
stream
async
stream(messages, system=None, max_tokens=4096, temperature=0.7)
Stream text tokens as they are generated.
Source code in src/halo_agent_harness/ports/llm.py
71 72 73 74 75 76 77 78 79 | |
Contract loading (spec §10)
halo_agent_harness.contract
Agent Contract loading + validation (spec §10, ADR-0006).
Validates a contract against docs/spec/agent-contract.schema.json and then applies the semantic
binding rule (declared capabilities must be within the declared authority level, spec §3.3) that the
JSON Schema alone cannot express. Requires the contract extra (jsonschema).
validate_contract
validate_contract(contract, schema_path=None)
Validate a contract dict; return it unchanged on success, else raise ContractValidationError.
Source code in src/halo_agent_harness/contract.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |
load_contract
load_contract(path, schema_path=None)
Load a contract JSON file and validate it (spec §10).
Source code in src/halo_agent_harness/contract.py
59 60 61 62 | |
Reference adapters
halo_agent_harness.adapters.sla
Human-review SLA monitor (spec §7.4).
Sweeps a review queue for items past their SLA deadline and emits a breach counter through the ObservabilityPort. Each breached item is counted at most once, so repeated sweeps are idempotent. This is the enforcement/monitoring hook the spec calls for; escalation policy (paging, reassignment) is the integrator's concern and sits above this signal.
SlaMonitor
Counts SLA breaches in a human-review queue, idempotently across sweeps.
Source code in src/halo_agent_harness/adapters/sla.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
sweep
sweep(now=None)
Return currently-overdue items; increment the breach counter for newly-breached ones.
Source code in src/halo_agent_harness/adapters/sla.py
32 33 34 35 36 37 38 39 | |
halo_agent_harness.adapters.redaction
Pluggable PII/secret redaction (spec §7.3).
Redaction runs before every audit write; zero PII in logs is a P1 condition. The default strategy
covers common patterns (JWT, email, card, SSN, phone, API key) — but every deployment has its own
sensitive shapes, so the strategy is pluggable: extend it with with_rule or build one from
scratch. A RedactionStrategy is callable, so it drops directly into any port that takes a
redactor.
The default patterns are best-effort, not a guarantee: order matters (specific before generic) and no regex set is exhaustive. Treat redaction as defense-in-depth, not the only control.
RedactionStrategy
dataclass
An ordered, immutable set of redaction rules. Callable so it plugs in wherever a redactor is asked.
Source code in src/halo_agent_harness/adapters/redaction.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
with_rule
with_rule(pattern, replacement)
Return a new strategy with one extra rule appended (the base stays immutable).
Source code in src/halo_agent_harness/adapters/redaction.py
51 52 53 54 | |
redact
redact(text)
Redact with the default strategy (spec §7.3). Kept as a free function for convenience.
Source code in src/halo_agent_harness/adapters/redaction.py
60 61 62 | |
halo_agent_harness.adapters.policy
PolicyPort reference adapter (spec §7) — evaluate action against immutable rules.
Rules are fixed at construction (frozen dataclasses): a policy cannot be mutated at runtime, which
upholds the no-self-escalation invariant (INV-3) — an agent cannot loosen the rules that bind it. The
first matching rule wins; if none match, the configured default applies. load_policy builds the same
immutable policy from a durable JSON file.
PolicyRule
dataclass
A single allow/deny rule. None in a field means 'any' (not a wildcard string — an explicit any).
Source code in src/halo_agent_harness/adapters/policy.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | |
RuleBasedPolicy
dataclass
Immutable rule set. First matching rule wins; otherwise default_allow decides.
Source code in src/halo_agent_harness/adapters/policy.py
37 38 39 40 41 42 43 44 45 46 47 48 | |
load_policy
load_policy(path)
Load an immutable policy from a JSON file: {"default_allow": bool, "rules": [ {...}, ... ]}.
Source code in src/halo_agent_harness/adapters/policy.py
51 52 53 54 55 56 57 58 59 60 61 62 63 | |