Skip to main content

COG-16: Human-in-the-Loop Requests

Status:      Draft
Version: 0.1
Created: 2026-07-19
Authors: Mike Anderson
Work in Progress

This specification is under active development. Structure and details may change significantly based on implementation experience and community feedback.

Related specifications

This document specifies how agents and operations request decisions, approvals, and information from humans on the Covia Grid. It builds on the Job lifecycle of COG-8: Jobs, the per-user lattice namespaces of COG-4: Grid Lattice, the capability model of COG-13: Agent Capabilities, and the federation mechanisms of COG-15: Cross-Venue Federation. It introduces no new trust primitive — HITL composes the existing ones.

This standard specifies Human-in-the-Loop (HITL) requests — the mechanism by which an agent or operation asks a human for input, approval, or authority, and receives a structured, auditable answer.

Purpose

Autonomous agents routinely reach points where a human decision is required: approving a payment, choosing between alternatives, supplying missing information, or consenting to an expansion of the agent's authority. HITL requests make these moments first-class grid citizens:

  • Durable — requests land in the target user's inbox and survive venue restarts; humans respond in minutes or in weeks
  • Structured — asks are typed (text, approval, choice, checkboxes) so clients can render them and answers can be validated
  • Auditable — the request, the response, and any authority granted are recorded with verified identities on both sides
  • Job-shaped — every request is carried by a standard Job (COG-8), so requesters wait, poll, stream, and cancel with the machinery they already have
  • Consent-preserving — a request MAY offer capability grants, but authority moves only through an explicit choice the human makes
  • Federated — a request can target a user on a remote venue, with caller identity and authority forwarded per COG-15

Terminology

See COG-1: Architecture for Grid terminology, COG-8 for the Job lifecycle, and COG-13 for capabilities and UCANs.

TermDefinition
HITL requestA structured request for human input, delivered as a record in the target user's h/ namespace
AskA single typed question within a request (text, approval, choice, checkboxes)
RequesterThe verified identity (user or agent acting for a user) that created the request
ResponderThe human user the request is addressed to — the owner of the h/ inbox it lands in
InboxThe responder's h/ namespace, holding open and resolved HITL request records
Offered grantA capability {with, can} attached to a request, ask, or option, which the responder may confer on the requester by approving it
ResponseThe responder's resolution of a request: an answer set, or a rejection

Model

A HITL request separates three concerns, each carried by an existing grid primitive:

  1. The request record — a durable document in the responder's h/ namespace. This is the source of truth for HITL semantics: what is being asked, by whom, what is offered, and how it was resolved.
  2. The Job — the requester-side lifecycle carrier. It is created when the request is submitted and reaches a terminal state exactly when the request is resolved: COMPLETE with the response as output, or FAILED on rejection or expiry.
  3. The response operation — the responder's action. It validates the answers against the asks, resolves the record, completes (or fails) the Job, and issues any approved capability grants.

HITL is not a Job status

A Job waiting on a HITL request SHOULD be in the INPUT_REQUIRED state, because that is the accurate lifecycle state: the job is blocked awaiting external input. However, the implication runs in one direction only:

  • INPUT_REQUIRED does not imply HITL. The status means input is required from some external party — another agent, a multi-turn caller, or a human. Many non-HITL flows (agent-to-agent conversations, multi-turn operations) use the same status.
  • HITL semantics live entirely in the h/ record and the hitl:* operations that manage it. No HITL behaviour is keyed off Job status, and clients MUST NOT discover pending human asks by scanning for INPUT_REQUIRED jobs. The inbox is h/.

Lifecycle overview

Requester (agent/user)                    Responder (human)
| |
|-- hitl:request ----------------------->|
| Job: PENDING -> INPUT_REQUIRED | record written to h/<id>, status "open"
| |
| (minutes ... weeks) | responder lists h/, reviews asks
| | and any offered grants
| |
|<------------------- hitl:respond ------|
| |
| outcome "answer": | record status "answered"
| Job: COMPLETE, output = response | approved grants issued as UCAN
| outcome "reject": | record status "rejected"
| Job: FAILED |
| |
| (no response before expiry) | record status "expired"
| Job: FAILED |
| (requester cancels the Job) | record status "cancelled"
| Job: CANCELLED |

