Agent Runtime API
Table of Contents
upsquad/runtime/v1/runtime.proto
AskUserResume
AskUserResume carries a resolved agent-initiated ask_user answer back into a worker turn so it resumes IN-CONTEXT (AIU-3, #2320). It is distinct from the approval-interrupt resume (InitWorkerSession.resume_checkpoint) because an ask_user is an ELICITATION, not a gated side-effect: there is no MCP proxy egress to dedup and no approval decision — only a human answer to splice as the ask_user tool's result.
| Field | Type | Label | Description |
|---|---|---|---|
| checkpoint | bytes | checkpoint is the frozen in-step batch the worker emitted on the input_required event (InputRequired.checkpoint) — the JSON snapshot of the mid-turn state (messages incl. the ask_user tool_use, the pending current_tool_calls, and the executed-sibling tool_results). Opaque to the orchestrator/interpreter: it rides the Temporal activity boundary verbatim and is handed back here unchanged so the worker restores exactly the frozen turn. Never persisted; never carries provider keys or bearers (stripped at emit, exactly like CheckpointEvent). | |
| answer_tool_result | bytes | answer_tool_result is the synthesized ask_user tool_result the worker splices onto the resumed turn: a compact JSON object {tool_call_id, result} where tool_call_id is the pending ask_user call and result is the human's answer envelope ({choice, text, answered_by}). The Go interpreter builds it deterministically from the durable prompt row's response_payload (AIU-3), so the same bytes resume the turn whether the answer arrived via the signal, the AlreadyAnswered dedup, or the re-poll. |
CheckpointEvent
CheckpointEvent carries a serialized LangGraph state snapshot for persistence and session recovery.
| Field | Type | Label | Description |
|---|---|---|---|
| state | bytes | state is the serialized LangGraph state bytes. | |
| hash | string | hash is the SHA-256 hex digest of the state bytes. | |
| created_at | google.protobuf.Timestamp | created_at is when the checkpoint was created. |
CheckpointRequest
CheckpointRequest carries a worker-produced LangGraph state snapshot
that the Orchestrator must persist to agent_checkpoints for resume.
The Orchestrator-facing side of this RPC (worker → orchestrator)
receives the opaque bytes blob; the Worker-facing side (orchestrator
→ worker) may also use this request with only session_id set as a
"please serialise and return state" poll. The two surfaces share the
proto definition; which fields are populated depends on direction.
Delta §5.2 / OQ-5: the bytes blob carries a 4-byte big-endian schema
version prefix produced by internal/runtime/checkpointstore; the
Python resume path (#174) mirrors the same header format.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the session being checkpointed. | |
| bytes | bytes | bytes is the opaque LangGraph state blob with a 4-byte big-endian schema version prefix. Populated on the worker → orchestrator path. | |
| bytes_sha256 | string | bytes_sha256 is the SHA-256 hex digest of bytes (including the schema version prefix). Used by the orchestrator for an integrity check on duplicate-key retry (OQ-3). Populated on the worker → orchestrator path. | |
| loop_iteration | int32 | loop_iteration is the worker's monotonic iteration counter for this session. It forms the upsert key together with session_id — a duplicate (session_id, loop_iteration) with matching hash is the idempotent replay path (OQ-3). Populated on the worker → orchestrator path. |
CheckpointResponse
CheckpointResponse carries the serialized worker state.
| Field | Type | Label | Description |
|---|---|---|---|
| state | bytes | state is the serialized LangGraph state bytes. | |
| hash | string | hash is the SHA-256 hex digest of the state bytes. |
CompletionEvent
CompletionEvent signals the step has finished with a final response.
| Field | Type | Label | Description |
|---|---|---|---|
| final_response | string | final_response is the agent's complete response text. | |
| token_usage | TokenUsage | token_usage contains the aggregated token counts and cost for this step. | |
| loop_iterations | int32 | loop_iterations is the number of think-act-observe iterations performed. | |
| duration_ms | int32 | duration_ms is the total wall-clock time for the step. |
ContentPatternProto
ContentPatternProto is the wire representation of a user-defined or platform-defined regex pattern for serialization to the Python worker. LLD: #208 Section 13.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is the unique pattern identifier. | |
| regex | string | regex is the regular expression to match. | |
| severity | string | severity is the action on match: "block", "redact", "log_only". | |
| category | string | category classifies the pattern: "injection", "secret", "pii", "custom". | |
| description | string | description is a human-readable pattern description. |
DecompositionEvent
DecompositionEvent carries the structured multi-step plan the worker produced on the autonomous PLAN step. The orchestrator parses todos into Plan.Todos (re-indexing ids and clamping to its MaxTodos bound); todos MAY be empty, in which case the orchestrator keeps its single seed todo and pursues the goal directly (safe degrade). See the T4 PLAN-result parser (#1614).
| Field | Type | Label | Description |
|---|---|---|---|
| todos | TodoSpec | repeated | todos is the ordered sub-goal list. May be empty. |
ErrorEvent
ErrorEvent signals a recoverable or terminal error during step execution.
| Field | Type | Label | Description |
|---|---|---|---|
| code | string | code is a machine-readable error code (e.g., "LLM_RATE_LIMIT"). | |
| message | string | message is a human-readable error description. | |
| retryable | bool | retryable indicates whether the caller should retry this step. | |
| detail | string | detail contains additional diagnostic information. |
ExecuteStepEvent
ExecuteStepEvent is the server-streamed response from ExecuteStep. Each event carries exactly one of the typed event payloads.
| Field | Type | Label | Description |
|---|---|---|---|
| token | TokenEvent | token carries a single streamed token from the LLM response. | |
| tool_call | ToolCallEvent | tool_call indicates the agent is invoking a tool. | |
| tool_result | ToolResultEvent | tool_result carries the output from a completed tool invocation. | |
| status | StatusEvent | status signals an agent status transition (thinking, acting, etc.). | |
| completion | CompletionEvent | completion signals the step has finished with a final response. | |
| metrics | MetricsEvent | metrics carries per-LLM-call cost and latency data. | |
| error | ErrorEvent | error signals a recoverable or terminal error. | |
| checkpoint | CheckpointEvent | checkpoint carries a serialized LangGraph state snapshot. | |
| security | SecurityEvent | security signals a security violation detected by the worker. | |
| structured_answer | StructuredAnswerEvent | structured_answer carries a typed answer payload (summary + table + followups) emitted when the LLM returns structured output instead of free-form text. Added in 2026-04 redesign GAP-12 (Quad chat). | |
| step_progress | StepProgress | step_progress carries the per-step progress signals the orchestrator's ProgressEval uses for stall detection in EXECUTION_MODE_AUTONOMOUS. The worker reports signals only; it never decides termination. Added in LLD-autonomous-loop-langgraph §6.1 (T4-2, #1438). Tag 11 — the oneof already occupies 1–10 (structured_answer = 10), so this MUST be 11 (no renumbering of existing arms). | |
| interrupt_required | InterruptRequired | interrupt_required signals that a tool call inside the worker cycle tripped a requires_approval policy mid-step and the worker has suspended (in-step interrupt). The orchestrator routes the pending tool intent through approval.Service and resumes. Added in LLD-autonomous-loop-langgraph §6.1/§7 (T4-2, #1438). Tag 12. | |
| decomposition | DecompositionEvent | decomposition carries the structured todo plan the worker produced on the PLAN step (step_goal == "decompose goal into todo plan") in EXECUTION_MODE_AUTONOMOUS. The orchestrator parses it into Plan.Todos and pursues each todo across subsequent steps. The worker reports the plan only; it never decides termination or cursor advance. An empty or absent decomposition makes the orchestrator degrade to single-todo pursuit (the empty-plan guard). Added in the T4 PLAN-result parser (T4-3 item 4 / T4-6, #1614). Tag 13 — arms 1–12 are occupied, so this MUST be 13 (no renumbering of existing arms). | |
| write_confirm | WriteConfirmEvent | write_confirm carries the structured payload of a governed write-tool confirm — the confirmation_required{approval_id, summary, deep_link, required_clearance, action_type} result a governed write tool (trigger_workflow / record_decision) returns when it stages a mutation for the user to approve in-thread. The worker detects the confirmation_required tool result and emits this ONCE per approval_id, tied to the settling turn, so the Quad client can bind an in-thread confirm card directly to the approval instead of cross-referencing ListApprovals (core#2192, kills the newest-pending-since-turn-start pairing heuristic). Tag 14 — arms 1–13 are occupied, so this MUST be 14 (no renumbering of existing arms). | |
| input_required | InputRequired | input_required signals that the agent CALLED the built-in ask_user tool mid-turn (AIU-1, #2320) to elicit human input on its own judgment — NO pre-authored user_input DSL step. The worker suspended the SAME in-step interrupt path interrupt_required uses, but WITHOUT running governance (an ask_user is an elicitation, not a side-effect). It is DISTINCT from interrupt_required (arm 12) precisely so the Go side routes it to OpenUserPrompt (the durable question row + attributed live card + park), NOT to the approval-tool-gate. The orchestrator opens a user_prompt row, parks, and on the human answer resumes the turn with the answer as the ask_user tool's result (AskUserResume). Tag 15 — arms 1–14 are occupied, so this MUST be 15 (no renumbering of existing arms). |
ExecuteStepRequest
ExecuteStepRequest carries the user message and session context for a single think-act-observe iteration.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the active session. | |
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT, injected by Orchestrator). | |
| agent_id | string | agent_id is the UUID of the agent configuration executing this step. | |
| user_message | string | user_message is the raw user input for this step. | |
| execution_mode | ExecutionMode | execution_mode controls single-turn vs multi-turn behaviour. | |
| provider_keys | ExecuteStepRequest.ProviderKeysEntry | repeated | provider_keys contains decrypted BYOK keys, passed in-memory over mTLS gRPC. The worker holds these only during the LLM call and never persists them to disk or logs. |
| snapshot | SessionSnapshot | snapshot is the immutable session configuration snapshot. | |
| step_goal | string | step_goal is the active todo text (or "decompose goal into todo plan" for the planning step) that seeds this cycle's intent when the orchestrator drives EXECUTION_MODE_AUTONOMOUS. Empty ⇒ legacy user_message behaviour (the relay path is preserved). Added in LLD-autonomous-loop-langgraph §6.1 (T4-2, #1438); field 8 is next-free. | |
| agent_gateway_token | string | agent_gateway_token is a short-TTL (≈10m), agent-scoped bearer the orchestrator mints at session-init and delivers PER STEP — exactly like provider_keys (field 6): never persisted, refreshed every step, so a churned/stateless worker (#1622) always holds a fresh one. It authenticates the worker to the Go team gateway /mcp/team/{unit} AS THE AGENT: its agent_id claim is JWKS-signed and therefore unforgeable, which is what makes AuthorizeAggregated's per-agent credential + audit chain correct (a worker can never borrow a peer's id). Empty ⇒ MCP is disabled for this agent (no team gateway call is attempted). Backward-compatible: pre-MCP workers ignore an unset field. LLD #1855 PR-1 (ADR-0022). Field 9 next-free. | |
| context_engine_token | string | context_engine_token is a short-TTL (≈5m), agent-scoped bearer the orchestrator mints per step (like agent_gateway_token, field 9) and the worker attaches as Authorization: Bearer on every Context Engine call (AssembleContext / IngestEvent / ValidatePrompt). Its aud pins the Context Engine (NOT the MCP gateway) and its agent_id claim is JWKS-signed and therefore unforgeable — the CE resolves the caller's RAG scope (org/pillar/team/clearance) from a trusted DB lookup on that claim, never from the request body (#2030 Option C, founder-approved 2026-07-23). Empty ⇒ the worker falls back to unauthenticated / dev-header context calls. Never persisted; refreshed every step so a churned/stateless worker always holds a fresh one. Field 10 next-free. | |
| prior_turns | PriorTurn | repeated | prior_turns is the bounded, role-labeled cross-turn conversation history for this session, injected by the orchestrator on the lifecycle SendMessage relay path (#2156). It is the AUTHORITATIVE cross-turn history: the worker rebuilds its working message set from prior_turns EVERY relay turn rather than relying on cross-turn in-memory carryover — the worker selector has no session affinity, so a session's turns can land on any replica and a warm in-memory history is not guaranteed. Bounded server-side (last N turns / ~M tokens, newest-kept) and EXCLUDES the current turn (read before the current user turn is captured), so it never duplicates user_message (field 4). Each entry is a final user or assistant turn — never loop-0 preambles, never tool blocks. Empty on the autonomous / temporalwf path (that path keeps its in-memory step carryover) and on a thread's first turn. Additive; pre-#2156 workers ignore it. Field 11 next-free. |
| ask_user_resume | AskUserResume | ask_user_resume carries the ANSWER to a prior agent-initiated ask_user elicitation (AIU-1, #2320) so the worker resumes the SAME frozen turn in-context with the answer as the ask_user tool's result. It is the workflow-path analogue of the Quad-path InitWorkerSession(resume_checkpoint) seam: a WORKFLOW agent turn runs stateless single-turn (a fresh session per Temporal ExecuteAgentStep activity — cmd/agent-orchestrator/stepbridge.go), so the frozen mid-turn checkpoint cannot live worker-side across the human wait; it travels over the activity boundary (Temporal history) and returns here. When set the worker rehydrates the frozen in-step batch from checkpoint, splices answer_tool_result as a role=tool message keyed to the pending ask_user tool_call_id, and re-enters execute_tools — the skip-done logic (keyed on tool_call_id) prevents re-raising the ask. Empty on every non-ask turn ⇒ byte-for-byte today's ExecuteStep. Additive; pre-AIU workers ignore it. Field 12 next-free. |
ExecuteStepRequest.ProviderKeysEntry
| Field | Type | Label | Description |
|---|---|---|---|
| key | string | ||
| value | string |
FallbackChain
FallbackChain defines the model fallback order for LLM provider resilience. If the primary model fails, the worker tries secondary, then tertiary.
| Field | Type | Label | Description |
|---|---|---|---|
| primary_model | string | primary_model is the preferred LLM model. | |
| secondary_model | string | secondary_model is the first fallback if primary fails. | |
| tertiary_model | string | tertiary_model is the second fallback if secondary also fails. |
HeartbeatRequest
HeartbeatRequest is sent by the worker to signal liveness.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the active session. | |
| worker_id | string | worker_id is the unique identifier of the worker process. | |
| active_sessions | int32 | active_sessions is the number of sessions this worker is handling. |
HeartbeatResponse
HeartbeatResponse acknowledges the heartbeat.
| Field | Type | Label | Description |
|---|---|---|---|
| acknowledged | bool | acknowledged indicates the Orchestrator received the heartbeat. |
InitWorkerSessionRequest
InitWorkerSessionRequest is sent by the Orchestrator to initialize a worker with session configuration.
Resume path (delta §5.3, US-AR-6): the crash-recovery sweeper
(internal/runtime/session/sweeper) reuses this RPC to rehydrate a new
worker from the latest agent_checkpoints row. When resume_checkpoint
is non-empty the worker MUST reload LangGraph state from those bytes
instead of starting at loop_iteration=0. The blob carries the same
4-byte big-endian schema version prefix produced by
internal/runtime/checkpointstore.PrependSchemaVersion; the worker
strips and validates it via the Python helper shipped in #174. A
version mismatch is a P1 failure and the worker MUST NOT start the
session — it returns success=false with an error_message the sweeper
then translates to a session_crashed audit row + reset.
| Field | Type | Label | Description |
|---|---|---|---|
| snapshot | SessionSnapshot | snapshot is the immutable session configuration snapshot. | |
| resume_checkpoint | bytes | resume_checkpoint is the latest persisted LangGraph state blob for this session, as produced by the checkpointstore (4-byte schema version prefix + opaque body). Empty on fresh session init; populated by the sweeper on US-AR-6 reassignment. The worker uses this to restore LangGraph state before the first ExecuteStep call. | |
| resume_loop_iteration | int32 | resume_loop_iteration is the loop_iteration column of the agent_checkpoints row whose bytes are carried in resume_checkpoint. The worker uses it only for observability — logs/metrics/tracing — so the orchestrator-side log and the worker-side log agree on the restore point. Ignored when resume_checkpoint is empty. |
InitWorkerSessionResponse
InitWorkerSessionResponse indicates whether the worker successfully initialized the session.
| Field | Type | Label | Description |
|---|---|---|---|
| success | bool | success indicates whether initialization succeeded. | |
| error_message | string | error_message contains the failure reason if success is false. |
InputRequired
InputRequired is the agent-initiated ask_user elicitation signal (AIU-1, #2320). It is the sibling of InterruptRequired: both suspend the worker's in-step interrupt path and freeze the mid-turn checkpoint, but an InputRequired arm carries a QUESTION for a human (not a gated tool intent for an approver) and the worker did NOT run governance on it (elicitation, not a side-effect). The orchestrator opens a durable user_prompt row from question_spec (reusing the LBE user_input machinery), parks the run, and on the answer re-drives the turn with the answer spliced as the ask_user tool result.
| Field | Type | Label | Description |
|---|---|---|---|
| tool_call_id | string | tool_call_id is the identifier of the pending ask_user tool call the turn suspended on. The answer is spliced as a tool_result keyed to it on resume. | |
| tool_name | string | tool_name is the built-in tool name — always "ask_user". | |
| idempotency_key | string | idempotency_key is sha256(session_id : loop_iteration : tool_call_id) — the deterministic key that dedups a re-delivered ask across resumes/retries. It rides the durable prompt row so a re-driven turn recognises the SAME ask. | |
| question_spec | bytes | question_spec is the typed question the model asked (JSON: question, options[], allow_text, answerable_by), shaped so the Go side can persist it as the user_prompt row's prompt_spec (the same PromptSpec shape the DSL user_input node uses). Opaque bytes on the wire. | |
| checkpoint | bytes | checkpoint is the JSON snapshot of the frozen mid-turn state (messages incl. the ask_user tool_use, the pending current_tool_calls, and the executed-sibling tool_results). It rides the Temporal activity boundary verbatim and returns on AskUserResume.checkpoint so the worker resumes the exact frozen turn. Never carries provider keys or bearers. |
InterruptRequired
InterruptRequired signals that a tool call inside the worker cycle tripped a requires_approval policy mid-step, so the worker raised interrupt() and suspended (in-step interrupt path, net-new — §7). The already-executed sibling tool_results for the in-flight cycle are persisted into the in-step checkpoint so resume skips them. The orchestrator routes pending_tool_intent through approval.Service and, on approval, resumes the cycle re-entering only the interrupted tool call.
Carried as field 12 in ExecuteStepEvent oneof (worker → orchestrator). LLD: LLD-autonomous-loop-langgraph §6.1 / §7 (T4-2, #1438).
| Field | Type | Label | Description |
|---|---|---|---|
| tool_call_id | string | tool_call_id is the identifier of the gated tool call that suspended. | |
| tool_name | string | tool_name is the name of the gated tool. | |
| idempotency_key | string | idempotency_key is sha256(session_id : loop_iteration : tool_call_id) — the deterministic key the MCP proxy egress uses to dedup the gated call across resumes (§3.4 / §8.4). | |
| pending_tool_intent | bytes | pending_tool_intent is the structured intent for the approval payload (opaque bytes; the orchestrator forwards it to approval.Service). |
McpServerRef
McpServerRef is the per-server translation entry in SessionSnapshot.mcp_servers (LLD #1855 §5). It carries only what the worker needs to reach the governed team gateway and translate the aggregated tool namespace — never a credential or an authorization decision (those live in the gateway).
| Field | Type | Label | Description |
|---|---|---|---|
| prefix | string | prefix is the server's display name (mcp_servers.name) — the {prefix} segment of the aggregated {prefix}__{tool} exposed-tool namespace the team gateway advertises. Unique per org among active servers (migration 126). | |
| unit_id | string | unit_id is the org_unit whose team gateway /mcp/team/{unit_id} serves this server for this agent — the agent's home team unit (the aggregated endpoint's own unit scope). All of an agent's MCP servers are reached through this one gateway, so this is the same value for every entry in the map. |
MetricsEvent
MetricsEvent carries per-LLM-call cost and latency data for observability.
| Field | Type | Label | Description |
|---|---|---|---|
| model | string | model is the LLM model identifier used. | |
| input_tokens | int32 | input_tokens is the number of prompt tokens consumed. | |
| output_tokens | int32 | output_tokens is the number of completion tokens generated. | |
| cost_usd | double | cost_usd is the estimated cost in US dollars. | |
| ttft_ms | int32 | ttft_ms is the time-to-first-token latency in milliseconds. | |
| total_ms | int32 | total_ms is the total LLM call duration in milliseconds. |
PriorTurn
PriorTurn is one role-labeled turn of prior conversation history carried on ExecuteStepRequest.prior_turns (#2156). It is a compact {role, text} pair; the text is the final user or assistant text for that turn.
| Field | Type | Label | Description |
|---|---|---|---|
| role | PriorTurn.Role | role identifies the speaker of this turn. | |
| text | string | text is the turn's final text (never tool blocks or loop-0 preambles). |
SecurityConfigProto
SecurityConfigProto is the per-tenant security pipeline configuration embedded in SessionSnapshot. The worker uses this to configure its local ToolOutputScanner and to understand the pipeline mode. LLD: #208 Section 13.
| Field | Type | Label | Description |
|---|---|---|---|
| input_filter_mode | string | input_filter_mode controls input scanning: "strict", "standard", "permissive", "disabled". | |
| output_filter_mode | string | output_filter_mode controls output scanning: "strict", "standard", "permissive", "disabled". | |
| redact_secrets | bool | redact_secrets enables automatic secret redaction in LLM output. | |
| redact_pii | bool | redact_pii enables automatic PII redaction in LLM output. | |
| redact_system_prompt | bool | redact_system_prompt enables system prompt leakage detection. | |
| schema_validation_mode | string | schema_validation_mode controls schema enforcement: "log_only", "warn", "block". | |
| tool_scan_enabled | bool | tool_scan_enabled enables tool output scanning in the Python worker. | |
| tool_result_max_bytes | int32 | tool_result_max_bytes is the maximum tool result size before truncation. | |
| custom_block_patterns | ContentPatternProto | repeated | custom_block_patterns are tenant-defined input blocking patterns. |
| custom_pii_patterns | ContentPatternProto | repeated | custom_pii_patterns are tenant-defined PII detection patterns. |
SecurityEvent
SecurityEvent signals a security violation detected by the worker or pipeline filters. Carried as field 9 in ExecuteStepEvent oneof. LLD: #208 Section 8.4.
| Field | Type | Label | Description |
|---|---|---|---|
| violation_code | string | violation_code is a machine-readable code (e.g., "input_injection_detected"). | |
| detail | string | detail is a human-readable description (NEVER returned to external clients). | |
| severity | string | severity is the action taken: "log_only", "redact", or "block". | |
| pattern_id | string | pattern_id identifies the specific pattern that triggered the event. |
SessionSnapshot
SessionSnapshot is the immutable configuration snapshot sent to the worker at session initialization. The snapshot_hash ensures config drift detection.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the session. | |
| tenant_id | string | tenant_id is the tenant organisation UUID. | |
| agent_id | string | agent_id is the agent configuration UUID. | |
| model_id | string | model_id is the primary LLM model identifier. | |
| temperature | double | temperature is the LLM sampling temperature. | |
| max_tokens | int32 | max_tokens is the maximum tokens per LLM response. | |
| tools | string | repeated | tools is the list of tool names the agent is allowed to invoke. |
| guardrail_hashes | string | repeated | guardrail_hashes contains content-addressable hashes of active guardrails. |
| rbac_grants | SessionSnapshot.RbacGrantsEntry | repeated | rbac_grants maps resource patterns to permitted actions. |
| persona | string | persona is the system prompt / persona text for the agent. | |
| snapshot_hash | string | snapshot_hash is the SHA-256 hex digest of the frozen configuration. | |
| max_loops | int32 | max_loops is the maximum think-act-observe iterations before forced stop. | |
| execution_mode | ExecutionMode | execution_mode is the default execution mode for the session. | |
| fallback_chain | FallbackChain | fallback_chain defines the model fallback order for resilience. | |
| context_engine_address | string | context_engine_address is the gRPC address of the Context Engine service. | |
| security_config | SecurityConfigProto | security_config is the per-tenant security pipeline configuration. Added in Wave 3 (LLD #208 Section 13). | |
| runtime | string | runtime selects the worker executor: "langgraph" (default) or "claude_sdk". Empty ⇒ langgraph. Added in #1631 (ADR-0020). Part of the frozen config ⇒ included in the snapshot hash. | |
| llm_base_url | string | llm_base_url is the Anthropic-format endpoint override for the claude_sdk runtime (ANTHROPIC_BASE_URL). Empty ⇒ SDK default. Ignored when runtime != "claude_sdk". Added in #1631 (ADR-0020). | |
| org_version_seq | int64 | org_version_seq is the org structure version (MAX(org_versions.version_seq)) the inheritance resolver resolved against when this snapshot was frozen. It is provenance/explainability metadata for the "why did this agent have tool X" audit view (ADR-0021 #1682, W2 review #1647 condition 5) — it records the exact org structure the frozen tool/guardrail set was derived from and is immutable for the session lifetime. It is DELIBERATELY EXCLUDED from snapshot_hash: the hash is an integrity check over the effective config, and folding a monotonic version counter into it would (a) drift every existing session's hash and (b) make otherwise-identical configs hash differently across unrelated publishes. 0 ⇒ resolver not consulted / no published version (legacy + v2.3 orgs), which serialises byte-identically to pre-#1686. Added in #1686 (T3). | |
| mcp_servers | SessionSnapshot.McpServersEntry | repeated | mcp_servers is the worker's MCP translation table: for each MCP server that SURVIVED the freeze (i.e. is present in tools[] as "mcp:<server_id>"), it maps the server_id → {prefix, unit_id}. The worker needs this to (a) pick the team gateway URL /mcp/team/{unit_id} and (b) intersect the live tools/list surface down to the {prefix}__{tool} entries this agent is allowed (the per-agent #1744 narrowing, applied worker-side). |
It is DELIBERATELY EXCLUDED from snapshot_hash (like org_version_seq): the integrity-pinned effective MCP set already lives in tools[] as the "mcp:<server_id>" entries — this map is pure derived translation metadata (server display name + home team unit), never an authorization surface. Tool NAMES are NOT frozen here — they are discovered live via tools/list so the snapshot never goes stale when an upstream adds a tool. Empty for MCP-less agents, which serialise byte-identically to pre-#1855. LLD #1855 PR-1 (ADR-0022). Field 20. |
SessionSnapshot.McpServersEntry
| Field | Type | Label | Description |
|---|---|---|---|
| key | string | ||
| value | McpServerRef |
SessionSnapshot.RbacGrantsEntry
| Field | Type | Label | Description |
|---|---|---|---|
| key | string | ||
| value | string |
StatusEvent
StatusEvent signals an agent status transition.
| Field | Type | Label | Description |
|---|---|---|---|
| status | AgentStatus | status is the new agent status. | |
| detail | string | detail provides a human-readable description of the transition. | |
| timestamp | google.protobuf.Timestamp | timestamp is when the status change occurred. | |
| pending_pause | bool | pending_pause is true when a pause has been requested and the session is finishing its current step before transitioning to SUSPENDED. Added in Wave 2 LLD 5. |
StepProgress
StepProgress carries the per-step progress signals the orchestrator uses to detect stall in EXECUTION_MODE_AUTONOMOUS. The worker emits this at the end of each cycle (ingest_result); the orchestrator's ProgressEval derives a progress score and flags no-progress when, over the stall window, no new artifacts AND no completed todos AND a repeating response_delta_hash are observed. The worker reports signals only — it never decides termination (HLD R7, no split-brain).
Carried as field 11 in ExecuteStepEvent oneof (worker → orchestrator). LLD: LLD-autonomous-loop-langgraph §5.3 / §6.1 (T4-2, #1438).
| Field | Type | Label | Description |
|---|---|---|---|
| made_tool_calls | bool | made_tool_calls is true if the worker invoked at least one tool this step. | |
| tool_calls_errored | uint32 | tool_calls_errored is the count of tool invocations that returned an error. | |
| produced_new_artifacts | bool | produced_new_artifacts is true if the step yielded new output artifacts. | |
| todo_marked_complete_by_model | bool | todo_marked_complete_by_model is true if the model signalled the active todo is done (the orchestrator re-confirms against todo state — §5.2). | |
| response_delta_hash | bytes | response_delta_hash is a hash of the step's structured-answer sniff, used by the orchestrator's novelty signal for stall detection. |
StructuredAnswerEvent
StructuredAnswerEvent is a typed answer payload emitted by the worker
when the LLM returns structured output instead of free-form prose.
The frontend Quad chat page (upsquad-client redesign-2026-04 quad.jsx)
renders the summary + optional table + optional follow-up chips. The
payload is intentionally schema-free at v1 so the frontend can evolve
the renderer without forcing a proto bump every iteration; bind a
schema_id when richer typing is needed (e.g., chart/card variants).
Carried as field 10 in ExecuteStepEvent oneof (worker → orchestrator) and field 7 in AgentEvent oneof (orchestrator → portal).
Sizing budgets (enforced at the orchestrator forward path so a hostile or malformed worker payload can't blow up the portal stream):
- summary ≤ 4 KiB
- note ≤ 2 KiB
- columns ≤ 32 entries, each ≤ 64 bytes
- rows ≤ 200 entries
- cells per row ≤ 32, each ≤ 1 KiB
- followups ≤ 8 entries, each ≤ 256 bytes
Over-cap variants are dropped with a debug log; the rest of the stream
keeps flowing. Limits live in
internal/runtime/server/structured_answer.go.
LLD: 2026-04 redesign GAP-12 (#1099).
| Field | Type | Label | Description |
|---|---|---|---|
| summary | string | summary is the prose lede shown above the table (always present). | |
| columns | string | repeated | columns are optional column headers for table; positionally aligned with StructuredAnswerRow.cells. Empty = no header row. |
| rows | StructuredAnswerRow | repeated | rows is the optional tabular payload. Empty = summary-only answer. |
| note | string | note is an optional caption rendered below the table (e.g., "Source: AnalyticsService.GetWorkflowsBreakdown — 24h window"). | |
| followups | string | repeated | followups are short suggested user follow-up prompts rendered as chips beneath the answer (e.g., "Show pending approvals"). |
| schema_id | string | schema_id is an optional opaque tag identifying a richer client-side renderer (reserved — empty in v1, where the table+summary+chips shape is the only renderer). |
StructuredAnswerRow
StructuredAnswerRow is a single row in a StructuredAnswerEvent.table. Cells are positionally aligned with StructuredAnswerEvent.columns; an empty StructuredAnswerEvent.columns means cells are header-less and rendered as a free table by the client. Added in 2026-04 redesign GAP-12 (Quad chat).
| Field | Type | Label | Description |
|---|---|---|---|
| cells | string | repeated | cells are the row's cell values, positionally aligned with the parent StructuredAnswerEvent.columns when columns is non-empty. |
TerminateSessionRequest
TerminateSessionRequest asks the worker to clean up a session.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the session to terminate. | |
| reason | string | reason describes why the session is being terminated. |
TerminateSessionResponse
TerminateSessionResponse indicates whether the session was terminated.
| Field | Type | Label | Description |
|---|---|---|---|
| success | bool | success indicates whether termination succeeded. |
TodoSpec
TodoSpec is one worker-proposed sub-goal in a DecompositionEvent.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is a worker-suggested identifier. ADVISORY only — the orchestrator overwrites it with a deterministic todo-<n> id so id authority stays orchestrator-side. | |
| title | string | title is the imperative sub-goal text. Maps to the orchestrator's Todo.Text. A todo with an empty title is dropped by the orchestrator. | |
| ordering | uint32 | ordering is the 0-based intended position. Advisory: the orchestrator stable-sorts by it, then re-indexes ids regardless. | |
| acceptance | string | acceptance is the optional done-criteria (free text). Carried for forward-compatibility; the v1 orchestrator does not yet persist or evaluate it (T4 PLAN-result parser v1, #1614). |
TokenEvent
TokenEvent carries a single streamed token from the LLM response.
| Field | Type | Label | Description |
|---|---|---|---|
| text | string | text is the token text content. | |
| index | int32 | index is the token sequence number within this response. |
TokenUsage
TokenUsage contains token counts and cost for a single LLM interaction.
| Field | Type | Label | Description |
|---|---|---|---|
| input_tokens | int32 | input_tokens is the number of prompt tokens consumed. | |
| output_tokens | int32 | output_tokens is the number of completion tokens generated. | |
| model | string | model is the LLM model identifier used. | |
| cost_usd | double | cost_usd is the estimated cost in US dollars. |
ToolCallEvent
ToolCallEvent indicates the agent is invoking a tool.
| Field | Type | Label | Description |
|---|---|---|---|
| tool_name | string | tool_name is the name of the tool being invoked. | |
| tool_call_id | string | tool_call_id is the unique identifier for this tool invocation. | |
| arguments | google.protobuf.Struct | arguments contains the tool call arguments as a JSON structure. |
ToolResultEvent
ToolResultEvent carries the output from a completed tool invocation.
| Field | Type | Label | Description |
|---|---|---|---|
| tool_call_id | string | tool_call_id is the identifier of the originating ToolCallEvent. | |
| tool_name | string | tool_name is the name of the tool that was invoked. | |
| result | string | result is the tool output as a string. | |
| is_error | bool | is_error indicates whether the tool invocation failed. | |
| duration_ms | int32 | duration_ms is the wall-clock time of the tool execution. |
WriteConfirmEvent
WriteConfirmEvent is the structured projection of a governed write-tool
confirmation_required result (internal/platformmcp/write_tools.go). A
governed write tool (trigger_workflow / record_decision) freezes the
mutation, mints a pending approval, and returns
confirmation_required{approval_id, summary, deep_link, ...} as its tool
result. That result flows back to the LLM as data (the model relays the
summary + link and stops), so the approval id historically never reached
the client — the Quad card had to guess the pairing via ListApprovals
(core#2192). This event carries the same server-authoritative fields onto
the stream so the client binds the card to approval_id from the SAME turn.
The fields are all server-derived (never model-steered): summary is the
clamped human summary the user authorises on; deep_link targets the
approvals surface; required_clearance is the clearance tier the viewer
needs to decide; action_type names the governed action (e.g.
"trigger_workflow" / "record_decision"). The orchestrator re-clamps every
field at the relay boundary before forwarding to the portal AgentEvent
stream. Added in core#2192.
| Field | Type | Label | Description |
|---|---|---|---|
| approval_id | string | approval_id is the pending approval UUID the user must decide. Stable binding key for the in-thread confirm card + the RecordDecision call. | |
| summary | string | summary is the server-derived, human-readable summary of the staged mutation the user authorises on. Never the raw model-typed intent. | |
| deep_link | string | deep_link is the relative approvals-surface link the user can open to decide out-of-thread (fallback when the in-thread card is unavailable). | |
| required_clearance | int32 | required_clearance is the clearance tier (1..5) the viewer needs to decide this confirm. 0 when the write tool did not surface one. | |
| action_type | string | action_type names the governed write action (e.g. "trigger_workflow", "record_decision") so the client can pick the right card affordances. |
AgentStatus
AgentStatus represents the lifecycle state of an agent session.
| Name | Number | Description |
|---|---|---|
| AGENT_STATUS_UNSPECIFIED | 0 | AGENT_STATUS_UNSPECIFIED is the default zero value. |
| AGENT_STATUS_THINKING | 1 | AGENT_STATUS_THINKING indicates the agent is processing input. |
| AGENT_STATUS_ACTING | 2 | AGENT_STATUS_ACTING indicates the agent is executing a tool call. |
| AGENT_STATUS_COMPLETE | 3 | AGENT_STATUS_COMPLETE indicates the step finished successfully. |
| AGENT_STATUS_ERROR | 4 | AGENT_STATUS_ERROR indicates the step ended with an error. |
| AGENT_STATUS_SUSPENDED | 5 | AGENT_STATUS_SUSPENDED indicates the session is paused. |
Note: WAITING_APPROVAL and DELEGATING are reserved for Wave 2. |
ExecutionMode
ExecutionMode controls how the agent processes a step.
| Name | Number | Description |
|---|---|---|
| EXECUTION_MODE_UNSPECIFIED | 0 | EXECUTION_MODE_UNSPECIFIED is the default zero value. |
| EXECUTION_MODE_SINGLE_TURN | 1 | EXECUTION_MODE_SINGLE_TURN processes a single chat response. |
| EXECUTION_MODE_MULTI_TURN | 2 | EXECUTION_MODE_MULTI_TURN processes a workflow step that may loop. |
| EXECUTION_MODE_AUTONOMOUS | 3 | EXECUTION_MODE_AUTONOMOUS drives the orchestrator-side deep plan loop (goal → plan → step → observe → reflect → terminate). The worker stays stateless and is driven one cycle per step; all plan state, stall detection, and termination decisions live in the Go orchestrator. Added in LLD-autonomous-loop-langgraph §6.1 (T4-2, #1438). |
PriorTurn.Role
Role labels who produced the turn text.
| Name | Number | Description |
|---|---|---|
| ROLE_UNSPECIFIED | 0 | ROLE_UNSPECIFIED is the proto3 zero value; treated as user by the worker. |
| ROLE_USER | 1 | ROLE_USER marks a user turn. |
| ROLE_ASSISTANT | 2 | ROLE_ASSISTANT marks an assistant (agent) turn. |
RuntimeService
RuntimeService defines internal RPCs between the Go Orchestrator and Python Worker. It is not exposed to portal or gateway clients.
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| ExecuteStep | ExecuteStepRequest | ExecuteStepEvent stream | ExecuteStep runs the think-act-observe loop for a single user message. Returns a server-stream of ExecuteStepEvents (tokens, tool calls, status changes, metrics, and completion). |
| InitWorkerSession | InitWorkerSessionRequest | InitWorkerSessionResponse | InitWorkerSession initializes the worker with a session config snapshot. The worker uses the snapshot to configure LLM adapters, tools, and guardrails for this session. |
| TerminateSession | TerminateSessionRequest | TerminateSessionResponse | TerminateSession tells the worker to clean up and release resources associated with a session. |
| Checkpoint | CheckpointRequest | CheckpointResponse | Checkpoint requests the worker to serialize and return current LangGraph state for persistence and recovery. |
| Heartbeat | HeartbeatRequest | HeartbeatResponse | Heartbeat is sent by the worker to signal liveness. The Orchestrator uses missed heartbeats to detect crashed workers. |
upsquad/runtime/v1/lifecycle.proto
AgentEvent
AgentEvent is the server-streamed response from SendMessage. It carries a subset of ExecuteStepEvent types relevant to the portal/user.
| Field | Type | Label | Description |
|---|---|---|---|
| token | TokenEvent | token carries a single streamed token from the LLM response. | |
| status | StatusEvent | status signals an agent status transition. | |
| completion | CompletionEvent | completion signals the step has finished with a final response. | |
| error | ErrorEvent | error signals a recoverable or terminal error. | |
| session_paused | SessionPausedEvent | session_paused signals the session has transitioned to SUSPENDED. Added in Wave 2 LLD 5. | |
| session_resumed | SessionResumedEvent | session_resumed signals the session has transitioned back to ACTIVE. Added in Wave 2 LLD 5. | |
| structured_answer | StructuredAnswerEvent | structured_answer carries a typed answer payload (summary + table + followups) for the Quad chat renderer. Added in 2026-04 redesign GAP-12 (#1099). Defined in runtime.proto so it's reusable on the worker → orchestrator stream too. | |
| interrupt_required | InterruptRequiredEvent | interrupt_required signals that the autonomous loop parked the session on an in-step approval gate: a tool call tripped a requires_approval policy mid-cycle. Emitted just before the accompanying SessionPaused (PAUSE_SOURCE_APPROVAL) so the dashboard can render an in-step approval banner with the gated tool + reason + approval id. The internal InterruptRequired detail (runtime.proto, worker → orchestrator) is translated into this client-safe projection with sanitized args; only governance-allowed fields are surfaced. Slice 2 dashboard wiring (#1533). | |
| delegated | DelegatedEventEnvelope | delegated carries a LIVE event bubbled up from a delegated child sub-agent session into the PARENT (Quad) session's stream (M1 T8, #1541). It is emitted ONLY on the parent stream and ONLY for sessions that delegated (an @mention / delegate_to_agent fired). A normal, non-delegating turn never sees this arm — its stream is byte-for-byte identical to the pre-T8 behaviour. The client (#383) renders these as a nested, agent-attributed bubble inside the parent Quad thread so the user watches the sub-agent work live; the child's FINAL result still arrives via the existing delivery/resume path (no double-delivery). Tag 9. | |
| write_confirm | WriteConfirmEvent | write_confirm carries the structured payload of a governed write-tool confirm (WriteConfirmEvent, defined in runtime.proto so it is reusable on the worker → orchestrator stream too). When a governed write tool (trigger_workflow / record_decision) stages a mutation for the user to approve, the worker returns a confirmation_required{approval_id, summary, deep_link, ...} tool result. That result flows back to the LLM as data (the model relays the summary + link and stops) and historically never reached the client, forcing the Quad confirm card to guess the pairing via a ListApprovals cross-reference (core#2192). This event relays the server-authoritative payload onto the SAME turn's stream so the client binds the in-thread confirm card directly to approval_id — killing the newest-pending-since-turn-start heuristic (no clock-skew window, no org-wide newest-wins mispairing under concurrency). Every field is re-clamped at the orchestrator relay boundary before it reaches the portal. Tag 10 — arms 1–9 are occupied, so this MUST be 10 (no renumbering of existing arms). Added in core#2192. |
CreateSessionRequest
CreateSessionRequest explicitly creates a new agent session.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT, injected by gateway). | |
| user_id | string | user_id is the authenticated user UUID (from JWT). | |
| agent_id | string | agent_id is the UUID of the agent to create a session for. | |
| execution_mode | ExecutionMode | execution_mode controls single-turn vs multi-turn behaviour. | |
| model_override | string | model_override allows task-level model selection (AR-F66). |
CreateSessionResponse
CreateSessionResponse returns metadata for the newly created session.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the created session. | |
| snapshot_hash | string | snapshot_hash is the SHA-256 of the frozen session configuration. | |
| status | AgentStatus | status is the initial session status. |
DelegatedEventEnvelope
DelegatedEventEnvelope wraps a single AgentEvent produced by a delegated child sub-agent session and tags it with the attribution the client needs to render a nested bubble inside the PARENT (Quad) thread (M1 T8, #1541).
Contract (the client #383 renders against THIS shape):
- inner — the child's original AgentEvent, verbatim (token,
status, tool_call/tool_result projection, completion,
interrupt_required, …). The client renders
innerexactly as it would a top-level event, but inside the attributed child bubble keyed by child_session_id. - child_session_id — the delegated child session that produced the event. Stable correlation key for grouping a child's events into one nested bubble (matches the correlation_id on the parent's SessionPaused(DELEGATION) wait marker).
- child_agent_id — the agent running the child session (attribution: which agent is speaking). May be empty if not yet resolved.
- subagent_invocation_id — the subagent_invocations bridge-row id linking parent ↔ child. Lets the client correlate the live bubble with the poll-based progress panel (T7's GetSubagentTree) without guessing.
Tenant isolation: a child and its parent are always the same tenant, and the envelope is published only onto the parent session's tenant-scoped stream channel (stream:{tenantID}:{parentSessionID}) — it never crosses tenants.
| Field | Type | Label | Description |
|---|---|---|---|
| inner | AgentEvent | inner is the child's original AgentEvent, carried verbatim. | |
| child_session_id | string | child_session_id is the delegated child session that produced inner. | |
| child_agent_id | string | child_agent_id is the agent running the child session (attribution). | |
| subagent_invocation_id | string | subagent_invocation_id is the parent↔child bridge-row id. |
GetParentSessionRequest
GetParentSessionRequest asks for the parent of a session.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). Optional. | |
| session_id | string | session_id is the child UUID whose parent is being resolved. |
GetParentSessionResponse
GetParentSessionResponse returns the parent link, or empty fields for a root session (NOT an error — callers should check parent.session_id).
| Field | Type | Label | Description |
|---|---|---|---|
| parent | SubagentLink | ||
| is_root | bool | is_root is true when the queried session is a root session (no parent). In that case parent is unset. |
GetProvenanceChainRequest
GetProvenanceChainRequest requests the ancestor chain for an audit action.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). Optional — the gateway-injected tenant always takes precedence. | |
| session_id | string | session_id is the UUID of the session the action belongs to. | |
| action_id | string | action_id is the UUID of the agent_audit_log row whose chain is being queried. | |
| cross_session | bool | cross_session, when true, traverses subagent bridge boundaries so the returned chain spans the full parent↔child hierarchy up to the root user message. When false (default), the walk stops at the session boundary. Wave 3 LLD 13. |
GetProvenanceChainResponse
GetProvenanceChainResponse carries the resolved provenance chain.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id echoes the request. | |
| action_id | string | action_id echoes the request (the chain leaf). | |
| chain | ProvenanceChainLink | repeated | chain is ordered oldest-to-newest: chain[0] is the initiating user message, chain[len-1] is the requested action itself. |
| truncated_at_boundary | bool | truncated_at_boundary is true when a cross_session walk stopped at a boundary whose anchor failed dual-hash verification. The returned chain contains the trusted prefix; callers SHOULD alert. Wave 3 LLD 13. |
GetSessionRequest
GetSessionRequest retrieves a specific session's metadata.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). | |
| session_id | string | session_id is the UUID of the session to retrieve. |
GetSessionResponse
GetSessionResponse contains session metadata and current state.
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the session. | |
| agent_id | string | agent_id is the UUID of the agent running this session. | |
| status | AgentStatus | status is the current session status. | |
| loop_count | int32 | loop_count is the number of think-act-observe iterations completed. | |
| max_loops | int32 | max_loops is the configured maximum iterations. | |
| started_at | google.protobuf.Timestamp | started_at is when the session was created. | |
| last_heartbeat | google.protobuf.Timestamp | last_heartbeat is when the worker last sent a heartbeat. | |
| snapshot_hash | string | snapshot_hash is the SHA-256 of the session configuration. | |
| model_id | string | model_id is the LLM model being used for this session. |
GetSubagentTreeRequest
GetSubagentTreeRequest asks for the tree rooted at session_id.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). Optional. | |
| session_id | string | session_id is the root of the requested tree. | |
| max_depth | int32 | max_depth bounds the walk. 1..10. 0 uses the default (3). |
GetSubagentTreeResponse
GetSubagentTreeResponse carries the DFS-ordered tree nodes.
| Field | Type | Label | Description |
|---|---|---|---|
| nodes | SubagentLink | repeated | nodes is ordered: root first, then each subtree DFS. |
| truncated_at_depth | int32 | truncated_at_depth is >0 when max_depth cut the walk short; equals the max_depth actually applied. | |
| truncated_at_fanout | bool | truncated_at_fanout is true when a node had more direct children than the per-level fan-out cap; callers should paginate with ListChildren on that node. |
InterruptRequiredEvent
InterruptRequiredEvent is the portal-stream projection of an in-step approval gate (T4-7). It is emitted just before the accompanying SessionPausedEvent (PAUSE_SOURCE_APPROVAL) when the autonomous loop parks the session because a tool call tripped a requires_approval policy mid-cycle. It is the client-safe translation of the INTERNAL runtime.InterruptRequired (worker → orchestrator stream); only governance-allowed, sanitized fields are surfaced — the raw tool intent (pending_tool_intent) is NEVER forwarded to the client. The dashboard renders an in-step approval banner from these fields and uses approval_id to resolve the decision. Added for Slice 2 dashboard wiring (#1533).
| Field | Type | Label | Description |
|---|---|---|---|
| tool_name | string | tool_name is the name of the gated tool whose call tripped the policy. | |
| args_summary | string | args_summary is a sanitized, human-readable summary of the gated tool call's arguments — safe to render in the banner. Never the raw intent. Empty when no governance-safe summary is available. | |
| reason | string | reason is the human-readable reason the call was gated (the tripped policy / clearance requirement). Empty when no specific reason is known beyond "requires approval". | |
| approval_id | string | approval_id is the pending approval the operator must decide. The dashboard resolves the in-step gate against this id. | |
| tool_call_id | string | tool_call_id is the identifier of the gated tool call within the worker cycle (correlation with audit + resume). |
ListChildrenRequest
ListChildrenRequest asks for direct children of session_id (one level).
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). Optional. | |
| session_id | string | session_id is the parent UUID; children of this session are returned. | |
| page_size | int32 | page_size bounds the response. 1..100. 0 uses the default (20). | |
| page_token | string | page_token is the cursor for the next page. |
ListChildrenResponse
ListChildrenResponse returns paginated direct children.
| Field | Type | Label | Description |
|---|---|---|---|
| children | SubagentLink | repeated | |
| next_page_token | string | next_page_token is empty when no further pages remain. | |
| total_count | int32 | total_count is the number of direct children (may be larger than len(children) under pagination). |
ListSessionsRequest
ListSessionsRequest queries sessions with optional filters and pagination.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). | |
| agent_id | string | agent_id optionally filters sessions by agent. | |
| status_filter | string | status_filter optionally filters sessions by status. | |
| page_size | int32 | page_size is the maximum number of sessions to return. Max 100. | |
| page_token | string | page_token is the cursor for the next page of results. |
ListSessionsResponse
ListSessionsResponse contains a paginated list of sessions.
| Field | Type | Label | Description |
|---|---|---|---|
| sessions | GetSessionResponse | repeated | sessions is the list of session metadata entries. |
| next_page_token | string | next_page_token is the cursor for the next page. Empty if no more pages. | |
| total_count | int32 | total_count is the total number of sessions matching the filters. |
PauseSessionRequest
PauseSessionRequest asks to suspend an active session. The pause commits only at the next message boundary (never mid-tool or mid-LLM).
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT, injected by gateway). | |
| session_id | string | session_id is the UUID of the session to pause. | |
| reason | string | reason is a human-readable explanation recorded in audit trail. | |
| pause_source | PauseSource | pause_source identifies who initiated the pause. | |
| correlation_id | string | correlation_id is optional — set to approval_id when pause_source=APPROVAL. |
PauseSessionResponse
PauseSessionResponse returns the post-pause session state.
| Field | Type | Label | Description |
|---|---|---|---|
| status | AgentStatus | status is the session status after pause (SUSPENDED on success). | |
| checkpoint_key | string | checkpoint_key is the key of the checkpoint persisted at pause commit. | |
| paused_at | google.protobuf.Timestamp | paused_at is the timestamp the session transitioned to SUSPENDED. | |
| was_already_suspended | bool | was_already_suspended is true when the session was already SUSPENDED at RPC entry; idempotent double-pause returns OK with this flag set. |
ProvenanceChainLink
ProvenanceChainLink is one entry in a provenance chain, ordered from root (initiating user message) to leaf (the requested action).
| Field | Type | Label | Description |
|---|---|---|---|
| action_id | string | action_id is the UUID of the agent_audit_log row for this link. | |
| action_type | string | action_type is the audit_action_type enum value (e.g. "message_received"). | |
| agent_id | string | agent_id is the agent that produced this action, when applicable. | |
| created_at | google.protobuf.Timestamp | created_at is the timestamp of the link, RFC3339 in UTC. | |
| parent_action_id | string | parent_action_id is the action_id of the parent link in the same session. Empty on root actions (session_started, message_received). | |
| schema_version | string | schema_version identifies the chain link schema revision for forward-compatible evolution. Currently "1". | |
| session_id | string | session_id is the session the link's audit row lives in. Identical across all links of a same-session walk; differs across a cross_session walk's subagent boundary crossings. Wave 3 LLD 13. | |
| is_boundary_crossing | bool | is_boundary_crossing is true when the previous link lived in a different session (subagent bridge boundary). Wave 3 LLD 13. | |
| anchor_verified | bool | anchor_verified is true when the bridge row's dual-hash anchor verification succeeded at the boundary crossing. When false, the link is the last trusted link before the boundary; subsequent links returned are provided for observability but MUST be treated as untrusted. Wave 3 LLD 13. |
ResumeSessionRequest
ResumeSessionRequest asks to resume a suspended session.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT, injected by gateway). | |
| session_id | string | session_id is the UUID of the session to resume. | |
| operator_input | bytes | operator_input is optional JSON injected as a system-tagged context message on resume. Bounded at 64 KiB; larger payloads are rejected with InvalidArgument. | |
| resume_reason | string | resume_reason is a human-readable explanation recorded in audit trail. |
ResumeSessionResponse
ResumeSessionResponse returns the post-resume session state.
| Field | Type | Label | Description |
|---|---|---|---|
| status | AgentStatus | status is the session status after resume (ACTIVE on success). | |
| resumed_at_loop | int32 | resumed_at_loop is the loop_iteration read from the latest checkpoint. | |
| resumed_at | google.protobuf.Timestamp | resumed_at is the timestamp the session transitioned back to ACTIVE. |
SendMessageRequest
SendMessageRequest carries a user message to an agent session.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT, injected by gateway). | |
| user_id | string | user_id is the authenticated user UUID (from JWT). | |
| agent_id | string | agent_id is the UUID of the agent to send the message to. | |
| session_id | string | session_id is the UUID of an existing session. Empty means create new. | |
| message | string | message is the user's input text. | |
| execution_mode | ExecutionMode | execution_mode controls single-turn vs multi-turn behaviour. |
SessionPausedEvent
SessionPausedEvent is emitted on the portal stream when a session transitions to SUSPENDED. After this event the stream stays open with keepalive pings until a SessionResumedEvent or TerminateSession.
| Field | Type | Label | Description |
|---|---|---|---|
| reason | string | reason is the human-readable reason recorded on the pause RPC. | |
| pause_source | PauseSource | pause_source identifies who initiated the pause. | |
| correlation_id | string | correlation_id echoes PauseSessionRequest.correlation_id. | |
| checkpoint_key | string | checkpoint_key is the key of the checkpoint persisted at pause commit. | |
| paused_at | google.protobuf.Timestamp | paused_at is the timestamp of the transition. |
SessionResumedEvent
SessionResumedEvent is emitted on the portal stream when a session transitions from SUSPENDED back to ACTIVE.
| Field | Type | Label | Description |
|---|---|---|---|
| resumed_by_approval | bool | resumed_by_approval is true when the resume was triggered from the approval engine (LLD 6); false for operator-initiated resume. | |
| approval_id | string | approval_id is populated when resumed_by_approval=true. | |
| resumed_at_loop | int32 | resumed_at_loop is the loop_iteration the worker restored from. | |
| resumed_at | google.protobuf.Timestamp | resumed_at is the timestamp of the transition. |
SubagentLink
SubagentLink is one node in a delegation tree. Fields mirror the agent_sessions row + the join against subagent_invocations for the bridge metadata (role, status, result summary).
| Field | Type | Label | Description |
|---|---|---|---|
| session_id | string | session_id is the UUID of the child session. | |
| parent_session_id | string | parent_session_id is the UUID of the parent session. Empty on the root of a tree. | |
| root_session_id | string | root_session_id is the UUID of the tree root. | |
| delegation_depth | int32 | delegation_depth is 0 on the root, 1+ for descendants. | |
| agent_id | string | agent_id is the UUID of the agent running this child session. | |
| subagent_role | string | subagent_role is the symbolic role (e.g. "research"). Empty on the root session. | |
| subagent_invocation_id | string | subagent_invocation_id is the bridge row primary key. Empty on the root session. | |
| status | string | status is the session status (active, suspended, terminated, error, crashed, initializing). | |
| started_at | google.protobuf.Timestamp | started_at is when this child session was created. | |
| ended_at | google.protobuf.Timestamp | ended_at is when this child session transitioned to terminal (null on live sessions). | |
| result_status | string | result_status is the bridge row's status (pending, ok, error, timeout, cancelled). Empty on the root session. | |
| result_error_code | string | result_error_code echoes subagent_invocations.error_code when the result_status is not ok; empty otherwise. |
TerminateLifecycleSessionRequest
TerminateLifecycleSessionRequest asks to end an active session. Named differently from runtime.TerminateSessionRequest to avoid proto name collision within the same package.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). | |
| session_id | string | session_id is the UUID of the session to terminate. | |
| reason | string | reason describes why the session is being terminated. |
TerminateLifecycleSessionResponse
TerminateLifecycleSessionResponse confirms session termination.
| Field | Type | Label | Description |
|---|---|---|---|
| success | bool | success indicates whether termination succeeded. | |
| status | AgentStatus | status is the final session status after termination. |
TerminateSubagentTreeRequest
TerminateSubagentTreeRequest requests cascade termination of a subtree.
| Field | Type | Label | Description |
|---|---|---|---|
| tenant_id | string | tenant_id is the tenant organisation UUID (from JWT). Optional. | |
| session_id | string | session_id is the root of the subtree to terminate. | |
| reason | string | reason is a human-readable explanation recorded on every terminated session's audit trail. | |
| include_root | bool | include_root, when true, terminates session_id itself in addition to its descendants. When false, only descendants are terminated. |
TerminateSubagentTreeResponse
TerminateSubagentTreeResponse reports the cascade outcome.
| Field | Type | Label | Description |
|---|---|---|---|
| terminated_session_ids | string | repeated | terminated_session_ids lists every session flipped to terminated by this call. |
| skipped_session_ids | string | repeated | skipped_session_ids lists sessions already in a terminal state. |
| updated_bridge_row_count | int32 | updated_bridge_row_count counts subagent_invocations rows flipped to cancelled as part of the cascade. |
PauseSource
PauseSource identifies who initiated a pause. APPROVAL and POLICY are reserved for LLD 6 (approval engine) and future policy-driven pauses. DELEGATION and DELEGATION_APPROVAL were added in Wave 3 LLD 10 for parent-awaits-child and parent-awaits-approval-to-spawn-child flows.
| Name | Number | Description |
|---|---|---|
| PAUSE_SOURCE_UNSPECIFIED | 0 | PAUSE_SOURCE_UNSPECIFIED is the default zero value. |
| PAUSE_SOURCE_OPERATOR | 1 | PAUSE_SOURCE_OPERATOR is a human-operator-initiated pause. |
| PAUSE_SOURCE_APPROVAL | 2 | PAUSE_SOURCE_APPROVAL is an approval-engine-initiated pause (LLD 6). |
| PAUSE_SOURCE_POLICY | 3 | PAUSE_SOURCE_POLICY is a policy-driven pause (reserved). |
| PAUSE_SOURCE_DELEGATION | 4 | PAUSE_SOURCE_DELEGATION — parent session awaiting a child subagent to return a result (Wave 3 LLD 10/12/13). |
| PAUSE_SOURCE_DELEGATION_APPROVAL | 5 | PAUSE_SOURCE_DELEGATION_APPROVAL — parent awaiting approval to spawn a child subagent (Wave 3 LLD 14). |
LifecycleService
LifecycleService defines portal-facing RPCs for session management and user messaging. The WebSocket Gateway calls these on behalf of users.
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| SendMessage | SendMessageRequest | AgentEvent stream | SendMessage sends a user message and returns a stream of AgentEvents. Called by the WebSocket Gateway on behalf of the user. If session_id is empty, a new session is created automatically. |
| CreateSession | CreateSessionRequest | CreateSessionResponse | CreateSession explicitly creates a new agent session without sending a message. Useful for pre-warming or explicit session management. |
| GetSession | GetSessionRequest | GetSessionResponse | GetSession retrieves the current state and metadata of a session. |
| ListSessions | ListSessionsRequest | ListSessionsResponse | ListSessions returns paginated sessions for an agent or tenant. |
| TerminateSession | TerminateLifecycleSessionRequest | TerminateLifecycleSessionResponse | TerminateSession ends an active session and releases its resources. |
| GetProvenanceChain | GetProvenanceChainRequest | GetProvenanceChainResponse | GetProvenanceChain returns the full ancestor chain for an audit action, walking from the requested action back to the initiating user message. Implements US-AR-10 read surface (Wave 2 delta §7). |
| PauseSession | PauseSessionRequest | PauseSessionResponse | PauseSession transitions an ACTIVE session to SUSPENDED at the next clean message boundary. Idempotent: pausing an already-SUSPENDED session returns OK with was_already_suspended=true. LLD: Wave 2 LLD 5. |
| ResumeSession | ResumeSessionRequest | ResumeSessionResponse | ResumeSession re-hydrates a SUSPENDED session from its latest checkpoint, optionally injecting operator_input as a system-tagged context message. Returns FailedPrecondition on non-SUSPENDED sessions. LLD: Wave 2 LLD 5. |
| ListChildren | ListChildrenRequest | ListChildrenResponse | ListChildren returns the direct children of a session (one level deep). Paginated for wide fan-out trees. Tenant-scoped (RLS). Added in Wave 3 LLD 13. |
| GetParentSession | GetParentSessionRequest | GetParentSessionResponse | GetParentSession returns the parent of a session. Returns an empty parent (not an error) for root sessions. Tenant-scoped (RLS). Added in Wave 3 LLD 13. |
| GetSubagentTree | GetSubagentTreeRequest | GetSubagentTreeResponse | GetSubagentTree returns the DFS-ordered delegation tree rooted at a session. Truncated at max_depth to bound response size; callers can paginate by repeatedly calling with the returned subtree IDs. Tenant-scoped (RLS). Added in Wave 3 LLD 13. |
| TerminateSubagentTree | TerminateSubagentTreeRequest | TerminateSubagentTreeResponse | TerminateSubagentTree cascades a TerminateSession across a session and every descendant. Operator-triggered; tenant-scoped. Added in Wave 3 LLD 13. |
upsquad/runtime/v1/model.proto
CapabilityProfile
CapabilityProfile scores a model across key dimensions (0.0-1.0 each). Used by the recommendation engine to match models to agent roles.
| Field | Type | Label | Description |
|---|---|---|---|
| reasoning | double | reasoning is the model's reasoning and analysis capability score. | |
| code_generation | double | code_generation is the model's code generation capability score. | |
| structured_ops | double | structured_ops is the model's structured output capability score. | |
| long_context | double | long_context is the model's long context handling capability score. | |
| cost_efficiency | double | cost_efficiency is the model's cost-to-quality ratio score. |
CompareModelsRequest
CompareModelsRequest asks for side-by-side model comparison data.
| Field | Type | Label | Description |
|---|---|---|---|
| model_ids | string | repeated | model_ids is the list of model identifiers to compare. |
CompareModelsResponse
CompareModelsResponse contains the compared model entries.
| Field | Type | Label | Description |
|---|---|---|---|
| models | ModelEntry | repeated | models contains the full model entries for each requested model. |
GetModelRecommendationRequest
GetModelRecommendationRequest asks the recommendation engine for the best model given a role, connected providers, and optimization goal.
| Field | Type | Label | Description |
|---|---|---|---|
| agent_role | string | agent_role is the agent's functional role (e.g., "architect", "backend-sme", "qa-engineer"). | |
| connected_providers | string | repeated | connected_providers lists the providers the tenant has BYOK keys for. |
| optimization_goal | string | optimization_goal is the optimization preference: "quality", "cost", or "balanced". |
GetModelRecommendationResponse
GetModelRecommendationResponse contains the recommended model and alternatives with reasoning.
| Field | Type | Label | Description |
|---|---|---|---|
| recommended | ModelEntry | recommended is the top recommended model for the given criteria. | |
| alternatives | ModelEntry | repeated | alternatives are other suitable models ranked by fit. |
| reasoning | string | reasoning explains why the recommended model was chosen. |
ListModelsRequest
ListModelsRequest queries the model catalogue.
| Field | Type | Label | Description |
|---|---|---|---|
| provider_filter | string | provider_filter optionally restricts results to a single provider. |
ListModelsResponse
ListModelsResponse contains the list of models matching the filter.
| Field | Type | Label | Description |
|---|---|---|---|
| models | ModelEntry | repeated | models is the list of model catalogue entries. |
ModelEntry
ModelEntry describes a single LLM model in the catalogue.
| Field | Type | Label | Description |
|---|---|---|---|
| model_id | string | model_id is the canonical model identifier (e.g., "claude-sonnet-4-20250514"). | |
| provider | string | provider is the LLM provider name ("anthropic", "openai", "google"). | |
| display_name | string | display_name is the human-readable model name for UI display. | |
| capabilities | CapabilityProfile | capabilities contains the model's capability profile scores. | |
| pricing | ModelPricing | pricing contains the per-token cost data. | |
| max_context_tokens | int32 | max_context_tokens is the maximum context window size in tokens. | |
| supports_streaming | bool | supports_streaming indicates whether the model supports token streaming. | |
| supports_tool_use | bool | supports_tool_use indicates whether the model supports tool/function calling. | |
| tool_use_quality_score | double | tool_use_quality_score is a 0.0-1.0 rating of tool-use reliability. | |
| is_deprecated | bool | is_deprecated indicates the model is scheduled for removal. |
ModelPricing
ModelPricing contains per-token cost data for a model.
| Field | Type | Label | Description |
|---|---|---|---|
| input_per_million_tokens | double | input_per_million_tokens is the cost in USD per million input tokens. | |
| output_per_million_tokens | double | output_per_million_tokens is the cost in USD per million output tokens. |
ModelRegistryService
ModelRegistryService provides a curated model catalogue with capability profiles, pricing data, and role-based model recommendations.
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| ListModels | ListModelsRequest | ListModelsResponse | ListModels returns the full model catalogue, optionally filtered by provider. |
| GetModelRecommendation | GetModelRecommendationRequest | GetModelRecommendationResponse | GetModelRecommendation returns the best model for a given agent role and optimization goal, considering which providers the tenant has connected via BYOK. |
| CompareModels | CompareModelsRequest | CompareModelsResponse | CompareModels returns side-by-side capability and pricing data for a set of models. Used by the portal comparison UI. |
Scalar Value Types
| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
|---|---|---|---|---|---|---|---|---|
| double | double | double | float | float64 | double | float | Float | |
| float | float | float | float | float32 | float | float | Float | |
| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| bool | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | |
| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |