Invocation Model
How all agent work flows through a single unified entry point with consistent policy enforcement — agent existence/state checks, provider-drain gating, rate limiting, idempotency, and observability.
Agents do not declare accepted invocation sources
There is no TriggerConfig/TriggerRegistry anywhere in hoziron-core,
no TriggerMismatch variant in ErrorCategory, and no triggers field
on the agent manifest. Source-acceptance gating happens upstream of
invoke_agent, per source, not inside it:
| Source | How it's gated |
|---|---|
Api | No gating — every agent accepts direct API calls |
Channel | AgentBinding/router config plus per-channel RBAC, resolved by the channel bridge before invoke_agent is ever called |
Cron | Platform-managed — a cron job is bound to one agent at creation time; there's no separate "does this agent accept cron" check |
Condition | Same as Cron — platform-managed, bound at creation (ADR-065) |
Workflow | Pinned by the workflow step's own agent reference in the workflow definition |
See "Mediated Ingress (ADR-061)" below for why this is a deliberate structural choice.
Unified Entry Point
Every agent invocation — whether from the API, a channel message, a cron schedule, a matched condition, or a workflow step — flows through the same invoke_agent pipeline:
The provider-drain gate (Issue #407) only applies to standalone sources
(Api/Channel/Cron/Condition) — a Workflow-sourced invocation is already
gated by the workflow's own admission state, so it's exempted here to
avoid double-gating.
Invocation Sources
Each invocation carries metadata about its origin (InvocationSource, crates/platform/hoziron-core/src/invocation/context.rs):
| Source | Description |
|---|---|
Api | Direct HTTP API call |
Channel | Message from a channel integration — carries channel_type, channel_id, sender_id, sender_name |
Cron | Fired by a scheduled job — carries schedule_id, expression, scheduled_at |
Condition | Fired by a matched condition (ADR-065) — carries condition_id, a display-only rendering of the matched expression (condition kind + key + operator, never a raw memory value — this is a load-bearing invariant so a PII-bearing memory fact can never leak into audit/model context through this field), and matched_at |
Workflow | Step in a multi-agent pipeline — carries workflow_id, step_index, upstream_agent_id |
There is no Event source — system/lifecycle events are not a live invocation origin.
InvocationContext
The full payload delivered to the invocation layer:
{
"source": {
"Workflow": {
"workflow_id": "a1b2c3d4-...",
"step_index": 2,
"upstream_agent_id": "550e8400-..."
}
},
"message": "Process this claim...",
"idempotency_key": "req-abc123",
"correlation_id": "7c9e6679-..."
}
| Field | Purpose |
|---|---|
source | Origin metadata for routing, telemetry, and audit |
message | The payload to deliver to the agent |
idempotency_key | Optional deduplication key (requests with same key return cached result) |
correlation_id | UUID for distributed tracing across steps/services |
workflow_type | Optional entitlement/budget category (e.g. "fnol"); derived from InvocationSource::Workflow or the agent's configured mapping when unset |
Rate Limiting
Per-agent sliding window rate limiter:
- Window: 60 seconds (sliding)
- Default limit: 60 invocations per minute per agent
- Implementation: in-memory
HashMap<AgentId, Vec<Instant>>— timestamps older than 60s are evicted on each check - Disabled: set limit to 0 for a specific agent
When exceeded:
{
"error": {
"category": "RateLimited",
"message": "Agent has exceeded the rate limit of 60 invocations per minute",
"details": {"agent_id": "...", "limit": "60"}
}
}
Idempotency
Optional deduplication for exactly-once semantics:
- Requests with the same
idempotency_keyreturn the cached result without re-executing - Cache is in-memory with TTL (bounded size)
- Useful for webhook retries and workflow step replays
Mediated Ingress (ADR-061)
Every source in the table above funnels through invoke_agent and therefore through hoziron-core — this is a deliberate structural invariant, not an incidental convenience. An agent turn may only be originated by core, through this mediated path, with a request type the kernel cannot construct on its own. Concretely:
- The kernel's
send_message*family is not a callable agent-turn origin from outside core — there is no parallel "fast path" that bypasses the checks above or the inbound PII seam. - Kernel-internal triggers that must originate a turn (cron, condition, workflow resumption) do so through a core-authored dispatch closure (the
drive_runpattern) — the kernel owns timing/matching, core owns the mediated dispatch implementation the kernel's generic closure parameter calls into. This preserves the trust gradient: the kernel calls a function pointer, never core directly. - There is no kernel-internal event/publish mechanism that can originate a turn outside this seam — every path into an agent's context is mediated.
- Interior traffic (agent→agent messages, memory writes with already-tokenized content) is not re-mediated — the trust boundary is the agent domain as a whole, not each individual agent-to-agent hop.
Telemetry
Every invocation records structured metrics:
{
"agent_id": "550e8400-...",
"source": "Workflow",
"duration_ms": 2100,
"tokens_used": 342,
"success": true,
"correlation_id": "7c9e6679-..."
}
Available via the API as per-agent invocation metrics:
- Total invocations
- Error count / error rate
- Average duration
- Per-source breakdown (how many from API vs Channel vs Cron vs Condition vs Workflow)
InvocationResult
The structured response returned from every invocation:
{
"content": "There are 23 open claims in the queue.",
"tokens_used": 342,
"duration_ms": 2100,
"correlation_id": "7c9e6679-...",
"source": {"Api": {}}
}
Related:
- agent-execution.md — what happens once a turn is admitted
- ../architecture/object-model.md — the
AgentState/Runnablelifecycle contract - ../reference/api/cron.md — condition jobs (ADR-065), the one piece of proactive scheduling that's live today
docs/decisions/056-unified-activation-gate.md,061-agent-turn-ingress-authority.md,065-core-mediated-proactive-scheduling.md