> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Hold this session back from the organisation, or hand it back

> Marks one conversation `private`, or hands it back to `organisation`, which is what every session is until somebody says otherwise. Inside one organisation the team is the default and this mark is the only exception to it.
IT TAKES EFFECT ON THE NEXT READ, from anywhere. Nothing caches the answer: every read of a session asks the stored mark at the moment it is served, so a session marked here disappears from a colleague's list and their open window's next request at once, without a restart and without anything being pushed to them.
WHO MAY CALL IT: the session's owner, always — a person's own conversation is theirs to hold back — and a member who administers the organisation's membership, for anybody, because a departed colleague's work has to be able to come back to the team. Nobody else, and deliberately not the holder of `sessions:read-private`: that permission exists so a security question can be answered, and it does not extend to changing what everybody else may read.
Marking a session the mark it already carries is not an error: the state asked for holds. Nothing is announced and nothing is written into the transcript — who may read a conversation is a fact about it rather than something that happened in it, and its position in a session list does not move.



## OpenAPI

````yaml /es/openapi/v3-current/narya.yaml put /v1/sessions/{sessionId}/visibility
openapi: 3.1.0
info:
  title: Narya Host API
  version: 1.0.0
  description: >-
    The contract between the Narya host and every client. One long-lived host
    serves this API over a Unix socket in your Narya home. The terminal client
    and the one-shot command that Lerian ships drive this API, and a client you
    write drives the same one.


    Requests authenticate with a bearer token issued by the identity provider
    the host is configured with. An operation that declares another security
    scheme also accepts that credential. A request without a valid credential
    gets 401 with NRY-0011. A caller whose role lacks the permission an
    operation needs gets 403 with NRY-0028.


    Submitting a message returns 202 with a turn id. Everything the turn
    produces streams over GET /v1/events as server-sent events with a typed
    envelope, and a stream resumes from a Last-Event-ID header.


    One error envelope: code, title and message. Codes are NRY- followed by four
    digits. Cursors are opaque.
servers: []
security:
  - bearerAuth: []
tags:
  - name: host
    description: The host process itself — version, uptime, mode, store.
  - name: sessions
    description: Durable conversation containers. Archive, never destroy.
  - name: messages
    description: Submitting work into a session and interrupting it.
  - name: events
    description: The server-sent event stream every client consumes.
  - name: lanes
    description: Parallel tracks inside a session — main, subagent, side.
  - name: agents
    description: Named recipes — instructions, tools, model, policy. Read-only in v1.
  - name: ladder
    description: >-
      What a person can type: the skills and command files in force for one
      repository, and expanding one into text. Five origins merged, nearest
      winning a name, the repository's own rungs gated on trust.
  - name: permissions
    description: Pending permission asks, decisions, and the decision audit.
  - name: intercom
    description: Sessions on one machine finding and messaging each other.
  - name: packages
    description: The one thing a user installs — resources, Go code, or both.
  - name: extensions
    description: >-
      Host-side extensions and the operations each exposes over the wire. This
      is the generic lane a host extension uses to serve its own client half (a
      TUI component, a web panel) or any API-only consumer, without adding
      routes to this contract.
  - name: workflows
    description: Deterministic multi-agent orchestration runs.
  - name: providers
    description: Model suppliers, their auth state, and the model catalogue.
  - name: monitors
    description: >-
      Long-running watchers a session keeps beside its conversation — a test
      runner in watch mode, a build, a log being followed. Started by the model
      or by the person, always listed, always killable.
  - name: records
    description: The queryable local record of everything that happened.
  - name: environments
    description: >-
      Where a session's code lives and its commands run — this machine, or a
      container narya operates. A session that names none runs here.
  - name: schedules
    description: >-
      Work the host's own clock starts with nobody present — a repository, a
      prompt, a rule and what one fire may spend. Cancel, never destroy.
  - name: sharing
    description: >-
      Publishing a session from a developer's own home to the organisation's,
      and what is held back before a byte leaves the machine.
  - name: refinements
    description: >-
      What this owner has taught narya and allowed it to keep — distilled facts,
      and the skills, agents and commands the model wrote for itself. Propose,
      read, consent, roll back. Nothing here fires until a person answers.
  - name: platform
    description: >-
      Calls Lerian's control plane made to a home it hosts, as this home
      recorded them.