The h/ Inbox

Each user's lattice record (COG-4) includes the h/ namespace, reserved for HITL requests. It behaves like the other user namespaces (w/, o/): slash-separated paths, navigable JSON values, and durable last-write-wins merge semantics.

Addressing

A request record lives at:

h/<request-id>

where <request-id> is the hex encoding of the carrying Job's ID. Using the Job ID as the request ID gives every request a unique, time-ordered identifier and a direct link between record and Job.

Write discipline

The h/ namespace is framework-managed:

  • Records are created and resolved only by the venue, via the hitl:* operations. Direct covia:write into h/ MUST be rejected.
  • The inbox owner MAY read and list h/ freely (covia:read, covia:list, covia:slice, covia:inspect).
  • The inbox owner MAY delete resolved records (covia:delete h/<id>) for inbox hygiene. Deleting an open record is equivalent to rejecting it: the venue MUST fail the carrying Job.
  • Cross-user reads of h/ records follow the standard delegation rules (COG-13): a requester holding a crud/read delegation on the record path can observe the record state directly; otherwise the Job is the requester's view.

Request record schema

FieldTypeSet byDescription
idstringvenueRequest ID (Job ID hex)
fromstringvenueRequester's verified DID. Never caller-supplied
agentstringvenueOriginating agent (e.g. g/assistant), when the requester is an agent
originstringvenueOrigin venue DID, for requests delivered across venues
titlestringrequesterShort human-readable summary
descriptionstringrequesterMarkdown body giving full context for the decision
asksarrayrequesterThe list of asks (see Asks)
statusstringvenueopenanswered | rejected | expired | cancelled
createdintegervenueCreation timestamp (Unix ms)
expiresintegervenueAbsolute expiry timestamp (Unix ms), when a timeout was requested
responseobjectvenueThe response (see Responses), set on resolution

Asks

A request contains an ordered list of one or more asks. Each ask is a typed question:

typeMeaningAnswer form
textFree-text inputstring
approvalApprove or reject a single stated actionboolean
choiceSelect exactly one optionoption id (string)
checkboxesSelect zero or more optionsarray of option ids

Ask schema

FieldTypeApplies toDescription
idstringallUnique (within the request) identifier for the ask
typestringallOne of text, approval, choice, checkboxes
promptstringallThe question, as shown to the human
requiredbooleanallIf true, an answer outcome MUST include an answer for this ask. Default false
commentbooleanallIf true, clients SHOULD offer an optional free-text comment alongside the answer. Default false
optionsarraychoice, checkboxesThe selectable options. REQUIRED for these types, and MUST be non-empty
grantsarrayapprovalGrants conferred if this ask is approved (see Capability Grants)

Option schema

FieldTypeDescription
idstringUnique (within the ask) identifier for the option
labelstringHuman-readable label
descriptionstringOptional longer explanation
grantsarrayGrants conferred if this option is selected

Validation

Venues MUST validate requests at submission: ask ids unique, types recognised, options present and non-empty where required, option ids unique within their ask, and grants attached only where permitted — on approval asks and on options. Invalid requests are rejected synchronously — no record is written and no Job parks.

Responses

A response resolves a request. Its schema:

FieldTypeDescription
outcomestringanswer or reject
answersobjectMap of ask id → answer, in the form required by the ask type. REQUIRED for answer; ignored for reject
commentsobjectOptional map of ask id → free-text comment, for asks with comment: true
commentstringOptional overall comment. For reject, this is the rejection reason
grantsarrayThe offered grants the responder explicitly approves (see Consent)

Venues MUST validate responses against the record's asks: every required ask answered (for answer outcomes), answer forms matching ask types, choice and checkboxes answers naming existing option ids, and no answers for unknown ask ids. An invalid response fails the hitl:respond call and leaves the record open.

Job resolution

EventRecord statusJob terminal stateJob payload
answer responseansweredCOMPLETEoutput = {id, outcome, answers, comments?, comment?, grants?, token?}
reject responserejectedFAILEDerror = "HITL request rejected" plus the responder's comment, when given
Expiry reachedexpiredFAILEDerror = "HITL request expired"
Requester cancels the JobcancelledCANCELLEDstandard cancellation

