Transcript streaming¶
octomate_protocol.stream ¶
The transcript stream's wire protocol — what a client-side tail exchanges with the server's stream endpoint when a native session's transcript lives on another machine.
The payload is raw transcript lines plus the byte offsets they occupy in the client's own file. The client never parses what it ships — every translation stays server-side, so a transcript format change is a server-only fix — which also keeps this protocol agent-neutral: the same frames carry a Claude session file or a Codex rollout, and which tree a stream feeds is the mounting endpoint's business.
The server is the only cursor authority. A connect opens with hello/welcome: the
client claims a session, the server answers where each of its files resumes (recomputed
from the committed runs), and the client holds no durable state — a crash anywhere
costs a re-stream, never a loss. Lines then flow contiguously per file; finalize
(server → client, relaying SessionEnd from the hook pipe) and eof (client → server,
"drained, commit the trailing turns") close a session out.
StreamClientMessage
module-attribute
¶
StreamClientMessage = Annotated[
StreamHello
| StreamLine
| StreamSnapshotStart
| StreamEof,
Field(discriminator="type"),
]
StreamServerMessage
module-attribute
¶
StreamServerMessage = Annotated[
StreamWelcome | StreamFinalize | StreamSnapshotStored,
Field(discriminator="type"),
]
client_message_adapter
module-attribute
¶
server_message_adapter
module-attribute
¶
StreamHello
pydantic-model
¶
Bases: BaseModel
The client's opening claim: which session it tails, where the transcript lives
on the client's machine (a label in the client's namespace — the server never
opens it), and the directory the session runs in, which files the session's thread
under its project exactly as the http hooks' cwd does.
Fields:
-
type(Literal['hello']) -
protocol(int) -
session_id(str) -
transcript_path(str) -
cwd(str) -
client_version(str)
Source code in protocol/octomate_protocol/stream.py
StreamLine
pydantic-model
¶
Bases: BaseModel
One complete transcript line and the byte range it occupies in its file.
start/end are the client file's own offsets, end past the newline the line
was framed on — the same range the assembled run's start_offset/end_offset
records. Lines arrive contiguously per file, blank ones included (skipping one
would read as a gap); the server closes on a gap and the reconnect re-asks where
to resume.
Fields:
Source code in protocol/octomate_protocol/stream.py
StreamEof
pydantic-model
¶
Bases: BaseModel
The client drained its files to EOF and is done — answering a finalize, or on
its own idle give-up. The server commits the trailing turns on this; a connection
that drops without it leaves them uncommitted for the next connect to re-stream.
Fields:
-
type(Literal['eof'])
Source code in protocol/octomate_protocol/stream.py
StreamSnapshotCursor
pydantic-model
¶
Bases: BaseModel
The durable transcript prefix the client must verify before appending.
Fields:
Source code in protocol/octomate_protocol/stream.py
StreamWelcome
pydantic-model
¶
Bases: BaseModel
The server's answer to hello: where each file resumes, keyed by SESSION_FILE
or agent id. A file absent from the map starts at 0.
Fields:
-
type(Literal['welcome']) -
offsets(dict[str, int]) -
transcript(StreamSnapshotCursor | None)
Source code in protocol/octomate_protocol/stream.py
StreamSnapshotStart
pydantic-model
¶
Bases: BaseModel
Client announces the next transcript range before sending its bytes.
Fields:
-
type(Literal['snapshot_start']) -
transfer_id(UUID) -
start(int) -
end(int)
Source code in protocol/octomate_protocol/stream.py
StreamSnapshotStored
pydantic-model
¶
Bases: BaseModel
Server acknowledgment after the uploaded snapshot is durably stored.
Fields:
-
type(Literal['snapshot_stored']) -
transfer_id(UUID)
Source code in protocol/octomate_protocol/stream.py
StreamFinalize
pydantic-model
¶
Bases: BaseModel
Server → client: the session ended (SessionEnd arrived on the hook pipe) —
drain the remaining bytes, answer with eof, and exit.
A snapshot starts with snapshot_start, followed by binary messages totaling
end - start bytes. The client waits for snapshot_stored before sending eof.
Each batch's transcript lines precede its binary messages; they never interleave.
Batches may upload throughout a turn, before finalization.
Fields:
-
type(Literal['finalize'])