Agent module stable¶
Purpose & Scope¶
The Agent module records the typed configuration for each agent that participates in CORA, whether it is backed by a language model or by a fixed rule. An Agent is the digital identity card of a kind of automation: "the RunDebriefer agent runs on Claude Haiku 4.5, gets the prompt template at this id, and writes its findings as Decisions on the Run it watched"; "the ClearanceExpirer agent is rule-based, watches Active safety clearances, and expires the ones whose validity window has passed". The aggregate carries everything needed to identify, version, and gate an agent's behaviour for reproducibility; the runtime that acts on the configuration lives outside the aggregate.
Agents share their identity with the Access module's Actors: the same UUID names the agent's record here and the agent's Actor record over there, written atomically at definition. Every Decision an agent writes, and every authorisation check that runs against an agent's action, refers to that single id.
An Agent carries five roles:
- Identity shared with an Actor.
Agent.idis the same UUID as Access'sActor.idfor the same agent.define_agentwritesAgentDefinedandActorRegistered(kind="agent")atomically across both BCs in a single transaction. Every cross-BC reference (Decision authorship, Authorize checks, logbook attribution) works uniformly for humans and agents. - A four-state lifecycle. An Agent moves through
Defined(registered but not yet invocable),Versioned(promoted to ready-for-invocation; subscribers filter on this),Suspended(operator pause fromVersioned; non-terminal, returns viaresume_agent), andDeprecated(terminal). Versioning is per-Agent-id rainbow-style: multipleVersionedagents may sharekindconcurrently with differentids. - A typed configuration record. Required:
kind,name,version,model_ref(provider plus model plus optional snapshot pin). Optional:description,canonical_uri(https-only, A2A-forward-compat),prompt_template_id,capabilities(free-form, cardinality-capped). All bounded-text fields trim and validate at the value-object boundary. - Tool grants and budget declarations.
toolsis a frozenset of MCP tool names the agent is authorised to invoke; grants and revocations are idempotent and stay editable inDefined,Versioned, andSuspended(onlyDeprecatedblocks them).budgetcarries optionalmonthly_usd_capanddaily_token_cap, and both are enforced. The gate is coarse and post-hoc: it debits the recordedcost_usdafter each call and refuses the NEXT call once a cap is exhausted, so overspend is bounded to roughly one in-flight call. Windows are summed over the UTC calendar month and day containing the triggering event'soccurred_at, not wall clock, so a replayed work item gates identically. Above the per-agent caps sits the instrument-wide Allocation envelope, which the same gate reads. - Cross-BC action slices. Two slices today drive cross-BC writes:
regenerate_run_debriefinvokes the RunDebriefer agent on demand and writes a Decision on the named Run;promote_caution_proposalreads a CautionDrafter agent'sCautionProposalDecision and writes the proposed Caution into the Caution module after operator review.
The agent fleet¶
Thirteen agents are seeded today. They split two ways. By how they decide: the two LLM agents (RunDebriefer, CautionDrafter) call a model; the eleven deterministic agents apply a fixed rule and carry a sentinel model_ref with no prompt template. By what they do: passive agents only advise (they write a Decision and stop); active agents decide and then act, but only by issuing an existing spine command through the same authorized path a human uses, so the resulting record is byte-identical whether a human or the agent acted.
The runtime that drives an agent takes one of three host shapes:
- On-demand slice. A REST/MCP call invokes the agent directly (
regenerate_run_debrief). - Event-triggered subscriber. A subscriber in the projection worker reacts to a domain event: RunDebriefer and CautionDrafter on a terminal Run, CautionPromoter on a registered
CautionProposalDecision. - Composition-root periodic loop. A background task sweeps on a timer: RunSupervisor watches in-flight Runs and issues
hold_run/resume_run/stop_runas facility beam is lost and returns; ClearanceExpirer sweeps Active clearances and issuesexpire_clearanceonce a validity window has passed; ClearanceWatcher watches front-of-lifecycle clearances (Submitted, UnderReview, Approved) and records a flag Decision when one stalls past an operator window; CalibrationWatcher watches Provisional calibrations and records a flag Decision when one's newest revision has sat unverified past an operator window; ProcedureWatcher watches in-conduct procedures (Running, Held) and records a flag Decision when one has sat past an operator window without progressing, folding in the latest activity recency first so an actively-logging Running conduct is not falsely flagged; CampaignWatcher watches Held campaigns and records a flag Decision when one has sat operator-paused past an operator window without being resumed or closed.
| Agent | Decides | Host | Acts |
|---|---|---|---|
| RunDebriefer | LLM | on-demand slice + subscriber | writes a RunDebrief Decision (passive) |
| CautionDrafter | LLM | subscriber | writes a CautionProposal Decision (passive) |
| RunSupervisor | deterministic | periodic loop | hold_run / resume_run / stop_run + RunSupervision Decision |
| CautionPromoter | deterministic | subscriber | registers a Caution + CautionPromotion Decision |
| ClearanceExpirer | deterministic | periodic loop | expire_clearance + ClearanceExpiry Decision |
| ClearanceWatcher | deterministic | periodic loop | writes a ClearanceProgress flag Decision (passive) |
| CalibrationWatcher | deterministic | periodic loop | writes a CalibrationVerification (Stale) flag Decision (passive) |
| ProcedureWatcher | deterministic | periodic loop | writes a ProcedureProgress (Stall) flag Decision (passive) |
| CampaignWatcher | deterministic | periodic loop | writes a CampaignProgress (Stuck) flag Decision (passive) |
| AuthorityRevocationHolder | deterministic | subscriber | on PolicyGrantRevoked, holds every in-flight Run the revoked principal drives + an AuthorityRevocationHold Decision per Run |
| RatificationEnforcer | deterministic | subscriber | holds the target Run on RatificationRequested, resumes it on RatificationGranted |
| RunInitiator | deterministic | periodic loop | issues start_run to begin a Run autonomously, the proactive counterpart to RunSupervisor's reactive protection |
| ExperimentSteerer | deterministic | subscriber | across-Procedure disposition for a steered experiment (steer again, conclude, or hold the campaign) + an ExperimentSteering Decision |
The active runtimes (RunSupervisor, CautionPromoter, ClearanceExpirer, RunInitiator) ship off by default, gate every actuation through the Authorize port like any principal, and stand down the moment their Actor is deactivated. Two act without an enable flag, deliberately: AuthorityRevocationHolder is the kill switch that holds a revoked principal's in-flight Runs, and RatificationEnforcer is the consequence gate that holds a Run until its co-signature lands. Both register unconditionally, because a safety hold that an operator can forget to switch on is not a safety hold. None of them reaches past the spine onto the real-time floor: an active agent only issues a command the spine already exposes. ClearanceWatcher, CalibrationWatcher, ProcedureWatcher, and CampaignWatcher are passive (each records a flag Decision and issues no command) and likewise ship off by default. The two LLM-backed reactions (RunDebriefer, CautionDrafter) also ship off by default, behind LLM_ENABLED: they are the seam that would send experiment metadata to an external model and spend on it, so the switch and a credential are both required before either registers. RunSupervisor additionally carries shadow observe-only rules (run-liveness, plus signal-quality and signal-stall against a live Run's observation channels) that log a would-flag and take no further action; each is a separate opt-in above the agent's own enable, and advise / act promotions are deferred.
Out of scope
- Per-call pre-estimate refusal. The shipped gate is the coarse tier: it debits after a call and refuses the next one. A caller that can exhaust a balance in a single expensive long-context call needs a pre-estimate tier above this, and that tier is deferred. The gate errs permissive on spend it cannot account for and hard-fails on a spend-lookup error, so the failure direction is deliberate rather than incidental.
- A2A endpoint serving.
canonical_uriandcard_signature(deferred) are forward-compat fields for the Agent2Agent protocol. CORA does not serve an A2A endpoint today. acts_on_behalf_ofdelegation. Per-operator agent delegation is deferred until the first concrete need.- Strict URI validation.
canonical_urivalidation is loose today (https scheme, no fragment, length cap). RFC-compliant parsing waits until A2A wiring lands. - Tool-name BNF.
ToolNameaccepts any 1-100 char trimmed string. Tightening to MCP's formal tool-naming BNF is a watch item. - Closed
AgentKindenum. Kinds are free-form strings today. Graduation to a closed StrEnum waits until the vocabulary stabilises in pilot use. - Decision integration in the aggregate. The Agent aggregate is config-only. The runtime that invokes the agent and writes the Decision lives in the subscriber and composition-root layers; the aggregate never knows it was invoked.
Aggregates¶
| Name | Identity | State summary | FSM |
|---|---|---|---|
Agent |
id: UUID (same UUID as Access's Actor.id for this agent) |
kind, name, version, model_ref, description?, canonical_uri?, prompt_template_id?, capabilities, status, deprecation_reason?, tools, budget?, suspended_at?, resumed_at?, suspension_reason? |
yes |
LanguageModel |
id: UUID |
name, provider, model, snapshot_pin?, served_via, endpoint_note?, cost_basis, data_tier, archivability, status |
yes (single axis) |
Lifecycle timestamps (defined_at, versioned_at, deprecated_at) live on the projection rather than on aggregate state, matching the Method / Plan / Practice / Family / Capability shape from the 2026-05-20 audit. suspended_at, resumed_at, and suspension_reason stay on state because suspension_reason is invariant-bearing (deciders read it).
LanguageModel is the facility's catalog of models an agent is allowed to name. It binds a model's identity (provider, model, optional snapshot pin) and the governance facts that decide whether it may see a given class of data (data_tier, archivability, cost_basis) to the infrastructure serving it (served_via: Direct where CORA's own adapter holds the credentials, Argo where a facility gateway is the choke point, InHouse for a facility GPU pool). It lives in this BC beside the fleet whose Agent.model_ref it governs, and carries the longer name because Equipment already owns Model for vendor equipment. Credentials, quotas, and per-user grants are deliberately absent: the serving layer owns credentials and Trust owns grants.
Value Objects¶
| Name | Shape | Where used |
|---|---|---|
AgentKind |
trimmed string, 1-100 chars | Agent.kind (free-form discriminator) |
AgentName |
trimmed string, 1-100 chars | Agent.name (display name; mirrors A2A AgentCard.name and OTel gen_ai.agent.name) |
AgentDescription |
trimmed string, 1-2000 chars | Agent.description (free-form prose) |
AgentVersion |
trimmed string, 1-50 chars | Agent.version (semver-like convention; not parsed) |
AgentCanonicalUri |
trimmed string, 1-2000 chars, starts with https://, no fragment |
Agent.canonical_uri (A2A-forward-compat) |
AgentCapability |
trimmed string, 1-100 chars per entry; frozenset capped at 32 entries | members of Agent.capabilities |
AgentDeprecationReason |
trimmed string, 1-500 chars; optional | Agent.deprecation_reason (operator-supplied) |
AgentSuspensionReason |
trimmed string, 1-500 chars; REQUIRED at suspend | Agent.suspension_reason |
ToolName |
trimmed string, 1-100 chars per entry; frozenset capped at 32 entries | members of Agent.tools (MCP tool allowlist) |
AgentBudget |
monthly_usd_cap: float? >= 0, daily_token_cap: int? >= 0; at least one non-None |
Agent.budget (declarative caps; no enforcement today) |
ModelRef |
provider: str (1-100), model: str (1-200), snapshot_pin: str? (1-100) |
Agent.model_ref (required at definition) |
ModelRef.snapshot_pin enables reproducibility-by-construction: an Anthropic snapshot string, an OpenAI model fingerprint, or any provider-specific pin that names the exact weights used. Different model_ref requires defining a new Agent with a new id; the model identity is not a mutable field.
Deterministic (rule-based) agents carry a sentinel model_ref (provider="deterministic", model="agent:<Kind>:v1") and no prompt_template_id: the field satisfies the aggregate's required-config contract but is never used to build a model client. RunSupervisor, CautionPromoter, and ClearanceExpirer all use this shape.
FSM¶
stateDiagram-v2
[*] --> Defined: define_agent
Defined --> Versioned: version_agent
Defined --> Deprecated: deprecate_agent
Versioned --> Suspended: suspend_agent
Suspended --> Versioned: resume_agent
Versioned --> Deprecated: deprecate_agent
Suspended --> Deprecated: deprecate_agent
Deprecated --> [*]
| From | To | Command | Event |
|---|---|---|---|
(none) |
Defined |
define_agent |
AgentDefined (plus ActorRegistered(kind="agent") on Access stream) |
Defined |
Versioned |
version_agent |
AgentVersioned |
Versioned |
Suspended |
suspend_agent |
AgentSuspended |
Suspended |
Versioned |
resume_agent |
AgentResumed |
Defined / Versioned / Suspended |
Deprecated |
deprecate_agent |
AgentDeprecated |
Guards. Beyond the source-state check, each transition enforces:
define_agent- All required VOs (
kind,name,version,model_ref) pass bounded-text validation;capabilitiescardinality 0-32; ifcanonical_uriis set it must behttps://with no fragment. The slice writes to both the Agent stream and the Access Actor stream viaEventStore.append_streams; either stream'sConcurrencyErrorrolls back the whole commit. version_agent- Source set is
{Defined}only. Cannot re-version aVersionedagent (multi-version-per-kind is achieved by defining a new Agent with the samekindand a differentid, not by re-versioning the sameid). suspend_agent- Source set is
{Versioned}only.reasonis REQUIRED (1-500 chars after trim) so the audit log always carries operator context for the pause. resume_agent- Source set is
{Suspended}only. Noreasonfield by design: the act of resuming is its own signal; if rationale matters, operators record a Decision separately. deprecate_agent- Source set is
{Defined, Versioned, Suspended}.reasonis optional bounded text. Terminal; cannot be re-deprecated. grant_tool_to_agent/revoke_tool_from_agent/update_agent_budget- All blocked only in
Deprecated. Open inDefined,Versioned, andSuspendedso operators can fix permissions or caps while an agent is paused. Tool grants and revocations are idempotent (a no-op grant or revoke emits no event); budget update always emits an event.
LanguageModel¶
The catalog entry has its own single-axis lifecycle. Approval is the facility's governance act: a Defined entry is registered but unusable by the define_agent gate until it is approved.
stateDiagram-v2
[*] --> Defined: define_language_model
Defined --> Approved: approve_language_model
Approved --> RetirementAnnounced: announce_language_model_retirement
RetirementAnnounced --> Retired: retire_language_model
Approved --> Retired: retire_language_model
Defined --> Deprecated: deprecate_language_model
Approved --> Deprecated: deprecate_language_model
RetirementAnnounced --> Deprecated: deprecate_language_model
Retired --> [*]
Deprecated --> [*]
The two terminals answer different audit questions, so they stay distinct: RetirementAnnounced then Retired models the VENDOR ending the model's service life, which an at-risk-results projection reads as an appended governance fact, while Deprecated models the FACILITY withdrawing its own approval. A provider that removes a model without warning goes Approved straight to Retired.
Events¶
| Event | Payload sketch | When emitted |
|---|---|---|
AgentDefined |
agent_id, kind, name, version, model_ref, description?, canonical_uri?, prompt_template_id?, capabilities, tools, budget_monthly_usd_cap?, budget_daily_token_cap?, occurred_at |
define_agent succeeds (co-written with ActorRegistered) |
AgentVersioned |
agent_id, version, occurred_at |
version_agent succeeds |
AgentSuspended |
agent_id, reason, occurred_at |
suspend_agent succeeds |
AgentResumed |
agent_id, occurred_at |
resume_agent succeeds |
AgentDeprecated |
agent_id, reason?, occurred_at |
deprecate_agent succeeds; terminal |
AgentToolGranted |
agent_id, tool_name, occurred_at |
grant_tool_to_agent succeeds (no event on a no-op re-grant) |
AgentToolRevoked |
agent_id, tool_name, occurred_at |
revoke_tool_from_agent succeeds (no event on a no-op re-revoke) |
AgentBudgetUpdated |
agent_id, monthly_usd_cap?, daily_token_cap?, occurred_at |
update_agent_budget succeeds |
AgentTargetPlanUpdated |
agent_id, target_plan_id, occurred_at |
update_agent_target_plan succeeds |
LanguageModelDefined |
language_model_id, name, provider, model, snapshot_pin?, served_via, endpoint_note?, cost_basis, data_tier, archivability, occurred_at |
define_language_model succeeds (genesis, status Defined) |
LanguageModelApproved |
language_model_id, occurred_at |
approve_language_model succeeds; the entry becomes usable by the define_agent gate |
LanguageModelRetirementAnnounced |
language_model_id, reason, effective_at?, occurred_at |
announce_language_model_retirement succeeds; records the vendor's announcement |
LanguageModelRetired |
language_model_id, reason?, occurred_at |
retire_language_model succeeds; terminal |
LanguageModelDeprecated |
language_model_id, reason, occurred_at |
deprecate_language_model succeeds; terminal |
define_agent is the only Agent-BC slice that writes across streams. The other lifecycle events are single-stream. The cross-BC action slices (regenerate_run_debrief, promote_caution_proposal) do not write to the Agent stream at all: they write a DecisionRegistered on the Decision stream and (for the promotion path) a CautionRegistered on the Caution stream.
Slices¶
| Command / query | REST | MCP tool |
|---|---|---|
AnnounceLanguageModelRetirement |
POST /language-models/{language_model_id}/announce-retirement |
announce_language_model_retirement |
ApproveLanguageModel |
POST /language-models/{language_model_id}/approve |
approve_language_model |
DefineAgent |
POST /agents |
define_agent |
DefineLanguageModel |
POST /language-models |
define_language_model |
DeprecateAgent |
POST /agents/{agent_id}/deprecate |
deprecate_agent |
DeprecateLanguageModel |
POST /language-models/{language_model_id}/deprecate |
deprecate_language_model |
DismissEventInReaction |
POST /agent/reactions/{subscriber_name}/dismiss-event |
dismiss_event_in_reaction |
GetAgent |
GET /agents/{agent_id} |
get_agent |
GrantToolToAgent |
POST /agents/{agent_id}/tools/grant |
grant_tool_to_agent |
ListAtRiskResults |
GET /language-models/{language_model_id}/at-risk-results |
list_at_risk_results |
PromoteCautionProposal |
POST /agents/caution-drafter/decisions/{decision_id}/promote |
promote_caution_proposal |
RegenerateRunDebrief |
POST /agents/run-debriefer/runs/{run_id}/regenerate-debrief |
regenerate_run_debrief |
ResumeAgent |
POST /agents/{agent_id}/resume |
resume_agent |
RetireLanguageModel |
POST /language-models/{language_model_id}/retire |
retire_language_model |
RevokeToolFromAgent |
POST /agents/{agent_id}/tools/revoke |
revoke_tool_from_agent |
SuspendAgent |
POST /agents/{agent_id}/suspend |
suspend_agent |
UpdateAgentBudget |
POST /agents/{agent_id}/budget |
update_agent_budget |
UpdateAgentTargetPlan |
POST /agents/{agent_id}/target-plan |
update_agent_target_plan |
VersionAgent |
POST /agents/{agent_id}/version |
version_agent |
Errors per slice. Beyond Pydantic boundary 422s, each slice raises:
DefineAgentInvalidAgentKind,InvalidAgentName,InvalidAgentVersion,InvalidAgentDescription,InvalidAgentCanonicalUri,InvalidAgentCapability,InvalidAgentCapabilities(over cardinality cap),InvalidModelRef,AgentAlreadyExists(defensive; UUIDv7 makes collision near-impossible),UnauthorizedVersionAgent/SuspendAgent/ResumeAgent/DeprecateAgentAgentNotFound,AgentCannotVersion/AgentCannotSuspend/AgentCannotResume/AgentCannotDeprecate(source-state guard),Unauthorized.SuspendAgentadditionally raisesInvalidAgentSuspensionReason;DeprecateAgentadditionally raisesInvalidAgentDeprecationReason.GrantToolToAgent/RevokeToolFromAgentAgentNotFound,AgentCannotGrantTool/AgentCannotRevokeTool(blocked inDeprecated),InvalidToolName,AgentToolsExceedsLimit(grant only),UnauthorizedUpdateAgentBudgetAgentNotFound,AgentCannotUpdateBudget,InvalidAgentBudget,UnauthorizedGetAgentAgentNotFoundRegenerateRunDebriefUnauthorized,AgentNotSeeded/AgentDeactivated(RunDebriefer agent missing or its Actor inactive), Run cross-aggregate-load failures, parent Decision mismatch,503if the LLM adapter is not wiredPromoteCautionProposalUnauthorized(including provenance gate: Decision was not emitted by a registered CautionDrafter agent),DecisionNotFound, malformedproposed_cautionpayload,CautionNotFound/CautionCannotSupersede(for the supersede arm)
DefineAgent, RegenerateRunDebrief, and PromoteCautionProposal are wrapped by the Idempotency-Key header pattern. The other lifecycle slices return 204 No Content and are not idempotency-wrapped: a second version_agent against an already-Versioned agent raises AgentCannotVersion rather than no-oping.
Storage & Projections¶
One read-side table backs the Agent module today.
CREATE TABLE proj_agent_summary (
agent_id UUID PRIMARY KEY,
kind TEXT NOT NULL,
name TEXT NOT NULL,
version TEXT NOT NULL,
status TEXT NOT NULL CHECK (
status IN ('Defined', 'Versioned', 'Suspended', 'Deprecated')
),
created_at TIMESTAMPTZ NOT NULL,
versioned_at TIMESTAMPTZ,
deprecated_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
The CHECK constraint encodes the closed AgentStatus enum at the row level. versioned_at and deprecated_at are nullable for agents that have not transitioned through those states yet; created_at is set once at AgentDefined and indexed for keyset pagination.
Suspended and Resumed lifecycle timestamps stay on aggregate state rather than on the projection, because suspension_reason is invariant-bearing (deciders read it to make decisions about subsequent transitions). The projection records the status field on every event, so a Suspended agent's current state is visible in the read model even though the timestamp pair is not.
GET /agents/{id} folds the aggregate's event stream and joins the projection for the lifecycle timestamps; tools, budget, and suspension_reason come from the aggregate state.
Cross-Module boundaries¶
| Module | Relationship | What's exchanged |
|---|---|---|
| Trust | gated-by | Every write-side Agent slice (lifecycle, grants, budget) is gated by the Authorize port resolving a Policy for the (principal, command, conduit, surface) tuple; deny outcomes refuse before the decider runs |
| Access | shared-id-with | Agent.id is the same UUID as Actor.id for this agent; define_agent co-writes ActorRegistered(kind="agent") on the Access stream via append_streams |
| Decision | writes-to (via subscriber, slice, and runtime) | Every agent writes its judgement as a DecisionRegistered, in its own context: RunDebrief (RunDebriefer), CautionProposal (CautionDrafter), RunSupervision (RunSupervisor), CautionPromotion (CautionPromoter), ClearanceExpiry (ClearanceExpirer). Agent-authored Decisions carry the agent's id in actor_id |
| Run | reads-from + writes-to | The RunDebriefer subscriber filters on terminal-state Run events and loads the Run plus its pinned_calibration_ids to build the debrief context; the RunSupervisor loop issues hold_run / stop_run on an in-flight Run when beam is lost |
| Caution | writes-to | promote_caution_proposal (operator-initiated) reads a CautionDrafter Decision's proposed_caution payload and writes CautionRegistered (plus, for the supersede arm, CautionSuperseded) atomically; the CautionPromoter loop performs the same Caution write automatically for high-confidence, Notice-only proposals |
| Safety | writes-to | The ClearanceExpirer loop issues expire_clearance on an Active clearance whose valid_until has passed, writing ClearanceExpired |
The two cross-BC action slices both gate on operator authorisation before any cross-BC write happens: regenerate_run_debrief requires the caller to be authorised to invoke the named agent; promote_caution_proposal requires the caller to be authorised to author Cautions, plus a provenance check that the named Decision was emitted by a registered CautionDrafter agent. Promotion via the slice is operator-initiated by design; the CautionDrafter never writes a Caution itself.
The active-agent runtimes write through the same authorized command paths a human uses, not through privileged back doors: RunSupervisor's hold_run / stop_run, CautionPromoter's Caution registration, and ClearanceExpirer's expire_clearance each pass the Authorize port for the agent principal before any write. This is what keeps the record byte-identical whether a human or an agent acted, and it is why the active runtimes can be turned off (or their Actor deactivated) without leaving a partial or privileged trail behind.
Examples¶
The four examples below follow the canonical path for one Agent: define it (atomically registering its Actor in Access), version it for invocation, invoke RunDebriefer on demand against a specific Run, and promote a CautionDrafter Decision into a real Caution. The caller's principal becomes the authoring actor on every write. For the REST/MCP equivalence, auth, and idempotency conventions these examples share, see Reading the examples on the Modules landing page.
Define a new Agent¶
POST /agents
Content-Type: application/json
Idempotency-Key: 4f5a6b7c-8d9e-0f1a-2b3c-4d5e6f7a8b9c
X-Principal-Id: 11111111-2222-3333-4444-555555555555
{
"kind": "RunDebriefer",
"name": "Run Debrief (Claude Haiku 4.5)",
"version": "v1.0.0",
"model_ref": {
"provider": "anthropic",
"model": "claude-haiku-4-5"
},
"description": "Watches terminal Run events and writes an advisory Decision summarising what happened.",
"canonical_uri": "https://agents.cora.aps.anl.gov/run-debrief/v1",
"capabilities": ["run-debrief", "decision-author"]
}
Returns 201 Created with the new agent_id. The same UUID becomes the agent's Actor.id in the Access module, co-written atomically.
mcp.call_tool(
"define_agent",
{
"kind": "RunDebriefer",
"name": "Run Debrief (Claude Haiku 4.5)",
"version": "v1.0.0",
"model_ref": {
"provider": "anthropic",
"model": "claude-haiku-4-5",
},
"description": "Watches terminal Run events and writes an advisory Decision summarising what happened.",
"canonical_uri": "https://agents.cora.aps.anl.gov/run-debrief/v1",
"capabilities": ["run-debrief", "decision-author"],
},
)
Version the Agent so subscribers will invoke it¶
Returns 204 No Content. The agent moves from Defined to Versioned; the RunDebriefer subscriber, which filters on status=Versioned, will now invoke it on the next terminal Run event.
Re-invoke RunDebriefer on demand¶
POST /agents/run-debriefer/runs/aaaa1111-2222-3333-4444-555555555555/regenerate-debrief
Content-Type: application/json
Idempotency-Key: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
X-Principal-Id: 22222222-3333-4444-5555-666666666666
{
"parent_decision_id": "bbbb1111-2222-3333-4444-555555555555"
}
Triggers a fresh RunDebriefer invocation against the named Run and returns 201 Created with the new decision_id. When parent_decision_id is supplied, the new Decision links back to the prior debrief via PROV-O wasInformedBy. Returns 503 Service Unavailable when the LLM adapter is not wired (LLM_ENABLED off, the default, or no API key); the body names which one applies.
Promote a CautionDrafter Decision into a Caution¶
POST /agents/caution-drafter/decisions/<decision-id>/promote
Idempotency-Key: 9c0d1e2f-3a4b-5c6d-7e8f-9a0b1c2d3e4f
X-Principal-Id: 33333333-4444-5555-6666-777777777777
No request body: the proposed Caution's text, workaround, target, and category are carried by the referenced Decision's inputs. Returns 201 Created with the new caution_id. The slice writes CautionRegistered on the Caution stream (and, for the supersede arm, CautionSuperseded on the parent Caution stream) atomically. Authorisation requires the caller to be authorised to author Cautions and the Decision to have been emitted by a registered CautionDrafter agent.