Job terminal states are sticky (COG-8); when a response races an expiry or a cancellation, exactly one resolution wins and the record MUST reflect the winner.

Rejection detail intended for the requester (the rejection comment) MUST travel in the Job error, since the requester generally cannot read the responder's h/ record.

Waiting, timeouts, and restarts

  • Jobs have no framework-level timeout (COG-8); an unbounded HITL request waits indefinitely by design. Requesters SHOULD set a timeout for time-sensitive decisions.
  • Expiry is a HITL-level property, enforced by the venue hosting the record. The venue MUST fail the Job when expires passes without a response, and MUST re-arm expiry enforcement for open requests after a restart.
  • Open requests and their parked Jobs survive venue restarts: paused-family Jobs are restored, and the h/ record is durable lattice state.
  • Requesters observe resolution through the standard Job surfaces: polling, blocking waits, and Job SSE streams. Responder clients discover work by listing h/ — venues MAY additionally surface notification channels, which are out of scope for this COG.

Operations

HITL is exposed through the hitl adapter. Operations follow the standard operation model (COG-7) and are catalogued under v/ops/hitl/.

hitl:request — submit a request

Creates the request: validates the asks, writes the record to the target user's h/, and parks the carrying Job in INPUT_REQUIRED. Asynchronous by nature — the Job is the handle.

Input:

FieldTypeDescription
userstringTarget responder DID. OPTIONAL — defaults to the requester's own user identity (an agent asking its owner)
titlestringREQUIRED. Short summary
descriptionstringMarkdown context for the decision
asksarrayREQUIRED. One or more asks
timeoutintegerSeconds until expiry. OPTIONAL — omitted means no expiry

Output (Job output on completion): the response resolution payload described above.

hitl:respond — resolve a request

Invoked by the responder. Validates the response, updates the record, resolves the Job, and issues approved grants.

Input:

FieldTypeDescription
idstringREQUIRED. The request ID
outcomestringREQUIRED. answer or reject
answersobjectAsk answers (for answer)
commentsobjectPer-ask comments
commentstringOverall comment / rejection reason
grantsarrayExplicitly approved grants

Output: {id, status} — the resolved record status. Responding to a request that is not open MUST fail without side effects.

hitl:list — list the caller's inbox

Convenience listing of the caller's own h/ records, filterable by status. Venues MAY implement this as a thin wrapper over the workspace listing operations; clients MUST NOT depend on it existing and can always fall back to covia:list h/.

Cancellation

There is no hitl:cancel operation. The requester cancels through the standard Job cancellation surface; the venue then marks the record cancelled. Responder clients MUST treat a cancelled record as closed.

Authorisation