paths:
  /v1/sessions/{sessionId}/visibility:
    parameters:
      - $ref: '#/components/parameters/SessionIdParam'
    put:
      tags:
        - sessions
      summary: Hold this session back from the organisation, or hand it back
      description: >-
        Marks one conversation `private`, or hands it back to `organisation`,
        which is what every session is until somebody says otherwise. Inside one
        organisation the team is the default and this mark is the only exception
        to it.

        IT TAKES EFFECT ON THE NEXT READ, from anywhere. Nothing caches the
        answer: every read of a session asks the stored mark at the moment it is
        served, so a session marked here disappears from a colleague's list and
        their open window's next request at once, without a restart and without
        anything being pushed to them.

        WHO MAY CALL IT: the session's owner, always — a person's own
        conversation is theirs to hold back — and a member who administers the
        organisation's membership, for anybody, because a departed colleague's
        work has to be able to come back to the team. Nobody else, and
        deliberately not the holder of `sessions:read-private`: that permission
        exists so a security question can be answered, and it does not extend to
        changing what everybody else may read.

        Marking a session the mark it already carries is not an error: the state
        asked for holds. Nothing is announced and nothing is written into the
        transcript — who may read a conversation is a fact about it rather than
        something that happened in it, and its position in a session list does
        not move.
      operationId: setSessionVisibility
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionVisibility'
      responses:
        '200':
          description: The session, under the visibility it now holds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RequestBodyTooLarge'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  parameters:
    SessionIdParam:
      name: sessionId
      in: path
      required: true
      description: The session's id.
      schema:
        type: string
        format: uuid
  schemas:
    SessionVisibility:
      type: object
      description: >-
        The visibility to put a session under. One field, required: an omitted
        field is refused rather than read as "hand it back to the organisation",
        because a request that did not say which visibility it meant has to be
        answered rather than guessed for — and the permissive guess would take
        the mark off a conversation somebody held back. SessionPosture's rule
        and its reason.
      required:
        - visibility
      properties:
        visibility:
          $ref: '#/components/schemas/Visibility'
      examples:
        - visibility: private
    Session:
      type: object
      description: >-
        A durable conversation container. Survives the process that created it;
        archiving hides it from active lists but its transcript remains
        queryable and exportable forever.
      required:
        - id
        - repository
        - status
        - clientKind
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          format: uuid
          description: The session's id.
        title:
          type: string
          maxLength: 512
          description: Human-readable title.
        repository:
          type: string
          maxLength: 4096
          description: >-
            Absolute path of the directory the session works in.

            It is a working-directory LABEL and carries no authority. The
            session's key is `id`, and whose session it is is recorded by the
            host from the identity it authenticated the request as — nothing is
            authorised, looked up or scoped to a tenant by this field. What it
            is read for is what a directory is for: where the tools are
            confined, which project's configuration and instruction files apply,
            and the `repository` filter on listSessions.
        status:
          type: string
          enum:
            - idle
            - running
            - waiting
            - archived
          description: >-
            Lifecycle: running while a turn executes or normal follow-ups chain;
            waiting after interrupt when admitted steering or follow-up inputs
            remain queued; idle only when neither execution nor queued input
            exists; archived accepts no new work.
        provider:
          type: string
          description: The model provider currently in use.
        model:
          type: string
          description: The model currently in use.
        variant:
          type: string
          description: >-
            The preset of request options this session's turns run under — the
            name, not the options it stands for, since what a name means is the
            catalogue's answer and can change while the session's own choice
            cannot. Absent on a session that never chose one, which is every
            session running its model at the model's own defaults.
        style:
          type: string
          maxLength: 64
          description: >-
            The named response style this session's answers are shaped by — the
            name, not the prompt it appends, since what a name means is
            configuration's answer and changes when somebody edits that file
            while the session's own choice does not. Absent on a session that
            never chose one, which is every session answering in narya's own
            voice. Unlike `variant` it survives a model switch: a style is a
            fact about how the conversation reads rather than about the model
            answering it. A client renders this and derives nothing — the names
            that exist are the host's answer (setSessionStyle refuses the rest).
        clientKind:
          $ref: '#/components/schemas/ClientKind'
          description: >-
            Which kind of client opened this session, recorded when it was
            created and never rewritten. Always present: `unknown` for a session
            created before this host recorded the datum, which is the honest
            answer rather than a guessed frontend.
        modelNotice:
          type: string
          maxLength: 1024
          description: >-
            One sentence stating what a client could not otherwise know about
            the model this session opened on or was just moved to, present on
            the responses to createSession and setSessionModel. Two facts earn
            it. The first is a model narya chose itself rather than being told
            to use: no default_model configured and no model previously chosen,
            so the first provider holding a usable credential answered — or a
            named model the catalogue no longer carries, for which the provider
            default was substituted. It names the alternatives that also
            qualified and how to choose another, because a choice nobody made
            has to be stated to be a choice at all rather than something that
            merely happened. The second is a level this session will run
            without: a model reference may carry one (see the variant field),
            and an endpoint that does not take the field runs at its own default
            instead — so a level named at creation or at a switch, or configured
            beside default_model, is said here rather than quoted in a picker
            while every request drops it. A client that renders the live event
            stream rather than the session's transcript has no other road to it.
            Otherwise absent, including for a model that was simply configured,
            chosen or named; absent on getSession and listSessions, which report
            state rather than how it came about.
        usage:
          $ref: '#/components/schemas/TokenUsage'
          description: >-
            What this session has consumed and cost across every turn it has run
            — a running total, not the last turn's figure. Carried by getSession
            and by every row of listSessions, so a client drawing a list of
            sessions can say what each one has cost without asking per row.
            Absent on a session that has never completed a turn, and its costUsd
            is absent whenever any one of that session's turns could not be
            priced: the sum over only the priced turns is smaller than what the
            session actually cost, and a figure that quietly understates money
            is worse than an admitted unknown.
        lastTurnUsage:
          $ref: '#/components/schemas/TokenUsage'
          description: >-
            What the newest round of this session's own conversation sent and
            produced — the LAST turn's figure, not the running total above.
            Carried by getSession only.

            It exists because the two are not interchangeable for the one
            question a reader reopening a long conversation asks: how much room
            is left before this compacts. That answer is the size of the prompt
            last sent, and usage above is a sum over every turn the session ever
            ran, which is a larger number growing without bound and means
            nothing as a fraction of a context window. The live event stream
            carries this figure on turn-finished, so a client watching a session
            has always had it; a client that READ the session had no source for
            it at all and drew no context figure until it spent a turn of its
            own. Restoring a recorded reading rather than deriving one is the
            whole of it — nothing here is computed from the transcript.

            The newest round of the MAIN lane: a delegation's rounds are another
            conversation's prompts, and the figure is about the reader's. Absent
            on a session that has never completed a round, and on one whose
            rounds all ran on delegates. Its costUsd is that one round's price,
            absent when the round could not be priced.
        pendingInputs:
          type: array
          description: >-
            Pending steering and follow-up references in durable operation
            order. Content is intentionally absent; fetch the transcript by
            entryId.
          items:
            $ref: '#/components/schemas/PendingInputReference'
        parentSessionId:
          type: string
          format: uuid
          description: When the session was branched, the session it branched from.
        summary:
          type: string
          maxLength: 4096
          description: >-
            What a model said this conversation tried and concluded. Absent
            until somebody asks for it (summarizeSession) — nothing summarises a
            branch because a rewind moved away from it. A model that answers
            past this length is trimmed to it, with a marker saying so rather
            than a sentence that just stops.
        summaryAt:
          type: string
          format: date-time
          description: >-
            When the summary was taken. Read against updatedAt it answers
            whether the summary predates the last thing that happened here.
        posture:
          $ref: '#/components/schemas/Posture'
          description: >-
            Whether somebody is there to answer a permission prompt raised here.
            Absent on a session nobody ever said either way about, which reads
            as `attended` — the posture is only ever recorded because somebody
            stated it (setSessionPosture), never inferred from whether a client
            happens to be connected.
        warming:
          $ref: '#/components/schemas/Warming'
          description: >-
            Whether this session pays to keep its provider cache alive while
            nobody is using it, and the most it may ever spend doing so. Absent
            on a session nobody ever turned it on for, which is every session
            until somebody does — warming is off unless somebody asked
            (setSessionWarming).
        viewedAt:
          type: string
          format: date-time
          description: >-
            When a reader last had this session in front of them
            (acknowledgeSessionViewed). Read against updatedAt it answers
            whether anything has happened here since somebody looked, which is
            what lets a client mark a background conversation as carrying news —
            and, because the fact is held here rather than in a window, what
            makes two clients agree about it and makes the answer survive a
            relaunch. ABSENT on a session nobody has ever viewed, which is not
            the same as viewed long ago: one has nothing to report, the other
            has everything that ever happened in it.
        lastOutcome:
          type: string
          enum:
            - finished
            - failed
          description: >-
            How the last turn here ended. It is what lets a client that was not
            running at the time tell a conversation that finished from one that
            failed. Absent on a session that has never run a turn. An interrupt
            is not among the values: somebody was there and stopped it
            themselves, so it is not news to bring back to them.
        cacheSpend:
          $ref: '#/components/schemas/CacheSpend'
          description: >-
            What this session's provider prompt cache has cost it, from both
            sides: what letting it expire cost, and what keeping it alive cost.
            Present only on getSession, and only once one of the two has
            happened — a session that has never paid for either carries nothing
            here rather than a pair of zeros.
        environment:
          $ref: '#/components/schemas/SessionEnvironment'
          description: >-
            Where this session's code lives and its commands run, present only
            when that is somewhere other than the host's own machine. Absent is
            the ordinary case and says the session runs here — a developer
            working on their own machine is told nothing, because there is
            nothing to tell.
        tasks:
          type: array
          description: >-
            The agent's own task list for this session — every item it has
            written, in the order it wrote them, whatever is in the store right
            now.

            It is here rather than behind a listing of its own because the list
            is a stored row and every client already reads the session when it
            attaches, so one optional field costs no round trip and no second
            operation. A change to the list is announced as `task-list`, which
            carries the same whole list for the same reason; this is where a
            client that was not attached at the time gets it.

            Present on getSession only, like `lastTurnUsage` and `cacheSpend`
            and for their reason: a listing that carried it would read every
            row's tasks to draw a page nobody asked for.

            ABSENT MEANS EMPTY, and it cannot mean anything narrower: the store
            holds rows, so a list nobody ever wrote and a list somebody cleared
            are the same read. The live stream is where those two differ — a
            clearing write announces `task-list` carrying an empty array — and a
            read that returned `[]` would be claiming to know which of the two
            it was looking at.
          items:
            $ref: '#/components/schemas/Task'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
          description: >-
            When this session's own ROW last changed. Eight writes stamp it and
            nothing else does: its status, its provider and model, its style,
            its title, its repository, its environment binding, its archival,
            and the status a restart settles it into.

            IT IS NOT A LAST-ACTIVITY CLOCK, and reading it as one is the
            mistake this description exists to prevent. A transcript entry, a
            streamed event, a tool call, a summary and a viewed mark all leave
            it exactly where it was — so a session that is `running` carries THE
            MOMENT ITS TURN WAS CLAIMED, not the moment anything last happened
            in it — and a moment later than that only when one of the other
            seven writes landed during the turn: a rename mid-turn is allowed
            and restamps the row, moving this field forward with nothing having
            happened in the conversation. A caller asking whether a run has gone
            quiet reads the event stream; read off this field, "nothing for N
            minutes" is true of every healthy turn that has been running longer
            than N, and false of a stuck one somebody renamed.

            `SessionAttention.since` carries the same warning for a waiting
            session, and for the same reason.
        owner:
          type: string
          readOnly: true
          description: >-
            Who this session belongs to: the identifier of the person the
            request that created it was authenticated as, inside the
            organisation this host serves. It is who the session is attributed
            to and whose allowance its cache warming is billed against.

            The host writes it from the authenticated identity and there is no
            owner field on any create request — its absence there is the rule
            rather than an omission, because an owner a caller can name is an
            owner anybody can be, and this value is read later by whoever
            decides who pays and who may open a private conversation. A request
            body naming an owner changes nothing.

            `unclaimed` is the one value that is not a person: a store that
            existed before this host recorded owners, on a machine nobody has
            signed in on yet. The first sign-in against such a store replaces it
            everywhere.
        visibility:
          $ref: '#/components/schemas/Visibility'
          description: >-
            Who inside this organisation may read this session. Always present
            on a host that serves this field, unlike `posture` beside it: every
            session HAS a visibility, and the absence of a mark is the
            organisation's rather than a third state, so there is nothing for an
            absent field to mean. A client draws the mark from this value and
            derives nothing — whether a session is held back, and whether this
            reader may see it at all, are the host's answers.
        attention:
          $ref: '#/components/schemas/SessionAttention'
          description: >-
            What this conversation is waiting for, when it is waiting for a
            person. `status` says `waiting` and does not say what for.


            Carried by every row of listSessions and resolved for the whole page
            in one read, which is the point of it: a page of a hundred rows is
            enough to draw a fleet, with no request per row.


            ABSENT WHEN THERE IS NOTHING TO SAY, and absent rather than
            null-shaped: a conversation nobody is being asked about carries no
            field, not an empty object. It is also absent when the caller may
            not see the prompt itself — on a conversation somebody marked
            private, a reader who is not its owner and does not hold the
            read-private permission sees no attention even though the request
            behind it exists, because what this field may say is bounded by the
            same door the ask is behind.
      examples:
        - id: 6b9f6d2e-1c3a-4f5b-9d7e-2a8c4e6f0b1d
          title: Fix flaky ledger test
          repository: /Users/dev/repos/midaz
          status: running
          provider: anthropic
          model: claude-sonnet-4-5
          clientKind: inline
          owner: user_01HZX3Q8N4KDPYV2A7C9M5T6RB
          usage:
            inputTokens: 152000
            outputTokens: 8400
            costUsd: 1.87
          createdAt: '2026-08-08T12:00:00.000Z'
          updatedAt: '2026-08-08T12:34:56.000Z'
    Visibility:
      type: string
      enum:
        - organisation
        - private
      x-enum-varnames:
        - VisibilityOrganisation
        - VisibilityPrivate
      description: >-
        Who inside this organisation may read a session. organisation — every
        member may, which is the default and what every session is until
        somebody marks it. private — its owner, plus a member holding
        `sessions:read-private`, plus whoever a share of it names.

        Two values and no third. There is no "unset": the absence of a mark IS
        the organisation's, so "nobody said" and "the team may read it" are one
        fact rather than two, and nothing in this product has a use for a
        visibility somebody would have to define.
    ClientKind:
      type: string
      enum:
        - inline
        - tui
        - oneshot
        - api
        - unknown
      x-enum-varnames:
        - ClientKindInline
        - ClientKindTui
        - ClientKindOneshot
        - ClientKindApi
        - ClientKindUnknown
      description: >-
        Which kind of client opened a session — the coarse classification a
        client filters and mixes on when it lists sessions it did not itself
        open. inline — the terminal client (`narya`). tui — the full-screen
        client (`narya --tui`). oneshot — a one-shot command (`narya -p`). api —
        a caller of this contract that named no kind; every narya frontend names
        itself, so this is what speaking the contract directly is. unknown —
        never recorded, which is every session created before this host carried
        the datum; a read answers `unknown` rather than picking a kind, and a
        create request naming it records nothing. Presentation metadata only:
        nothing in the host behaves differently because of its value.
    TokenUsage:
      type: object
      description: >-
        Token consumption totals and their cost. costUsd is priced from the
        catalogue's published rates for the model that ran, and is absent
        whenever no rate is known for it — a model configured by hand against a
        private endpoint, say. Absent means unknown; a false zero would mean
        free. On a session total it is absent unless every turn in the session
        could be priced.
      required:
        - inputTokens
        - outputTokens
      properties:
        inputTokens:
          type: integer
          minimum: 0
          description: Input tokens consumed.
        outputTokens:
          type: integer
          minimum: 0
          description: Output tokens produced.
        cacheReadTokens:
          type: integer
          minimum: 0
          description: Tokens served from the provider's prompt cache.
        cacheWriteTokens:
          type: integer
          minimum: 0
          description: Tokens written to the provider's prompt cache.
        costUsd:
          type: number
          minimum: 0
          description: Total cost in US dollars.
      examples:
        - inputTokens: 152000
          outputTokens: 8400
          cacheReadTokens: 120000
          cacheWriteTokens: 9000
          costUsd: 1.87
    PendingInputReference:
      type: object
      description: >-
        A content-free durable queued-input reference. Message content remains
        only in the transcript.
      required:
        - entryId
        - turnId
        - kind
        - sequence
        - enqueuedAt
      properties:
        entryId:
          type: string
          format: uuid
        turnId:
          type: string
          format: uuid
        kind:
          type: string
          enum:
            - steer
            - follow-up
        sequence:
          type: integer
          format: int64
          minimum: 1
        enqueuedAt:
          type: string
          format: date-time
      examples:
        - entryId: 4d8e2f6a-0b1c-4d3e-9f5a-7b9c1d3e5f0a
          turnId: 4d8e2f6a-0b1c-4d3e-9f5a-7b9c1d3e5f0a
          kind: follow-up
          sequence: 7
          enqueuedAt: '2026-08-10T15:04:05.000Z'
    Posture:
      type: string
      enum:
        - attended
        - detached
      x-enum-varnames:
        - PostureAttended
        - PostureDetached
      description: >-
        Whether somebody is there to answer a permission prompt raised in a
        session. attended — a prompt is shown and waited on, which is what every
        session starts as. detached — nobody is watching, so a prompt is refused
        and recorded with its reason rather than blocking the turn forever on a
        question nothing can answer.

        Orthogonal to clientKind, and deliberately not a value of it: clientKind
        is stamped once at creation and never rewritten, whereas a session
        BECOMES detached when somebody says so. It is a recorded fact with
        exactly one transition (setSessionPosture) and is never inferred from
        the presence or absence of a connection — a client that crashed, a lid
        that closed and a pipe that dropped all leave it exactly as it was.
    Warming:
      type: object
      description: >-
        One session's standing instruction about its provider cache while nobody
        is using it: whether to pay to hold the cached prefix past the five
        minutes a provider keeps it for free, and the lifetime dollars this
        session may spend doing so.

        Both fields travel together because they are one decision. Turning
        warming on without saying what it may cost is not a decision anybody
        makes, and there is no default narya could supply that is not a guess
        about somebody else's bill.

        The ceiling is a LIFETIME figure for this session, not a daily one: a
        session is a bounded thing with an end, and "this conversation may cost
        me at most fifty cents to keep warm" is the sentence a person means. A
        second, separate ceiling bounds the whole store's spending per UTC day
        and is operator configuration rather than part of any session — either
        one being reached stops warming rather than warning about it.
      required:
        - enabled
        - ceilingUsd
      properties:
        enabled:
          type: boolean
          description: >-
            Whether this session is warmed. False with a ceiling recorded is a
            session somebody turned off, which keeps the number they chose for
            the day they turn it back on.
        ceilingUsd:
          type: number
          format: double
          minimum: 0
          description: >-
            The most this session may ever spend on warming, in US dollars,
            counted across its whole life. Reaching it stops warming this
            session and nothing else; every other session keeps its own
            allowance. Zero is a legitimate value and means the same as off.
      examples:
        - enabled: true
          ceilingUsd: 0.5
    CacheSpend:
      type: object
      description: >-
        What this session's provider prompt cache has cost it, read from both
        sides of one decision: what letting the cached prefix expire cost, and
        what refusing to let it expire cost.

        The two numbers are reported together because they are the same decision
        seen from either end. Somebody weighing whether to turn warming on for
        this session is comparing exactly these figures, and either one alone
        answers half the question.

        The headline is the re-billed prompt: rounds that paid full input rates
        to send again a prefix the provider had been holding, because the
        session sat idle past the cache's lifetime. A duplicated cache WRITE is
        a real event too and is deliberately not reported here — it is an
        internal metric, not what a person reads on a bill.

        Absent on a session where neither has ever happened, which is every
        session until one of them does. Never a zero-filled object standing in
        for "nothing yet".
      required:
        - rebilledRounds
        - rebilledTokens
        - rebilledUsd
        - warmingUsd
      properties:
        rebilledRounds:
          type: integer
          minimum: 0
          description: >-
            How many rounds paid to re-send an expired prefix. Zero here with a
            warming figure present is a session that has been kept warm and has
            therefore never paid for its own idleness — which is warming
            working.
        rebilledTokens:
          type: integer
          minimum: 0
          description: >-
            The prefix re-sent across those rounds: the part of each round's
            cache write that had genuinely been cached before, never the whole
            write. A resumed conversation also writes the message that ended the
            wait, and that content would have been written with the prefix alive
            too — charging it to the expiry would blame idleness for the weight
            of the message that ended it.
        rebilledUsd:
          type: number
          format: double
          minimum: 0
          description: >-
            What that expiry cost, in US dollars: for each round, the difference
            between what its prompt was billed and what the same prompt would
            have cost with the prefix still cached.

            It DECOMPOSES money already reported in `usage.costUsd` rather than
            adding to it. A re-billed round's whole price is in the session
            total like every other round's, and this says how much of that price
            the expiry was; adding the two together counts the same dollars
            twice.
        rebilledUsdComplete:
          type: boolean
          description: >-
            Absent means true. Present and false makes `rebilledUsd` a FLOOR —
            "at least this much" — because at least one re-billed round ran on a
            model this host cannot price, contributing a real expiry and no
            dollars. The same honesty `costComplete` carries on a usage report,
            for the same reason: a total that quietly skipped the rounds it
            could not price is a number a reader will believe.
        warmingUsd:
          type: number
          format: double
          minimum: 0
          description: >-
            What this session has spent keeping its cache alive, in US dollars,
            counted across its whole life and against the ceiling in
            `warming.ceilingUsd`. Reservations are included: a warm that was
            paid for and whose outcome was never established is money that may
            already be gone, and leaving it out would let it be spent twice.
      examples:
        - rebilledRounds: 4
          rebilledTokens: 18240
          rebilledUsd: 0.0312
          warmingUsd: 0.045
    SessionEnvironment:
      type: object
      description: >-
        A session's binding to an environment, as a client draws it beside the
        session's identity. The id and the state, and deliberately nothing else:
        everything a person acts on is here, which keeps a page of twenty-five
        sessions from carrying twenty-five setup transcripts.
      required:
        - id
        - state
      properties:
        id:
          $ref: '#/components/schemas/EnvironmentId'
        state:
          $ref: '#/components/schemas/EnvironmentState'
    Task:
      type: object
      description: >-
        One item of the agent's task list: what the work is, and where it
        stands.

        Two fields and no more. There is no id, because the list is replaced
        whole on every write and position is the only order there is — an
        identifier would be a second thing two surfaces could disagree about.
        There is no priority, because nothing renders or orders by one.
      required:
        - content
        - status
      properties:
        content:
          type: string
          description: What the task is, in the agent's own words.
        status:
          type: string
          enum:
            - pending
            - in_progress
            - completed
            - cancelled
          description: >-
            Where the item stands. `cancelled` counts toward the total and not
            toward what is done — an abandoned step is still a step the list
            once held. The engine does not enforce "exactly one in_progress":
            that is guidance in the tool's own description, and a list holding
            two is drawn honestly rather than refused.
          x-enum-varnames:
            - TaskStatusPending
            - TaskStatusInProgress
            - TaskStatusCompleted
            - TaskStatusCancelled
      examples:
        - content: Reproduce the flaky ledger test
          status: completed
    SessionAttention:
      type: object
      description: One prompt standing on a conversation, waiting for a person.
      required:
        - kind
        - askId
        - since
      properties:
        kind:
          type: string
          enum:
            - permission
            - question
          description: >-
            Which of the two things a person answers is holding this
            conversation: a permission request for an action the agent wants to
            take, or a question the agent put to the user.


            A refinement round is deliberately not one of the values. It blocks
            no tool call, so a conversation standing on one is not waiting for
            anybody and must not be drawn as though a turn were parked.
        askId:
          type: string
          format: uuid
          description: >-
            The prompt itself — the permission id or the question id, according
            to `kind` — so a client acts on it directly instead of paging a
            listing to find which of several it just drew.
        since:
          type: string
          format: date-time
          description: >-
            When the PROMPT was raised, which is not when the session last
            changed. "Waiting forty minutes" has to mean the person has been
            kept waiting forty minutes; read off `updatedAt` it would mean only
            that nothing has happened here for forty minutes, which is true of
            an idle conversation nobody is blocked on.


            Where several prompts stand on one conversation this is the OLDEST,
            for the same reason: the question a fleet asks is how long somebody
            has been waiting, not when the most recent thing appeared.
    Error:
      type: object
      description: >-
        The single error envelope every operation returns. Codes are NRY-
        followed by four digits and are catalogued in the top-level
        x-error-catalog extension.
      required:
        - code
        - title
        - message
      properties:
        code:
          type: string
          pattern: ^NRY-[0-9]{4}$
          description: Machine-readable error code from the NRY catalogue.
        title:
          type: string
          maxLength: 256
          description: Short human-readable summary of the error class.
        message:
          type: string
          maxLength: 4096
          description: Specific, actionable description of what went wrong.
        fields:
          type: object
          description: Per-field validation problems.
          additionalProperties:
            type: string
      examples:
        - code: NRY-0002
          title: Session not found
          message: >-
            No session with id 6b9f6d2e-1c3a-4f5b-9d7e-2a8c4e6f0b1d exists on
            this host.
    EnvironmentId:
      type: string
      minLength: 1
      maxLength: 64
      pattern: ^[A-Za-z0-9][A-Za-z0-9._-]*$
      description: >-
        An environment's identifier, chosen by whoever creates it and unique on
        this host. It travels as one path segment, so its shape is bounded to
        what a path segment carries unchanged, exactly as a monitor's name is: a
        "/" would address no route and a "?" or "#" would end the segment early
        and address something else.
    EnvironmentState:
      type: string
      enum:
        - ready
        - degraded
        - gone
        - unusable
      x-enum-varnames:
        - EnvironmentStateReady
        - EnvironmentStateDegraded
        - EnvironmentStateGone
        - EnvironmentStateUnusable
      description: >-
        Whether an environment is answering, and — when it is not — whether
        waiting is the right move. The distinction is the whole point: a session
        waiting on an unreachable environment looks identical to a slow one
        otherwise, and what the person does next depends entirely on which it
        is. ready — answering; work proceeds. degraded — not answering right now
        and expected back (restarting, a dropped connection); the host retries
        and the next call may simply succeed. gone — destroyed, or the runtime
        no longer has it; nothing brings it back and a session bound to it needs
        a new one. unusable — its setup did not succeed; `setup` says what
        happened.
  responses:
    BadRequest:
      description: Malformed request — invalid parameter, cursor, or JSON body.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >-
        The caller is authenticated and is not allowed this operation (NRY-0028
        or NRY-0030).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: The addressed resource does not exist on this host.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RequestBodyTooLarge:
      description: >-
        The request body weighs more than this operation accepts (NRY-0024). The
        host holds one ceiling per operation and refuses at the door, in front
        of every handler: a body whose declared Content-Length is past the
        ceiling is refused before a byte of it is read, and a body that declares
        no length is read only as far as the ceiling and refused there. Nothing
        was read past that point and nothing was written.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnprocessableEntity:
      description: The request was well-formed but failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InternalServerError:
      description: The host failed — including a store that refuses writes (NRY-0012).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        Enforced on every transport, with no exempt operation. A person's
        request — over the default local unix socket exactly as over a TCP
        listener — must carry a JWT issued by the configured identity provider,
        which the host verifies itself against that issuer's key set: signature,
        issuer, expiry, and the person and organisation it names. Requests
        without a valid one receive 401 NRY-0011. The socket's file permissions
        are transport and are not an authorisation.

````