Skip to main content
Asking people
A Hug is a request a run makes of people while it is running: approve this plan, send it back, upload the guest list, tell me which customer this is about. Every way a team asks a person goes through the same Hug framework, so a review gate, an agent’s question and an LLM’s ask_human call all look the same to the people answering and behave the same while the run waits.

How it works

  • CliqHub stores the Hug, sends it to its channels, checks who may answer, validates answers against the requested inputs, stores uploaded files, sends reminders and expires the Hug. It never interprets an answer.
  • cliqd carries Hugs between the phase and CliqHub. It keeps every event, so a phase that stops or a cliqd that restarts picks up where it left off.
  • The phase (through cliq-sdk) decides what the answers mean — the policy: the first answer is enough, every reviewer group must approve, and so on.
With a local cliqd that isn’t signed in to CliqHub, the Hug stays on your machine and cliq run asks the question in the terminal.

Three ways to open a Hug

A run never asks for its own missing inputs: inputs are checked when the run starts, and a phase that still lacks one fails with Phase 'x' is missing inputs: a, b — supply them when starting the run.

Example 1 — an approval gate

The most common Hug: a person reviews the work and approves it, sends it back to an earlier phase, or rejects it.
The reviewer sees Approve, Send back to draft and Reject, then the note field and an optional comment. What each answer does: The first answer that isn’t an approval decides. A round is one review: sending the work back and reviewing it again is round 2.

Gate review fields


Example 2 — collect a file in the review

A review can ask for files. The files stay in storage (CliqHub, or cliqd’s own store for a terminal answer); a later phase saves the ones it needs with cliq hug files.
cliq hug files human-review guest_list guests/ saves each file as guests/<responder>-<file name> (a second file of the same name gets -2) and writes guests/files.json saying who sent what:
By default it takes the latest round of that phase’s review, the current file from everyone who sent one. Narrow or widen that with options:
  • Inside a phase, cliq hug files uses the run it is running in, and the folder must be inside the workspace. Outside a phase, name the run with --run (and -w for another workspace).
  • No matching file is not an error: the folder gets an empty files.json. A phase or input that has no files at all is (HUG_FILES_NOT_FOUND), and the error lists what the run does have — so a typo is caught.
  • Every file is checked against its size and SHA-256 before it is written, and written atomically.
The list of files comes from the run’s Hug files catalog, $CLIQ_RUN_DIR/hug-files.json (<workspace>/.cliq/runs/<run_id>/hug-files.json), which cliqd keeps up to date as answers arrive.

Example 3 — run @cliq/hug-example

@cliq/hug-example is a built-in team that shows a review collecting a file. A planner writes a party plan, a reviewer approves it and uploads the guest list, and the next phase writes one invitation per guest plus a shopping list. 1. Save this as guests.csv — you will upload it in the review:
The team keeps its parsing simple: the first line is a header (always skipped), names are in the first column, and names must not contain commas (quoted fields aren’t handled — "Smith, Jo" would be cut at the comma). Blank lines and duplicates are dropped. The file must be a .csv of at most 1 MB.
2. Run it, asking yourself to review:
3. Review it.
  • Signed in to CliqHub: you get a notification; open the Hug from it or from HUGs in the sidebar. Read plan.md (Preview), pick Approve, upload guests.csv, and Submit response.
  • Local cliqd (not signed in): cliq run asks in the terminal. Choose approve and give the path to guests.csv.
4. Check the result. The invitations phase saves the file with cliq hug files human-review guest_list guests/ and writes:
If you reject instead, the gate has no route target, so the run escalates and stops before invitations. Because the guest list is required, the form asks for a file whatever you decide. @cliq/hello-world is the smallest Hug: an exec phase writes review.txt, a gate (agent: hug, 60-minute timeout, policy: any) asks you to approve it, and a final phase runs on approval. It is the team Get started runs.

Example 4 — an agent asks a person

An agent written with cliq-sdk asks with ctx.ask. The phase’s hug: block in team.yml says where its questions go, how long they stay open and how often people are reminded (from examples/hello-ask in the cliq repo):
ctx.ask returns { values, responses, … }. With the default policy, values are the first answer’s values keyed by input name. It throws when the Hug ends without a result: All extend HugError. Uncaught, they fail the phase.

ctx.ask options


Example 5 — several reviewers, and letting the agent stop while waiting

With wait: 'yield' the agent’s process ends, the phase and run show awaiting_input, and cliqd starts the phase again when the Hug has news (an answer, its expiry or its cancellation). The SDK finds the same Hug again — it is never asked twice — and ctx.ask returns or throws as if it had waited. With wait: 'block' (the default, and what the review gate does) the run stays running. Either way the phase never times out while it waits for people: its timeout stops counting while any of its Hugs is open and resumes, with the time that was left, when they end. The Hug’s own expiry bounds the wait instead. For full control, open Hugs yourself with ctx.hug.open(spec, { state }) and wait on several with ctx.wait({ sessions, state }).

Example 6 — read uploaded files in agent code

