> ## 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.

# Read what a session changed on disk

> The files this session's write, edit and delete tools changed, each with what it did to the file, how many lines moved, and the content the file held before the session first touched it. It is what a diff view of a session's own work stands on, and what a touched-files list shows counts from. Outside a git repository narya keeps no file snapshots, so the session's answer there is empty and a step's rows say nothing was kept.
Every row is measured from FIRST touch. A file the session edited five times is one row comparing what it held before edit one against what is on disk now — not five rows, and not the last edit's delta. That is also why a file put back the way it was is absent from the answer: it changed nothing, and reporting it at zero lines either way would claim otherwise.
The current content is deliberately not served. The host and its clients share a filesystem, so a client reads the file itself and gets the version it is about to let somebody edit, rather than a copy that was already stale when it was sent. What only the host has is the before side.
Both sides are bounded at 1 MiB (1048576 bytes). Past that nothing was kept, and the row says so through beforeState rather than serving an empty file or a truncated one: beforeBytes still carries the real size, so a client can say how large the file it cannot diff is. linesAdded and linesRemoved are then absent, because a count nobody could compute is not zero.
The answer is read on the session's own machine and never wakes it: a session whose container is not running, whose environment this host does not hold, or one bound to none on a hosted home, is refused 422.



## OpenAPI