Delivering a request into a user's inbox is a cross-user act and is authorised at the point of delivery, following the capability model of COG-13:

  • Ability: hitl/request on the resource <responderDID>/h/.
  • Self-ask: when the target user is the authenticated requester's own identity (the common case — an agent asking its owner), delivery is always permitted.
  • Cross-user and federated asks: the requester MUST present authority covering hitl/request on the responder's inbox — a UCAN delegation issued by the responder, or authority admitted by the venue's trust policy (COG-15). Venues MAY additionally apply local policy (allow-lists, per-user opt-in) at this boundary.
  • Responding is restricted to the inbox owner in this version. Delegable response authority (e.g. an assistant answering on a user's behalf via a hitl/respond delegation) is a candidate extension.
  • Anonymous callers cannot submit or answer requests: both operations require an authenticated identity, and the default public capability ceiling (COG-10) withholds invoke in any case.

Because responding can issue capability grants, venues MUST authenticate responders with the same strength they require for capability issuance generally.

Capability Grants

A request MAY offer the responder the opportunity to confer authority on the requester. Every offered grant is bound to a specific affirmative choice — if you choose this, you give this:

  1. Approval-ask grants — conferred when that ask is approved (answered true)
  2. Option grants — conferred when that option is selected

Grants attach nowhere else. There are no request-level grants: authority transfer is never a side effect of merely answering a request, and a grant that is not the direct consequence of a choice the responder made MUST NOT be issued. A plain "grant me access to X" is therefore expressed as an approval ask whose approval carries the grant.

Each offered grant is a capability as defined by COG-13:

{ "with": "w/reports/", "can": "crud/read", "exp": 1795000000 }
Capability semantics are specified elsewhere

This COG deliberately specifies no capability semantics of its own. What a responder is able to grant, how delegations are issued and signed, how attenuation constrains onward use, and how issued tokens are trusted across venues are defined by COG-17: Capability Granting (with the capability model of COG-13 and the cross-venue trust rules of COG-15). HITL specifies only the consent surface: which choices confer which offered grants, and how the resulting token travels.

Grant rules

  • An offered grant MUST lie within the responder's grantable authority as defined by COG-17: the responder's own resources, or resources belonging to others over which the responder holds granting rights (grant/…). HITL is a consent surface, not a source of authority — a responder can confer through HITL exactly what they could delegate directly, and nothing more.
  • Grantability is enforced at issuance time, when the responder and their authority are known. A response echoing a grant outside the responder's grantable authority MUST fail without side effects.
  • Every grant issued through HITL MUST be time-bounded. A grant's exp is capped by venue policy; an offer without exp receives the venue's default grant lifetime.

Silent authority transfer is the central risk of HITL, and the protocol guards against it structurally:

  • Responder clients MUST display every offered grant (resource, ability, expiry) attached to the choice that confers it, so the cost of approving or selecting is visible at the point of choice.
  • The response MUST echo the grants being approved in its grants field. The venue issues exactly the intersection of (a) the echoed grants and (b) the offers actually triggered by the choices made (approved approval asks and selected options). An echoed grant that was not offered-and-triggered MUST fail the response.
  • Answering a request while echoing no grants is always valid: the responder answers the questions but confers nothing.

Issuance

On a valid answer outcome with approved grants, a delegation token is issued whose audience is the requester (from) and whose attenuations are exactly the approved grants. Token format, the signing model (venue-attested, or signed by a self-sovereign responder), attenuation validation, and how verifiers trust the token's root are all governed by COG-17 — not by this COG.

HITL adds only the transport: the issued token is returned in the Job output as token, alongside the approved grants list, and the requester thereafter presents it as a standard proof.

Cross-Venue Requests

A HITL request targets a user where their inbox lives. To ask a user on a remote venue, the requester invokes hitl:request on that venue — directly, or via the grid federation operations — and COG-15 supplies the machinery:

  • Caller identity crosses the hop as an audience-bound identity token; the responder's venue verifies the requester's own signature and attributes the request accordingly. The record's from is the verified caller DID and origin the originating venue.
  • Authority crosses in the proof channel: the requester forwards the UCAN delegation that covers hitl/request on the target user's inbox, and the remote venue enforces it at delivery exactly as in the local case.
  • The Job lives on the responder's venue (it carries the request); the requester's local grid job proxies its status and result through the standard federation job surfaces.
  • Granted tokens flow back in the Job output. The audience is the requester's DID, so the token is presentable on any venue, subject to the root-authority trust rules of COG-15.

A venue MUST apply the same delivery authorisation to federated requests as to local cross-user requests. Federation changes where the request comes from, not what it is allowed to do.

Agent Skill

Venues SHOULD ship a hitl skill (COG-11) in the venue skill library so any agent can discover and load the HITL capability. The skill bundles:

  • Tools: v/ops/hitl/request, plus the standard job-status operation for observing resolution
  • Instructions, covering at minimum:
    • Ask sparingly; batch related questions into one request rather than interrupting repeatedly
    • Put the full decision context in description — the human sees only the record, not the agent's conversation
    • Use the narrowest ask type that fits (approval for yes/no, choice/checkboxes for enumerable options, text only when free text is genuinely needed)
    • Set a timeout for time-sensitive decisions and handle the FAILED expiry outcome
    • Offer only the minimum grants needed, scoped as narrowly as possible; never treat a rejection as an error to retry

Security Considerations

  • Requester identity is venue-attested. from, agent, and origin are set by the venue from the verified request context and are never caller-supplied. A record's provenance is as strong as the venue's ingress authentication.
  • Social engineering. A HITL request is a phishing surface: it asks a human to approve actions and confer authority. The cross-user hitl/request gate exists so users receive asks only from parties they (or their venue's policy) have accepted. Clients MUST render the verified from/origin identities prominently, and MUST NOT render requester-controlled text (title, description, prompts) in a way that spoofs client chrome or verified fields.
  • Grant review. All authority transfer is bound to a specific affirmative choice, opt-in per grant, echoed explicitly in the response, restricted to the responder's grantable authority (COG-17), and time-bounded. Clients that cannot render grants MUST NOT submit responses that echo them.
  • Mutation and TOCTOU. The record the responder reviews is the record the venue resolves: requests are immutable once delivered (only the venue-set status/response fields change), and the echo-consent rule means a response approves specific capabilities, not "whatever the record now says".
  • Sensitive content. Ask prompts and answers may carry sensitive data. The standard secretFields redaction applies to the carrying Job's persisted record; the h/ record itself lives in the responder's own namespace under their access control. Requesters SHOULD NOT place secrets in requests destined for third parties.
  • Resource exhaustion. Every request parks a Job and writes a record. Venues SHOULD bound open requests per requester and per inbox, and SHOULD rate-limit hitl:request, particularly at the cross-user and federated boundaries.
  • Expiry integrity. Expiry enforcement MUST survive restarts; a venue that loses its timers must reconstruct them from the durable expires fields, not silently leave requests open forever when a timeout was promised.

Examples

Simple approval (agent asks its owner)

// invoke v/ops/hitl/request
{
"title": "Pay invoice INV-4711",
"description": "Acme Ltd invoice INV-4711 for £12,400, matched to PO-2231. Payment due Friday.",
"asks": [
{ "id": "pay", "type": "approval", "prompt": "Approve payment of £12,400 to Acme Ltd?",
"required": true, "comment": true }
],
"timeout": 172800
}

The Job parks in INPUT_REQUIRED; the record appears at h/<id> in the owner's inbox. The owner responds:

// invoke v/ops/hitl/respond
{
"id": "0195f3a2b4c6...",
"outcome": "answer",
"answers": { "pay": true },
"comments": { "pay": "Approved — this invoice only." }
}

The Job completes with:

{ "id": "0195f3a2b4c6...", "outcome": "answer",
"answers": { "pay": true },
"comments": { "pay": "Approved — this invoice only." } }

Mixed asks with an offered grant

{
"title": "Quarterly report run",
"description": "Ready to generate the Q3 report pack. I need output preferences and read access to the source reports.",
"asks": [
{ "id": "detail", "type": "choice", "prompt": "Report depth?",
"options": [
{ "id": "summary", "label": "Executive summary" },
{ "id": "full", "label": "Full detail" }
], "required": true },
{ "id": "sections", "type": "checkboxes", "prompt": "Include:",
"options": [
{ "id": "fin", "label": "Financials" },
{ "id": "ops", "label": "Operations" }
] },
{ "id": "access", "type": "approval", "prompt": "Grant read access to w/reports/ for 7 days?",
"grants": [ { "with": "w/reports/", "can": "crud/read", "exp": 1795000000 } ] },
{ "id": "notes", "type": "text", "prompt": "Anything to emphasise?" }
]
}

Response, explicitly echoing the approved grant:

{
"id": "0195f3c1d2e8...",
"outcome": "answer",
"answers": { "detail": "full", "sections": ["fin", "ops"], "access": true,
"notes": "Flag the Q3 logistics overspend." },
"grants": [ { "with": "w/reports/", "can": "crud/read", "exp": 1795000000 } ]
}

The Job output includes the answers, the approved grants, and a token — a UCAN audienced to the requesting agent, presentable as a proof on its subsequent w/reports/ reads.

Rejection

{ "id": "0195f3a2b4c6...", "outcome": "reject", "comment": "Wrong PO — resubmit against PO-2240." }

The Job fails with error: HITL request rejected: Wrong PO — resubmit against PO-2240. The requester sees the reason through the Job; the resolved record remains in the responder's inbox until deleted.