The same catalog cliq hug files reads is available to agents:
ctx.hug_files({ phase, input, round?, responder?, include_replaced? }) takes the same choices as the CLI (round: 'latest' (default), 'all' or a number). ctx.download(ref) returns a readable stream; ctx.download(ref, dest) saves to a path inside the workspace and returns { path, size, sha256 }. Both read through cliqd, wherever the file is stored, and retry for up to two minutes if cliqd is restarting.

Example 7 — an LLM agent asks

Agents that drive a model (LLM and CLI agents) can ask people without any code:
  • the ask_human tool, when the model supports tools;
  • a fenced cliq_hug block with the same JSON as ctx.ask (title, prompt, requested_inputs);
  • a final line QUESTION: <text>, which becomes a one-field text question.
These always stop the agent while people answer (wait: 'yield'), and use the phase’s hug: block for channels, expiry and reminders. The answer comes back to the model as the next turn.

Requested inputs

Each requested input is one field in the form. Every input has name (lowercase letters, digits and _, starting with a letter), and optionally label, help, default and required — required defaults to true. At most 50 per Hug. The review gate’s review.inputs use friendlier names that map onto these: The gate adds its own decision and comment fields; those two names are reserved. Files are checked against accept by declared type, extension and their first bytes, and against max_bytes and the server limit (CLIQ_ARTIFACT_MAX_MB, default 50 MB) — an input can lower the limit, never raise it.

Who is asked, and who may answer

  • Channels say who is asked: a person (by username, or me), or a shared channel your org set up (email, Slack, in-app…). {{inputs.x}} uses a run input — usually a type: channel input. Unknown names raise a notification.unknown_channel event; a Hug with no channel at all is refused.
  • Seeing a Hug needs runs.view in its realm. Answering also needs it to be addressed to you — through your own channel or a shared channel it was sent to. Being asked grants nothing: a person reached by a channel who can’t view the realm’s runs is skipped, shown as “Not sent to …” on the Hug page, and raises notification.no_access. If nobody left can answer, the Hug is refused (no_one_can_answer).
  • Changing an answer: a person can submit again while the Hug is open; their latest answer counts. Files from the earlier answer stay in the catalog with current: false (--include-replaced / include_replaced to see them).
  • Several people: each answer goes to the phase as it arrives; the policy decides when it’s complete — any() on the first answer, groups when every group is satisfied, or your own custom(fn).

Answering

On CliqHub

Each Hug has a page at /hugs/<hug id>, linked from its notifications and from HUGs in the sidebar (its badge counts open Hugs addressed to you that you haven’t answered yet; the HUGs page has Waiting and Done tabs). The page shows:
  • who it was sent to, when it opened and when it expires;
  • the title and prompt;
  • the run’s workflow — click a phase to see its output;
  • the context: attached files as a list (Preview opens a dialog, with ‹ › to move between files; open in a new tab or download), notes and check results;
  • Your response: required fields first, then optional ones, and Submit response. Files upload as you pick them.
After you answer you can change it while the Hug is open. Once it ends the page shows the outcome and everyone’s responses.

In the terminal

With a local cliqd that isn’t signed in to CliqHub, cliq run asks each requested input in the terminal and checks it as you type. For a file input, give a path: the file is checked against accept and max_bytes, and cliqd keeps it in its own file store (~/.cliqrc/artifacts/<run_id>/, or CLIQ_ARTIFACTS_DIR) — the answer holds a reference, nothing is left in the workspace. Not at a terminal, cliq run prints the question instead.

Lifecycle

  • Reminders go to people who haven’t answered, every remind_every / reminders.every_min.
  • When a run ends, its open Hugs are cancelled.
  • If cliqd restarts while a run waits on a Hug, the run is picked up again and its phase finds the same Hug — nobody is asked twice (root runs; a sub-team’s run can’t be re-attached and is marked crashed). cliqd catches up on anything that happened while it was down.

Defaults and limits


Notifications and events

A new Hug is sent straight to its channels (“Input requested”). The rest are events you can route with notification rules — hug.* matches them all: For example, tell the release channel whenever a review expires:
The run’s log also records hug.opened, hug.responded, hug.closed, hug.expired, hug.cancelled, and phase.waiting / phase.resumed when a phase stops and starts again around a Hug.

APIs

  • CliqHub (Hub API › Hugs): hugs/get (your Hugs, by realm or org), hugs/get_by_id, hugs/respond (answer one). hugs/create, hugs/close and hugs/get_events are for daemons. Errors: HUG_EXPIRED (410), HUG_CLOSED (409), invalid_values (422 — an answer that doesn’t fit the requested inputs).
  • cliqd (Daemon API › Hugs): hugs/open, hugs/next, hugs/save_state, hugs/close for agents (cliq-sdk calls these for you), hugs/deliver for CliqHub, and hugs/respond for terminal answers. Errors: HUG_NOT_FOUND (404), HUG_CLOSED (409), HUG_EXPIRED (410), HUG_REFUSED (422).