````yaml /pt/openapi/v3-current/narya.yaml get /v1/sessions/{sessionId}/changes
openapi: 3.1.0
info:
  title: Narya Host API
  version: 1.0.0
  description: >-
    The contract between the Narya host and every client. On your own machine,
    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. On your own machine, the host also accepts a
    request with no token from your own operating system user. Once signed-in
    work has claimed the home for an organization, it does so only while this
    machine is signed in to that organization. Such a request gets 401 with
    NRY-0011 on work that reaches your organization beyond the machine. An
    operation that declares another security scheme also accepts that
    credential. Any other 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}/changes:
    parameters:
      - $ref: '#/components/parameters/SessionIdParam'
    get:
      tags:
        - sessions
      summary: Read what a session changed on disk
      description: >-
        The files this session's write, edit and delete tools changed, each with
        what it did to the file, how many lines moved, and the content the file
        held before the session first touched it. It is what a diff view of a
        session's own work stands on, and what a touched-files list shows counts
        from. Outside a git repository narya keeps no file snapshots, so the
        session's answer there is empty and a step's rows say nothing was kept.

        Every row is measured from FIRST touch. A file the session edited five
        times is one row comparing what it held before edit one against what is
        on disk now — not five rows, and not the last edit's delta. That is also
        why a file put back the way it was is absent from the answer: it changed
        nothing, and reporting it at zero lines either way would claim
        otherwise.

        The current content is deliberately not served. The host and its clients
        share a filesystem, so a client reads the file itself and gets the
        version it is about to let somebody edit, rather than a copy that was
        already stale when it was sent. What only the host has is the before
        side.

        Both sides are bounded at 1 MiB (1048576 bytes). Past that nothing was
        kept, and the row says so through beforeState rather than serving an
        empty file or a truncated one: beforeBytes still carries the real size,
        so a client can say how large the file it cannot diff is. linesAdded and
        linesRemoved are then absent, because a count nobody could compute is
        not zero.

        The answer is read on the session's own machine and never wakes it: a
        session whose container is not running, whose environment this host does
        not hold, or one bound to none on a hosted home, is refused 422.
      operationId: listSessionChanges
      parameters:
        - name: path
          in: query
          required: false
          description: >-
            Filter to one file, by the absolute path this operation reports for
            it, and serve that file's before content. Without it the answer
            covers every file the session changed and carries no content at all
            — a change list stays cheap, and the one file a diff view is opening
            is the one it asks for. A path this session never changed is an
            empty answer, not an error.
          schema:
            type: string
            maxLength: 4096
        - name: entryId
          in: query
          required: false
          description: >-
            Answer for ONE step of the conversation instead of for the session:
            what the tool call recorded on this entry did to each file it
            mutated, measured from the restore point taken before that entry to
            the one taken at it. Both sides are then served (beforeContent and
            afterContent), because the after side is a moment that has since
            been edited over and nothing on disk remembers it. It exists because
            the session-scoped answer above is the wrong one under an old
            receipt: a file edited five times has one row carrying all five
            edits, so drawing it under the first receipt credits that call with
            every line that came after it. An entry whose step mutated nothing
            is an empty answer, not an error, and a step the host never
            snapshotted comes back with beforeState "too-large" — nothing was
            kept — rather than as an empty diff. path and entryId are not
            combined; entryId wins when both are given.
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: The files this session changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileChangeSet'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '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:
    FileChangeSet:
      type: object
      description: >-
        Every file a session changed, in the order the session first met them.
        Not paginated: the set is bounded by what one conversation's tools
        actually mutated, and a client that wants one file asks for it by path.
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/FileChange'
      examples:
        - items:
            - path: /home/dev/ledger/validate.go
              op: modified
              beforeState: text
              beforeBytes: 4211
              linesAdded: 12
              linesRemoved: 3
    FileChange:
      type: object
      description: >-
        One file a session changed, measured from the content it held when the
        session first touched it to what is on disk now.
      required:
        - path
        - op
        - beforeState
        - beforeBytes
      properties:
        path:
          type: string
          description: The file's absolute path.
        op:
          type: string
          enum:
            - added
            - modified
            - deleted
            - renamed
          x-enum-varnames:
            - FileChangeOpAdded
            - FileChangeOpModified
            - FileChangeOpDeleted
            - FileChangeOpRenamed
          description: >-
            What the session did to the file, at file granularity: it was not
            there and now is, it was there and changed, it was there and is
            gone, or it is the same bytes under another name. This is the
            file-level fact a change list shows, not the per-call
            read/write/edit a tool receipt carries.

            renamed is reported only where it can be known for certain and costs
            nothing to know: inside one change set, a file that went and a file
            that arrived whose content is byte-identical are one row naming both
            paths, and previousPath carries the name it had. There is no rename
            DETECTION behind this — no git, no similarity score — so a file that
            was moved AND edited is still reported as an added row beside a
            deleted one, which is what a reader without this value gets for
            every move. A client that does not know the value degrades to
            whatever it draws for an op it cannot name.
        beforeState:
          type: string
          enum:
            - absent
            - text
            - not-text
            - too-large
          x-enum-varnames:
            - FileChangeBeforeStateAbsent
            - FileChangeBeforeStateText
            - FileChangeBeforeStateNotText
            - FileChangeBeforeStateTooLarge
          description: >-
            What is known about the content the file held before the session
            changed it, so an unavailable diff is never mistaken for an empty
            one. absent — the file did not exist, and the whole of it is added.
            text — beforeContent carries it when this read asked about a single
            path. not-text — the bytes were kept but are not valid UTF-8, so
            they are never served as a string and no line count over them would
            mean anything. too-large — the file was past the 1 MiB bound and
            nothing was kept; beforeBytes says how large it was.
        beforeBytes:
          type: integer
          format: int64
          minimum: 0
          description: >-
            The file's size when the session first touched it, 0 when it did not
            exist. True even when the content itself was too large to keep.
        beforeContent:
          type: string
          description: >-
            The content the file held before the session changed it. Present
            only when the request named a single path or one entry AND
            beforeState is text or absent — a file the step created has no
            before side, so absent pairs with the empty string here, which is
            the side a created file is diffed against. A client diffs it against
            afterContent when present, and otherwise against the file it reads
            from disk itself, which it may edit.
        afterContent:
          type: string
          description: >-
            The other side. Present on an entryId read, because one step's
            change ends at a moment that has since been edited over, so the host
            serves both ends or the diff cannot be drawn at all. Present on a
            path read of a session whose code is on another machine (a
            container) while that file is text, because the client shares no
            disk with it, and shows that diff read-only. Absent on a path read
            of a session on this host's own machine, whose file the client reads
            from disk itself.
        linesAdded:
          type: integer
          minimum: 0
          description: >-
            Lines this session added, counted from the before content against
            the file as it is now. Absent when no diff could be computed — see
            beforeState — because a count nobody could compute is not zero.
        linesRemoved:
          type: integer
          minimum: 0
          description: Lines this session removed, on the same terms as linesAdded.
        previousPath:
          type: string
          description: >-
            The absolute path the file had before the session moved it, on a
            renamed row and nowhere else. path is where the file is now, so a
            reader states the move from the two together. The row's before side
            — beforeState, beforeBytes, beforeContent — belongs to this path,
            since that is the file the content was measured from.
      examples:
        - path: /home/dev/ledger/validate.go
          op: modified
          beforeState: text
          beforeBytes: 4211
          beforeContent: |
            package ledger
          linesAdded: 12
          linesRemoved: 3
    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.
  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'
    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 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 — except a request with no
        token on a laptop's socket from the OS user the kernel names as the
        host's own, which is that machine's user.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.