# The RPC contract

<!-- Generated by protocol/generate_docs.py from protocol/tsugumi.fbs: do not edit. -->

The RPCs by which the UI, the Web and the Agent talk to `tsugumid`, generated from the doc comments of [protocol/tsugumi.fbs](https://github.com/gen740/Tsugumi/blob/main/protocol/tsugumi.fbs).

- **Format**: one message is one FlatBuffer (root `Tsugumi.RPC.Message`, file identifier `TSG1`). The WebSocket subprotocol is `tsugumi.rpc.v1`; on a Unix socket, a 4-byte length (little endian) is followed by the body.
- **Failure**: any request may be answered with `Failure`.
- **Requests that change something**: carry an `op_id`, and a resend with the same `op_id` is applied only once. If a response is lost, check with `OperationGet`.
- **The Agent's tools**: made from the same schema (`tsugumi.bfbs`) ([Features](features.md#9-agent)).

There are 50 requests.

## Connection

| Request | Response | Description |
| --- | --- | --- |
| [`SessionHello`](#sessionhello) | HelloResult | token is required on WebSocket. Project mismatch is permission_denied. Must be the first request, within 5 s of connecting. `protocol` is the newest version the client speaks and `min_protocol` the oldest (default: protocol); the server answers with the newest version both speak (HelloResult.protocol), or protocol_mismatch. |

### SessionHello

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `protocol` | `uint32` |  |  |
| `client_id` | `string` | yes |  |
| `expected_project_id` | `string` |  |  |
| `token` | `string` |  |  |
| `min_protocol` | `uint32` |  |  |
| `language` | `string` |  | The client's language ("ja" or "en"; anything else is Japanese). What tsugumid writes into the project's git for the runs this connection starts (the commit message, the run's note) is in it. |

## Projects and saving

| Request | Response | Description |
| --- | --- | --- |
| [`ProjectOpen`](#projectopen) | OpenResult | Creates (or replaces) the connection's subscription. |
| [`ProjectClose`](#projectclose) | Ack | Ends only the given subscription. Unknown ids are acknowledged. |
| [`ProjectInspect`](#projectinspect) | ProjectSnapshot | No subscription, build or reload. |
| [`CardId`](#cardid) | CardIdResult | Reserves a permanent identifier for a new Card without changing the document revision. Supply its human name; use the returned id in the canvas, definition paths and wires, and name in _meta.json.name. Each call reserves a fresh id; save the definition and canvas with project_save. |
| [`ProjectSave`](#projectsave) | SaveResult | Apply all changes in one transaction at base_rev. |
| [`ProjectReload`](#projectreload) | SaveResult | Takes external edits of the allowed area into the document. Every permission widening requires explicit approval, regardless of origin. |
| [`ProjectValidate`](#projectvalidate) | ProjectValidateResult | targets = [] means everything; mode = executable. |
| [`ProjectShell`](#projectshell) | ProjectShellResult | Runs in the project VM as the project user, with the project directory as cwd. Available only when the isolated project launcher enables it. timeout_ms defaults to 10000 and must be between 1 and 30000. Each output stream is capped at 262144 bytes. Disconnecting cancels the process group. An operation ID is never executed twice, even after an interrupted daemon. Agent use requires explicit Shell access in addition to Read, Write and Run. With origin "agent", project files are read-only, using bubblewrap on Linux and Seatbelt on macOS; only diagnostic temporary files are writable. Isolation unavailable means the request is refused. Shell commands are for diagnosis and reads. Analysis runs in Cards; Agent edits go through ProjectSave. A person's UI terminal is a separate writable path. |
| [`ProjectCacheClear`](#projectcacheclear) | ProjectCacheClearResult | Only while no run or SDK job is active. |
| [`ProjectCacheUsage`](#projectcacheusage) |  | Cached shared-store measurement; never triggers a store traversal on the RPC lane. |
| [`RunScratchDelete`](#runscratchdelete) | RunScratchDeleteResult | Deletes only the listed scratch folders from the usage report. Refuses active runs, pending remote results and the latest run. |
| [`OperationGet`](#operationget) | OperationResult |  |

### ProjectOpen

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `view_id` | `string` |  |  |

### ProjectClose

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `subscription_id` | `string` | yes |  |

### ProjectInspect

No fields.

### CardId

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | yes |  |
| `project_path` | `string` |  | Project-relative directory: empty for the root, or externals/<id>/, repeated for nesting. May name a project being created in the same draft. |

### ProjectSave

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `base_rev` | `uint64` |  |  |
| `op_id` | `string` | yes |  |
| `origin` | `string` | yes |  |
| `changes` | `[DocumentChange]` | yes |  |
| `approved_permissions` | `[string]` |  | The Card permissions this save widens that the user approved, each as an `approval_required` failure names it. Set by the client after asking the user, never by a model; a save from a model (origin "agent") is rejected unless every widening is listed. |

### ProjectReload

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `base_rev` | `uint64` |  |  |
| `op_id` | `string` | yes |  |
| `origin` | `string` | yes |  |
| `approved_permissions` | `[string]` |  | The widenings named by approval_required that the user approved. Set by the client after asking the user, never by a model. |

### ProjectValidate

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `at_rev` | `uint64` |  | Omitted: the latest revision. |
| `targets` | `[CardPath]` |  |  |
| `mode` | `ValidateMode` |  |  |
| `changes` | `[DocumentChange]` |  | Proposed changes (as in project_save): the document as it would be after them is checked and nothing is saved. For the apps' editors; the model is not offered this field (it saves, then checks). |

### ProjectShell

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |
| `command` | `string` | yes |  |
| `timeout_ms` | `uint32` |  |  |
| `origin` | `string` | yes |  |

### ProjectCacheClear

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |

### ProjectCacheUsage

No fields.

### RunScratchDelete

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |
| `paths` | `[string]` | yes |  |

### OperationGet

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |

## Files

| Request | Response | Description |
| --- | --- | --- |
| [`ProjectFileList`](#projectfilelist) | SourcePage | depth = 0 (0..8), include_directories = false, limit = 100. |
| [`ProjectFileStat`](#projectfilestat) | SourceInfo |  |
| [`ProjectFileRead`](#projectfileread) | FileContent | offset = 0, limit_bytes = 262144 (1..1 MiB). |
| [`ProjectFileFormat`](#projectfileformat) | ProjectFileFormatResult | Never changes the document. |
| [`AttachedFolderRead`](#attachedfolderread) | FileContent | Reads a folder of the Mac attached to a chat (read-only, shared with this project only): a file's bytes, or for a folder its entries as text, one per line (folders end with "/"). `folder` is the attached folder's path on the Mac; `path` is relative to it ("" = itself). |

### ProjectFileList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | `string` | yes | Relative to the project; "" or "." is the project folder. |
| `at_rev` | `uint64` |  | Omitted: the latest revision. |
| `depth` | `uint32` |  |  |
| `include_directories` | `bool` |  |  |
| `cursor` | `string` |  |  |
| `limit` | `uint32` |  |  |

### ProjectFileStat

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | `string` | yes |  |
| `at_rev` | `uint64` |  |  |

### ProjectFileRead

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | `string` | yes |  |
| `at_rev` | `uint64` |  | Omitted: the latest revision. |
| `if_hash` | `string` |  |  |
| `offset` | `uint64` |  |  |
| `limit_bytes` | `uint32` |  |  |

### ProjectFileFormat

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | `string` | yes |  |
| `text` | `string` | yes |  |
| `language` | `FormatLanguage` |  |  |

### AttachedFolderRead

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `folder` | `string` | yes |  |
| `path` | `string` | yes |  |
| `offset` | `uint64` |  |  |
| `limit_bytes` | `uint32` |  |  |

## Card SDKs and types

| Request | Response | Description |
| --- | --- | --- |
| [`CardSDKPrepare`](#cardsdkprepare) | CardSDKPrepareResult | Accepted asynchronously. |
| [`CardSDKStatus`](#cardsdkstatus) | CardSDKPrepareResult or Failure |  |
| [`OutputTypes`](#outputtypes) | OutputTypesResult | Compiles an _outputs.fbs text (nothing is saved): its reflection schema (bfbs), from which a client lists the types. With port and type_name, also the text with that port's type and kind set (File / Directory: the tsugumi attribute). |
| [`NixSourceResolve`](#nixsourceresolve) | NixSourceResolved | Looks a flake up for nixSources (nothing is saved): its current commit and hash, and the packages it has for this system. To customize an approved package, save nixSources[].overrides = {"packageName":{"override":{"argument":"value"},"overrideAttrs":{"pname":"custom"}}}. Both maps contain JSON values, not Nix functions. A value {"fetchurl":{"url":"https://...","sha256":"..."}} fetches a hash-pinned source or patch in the external evaluator. Modifying these overrides needs user approval. Cards consume inputs.sources.<name>.packages.<packageName>, with the system already selected. Unsupported override/overrideAttrs functions return an actionable evaluation error. Slow the first time: it evaluates them, fetching what they need. |

### CardSDKPrepare

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `card` | `CardPath` | yes |  |
| `at_rev` | `uint64` |  |  |
| `op_id` | `string` | yes |  |

### CardSDKStatus

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |

### OutputTypes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `text` | `string` | yes |  |
| `port` | `string` |  |  |
| `type_name` | `string` |  |  |
| `kind` | `PortKind` |  |  |

### NixSourceResolve

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `ref` | `string` | yes |  |

## Running

| Request | Response | Description |
| --- | --- | --- |
| [`RunPlan`](#runplan) | RunPlanResult | propagate = true. |
| [`Run`](#run) | RunAccepted | propagate = true, max_concurrency = 4 (1..64), nix_jobs = 1, nix_cores = 1. |
| [`RunCancel`](#runcancel) | RunStatus | Idempotent. Unknown id is not_found. |
| [`RunStatus`](#runstatus) | RunStatus | Omitted run_id: the active run, else the latest locally started run, else idle. partial_run marks debugging with saved inputs; uses_partial_result marks values derived from a partial run, within this run or through saved inputs. Both flags persist in RunSummary.status in history/comparison replies and run events, including runs with recorded values and retained artifacts. |

### RunPlan

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `at_rev` | `uint64` |  |  |
| `card` | `CardPath` | yes |  |
| `propagate` | `bool` |  |  |

### Run

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `base_rev` | `uint64` |  |  |
| `op_id` | `string` | yes |  |
| `origin` | `string` | yes |  |
| `card` | `CardPath` | yes |  |
| `propagate` | `bool` |  |  |
| `max_concurrency` | `uint32` |  |  |
| `nix_jobs` | `uint32` |  |  |
| `nix_cores` | `uint32` |  |  |

### RunCancel

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |

### RunStatus

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` |  |  |

## History and artifacts

| Request | Response | Description |
| --- | --- | --- |
| [`RunHistory`](#runhistory) | RunPage, newest accepted first |  |
| [`RunInvocations`](#runinvocations) | InvocationPage, ascending invocation number |  |
| [`RunLatestInvocations`](#runlatestinvocations) | InvocationPage | Each Card's latest invocation of a run, for a client that opens the project after the run: it did not get RunCardChanged for them. |
| [`RunRecord`](#runrecord) | RecordResult | invocation and follow_latest = true are exclusive. |
| [`RunValueRead`](#runvalueread) | FileContent | offset = 0, limit_bytes = 1048576. |
| [`RunHistoryDelete`](#runhistorydelete) | RunHistoryDeleteResult | Deletes finished runs (all, or those that ended before ended_before, Unix ms) with their records, values, logs and files, here and in the project's copy. Only while no run is active. Approvals must set restrict_to_run_ids and pass the exact run_ids shown to the person (an empty set deletes nothing). Without that flag, run_ids must be empty and the UI's all-runs or date scope applies. The separate flag distinguishes an empty selection from object APIs' default empty vectors. |
| [`ArtifactList`](#artifactlist) | ArtifactPage | invocation requires card. |
| [`ArtifactRead`](#artifactread) | FileContent | member only for Directory artifacts. |
| [`LogRange`](#logrange) | LogSlice | limit_bytes = 65536 (4..256 KiB). tail with offset != 0 is rejected. invocation requires card. |
| [`RunEnvironments`](#runenvironments) | BuildEnvironmentPage | Recorded environments, in first-use order; no Nix evaluation. offset defaults to 0; limit defaults to 100 (maximum 500). |
| [`RunEnvironmentPaths`](#runenvironmentpaths) | EnvironmentPathPage | Runtime closure, including the manifest, in store-path order. These are runtime references, not the full set of build-time dependencies. |

### RunHistory

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `card` | `CardPath` |  |  |
| `artifacts_only` | `bool` |  |  |
| `cursor` | `string` |  |  |
| `limit` | `uint32` |  |  |

### RunInvocations

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |
| `card` | `CardPath` | yes |  |
| `cursor` | `string` |  |  |
| `limit` | `uint32` |  |  |

### RunLatestInvocations

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |

### RunRecord

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |
| `card` | `CardPath` | yes |  |
| `invocation` | `uint64` |  |  |
| `follow_latest` | `bool` |  |  |

### RunValueRead

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |
| `card` | `CardPath` | yes |  |
| `invocation` | `uint64` |  |  |
| `side` | `ValueSide` |  |  |
| `port` | `string` | yes |  |
| `offset` | `uint64` |  |  |
| `limit_bytes` | `uint32` |  |  |

### RunHistoryDelete

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |
| `ended_before` | `int64` |  |  |
| `run_ids` | `[string]` |  |  |
| `restrict_to_run_ids` | `bool` |  |  |

### ArtifactList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |
| `card` | `CardPath` |  |  |
| `invocation` | `uint64` |  |  |
| `cursor` | `string` |  |  |
| `limit` | `uint32` |  |  |

### ArtifactRead

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `artifact_id` | `string` | yes |  |
| `member` | `string` |  |  |
| `offset` | `uint64` |  |  |
| `limit_bytes` | `uint32` |  |  |

### LogRange

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |
| `card` | `CardPath` |  |  |
| `invocation` | `uint64` |  |  |
| `stream` | `LogStream` |  |  |
| `offset` | `uint64` |  |  |
| `tail` | `bool` |  |  |
| `limit_bytes` | `uint32` |  |  |

### RunEnvironments

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |
| `offset` | `uint64` |  |  |
| `limit` | `uint32` |  |  |

### RunEnvironmentPaths

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `run_id` | `string` | yes |  |
| `environment_id` | `string` | yes |  |
| `offset` | `uint64` |  |  |
| `limit` | `uint32` |  |  |
| `query` | `string` |  | Case-insensitive literal substring search. Offset and total refer to matching paths. |

## Views

| Request | Response | Description |
| --- | --- | --- |
| [`ViewSetVisible`](#viewsetvisible) | Ack | Replaces the subscription's Display targets. enabled = true. |
| [`ViewSave`](#viewsave) | ViewSaveResult | CAS on view_rev only. |
| [`ModuleRead`](#moduleread) | FileContent | Reads immutable module source in bounded pages. For a Wasm view, the core assembles the shared index.js template and local view.wasm. Only canonical /nix/store/<hash>-<name> directories with index.js qualify. |
| [`LayoutArrange`](#layoutarrange) | LayoutProposal | Places the scope's Cards, output frames and notes, then gives ports the order that crosses fewer wires and places the Cards once more for it. The bars are taken to follow the Cards again. Nothing is saved. |
| [`LayoutUntangle`](#layoutuntangle) | LayoutProposal (port_orders only) | Reorders ports where the wires then cross less; no Card moves. With `around` (the Cards a drag just moved) only they and the Cards wired to them may change. Nothing is saved. |

### ViewSetVisible

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `subscription_id` | `string` | yes |  |
| `cards` | `[CardPath]` | yes |  |
| `enabled` | `bool` |  |  |

### ViewSave

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `view_id` | `string` | yes |  |
| `base_view_rev` | `uint64` |  |  |
| `view` | `ViewState` | yes |  |

### ModuleRead

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `module` | `string` | yes |  |
| `offset` | `uint64` |  |  |
| `limit_bytes` | `uint32` |  |  |

### LayoutArrange

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `scene` | `LayoutScene` | yes |  |

### LayoutUntangle

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `scene` | `LayoutScene` | yes |  |
| `around` | `[string]` |  |  |

## Versions

| Request | Response | Description |
| --- | --- | --- |
| [`VersionInit`](#versioninit) | VersionResult | Makes the project a git repository: a first commit of what is saved (a demo's history file becomes its versions). Nothing happens when it already is one. |
| [`VersionMark`](#versionmark) | VersionResult | Names the current version: what is saved is committed (when it changed) and gets an annotated tag `name` with `message`. |
| [`VersionCheckout`](#versioncheckout) | SaveResult | Starts from a version: what is saved now is committed, then the project's files become those of `commit` and the document is read again (like ProjectReload). Only while no run is active. |
| [`VersionGraph`](#versiongraph) | VersionGraph, newest first |  |
| [`VersionCanvas`](#versioncanvas) | VersionCanvas: the Cards of a version and how they connect (a minimap) |  |
| [`VersionDiff`](#versiondiff) | VersionDiff: what a version changed from another version (its first parent by default), in the text a summary is written from | Only code and settings: never data files. |
| [`VersionNote`](#versionnote) | VersionNote | Core-generated mechanical record and the saved Agent note. The host Agent service chooses which to return to the UI. Mechanical records are kept in refs/notes/tsugumi-record-{ja,en}. |
| [`VersionNoteStore`](#versionnotestore) | VersionResult | The host Agent service stores the generated note in refs/notes/tsugumi-summary. Clients request notes from agentd. |

### VersionInit

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |
| `origin` | `string` | yes |  |

### VersionMark

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |
| `origin` | `string` | yes |  |
| `name` | `string` | yes |  |
| `message` | `string` |  |  |

### VersionCheckout

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `base_rev` | `uint64` |  |  |
| `op_id` | `string` | yes |  |
| `origin` | `string` | yes |  |
| `commit` | `string` | yes |  |

### VersionGraph

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | `uint32` |  |  |

### VersionCanvas

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `commit` | `string` | yes |  |

### VersionDiff

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `commit` | `string` | yes |  |
| `from_commit` | `string` |  | Compare from this version; absent means the first parent of commit. |

### VersionNote

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `commit` | `string` | yes |  |
| `language` | `string` | yes |  |

### VersionNoteStore

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `op_id` | `string` | yes |  |
| `origin` | `string` | yes |  |
| `commit` | `string` | yes |  |
| `summary` | `string` | yes |  |

## Notifications

Sent from `tsugumid` to connected clients.

- `DocPatched`
- `RunEvent`
- `DisplayUpdated`
- `LogAvailable`

### Run events

The `value` inside a `RunEvent` has one of these types.

- `RunAcceptedEvent`
- `RunStatusChanged`
- `RunCardChanged`
- `RunTimeline`
- `RunFinished`
- `RunBatchChanged`
