Skip to main content

Agent API

Table of Contents​

Top

upsquad/agent/v1/agent.proto​

Agent​

Agent represents an AI agent instance within the scope hierarchy.

FieldTypeLabelDescription
idstringid is the UUID of the agent.
org_idstringorg_id is the tenant organisation UUID.
pillar_idstringpillar_id is the business unit UUID.
team_idstringteam_id is the team UUID the agent belongs to.
namestringname is the human-readable agent name.
clearanceint32clearance is the agent's clearance level (1-5).
created_atgoogle.protobuf.Timestampcreated_at is when the agent was provisioned.
deleted_atgoogle.protobuf.Timestampdeleted_at is set when the agent is soft-deleted; null for active agents.
is_headlessboolis_headless indicates the agent was provisioned via RegisterAgent (API).
statusstringstatus is the agent runtime status: "active" (default) or "paused". GAP-37 (#1085) — set by PauseAgent / ResumeAgent. Empty string is treated as "active" for forward-compat with rows persisted before migration 083 was applied.

AgentConfiguration​

AgentConfiguration holds per-agent configuration data.

FieldTypeLabelDescription
idstringid is the configuration record UUID.
agent_idstringagent_id is the UUID of the agent this configuration belongs to.
personastringpersona is the agent's system prompt / persona text.
model_idstringmodel_id is the LLM model identifier (e.g. "claude-sonnet-4-20250514").
toolsstringtools is the per-agent MCP allowlist: a JSON array of mcp_servers UUIDs that NARROWS the agent's team-resolved MCP set (ADR-0022 W3/B8, #1744). Empty/absent = inherit ALL team MCP servers. REPURPOSED from the legacy tool-definitions array (the static-list read is bypassed by the v2.4 resolver).
configstringconfig is a JSON-encoded map of arbitrary configuration.
updated_atgoogle.protobuf.Timestampupdated_at is the last modification timestamp.
gateway_routedboolgateway_routed reports whether this agent's LLM traffic is routed through the Model Gateway — MG-4.1's per-agent opt-in (migration 240, issue #3143).

It is READ-ONLY on this message. The only write path is SetAgentGatewayRouting, which carries its own authorisation; a field on the configuration a create/register call echoes back would be a second write surface for a governance switch, which is how the two drift.

It is RUNTIME-AGNOSTIC and it is NOT llm_base_url (field 10 of RegisterAgentRequest): that one stays an Anthropic-format claude_sdk endpoint override. Two meanings, two fields. |

AgentLLMEnablement​

AgentLLMEnablement is one agent's NARROW-ONLY LLM selection (MG-2.6).

EMPTY MEANS NO NARROWING ON THAT AXIS, INDEPENDENTLY. This is the same empty=no-constraint rule agent_configurations.tools and llm_enablement already use (LLD #2801 section 3.2: "a source that is empty contributes no constraint; it never contributes permission"). The opposite reading — empty means nothing is permitted — looks fail-closed and is a trap: an operator who clears a selection through the product would brick the agent, and clearing is the operation this surface exists to make reversible.

A non-empty list is the ONLY set the agent may use on that axis. Because it is a NARROWING it can never widen past the team's approved set (gate 7c) or the spend cap (gate 7d) — those still bound the agent whatever is listed here.

FieldTypeLabelDescription
endpoint_idsstringrepeatedendpoint_ids are llm_endpoints UUIDs the agent may route to. Every entry must be an endpoint the agent's team holds an APPROVED GRANT binding for.
model_idsstringrepeatedmodel_ids are model identifiers the agent may request, matched EXACTLY (never by prefix — prefix matching is the endpoint-side semantics of llm_endpoints.model_ids and applying it here would silently widen a selection). When the team carries an approved model allowlist, every entry must be in it; when the team carries none, the team restricts no model and any entry is a legitimate narrowing.

AgentTemplate​

AgentTemplate is one entry in the prototype-template catalog backing the redesigned 4-step Deploy wizard. Fields mirror the wizard's step-1 card semantics.

FieldTypeLabelDescription
idstringid is the stable template identifier (e.g. "support-triage").
namestringname is the short display name shown in the wizard card title.
descriptionstringdescription is the one-paragraph summary shown under the name.
iconstringicon is a stable icon token the client maps to its icon registry (e.g. "lifebuoy", "receipt", "search", "wrench").
default_actionsstringrepeateddefault_actions is the seed list of actions the wizard pre-populates for this template. Free-form strings — the agent persona text synthesises them into a system prompt at deploy time.
best_forstringbest_for is the "use this when…" tagline shown on the card.

BudgetStatus​

BudgetStatus mirrors GetBudgetStatusResponse but is embedded inline on each ListAgentRow to avoid the N+1 the redesigned Agents directory would otherwise incur (one GetBudgetStatus per row). GAP-21 (#1084).

FieldTypeLabelDescription
daily_token_capint64daily_token_cap is the configured daily token limit (0 = unlimited).
monthly_token_capint64monthly_token_cap is the configured monthly token limit (0 = unlimited).
daily_cost_capdoubledaily_cost_cap is the configured daily cost limit in USD (0 = unlimited).
monthly_cost_capdoublemonthly_cost_cap is the configured monthly cost limit in USD (0 = unlimited).
daily_tokens_usedint64daily_tokens_used is the tokens consumed today.
monthly_tokens_usedint64monthly_tokens_used is the tokens consumed this month.
daily_cost_useddoubledaily_cost_used is the cost incurred today in USD.
monthly_cost_useddoublemonthly_cost_used is the cost incurred this month in USD.
status_colorstringstatus_color is a denormalised UI signal: "green" (well under cap), "amber" (>= 75% of any cap), or "red" (>= cap). Computed on the server from the same {used, cap} pairs so the client never has to re-derive the threshold. Empty when no row exists in agent_budgets (the agent has not had a budget configured yet).

CreateAgentRequest​

CreateAgentRequest provisions a new agent within the caller's scope.

FieldTypeLabelDescription
pillar_idstringpillar_id is the OPTIONAL business unit to create the agent in. Empty for agents on root teams, which have no pillar (uniform team graph, #1851).
team_idstringteam_id is the team to create the agent in.
namestringname is the human-readable agent name.
clearanceint32clearance is the agent's clearance level (1-5).
personastringpersona is the optional initial persona text.
model_idstringmodel_id is the optional LLM model identifier; defaults to "claude-sonnet-4-20250514".
toolsstringtools is the optional initial per-agent MCP allowlist: a JSON array of mcp_servers UUIDs that NARROWS the agent's team-resolved MCP set (ADR-0022 W3/B8, #1744). Empty/absent = inherit ALL team MCP servers. REPURPOSED from the legacy tool-definitions array.

CreateAgentResponse​

CreateAgentResponse confirms agent creation.

FieldTypeLabelDescription
agentAgentagent is the newly created agent record.
configurationAgentConfigurationconfiguration is the initial agent configuration.

DeleteAgentRequest​

DeleteAgentRequest soft-deletes an agent by ID.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent to soft-delete.

DeleteAgentResponse​

DeleteAgentResponse confirms the soft deletion.

FieldTypeLabelDescription
deleted_atgoogle.protobuf.Timestampdeleted_at is the timestamp when the agent was marked as deleted.

DeployAgentToTeamRequest​

DeployAgentToTeamRequest assigns an agent to a team.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent.
team_idstringteam_id is the UUID of the target team.

DeployAgentToTeamResponse​

DeployAgentToTeamResponse confirms the deployment.

FieldTypeLabelDescription
agentAgentagent is the updated agent record.
deployed_atgoogle.protobuf.Timestampdeployed_at is the timestamp of the deployment.

DeployFromTemplateBudget​

DeployFromTemplateBudget carries the budget caps for the wizard's budget step. Fields mirror SetBudgetCapRequest.

FieldTypeLabelDescription
daily_token_capint64daily_token_cap is the daily token limit (0 = unlimited).
monthly_token_capint64monthly_token_cap is the monthly token limit (0 = unlimited).
daily_cost_capdoubledaily_cost_cap is the daily cost limit in USD (0.0 = unlimited).
monthly_cost_capdoublemonthly_cost_cap is the monthly cost limit in USD (0.0 = unlimited).

DeployFromTemplateRequest​

DeployFromTemplateRequest is the one-shot wizard commit. All fields are validated up front; on any failure the caller receives a single errdetails.BadRequest with FieldViolations pointing at the specific fields that need correction (LLD-4 §2.1 pattern). The composite is transactional — every sub-step (RegisterAgent + DeployAgentToTeam + SetBudgetCap + SetAutonomyLevel) commits atomically.

FieldTypeLabelDescription
template_idstringtemplate_id selects a row from ListAgentTemplates. Required.
namestringname is the human-readable agent name. Required.
rolestringrole is the free-text role tag rendered on the agent card (e.g. "Customer Support Tier-1"). Optional.
reports_tostringreports_to is the supervising member UUID — drives the inferred clearance via MemberService.InferAgentClearance. Required.
team_idstringteam_id is the destination team (= org_unit of unit_type='team'). Required.
pillar_idstringpillar_id is the team's parent pillar — required by the legacy RegisterAgent contract until org-units fully replaces it.
clearanceint32clearance is the agent's clearance level (1..5). The wizard pre-fills this from InferAgentClearance but the caller may override. Final value is still constrained by the supervising member's clearance via the existing CreateMember invariant.
autonomyAutonomyLevelautonomy is the governance autonomy level the wizard's slider selects. Required.
approval_templatestringapproval_template is the JSON approval-template required when autonomy crosses the governance-review threshold. Optional otherwise.
budgetDeployFromTemplateBudgetbudget carries the daily/monthly token + cost caps from the wizard's budget step.
guardrail_idsstringrepeatedguardrail_ids is the optional list of guardrail UUIDs to attach. Empty = no guardrails. Currently a forward-looking seam — the composite records the IDs in the lifecycle event metadata so the governance worker can pick them up; the actual attach RPC lives on a separate service (GAP-39, future).
model_idstringmodel_id is the LLM model id; defaults to the platform default (claude-sonnet-4-20250514) when empty.
personastringpersona is the optional system prompt; the wizard synthesises one from default_actions when empty.
toolsstringtools is the optional per-agent MCP allowlist: a JSON array of mcp_servers UUIDs that NARROWS the agent's team-resolved MCP set (ADR-0022 W3/B8, #1744). Empty/absent = inherit ALL team MCP servers; the wizard supplies an empty array when none are deselected. REPURPOSED from the legacy tool-definitions array. The wizard CANNOT grant — grants stay team-level.

DeployFromTemplateResponse​

DeployFromTemplateResponse returns the new agent's identity. Callers can fetch the full record via GetAgent if they need the persona / tools / budget back.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the newly-deployed agent.
created_atgoogle.protobuf.Timestampcreated_at is the timestamp the agent row was inserted.
applied_clearanceint32applied_clearance is the final clearance level persisted on the agent — surfaced so the wizard can render the "applied X (capped by manager)" hint without a follow-up call.

GetAgentHealthRequest​

GetAgentHealthRequest reads real-time health signals for an agent.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent.

GetAgentHealthResponse​

GetAgentHealthResponse returns real-time health data.

FieldTypeLabelDescription
statusstringstatus is the derived health status: "healthy", "degraded", "down", or "idle".
last_activegoogle.protobuf.Timestamplast_active is the timestamp of the agent's most recent action.
error_ratedoubleerror_rate is the percentage of errors in the recent window (0-100).
queue_depthint64queue_depth is the number of pending items in the agent's work queue.
tokens_used_24hint64tokens_used_24h is the total tokens consumed in the last 24 hours.

GetAgentLLMEnablementRequest​

GetAgentLLMEnablementRequest reads an agent's selection and its ceiling.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent (agents.id).

GetAgentLLMEnablementResponse​

GetAgentLLMEnablementResponse carries the selection and the ceiling.

FieldTypeLabelDescription
selectionAgentLLMEnablementselection is the agent's current narrow-only selection. Both lists empty means the agent narrows nothing and is bounded by its team alone.
available_endpoint_idsstringrepeatedavailable_endpoint_ids is the ENDPOINT CEILING: every llm_endpoints id the agent's team holds an approved GRANT binding for, on a live endpoint. An EMPTY list means the team has been granted no endpoint — so no non-empty endpoint selection is legal, which is not the same as "no constraint".

This axis is an enumerated GRANT and its emptiness therefore means "nothing granted", the OPPOSITE of what an empty selection means. The two emptiness rules sit on the same message on purpose; see model_selection_restricted for the other one. | | available_model_ids | string | repeated | available_model_ids is the MODEL CEILING: the union, over the endpoints above, of what each one SERVES narrowed by what the team has kept ENABLED (llm_endpoint_units.enabled_models, migration 262, core#3601).

It is DERIVED FROM THE ENDPOINT AXIS, which is the #3601 change. Until then it was the team's status='approved' entries in an ORG-WIDE model catalogue, unrelated to the endpoints the team was bound to — so the two axes on this message could each admit things the other made unusable.

Entries may be PREFIXES when an endpoint claims one and the team has not narrowed it (LLD #2673 §8.3m). A prefix here is honest: it is what the team may select, and every id under it is admitted.

MEANINGLESS UNLESS model_selection_restricted is true. | | model_selection_restricted | bool | | model_selection_restricted reports whether a team model restriction is in force at all. False means the team holds no approved grant binding on a live endpoint, available_model_ids is empty, AND every model is a legal selection — the incumbent direct-dial path, which consults no binding until 5E6.

A client MUST read this flag rather than deriving it from the list being empty — deriving it is a second implementation of the empty=allow-all rule, and it is the one that drifts. |

GetAgentRequest​

GetAgentRequest retrieves a single agent by ID.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent to retrieve.

GetAgentResponse​

GetAgentResponse returns the agent and its configuration.

FieldTypeLabelDescription
agentAgentagent is the retrieved agent record.
configurationAgentConfigurationconfiguration is the agent's current configuration.

GetBudgetStatusRequest​

GetBudgetStatusRequest retrieves current-period usage vs caps.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent.

GetBudgetStatusResponse​

GetBudgetStatusResponse returns current usage and caps.

FieldTypeLabelDescription
daily_token_capint64daily_token_cap is the configured daily token limit.
monthly_token_capint64monthly_token_cap is the configured monthly token limit.
daily_cost_capdoubledaily_cost_cap is the configured daily cost limit in USD.
monthly_cost_capdoublemonthly_cost_cap is the configured monthly cost limit in USD.
daily_tokens_usedint64daily_tokens_used is the tokens consumed today.
monthly_tokens_usedint64monthly_tokens_used is the tokens consumed this month.
daily_cost_useddoubledaily_cost_used is the cost incurred today in USD.
monthly_cost_useddoublemonthly_cost_used is the cost incurred this month in USD.
period_startgoogle.protobuf.Timestampperiod_start is the start of the current billing period.

ListAgentRow​

ListAgentRow is the rich shape returned by ListAgents — the bare agents repeated field is preserved for backwards compatibility, but new clients should consume rows instead. GAP-21 + GAP-36 (#1084).

FieldTypeLabelDescription
agentAgentagent is the canonical Agent record.
budgetBudgetStatusbudget is the inline budget snapshot (avoids N+1 GetBudgetStatus calls on the directory page). Nil when no row exists in agent_budgets.
owner_human_idstringowner_human_id is the UUID of the supervising human member — resolved by walking the agent's member row up the reports_to chain to the first MEMBER_TYPE_HUMAN ancestor. Empty when the agent has no associated member row (legacy headless agents pre-migration 058) or when no human ancestor exists in the chain.
owner_human_namestringowner_human_name is the denormalised display_name of owner_human_id, surfaced inline so the directory does not need a second round-trip per row.
last_action_atgoogle.protobuf.Timestamplast_action_at is the timestamp of the agent's most recent recorded action — sourced from Redis (agent:<id>:last_active). Nil when no activity has ever been observed.
actions_24hint64actions_24h is the cardinality of distinct actions observed in the last 24 hours, sourced from Redis (agent:<id>:actions).
team_deletedboolteam_deleted is true when the agent's team_id references an org_unit that no longer resolves to a LIVE team (soft-deleted or nonexistent). Integrity #1813: migration 070 dropped the agents.team_id FK, so an agent can outlive the team it was bound to and be silently orphaned onto a tombstone. The directory MUST render these as "team deleted / unassigned" rather than resolving the tombstone's name as if live. False for agents with a NULL/empty team_id (legitimately org-wide / headless) and for agents bound to a live team.

ListAgentTemplatesRequest​

ListAgentTemplatesRequest is empty for v1 (no filters yet).

ListAgentTemplatesResponse​

ListAgentTemplatesResponse returns the static template catalog.

FieldTypeLabelDescription
templatesAgentTemplaterepeatedtemplates is the ordered prototype-template catalog.

ListAgentsRequest​

ListAgentsRequest returns a paginated list of active agents.

All filter fields are optional and AND-combined. Legacy callers passing only pillar_id / team_id retain the original behaviour; new filters default to "no filter" so the response shape is unchanged when none are set.

FieldTypeLabelDescription
pillar_idstringpillar_id optionally filters agents by business unit.
team_idstringteam_id optionally filters agents by team.
pageint32page is the 1-based page number for pagination.
page_sizeint32page_size is the maximum number of results per page. Max 100, default 20.
clearance_filterint32clearance_filter restricts results to a specific clearance level (1..5). 0 = no filter. GAP-35 (#1084).
autonomy_filterAutonomyLevelautonomy_filter restricts results to a specific autonomy level. AUTONOMY_LEVEL_UNSPECIFIED = no filter. GAP-35.
status_filterstringstatus_filter restricts results to a specific status — "active" or "paused". Empty = no filter. GAP-35.
health_filterHealthFilterhealth_filter restricts results to a derived health bucket; see HealthFilter for semantics. GAP-35.

Implementation note for callers: OVER_BUDGET and UNDER_UTILIZED are applied as SQL predicates so total_count and the returned page are both filter-aware. RECENTLY_FAILING is a POST-FETCH filter applied in process against Redis-derived health state — the SQL layer returns the unfiltered page, then rows that don't match the recently-failing bucket are dropped before responding. As a result, the page size for a RECENTLY_FAILING request may shrink below page_size and total_count remains the pre-filter SQL count (the upper bound). See ListAgentsResponse.total_count for matching semantics on the response side. | | query | string | | query is a free-text substring matched case-insensitively against the agent name. Empty = no filter. GAP-35. |

ListAgentsResponse​

ListAgentsResponse returns a paginated list of agents.

FieldTypeLabelDescription
agentsAgentrepeatedagents is the list of agent records (legacy field — kept for backwards compatibility with pre-redesign clients).
total_countint32total_count is the total number of matching agents (for pagination). For OVER_BUDGET and UNDER_UTILIZED this reflects the SQL-side filtered count (matches the page size). For HEALTH_FILTER_RECENTLY_FAILING this is the pre-filter SQL count — an upper bound — because that bucket is computed post-fetch from Redis state; the rows / agents arrays may be shorter than the count for that filter only.
rowsListAgentRowrepeatedrows is the rich-shape list with budget inline + row metadata. New clients should consume this and ignore agents. GAP-21 + GAP-36.

PauseAgentRequest​

PauseAgentRequest transitions an agent to status=paused.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent to pause.

PauseAgentResponse​

PauseAgentResponse confirms the pause.

FieldTypeLabelDescription
agentAgentagent is the updated agent record (status=paused).
already_pausedboolalready_paused is true if the agent was already paused (idempotent no-op — no lifecycle event was emitted).

RegisterAgentRequest​

RegisterAgentRequest creates a headless agent with full configuration.

FieldTypeLabelDescription
pillar_idstringpillar_id is the OPTIONAL business unit UUID. Empty for agents on root teams, which have no pillar (uniform team graph, #1851).
team_idstringteam_id is the team UUID.
namestringname is the human-readable agent name.
clearanceint32clearance is the agent's clearance level (1-5).
personastringpersona is the system prompt text (max 32000 chars).
model_idstringmodel_id is the LLM model id; defaults to claude-sonnet-4-20250514.
toolsstringtools is the per-agent MCP allowlist: a JSON array of mcp_servers UUIDs that NARROWS this agent's team-resolved MCP set (ADR-0022 W3/B8, #1744). Empty/absent = inherit ALL of the team's approved MCP servers. It can only DESELECT — an id absent from the team set has no effect (never a grant). REPURPOSED from the legacy "JSON array of tool definitions": the legacy static-list read is BYPASSED by the Org Model v2.4 resolver, so no behaviour regression. Enforced at session freeze (freezeResolvedTools).
autonomy_levelAutonomyLevelautonomy_level is the governance mode.
runtimestringruntime selects the worker executor: "langgraph" (default)
llm_base_urlstringllm_base_url is an optional Anthropic-format endpoint for the claude_sdk runtime. Only valid when runtime == "claude_sdk". Added in #1631.

RegisterAgentResponse​

RegisterAgentResponse confirms headless agent creation.

FieldTypeLabelDescription
agentAgentagent is the newly created agent record.
configurationAgentConfigurationconfiguration is the initial agent configuration.

RegisterExternalAgentRequest​

RegisterExternalAgentRequest registers a BYO agent and issues its credential. It provisions a first-class agent (runtime="external") governed identically to an internal agent (team, clearance, per-agent MCP allowlist, audit), then records the register-once credential→agent binding in external_agent_clients.

FieldTypeLabelDescription
pillar_idstringpillar_id is the OPTIONAL business unit UUID. Empty for root-team agents.
team_idstringteam_id is the team UUID; it is also the agent's unit_id (the token audience /mcp/team/{unit}). Required.
namestringname is the human-readable agent name. Required.
clearanceint32clearance is the agent's clearance level (1-5). Required.
personastringpersona is the optional system prompt text (max 32000 chars).
model_idstringmodel_id is the optional LLM model id; defaults to claude-sonnet-4-20250514.
toolsstringtools is the optional per-agent MCP allowlist: a JSON array of mcp_servers UUIDs that NARROWS the agent's team-resolved MCP set (ADR-0022 W3/B8). Empty = inherit ALL team MCP servers.
autonomy_levelAutonomyLevelautonomy_level is the optional governance mode.
public_key_jwkstringpublic_key_jwk is the customer's PUBLIC key as a JWK JSON object (the PREFERRED private_key_jwt path). When set, NO shared secret is generated and client_secret is empty in the response. When empty, the server generates a shared secret and returns it ONCE. Exactly one credential is issued.

RegisterExternalAgentResponse​

RegisterExternalAgentResponse returns the new agent + its client credential.

FieldTypeLabelDescription
agentAgentagent is the newly created external agent record.
configurationAgentConfigurationconfiguration is the initial agent configuration (runtime="external").
client_idstringclient_id is the server-generated credential handle the external agent presents to the token endpoint (PR-4). Stable for the life of the binding.
client_secretstringclient_secret is the server-generated shared secret, returned EXACTLY ONCE. Populated ONLY for EXTERNAL_CREDENTIAL_TYPE_SHARED_SECRET; empty for the public-key path. It is never persisted in plaintext or logged.
credential_typeExternalCredentialTypecredential_type indicates how the agent authenticates (public key vs secret).

ResumeAgentRequest​

ResumeAgentRequest transitions an agent back to status=active.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent to resume.

ResumeAgentResponse​

ResumeAgentResponse confirms the resume.

FieldTypeLabelDescription
agentAgentagent is the updated agent record (status=active).
already_activeboolalready_active is true if the agent was already active (idempotent no-op — no lifecycle event was emitted).

RevokeExternalAgentCredentialRequest​

RevokeExternalAgentCredentialRequest disables a client credential.

FieldTypeLabelDescription
client_idstringclient_id identifies the credential to revoke. Required. Org-scoped.

RevokeExternalAgentCredentialResponse​

RevokeExternalAgentCredentialResponse confirms the revocation.

FieldTypeLabelDescription
revoked_atgoogle.protobuf.Timestamprevoked_at is the timestamp the credential was disabled. The agent is refused at its next token refresh (<=10 min).

RotateExternalAgentCredentialRequest​

RotateExternalAgentCredentialRequest rotates a client's credential in place.

FieldTypeLabelDescription
client_idstringclient_id identifies the credential to rotate. Required. Scoped to the caller's org (an explicit org_id predicate enforces ownership).
public_key_jwkstringpublic_key_jwk, when set, replaces the stored public key (private_key_jwt path). When empty, the server generates a NEW shared secret and returns it ONCE. The client_id and agent binding are unchanged; rotated_at is stamped.

RotateExternalAgentCredentialResponse​

RotateExternalAgentCredentialResponse returns the post-rotation credential.

FieldTypeLabelDescription
client_idstringclient_id is unchanged (rotation reuses the binding).
client_secretstringclient_secret is the newly-generated shared secret, returned EXACTLY ONCE. Populated ONLY for the shared-secret path; empty for the public-key path.
credential_typeExternalCredentialTypecredential_type indicates the post-rotation credential shape.
rotated_atgoogle.protobuf.Timestamprotated_at is the timestamp the rotation was recorded.

SetAgentGatewayRoutingRequest​

SetAgentGatewayRoutingRequest flips one agent's Model Gateway routing opt-in.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent (agents.id).
gateway_routedbooloptionalgateway_routed is the REPLACEMENT value: true routes this agent's LLM traffic through the Model Gateway, false returns it to the direct path.

optional IS LOAD-BEARING AND IS NOT DECORATION. A proto3 bool without presence has no distinguishable unset state, so "the client forgot the field" and "the operator opted this agent OUT" would be the SAME REQUEST on the wire — and the one that arrives by accident is the one that silently de-routes a live agent. With presence, an absent field is refused with InvalidArgument, exactly as SetAgentLLMEnablement refuses an absent selection rather than reading it as a clear.

This does NOT make opt-out a second action. Both directions are this RPC with this field set — true or false — under the same authorisation, and both emit the same audit event. #3060 constraint 1 asks that opt-out be the inverse of opt-in, not that an omitted field mean something. |

SetAgentGatewayRoutingResponse​

SetAgentGatewayRoutingResponse confirms the routing update.

FieldTypeLabelDescription
gateway_routedboolgateway_routed is the value AS STORED, read back from the row rather than echoed from the request, so a write that landed something other than what was asked for is visible here rather than only in later traffic going somewhere nobody expected.
updated_atgoogle.protobuf.Timestampupdated_at is the timestamp of the update.

SetAgentLLMEnablementRequest​

SetAgentLLMEnablementRequest replaces the agent's LLM enablement selection.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent (agents.id).
selectionAgentLLMEnablementselection is the REPLACEMENT selection. REQUIRED — an absent message is an InvalidArgument rather than a clear, for the same reason SetAgentTools rejects an empty tools string: "the client forgot the field" and "the operator cleared the selection" must not be the same request.

A PRESENT selection with two empty lists IS the clear, and it is recorded as such: the stored value becomes an explicit empty object rather than SQL NULL, so "cleared by an operator" stays distinguishable from "never expressed" (migration 218's stated reason for having no DEFAULT). |

SetAgentLLMEnablementResponse​

SetAgentLLMEnablementResponse confirms the enablement update.

FieldTypeLabelDescription
selectionAgentLLMEnablementselection is the selection AS STORED, read back from the row rather than echoed from the request, so a client can see exactly what a subsequent gateway call will enforce.
updated_atgoogle.protobuf.Timestampupdated_at is the timestamp of the update.

SetAgentPersonaRequest​

SetAgentPersonaRequest updates the persona text for an agent.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent.
personastringpersona is the new persona text (max 32000 characters).

SetAgentPersonaResponse​

SetAgentPersonaResponse confirms the persona update.

FieldTypeLabelDescription
updated_atgoogle.protobuf.Timestampupdated_at is the timestamp of the update.

SetAgentToolsRequest​

SetAgentToolsRequest replaces the agent's per-agent MCP allowlist.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent.
toolsstringtools is the per-agent MCP allowlist: a JSON array of mcp_servers UUIDs that NARROWS the agent's team-resolved MCP set (ADR-0022 W3/B8, #1744). Empty array = clear the allowlist / inherit ALL team MCP servers. Every id must be a live MCP server owned by the caller's tenant. REPURPOSED from the legacy tool-definitions array.

SetAgentToolsResponse​

SetAgentToolsResponse confirms the tools update.

FieldTypeLabelDescription
updated_atgoogle.protobuf.Timestampupdated_at is the timestamp of the update.

SetAutonomyLevelRequest​

SetAutonomyLevelRequest configures agent governance autonomy.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent.
autonomy_levelAutonomyLevelautonomy_level is the new governance level.
approval_templatestringapproval_template is an optional JSON approval template (for supervised/semi).

SetAutonomyLevelResponse​

SetAutonomyLevelResponse confirms the autonomy level update.

FieldTypeLabelDescription
updated_atgoogle.protobuf.Timestampupdated_at is the timestamp of the update.

SetBudgetCapRequest​

SetBudgetCapRequest sets daily/monthly token and cost limits.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent.
daily_token_capint64daily_token_cap is the daily token limit (0 = unlimited).
monthly_token_capint64monthly_token_cap is the monthly token limit (0 = unlimited).
daily_cost_capdoubledaily_cost_cap is the daily cost limit in USD (0.0 = unlimited).
monthly_cost_capdoublemonthly_cost_cap is the monthly cost limit in USD (0.0 = unlimited).

SetBudgetCapResponse​

SetBudgetCapResponse confirms the budget cap update.

FieldTypeLabelDescription
updated_atgoogle.protobuf.Timestampupdated_at is the timestamp of the update.

UpdateAgentRequest​

UpdateAgentRequest modifies mutable agent fields.

PARTIAL BY IGNORE-IF-EMPTY, AND THERE IS DELIBERATELY NO FIELD MASK. Every field here is "ignored when it carries its zero value", which is the convention this message has used since it was written and which every client of it already implements. Adding a mask for one new field would give the message two partial-update conventions at once — and the field that follows the newer one would be the field nobody's client sets correctly. The cost is stated rather than hidden: a value cannot be CLEARED through this message. model_id has no meaningful cleared state (the column is TEXT NOT NULL DEFAULT), so the cost is not paid here; a field that DOES need clearing is the change that should introduce the mask, for all fields at once.

FieldTypeLabelDescription
agent_idstringagent_id is the UUID of the agent to update.
namestringname is the new agent name (ignored if empty).
clearanceint32clearance is the new clearance level (ignored if zero).
model_idstringmodel_id is the new LLM model identifier for the agent (ignored if empty).

It is VALIDATED AGAINST THE AGENT'S MODEL-ACCESS PRODUCT before it is written: the model must be served by an endpoint the agent's team holds an approved GRANT binding for, admitted by the team's model allowlist, and admitted by the agent's own narrow-only llm_enablement selection — the same three predicates the Model Gateway's routing and gates 7b/7c apply at call time. A model outside that product is refused InvalidArgument naming the model and the allowed set, because storing it would produce an agent that reads as configured and has every turn denied no_matching_endpoint (#3146 measured 214 consecutive such denials on beta-dev).

Setting this requires the SAME authority as every other governed agent_configurations write (L4-or-team-Manager), NOT the ungated authority name and clearance carry — see the RPC comment. |

UpdateAgentResponse​

UpdateAgentResponse returns the updated agent.

FieldTypeLabelDescription
agentAgentagent is the updated agent record.

AutonomyLevel​

AutonomyLevel controls agent self-governance.

NameNumberDescription
AUTONOMY_LEVEL_UNSPECIFIED0AUTONOMY_LEVEL_UNSPECIFIED is the default zero value.
AUTONOMY_LEVEL_SUPERVISED1AUTONOMY_LEVEL_SUPERVISED means every action requires approval.
AUTONOMY_LEVEL_SEMI_AUTONOMOUS2AUTONOMY_LEVEL_SEMI_AUTONOMOUS means only escalations need approval.
AUTONOMY_LEVEL_AUTONOMOUS3AUTONOMY_LEVEL_AUTONOMOUS means no approval required.

ExternalCredentialType​

ExternalCredentialType names how an external agent authenticates to the platform token endpoint (PR-4). It is DERIVED from which credential material is stored (public key XOR shared secret) — the server sets it on responses so the caller knows which secret handling applies.

NameNumberDescription
EXTERNAL_CREDENTIAL_TYPE_UNSPECIFIED0EXTERNAL_CREDENTIAL_TYPE_UNSPECIFIED is the zero value; never persisted.
EXTERNAL_CREDENTIAL_TYPE_PUBLIC_KEY_JWT1EXTERNAL_CREDENTIAL_TYPE_PUBLIC_KEY_JWT is the PREFERRED path: the customer holds the private key and registered only the public JWK. Nothing secret is returned — the customer already holds the key.
EXTERNAL_CREDENTIAL_TYPE_SHARED_SECRET2EXTERNAL_CREDENTIAL_TYPE_SHARED_SECRET is the FALLBACK: the server generated a shared secret, returned EXACTLY ONCE in client_secret, and persisted only as a bcrypt hash.

HealthFilter​

HealthFilter classifies an agent by a derived runtime-health bucket. GAP-35 (#1084).

NameNumberDescription
HEALTH_FILTER_UNSPECIFIED0HEALTH_FILTER_UNSPECIFIED disables the health filter (returns all health buckets).
HEALTH_FILTER_OVER_BUDGET1HEALTH_FILTER_OVER_BUDGET keeps agents whose daily token / cost usage is at or above the configured cap (treats unset cap = 0 as "no cap" and excludes the row from this bucket).
HEALTH_FILTER_UNDER_UTILIZED2HEALTH_FILTER_UNDER_UTILIZED keeps agents that have been provisioned for more than 7 days and have zero tokens consumed today — useful for spotting deployed-but-unused agents.
HEALTH_FILTER_RECENTLY_FAILING3HEALTH_FILTER_RECENTLY_FAILING keeps agents whose recent error rate crosses the GetAgentHealth "degraded"/"down" threshold. Implemented as a Redis-side post-filter on the page rather than a SQL predicate — agents that match are kept; non-matches are dropped from the returned page (page_size may collapse). The total_count still reflects the pre-filter SQL row count so the caller knows the upper bound.

AgentService​

AgentService manages the lifecycle, configuration, and health of AI agents within the UpsQuad platform. All operations are scoped to a tenant organisation via JWT-derived scope claims.

Method NameRequest TypeResponse TypeDescription
CreateAgentCreateAgentRequestCreateAgentResponseCreateAgent provisions a new agent within a team, subject to the organisation's max_agents feature gate.
GetAgentGetAgentRequestGetAgentResponseGetAgent retrieves a single agent by ID, excluding soft-deleted agents.
UpdateAgentUpdateAgentRequestUpdateAgentResponseUpdateAgent modifies mutable agent fields (name, clearance, model_id).

THE THREE FIELDS DO NOT CARRY THE SAME AUTHORITY, and that asymmetry is deliberate (#3569). name and clearance are gated on nothing beyond the caller's verified org — that is how this RPC has always behaved. model_id is gated on the L4-or-team-Manager matrix, the same one SetAgentPersona, SetAgentTools, SetAgentLLMEnablement and SetAgentGatewayRouting apply, because it chooses which upstream endpoint an agent's traffic reaches and every other write to a governed agent_configurations column carries that bar. Giving a governance column the weaker of two bars already present in one package would make this RPC the bypass for the other four. | | DeleteAgent | DeleteAgentRequest | DeleteAgentResponse | DeleteAgent performs a soft delete by setting the deleted_at timestamp. Associated agent_configurations are NOT deleted. | | ListAgents | ListAgentsRequest | ListAgentsResponse | ListAgents returns a paginated list of active agents, optionally filtered by pillar_id and/or team_id. | | SetAgentPersona | SetAgentPersonaRequest | SetAgentPersonaResponse | SetAgentPersona updates the persona text in agent_configurations. Maximum 32000 characters. | | SetAgentTools | SetAgentToolsRequest | SetAgentToolsResponse | SetAgentTools replaces the agent's per-agent MCP allowlist (the repurposed agent_configurations.tools JSONB — see the field docs). Narrow-only; gated on the L4-OR-team-Manager clearance matrix and a per-id tenant IDOR check (ADR-0022 W3/B8, #1744). | | SetAgentLLMEnablement | SetAgentLLMEnablementRequest | SetAgentLLMEnablementResponse | SetAgentLLMEnablement replaces the agent's per-agent LLM enablement selection — the NARROW-ONLY set gate 7b of the Model Gateway enforces (MG-2.6; HLD #2649 section 5.1 step 7b; LLD #2801 section 11 task T4, issue #2810). It lands in agent_configurations.llm_enablement (migration 218) and it is the ONLY write path to that column.

Same shape and authorisation as SetAgentTools: an agent may DESELECT from what its team has been granted and may NEVER widen it. Every selected id is validated server-side against the team's approved set, resolved from the agent's OWN ROW, inside the request transaction. A request naming an id outside that set is refused with FailedPrecondition — a distinct refusal from the PermissionDenied a caller without authority gets. | | GetAgentLLMEnablement | GetAgentLLMEnablementRequest | GetAgentLLMEnablementResponse | GetAgentLLMEnablement returns the agent's current selection ALONGSIDE the ceiling it must stay inside, both derived by the same function the write validates against.

The ceiling is on the response deliberately. MG-2.6 requires that the UI never OFFER an over-grant — "an option that always errors is a bug report waiting to happen" — and the only way a picker can be structurally unable to offer one is to render the server's own ceiling rather than a set the client assembled from somewhere else. | | SetAgentGatewayRouting | SetAgentGatewayRoutingRequest | SetAgentGatewayRoutingResponse | SetAgentGatewayRouting is MG-4.1's per-agent opt-in: it routes this agent's LLM traffic through the Model Gateway (LLD #3138 task 4A, issue #3143, routing issue #3060). It is the ONLY write path to agent_configurations.gateway_routed (migration 240).

RUNTIME-AGNOSTIC. It applies to langgraph and claude_sdk alike and consults neither. That is the entire content of the requirement: the pre-existing endpoint knob (llm_base_url) is refused unless runtime == "claude_sdk", while the default runtime is langgraph, so the field that would carry "route this agent" could not be set on most of the fleet. This RPC does NOT touch llm_base_url, which stays an Anthropic-format claude_sdk override (#3060 constraint 5).

OPT-IN AND OPT-OUT ARE THE SAME ACTION — one RPC, one field, the other value. #3060 constraint 1: the staged cutover's whole value is that step 1 is undoable without a deploy, and a flag that can only be turned on is not an opt-in. There is deliberately no ClearAgentGatewayRouting.

Same authorisation as SetAgentTools and SetAgentLLMEnablement: the shared L4-OR-team-Manager matrix. THE FLAG IS NOT THE ORACLE — MG-4.2's migration baseline is computed from the ledger (llm_usage_events), never from this field (#3060 constraint 2). | | GetAgentHealth | GetAgentHealthRequest | GetAgentHealthResponse | GetAgentHealth reads real-time health signals from Redis and derives an overall health status (healthy, degraded, down, idle). | | RegisterAgent | RegisterAgentRequest | RegisterAgentResponse | RegisterAgent creates a headless (API-provisioned) agent with clearance, persona, model, tools, and optional autonomy level. | | SetAutonomyLevel | SetAutonomyLevelRequest | SetAutonomyLevelResponse | SetAutonomyLevel configures the agent's governance autonomy. | | SetBudgetCap | SetBudgetCapRequest | SetBudgetCapResponse | SetBudgetCap sets daily/monthly token and cost limits. | | GetBudgetStatus | GetBudgetStatusRequest | GetBudgetStatusResponse | GetBudgetStatus returns current-period usage vs caps. | | DeployAgentToTeam | DeployAgentToTeamRequest | DeployAgentToTeamResponse | DeployAgentToTeam assigns agent to team, wires governance, starts heartbeat. | | PauseAgent | PauseAgentRequest | PauseAgentResponse | PauseAgent transitions the agent to status=paused. Idempotent: calling on an already-paused agent returns the current row without re-emitting a lifecycle event. GAP-37 (#1085). | | ResumeAgent | ResumeAgentRequest | ResumeAgentResponse | ResumeAgent transitions the agent back to status=active. Idempotent. GAP-37 (#1085). | | ListAgentTemplates | ListAgentTemplatesRequest | ListAgentTemplatesResponse | ListAgentTemplates returns the prototype-template catalog backing step 1 of the redesigned 4-step Deploy wizard. Static-ish per release; no per-tenant authoring surface yet. GAP-38 (#1086). | | DeployFromTemplate | DeployFromTemplateRequest | DeployFromTemplateResponse | DeployFromTemplate is the one-shot wizard commit: equivalent to RegisterAgent + DeployAgentToTeam + SetBudgetCap + SetAutonomyLevel (+ guardrail attach) in a single transaction with rollback. The wizard never lands a half-deployed agent — every step succeeds or none of them do. GAP-41 (#1088). | | RegisterExternalAgent | RegisterExternalAgentRequest | RegisterExternalAgentResponse | RegisterExternalAgent registers an externally-hosted (BYO) agent as a first-class agent (runtime="external") AND issues its client credential in one transaction — #1864 trust-root PR-3 (#1956). The external agent later presents the credential to the platform token endpoint (PR-4) and receives the SAME agent-scoped gateway token an internal mint produces. Preferred credential is a customer-supplied public key (private_key_jwt); the fallback is a server-generated shared secret returned EXACTLY ONCE in the response. | | RotateExternalAgentCredential | RotateExternalAgentCredentialRequest | RotateExternalAgentCredentialResponse | RotateExternalAgentCredential rotates an external agent's public key or shared secret, keeping the client_id and the agent binding stable (rotated_at is stamped). A newly-generated secret is returned ONCE. #1956. | | RevokeExternalAgentCredential | RevokeExternalAgentCredentialRequest | RevokeExternalAgentCredentialResponse | RevokeExternalAgentCredential disables an external agent's credential (revoked_at is set). The agent is refused at its next token refresh (<=10 min) at the PR-4 endpoint. #1956. |

Scalar Value Types​

.proto TypeNotesC++JavaPythonGoC#PHPRuby
doubledoubledoublefloatfloat64doublefloatFloat
floatfloatfloatfloatfloat32floatfloatFloat
int32Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.int32intintint32intintegerBignum or Fixnum (as required)
int64Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.int64longint/longint64longinteger/stringBignum
uint32Uses variable-length encoding.uint32intint/longuint32uintintegerBignum or Fixnum (as required)
uint64Uses variable-length encoding.uint64longint/longuint64ulonginteger/stringBignum or Fixnum (as required)
sint32Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.int32intintint32intintegerBignum or Fixnum (as required)
sint64Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.int64longint/longint64longinteger/stringBignum
fixed32Always four bytes. More efficient than uint32 if values are often greater than 2^28.uint32intintuint32uintintegerBignum or Fixnum (as required)
fixed64Always eight bytes. More efficient than uint64 if values are often greater than 2^56.uint64longint/longuint64ulonginteger/stringBignum
sfixed32Always four bytes.int32intintint32intintegerBignum or Fixnum (as required)
sfixed64Always eight bytes.int64longint/longint64longinteger/stringBignum
boolboolbooleanbooleanboolboolbooleanTrueClass/FalseClass
stringA string must always contain UTF-8 encoded or 7-bit ASCII text.stringStringstr/unicodestringstringstringString (UTF-8)
bytesMay contain any arbitrary sequence of bytes.stringByteStringstr[]byteByteStringstringString (ASCII-8BIT)