Documentation
¶
Overview ¶
Package domain contains shared data types that are transport-agnostic. Only snapshot types are kept here; command types have moved to internal/msg.
Index ¶
- Constants
- func CountGroupsInTab(t *TabSnapshot) int
- func CountPanesInGroup(g *PaneGroupSnapshot) int
- func CountPanesInLane(l *LaneSnapshot) int
- func CountPanesInTab(t *TabSnapshot) int
- func CountPanesInWorkspace(snap *WorkspaceSnapshot) int
- func GroupContainsPane(g *PaneGroupSnapshot, paneID string) bool
- func GroupsInTab(t *TabSnapshot) iter.Seq2[*LaneSnapshot, *PaneGroupSnapshot]
- func IsShelllessPaneType(paneType string) bool
- func LaneContainsPane(l *LaneSnapshot, paneID string) bool
- func LocatePaneInWorkspace(snap *WorkspaceSnapshot, paneID string) (tab *TabSnapshot, site PaneSite, ok bool)
- func PaneCanHostAnAgent(paneType string) bool
- func PaneIDsInGroup(g *PaneGroupSnapshot) []string
- func PaneIDsInGroupWhere(g *PaneGroupSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
- func PaneIDsInLane(l *LaneSnapshot) []string
- func PaneIDsInLaneWhere(l *LaneSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
- func PaneIDsInTab(t *TabSnapshot) []string
- func PaneIDsInTabWhere(t *TabSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
- func PaneIDsInWorkspace(snap *WorkspaceSnapshot) []string
- func PaneIDsInWorkspaceWhere(snap *WorkspaceSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
- func PaneSitesInTab(t *TabSnapshot) iter.Seq[PaneSite]
- func PanesInGroup(g *PaneGroupSnapshot) iter.Seq[*PaneSnapshot]
- func PanesInLane(l *LaneSnapshot) iter.Seq[*PaneSnapshot]
- func PanesInTab(t *TabSnapshot) iter.Seq[*PaneSnapshot]
- func PanesInWorkspace(snap *WorkspaceSnapshot) iter.Seq[*PaneSnapshot]
- func TabContainsPane(t *TabSnapshot, paneID string) bool
- func WebSyncPaused(meta map[string]string) bool
- type ApprovalPaneSnapshot
- type ConversationMessageSnapshot
- type FrontendCaps
- type LaneRenderInfo
- type LaneSnapshot
- type PaneGroupSnapshot
- type PaneSite
- type PaneSnapshot
- type PaneSnapshotResponse
- type ShareRestrictions
- type TabSnapshot
- type WorkspaceSnapshot
Constants ¶
const ( FrontendCLI = "cli" FrontendApp = "app" )
Front-end names. These are the same values carried by a session record's Source field, which records which front-end CREATED a session — provenance used to describe degradations, never to deny access.
const ( // PaneTypeNormal is the ordinary shell pane. It is represented by the EMPTY // string on the wire — the literal "normal" is never assigned anywhere, so // do not start writing it. PaneTypeNormal = "" // PaneTypeApproval is the ephemeral approval pane (actors/approval_pane.go). // // Careful: an approval pane OVERLOADS PaneSnapshot.GivenName to carry // "requestID\x1FresponseSubject" for the TUI. Anything that renders a // pane's name to a human must skip these panes or it will one day print a // NATS subject as somebody's name. PaneTypeApproval = "approval" // PaneTypeReplay is the recorded-playback pane (design 006 v2, // actors/workspace_replay.go). PaneTypeReplay = "replay" // PaneTypeAgentsBoard is the threaded board showing what every agent in the // session is doing (design 025). A founder ruling made this a pane type // rather than an input mode. PaneTypeAgentsBoard = "agents-board" // PaneTypeMeeting is a meeting the human joins in the desktop app (design // 043, ##meeting join): its meta carries the meeting.* keys and the app // renders the meeting; the TUI shows a placeholder. PaneTypeMeeting = "meeting" )
Pane types — what a pane is INSTEAD of a shell.
This is a different axis from a pane's input mode (EnabledModes: shell/prompt/rysh/chat/external/email/web), which decides how keystrokes are read on a pane that does have a shell. Conflating the two is easy and was a documented wrong turn in design 025 §2.1.
const MinIDPrefix = 4
MinIDPrefix is the shortest id prefix a selector may abbreviate to. Four, following git's short-sha floor, so that a one- or two-character selector cannot resolve a lane or stack by accident.
const PaneMetaWebSync = "web.sync"
PaneMetaWebSync is the pane meta key that switches a pane's live repaint stream to web clients on or off (`##pane sync on|off`).
A fleet of twenty-plus claude/codex panes each repaints several times a second, and a browser showing all of them stalls under the combined pane_vt/pane_output stream. When the fleet is driven through its orchestrator and its agents board, nobody is watching the individual agent panes, so they can be told to stop streaming. The pane keeps running and keeps its layout slot; the web client just holds whatever it last received until sync is turned back on, at which point the server sends the pane's current content.
It lives in pane meta rather than a dedicated field so it rides the same durable KV sidecar (restored on a daemon restart) and can be stamped at birth by fleet tooling, exactly like board.id and fleet.role.
const WebSyncOff = "off"
WebSyncOff is the only value that pauses the stream. Anything else — the key absent, "on", a typo — streams, so a pane can never go dark by accident.
Variables ¶
This section is empty.
Functions ¶
func CountGroupsInTab ¶ added in v0.2.3
func CountGroupsInTab(t *TabSnapshot) int
CountGroupsInTab counts every pane group across the tab's lanes.
func CountPanesInGroup ¶ added in v0.2.3
func CountPanesInGroup(g *PaneGroupSnapshot) int
CountPanesInGroup counts every pane in the group.
func CountPanesInLane ¶ added in v0.2.3
func CountPanesInLane(l *LaneSnapshot) int
CountPanesInLane counts every pane across the lane's groups.
func CountPanesInTab ¶ added in v0.2.3
func CountPanesInTab(t *TabSnapshot) int
CountPanesInTab counts every pane across the tab's lanes.
func CountPanesInWorkspace ¶ added in v0.2.3
func CountPanesInWorkspace(snap *WorkspaceSnapshot) int
CountPanesInWorkspace counts every pane in every tab.
func GroupContainsPane ¶ added in v0.2.3
func GroupContainsPane(g *PaneGroupSnapshot, paneID string) bool
GroupContainsPane reports whether the group holds a pane with this id.
func GroupsInTab ¶ added in v0.2.3
func GroupsInTab(t *TabSnapshot) iter.Seq2[*LaneSnapshot, *PaneGroupSnapshot]
GroupsInTab yields every pane group in the tab together with its lane.
func IsShelllessPaneType ¶ added in v0.2.4
IsShelllessPaneType reports whether a pane of this type must never start a shell/PTY.
It exists so the shell-start guard is a set membership test in one place rather than a literal comparison scattered across the actor tree. The guard used to read `if p.paneType != "replay"`, which silently grew a PTY under any new read-only pane type added later — exactly what agents-board would have hit.
func LaneContainsPane ¶ added in v0.2.3
func LaneContainsPane(l *LaneSnapshot, paneID string) bool
LaneContainsPane reports whether any group in the lane holds this pane.
func LocatePaneInWorkspace ¶ added in v0.2.3
func LocatePaneInWorkspace(snap *WorkspaceSnapshot, paneID string) (tab *TabSnapshot, site PaneSite, ok bool)
LocatePaneInWorkspace is LocatePaneInTab across every tab; it additionally returns the containing tab.
func PaneCanHostAnAgent ¶ added in v0.2.4
PaneCanHostAnAgent reports whether a pane is the kind of thing that could ever speak on the agents board, and therefore belongs in its roster.
This is NOT a gate-3 restriction. Gate 3 says every claude may post, not only fleet members; nothing here looks at fleet meta, so a non-fleet pane counts exactly like a fleet one. What is excluded is pane TYPES that cannot host a claude at all: an approval dialog, a replay playback, and the board itself. A shell-less pane can never post, so listing it would put a name in the roster that never speaks — and a board counting itself among its own agents is the kind of small lie that makes a roster untrustworthy.
It lives here, in domain, because BOTH the actor-side producer and the TUI-side roster seed must apply the same rule. Two copies of a rule is how F-18 happened.
func PaneIDsInGroup ¶ added in v0.2.3
func PaneIDsInGroup(g *PaneGroupSnapshot) []string
PaneIDsInGroup returns the id of every pane in the group, in order.
func PaneIDsInGroupWhere ¶ added in v0.2.3
func PaneIDsInGroupWhere(g *PaneGroupSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
PaneIDsInGroupWhere returns the ids of panes the keep predicate accepts, and the number it rejected. A nil predicate accepts everything.
The predicate is how callers express an exclusion policy — "not shared upstream", "not in a pipeline tab" — without that policy leaking into the domain package, and the rejected count is what lets them report it ("skipped 2 shared panes") rather than silently dropping panes.
func PaneIDsInLane ¶ added in v0.2.3
func PaneIDsInLane(l *LaneSnapshot) []string
PaneIDsInLane returns the id of every pane in the lane, in order.
func PaneIDsInLaneWhere ¶ added in v0.2.3
func PaneIDsInLaneWhere(l *LaneSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
PaneIDsInLaneWhere is PaneIDsInGroupWhere across the lane's groups.
func PaneIDsInTab ¶ added in v0.2.3
func PaneIDsInTab(t *TabSnapshot) []string
PaneIDsInTab returns the id of every pane in the tab, in order.
func PaneIDsInTabWhere ¶ added in v0.2.3
func PaneIDsInTabWhere(t *TabSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
PaneIDsInTabWhere is PaneIDsInGroupWhere across the tab's lanes.
func PaneIDsInWorkspace ¶ added in v0.2.3
func PaneIDsInWorkspace(snap *WorkspaceSnapshot) []string
PaneIDsInWorkspace returns the id of every pane in every tab, in order.
func PaneIDsInWorkspaceWhere ¶ added in v0.2.3
func PaneIDsInWorkspaceWhere(snap *WorkspaceSnapshot, keep func(*PaneSnapshot) bool) (ids []string, skipped int)
PaneIDsInWorkspaceWhere is PaneIDsInGroupWhere across every tab.
func PaneSitesInTab ¶ added in v0.2.3
func PaneSitesInTab(t *TabSnapshot) iter.Seq[PaneSite]
PaneSitesInTab yields every pane in the tab along with its containing lane and group. This is the direct replacement for the lane→group→pane triple loop.
func PanesInGroup ¶ added in v0.2.3
func PanesInGroup(g *PaneGroupSnapshot) iter.Seq[*PaneSnapshot]
PanesInGroup yields a pointer to every pane in the group, in stable creation order.
func PanesInLane ¶ added in v0.2.3
func PanesInLane(l *LaneSnapshot) iter.Seq[*PaneSnapshot]
PanesInLane yields a pointer to every pane in the lane, group by group.
func PanesInTab ¶ added in v0.2.3
func PanesInTab(t *TabSnapshot) iter.Seq[*PaneSnapshot]
PanesInTab yields a pointer to every pane in the tab, lane by lane. Unlike TabSnapshot.FlatPanes it does not touch the panes it yields — no geometry is derived, and the pointers address the real tree, so writes through them stick.
func PanesInWorkspace ¶ added in v0.2.3
func PanesInWorkspace(snap *WorkspaceSnapshot) iter.Seq[*PaneSnapshot]
PanesInWorkspace yields a pointer to every pane in every tab.
func TabContainsPane ¶ added in v0.2.3
func TabContainsPane(t *TabSnapshot, paneID string) bool
TabContainsPane reports whether any lane in the tab holds this pane.
func WebSyncPaused ¶ added in v0.2.11
WebSyncPaused reports whether meta turns the pane's web repaint stream off.
Types ¶
type ApprovalPaneSnapshot ¶
type ApprovalPaneSnapshot struct {
ID string `json:"id"`
PaneType string `json:"pane_type"` // always "approval"
RequestID string `json:"request_id"`
SourcePaneID string `json:"source_pane_id"`
SourcePaneName string `json:"source_pane_name"`
ResponseSubject string `json:"response_subject"`
Type string `json:"type"` // "diff", "destructive_action", "choice", "question"
Description string `json:"description"`
Diff string `json:"diff,omitempty"` // unified diff content
DiffFilePath string `json:"diff_file_path,omitempty"`
Title string `json:"title"` // auto-generated for stack display
}
ApprovalPaneSnapshot carries the displayable state of an ephemeral approval pane.
type ConversationMessageSnapshot ¶
type ConversationMessageSnapshot struct {
TurnID string `json:"turn_id"`
TurnType string `json:"turn_type"` // "question" | "answer"
ConversationType string `json:"conversation_type"` // "shell" | "ai" | "rysh" | "chat" | "email" | "slack" | "chatbot"
InputType string `json:"input_type"` // "shell" | "prompt" | "command" | "approval" | "message"
MessageSource string `json:"message_source"` // "human" | "ai" | "external" | "agent" | "subagent" | "humanoid" | "system"
Content string `json:"content"`
TimestampMs int64 `json:"timestamp_ms"`
Sensitive bool `json:"sensitive,omitempty"`
Role string `json:"role,omitempty"`
Streaming bool `json:"streaming,omitempty"`
// ProviderName / Model attribute an ANSWER to the model that produced it.
// MessageSource only says "ai"; once `##llm select` can change the model
// mid-conversation, a pane's stored turns can come from two different
// models and nothing else in this struct tells them apart. Empty on
// questions and on answers recorded before attribution existed.
ProviderName string `json:"provider_name,omitempty"`
Model string `json:"model,omitempty"`
}
ConversationMessageSnapshot is the snapshot representation of a structured conversation message. It mirrors msg.ConversationMessage but lives in the domain package to avoid circular imports.
func (ConversationMessageSnapshot) Attribution ¶ added in v0.2.3
func (m ConversationMessageSnapshot) Attribution() string
Attribution renders an answer's producer for display, e.g. "openai (gpt-5.6-luna)". Empty when the message carries no attribution.
type FrontendCaps ¶ added in v0.2.3
type FrontendCaps struct {
// Frontend is FrontendCLI or FrontendApp.
Frontend string
// EmbeddedBrowser paints a live web page inside a web-mode pane (the app's
// Electron WebContentsView, or the server-side Chromium the browser
// renderer streams frames from). Without it a web pane shows its binding
// as a placeholder — the pane's browser automation still runs, driven by
// the CLI-owned headless Chromium (`##web headless on`).
EmbeddedBrowser bool
// EmailClient renders a humanoid's email pane as a three-column inbox. Both
// front-ends have one: the terminal's lives in internal/tui/email_view.go
// and speaks the same structured NATS request/reply as the app's.
EmailClient bool
// ChatClient renders a humanoid's chat pane (WhatsApp) as a threaded
// conversation with a reading pane. Without it the pane falls back to the
// humanoid's plain text buffer, which is readable but not interactive.
ChatClient bool
// FileBrowser is the graphical workspace file browser. The terminal browses
// files with ordinary shell commands instead.
FileBrowser bool
// NativeDialogs is OS-native chrome — folder pickers and the like — which
// only the packaged desktop app has.
NativeDialogs bool
}
FrontendCaps is the render surface set of one front-end.
func CapsFor ¶ added in v0.2.3
func CapsFor(frontend string) FrontendCaps
CapsFor returns the render capabilities of a front-end. Anything that is not the desktop app is treated as the terminal, matching NormalizeSource's rule that only "app" is explicitly recognised.
func (FrontendCaps) MissingVersus ¶ added in v0.2.3
func (c FrontendCaps) MissingVersus(other FrontendCaps) []string
MissingVersus lists, in human-readable form, the surfaces `other` can render that c cannot. It is empty when c is at least as capable as other, so the desktop app opening a terminal session produces no notes at all.
Each note names the degradation AND the way to keep working, because a note that only says "unavailable" sends the user hunting.
type LaneRenderInfo ¶
type LaneRenderInfo struct {
LaneID string `json:"lane_id"`
Flex int `json:"flex"`
Name string `json:"name,omitempty"` // lane name (effective, with pipeline-name fallback applied)
Collapsed bool `json:"collapsed,omitempty"` // drawn as a thin strip with the name running down it
VisiblePanes []PaneSnapshot `json:"visible_panes"`
}
LaneRenderInfo is produced by FlatLanes() for the TUI rendering pipeline. Each lane has its flex weight and a list of visible panes (one per group).
type LaneSnapshot ¶
type LaneSnapshot struct {
ID string `json:"id"`
Flex int `json:"flex"`
Name string `json:"name,omitempty"` // lane name; defaults to the tab's pipeline name
Collapsed bool `json:"collapsed,omitempty"` // folded to a thin strip; its width goes to the open lanes
PaneGroups []PaneGroupSnapshot `json:"pane_groups"`
ActivePaneID string `json:"active_pane_id"`
}
LaneSnapshot carries the state of a single lane (column) within a tab.
func FindLaneByID ¶ added in v0.2.3
func FindLaneByID(t *TabSnapshot, laneID string) *LaneSnapshot
FindLaneByID returns the lane with this id, or nil.
func LaneOfPane ¶ added in v0.2.3
func LaneOfPane(t *TabSnapshot, paneID string) *LaneSnapshot
LaneOfPane returns the lane holding this pane, or nil.
func ResolveLane ¶ added in v0.2.3
func ResolveLane(tab *TabSnapshot, arg string) *LaneSnapshot
ResolveLane resolves a lane selector within a tab. The empty selector picks the lane holding the tab's active pane, falling back to the first lane.
type PaneGroupSnapshot ¶
type PaneGroupSnapshot struct {
ID string `json:"id"`
RowFlex int `json:"row_flex"`
Panes []PaneSnapshot `json:"panes"`
ActivePaneID string `json:"active_pane_id"`
}
PaneGroupSnapshot carries the state of a pane group (a stack of panes within a lane).
func FindGroupByID ¶ added in v0.2.3
func FindGroupByID(t *TabSnapshot, groupID string) *PaneGroupSnapshot
FindGroupByID returns the pane group with this id anywhere in the tab, or nil.
func GroupOfPane ¶ added in v0.2.3
func GroupOfPane(t *TabSnapshot, paneID string) *PaneGroupSnapshot
GroupOfPane returns the pane group holding this pane, or nil.
func ResolveGroup ¶ added in v0.2.3
func ResolveGroup(lane *LaneSnapshot, arg string) *PaneGroupSnapshot
ResolveGroup resolves a pane-group selector within a lane. The empty selector picks the group holding the lane's active pane, falling back to the first. Pane groups have no name, so only id and index are matched.
type PaneSite ¶ added in v0.2.3
type PaneSite struct {
Lane *LaneSnapshot
Group *PaneGroupSnapshot
Pane *PaneSnapshot
LaneIndex int
GroupIndex int
PaneIndex int
}
PaneSite is a pane together with the lane and group that contain it, and its index at each level. It is what a hand-rolled triple-nested walk has in scope at its innermost point.
All three pointers address the tree passed in, so they may be written through.
func LocatePaneInTab ¶ added in v0.2.3
func LocatePaneInTab(t *TabSnapshot, paneID string) (site PaneSite, ok bool)
LocatePaneInTab finds a pane and returns it with the lane and group that hold it. ok is false when no such pane exists, in which case the site is zero.
type PaneSnapshot ¶
type PaneSnapshot struct {
ID string `json:"id"`
Title string `json:"title"`
Flex int `json:"flex"`
LaneID string `json:"lane_id,omitempty"`
Mode string `json:"mode"`
// EnabledModes is the per-pane ordered set of available input modes — the
// source of truth for every frontend's mode cycle. Empty in pre-field
// snapshots, in which case frontends fall back to the default set.
EnabledModes []string `json:"enabled_modes,omitempty"`
// Web-mode binding (only meaningful when "web" is in EnabledModes). The browser
// itself is rendered by the desktop app; the pane owns only this state.
WebURL string `json:"web_url,omitempty"`
WebProfile string `json:"web_profile,omitempty"`
WebTitle string `json:"web_title,omitempty"`
// WebActivateSeq increments every time `##mode new web` (re)binds the pane,
// even with the same profile/url. The desktop app uses it as an explicit
// "show & (re)bind the browser now" signal, so re-running the command from
// another display mode reliably switches the pane back to web.
WebActivateSeq int `json:"web_activate_seq,omitempty"`
// PTYRows/PTYCols are the pane's live PTY grid, and SizeViewports is how
// many viewports are currently claiming a size on it (see
// msg.MsgPaneResize). A pane on screen in more than one place is sized to
// the SMALLEST claim, so a large window can legitimately render a small
// grid with space around it. These three fields are what let `##pane info`
// answer "why is this pane smaller than its box" instead of leaving it as
// a mystery.
PTYRows int `json:"pty_rows,omitempty"`
PTYCols int `json:"pty_cols,omitempty"`
SizeViewports int `json:"size_viewports,omitempty"`
Output string `json:"output"`
Status string `json:"status"`
LastCommand string `json:"last_command"`
// Program is the pane's live FOREGROUND executable ("claude", "vim", …),
// empty when the pane is at its shell prompt or where it cannot be resolved
// (see processName). It answers "which panes are running an agent?" as a
// query over structure, rather than by rendering each pane's screen and
// looking for a TUI's box-drawing characters.
Program string `json:"program,omitempty"`
// ProgramPGID is the foreground process group of Program, 0 when Program
// is empty. A program started from the shell leads its own group, so this
// is its pid — which is how the board courier (design 050) finds a claude's
// inbox socket, /tmp/cc-socks/<pid>.sock, when no hook has stamped it.
ProgramPGID int `json:"program_pgid,omitempty"`
// Meta is free-form per-pane metadata owned by whoever is driving the pane
// (`##pane meta`). rysh never interprets it; it persists it, so a
// supervisor's notes about a pane outlive the supervisor.
Meta map[string]string `json:"meta,omitempty"`
ProviderName string `json:"provider_name"`
// ProviderOverride / ProviderOverrideModel persist the `##pane provider`
// runtime selection (design 002 §3.4) so it survives detach/attach.
// ProviderName above stays the EFFECTIVE provider (the override when set,
// otherwise the session provider).
ProviderOverride string `json:"provider_override,omitempty"`
ProviderOverrideModel string `json:"provider_override_model,omitempty"`
GivenName string `json:"given_name,omitempty"`
RegisteredHumanoid string `json:"registered_humanoid,omitempty"`
MergedHistory []string `json:"merged_history,omitempty"`
ShellHistory []string `json:"shell_history,omitempty"`
PromptHistory []string `json:"prompt_history,omitempty"`
RyshHistory []string `json:"rysh_history,omitempty"`
ChatHistory []string `json:"chat_history,omitempty"`
ExternalHistory []string `json:"external_history,omitempty"`
ShellOutput string `json:"shell_output,omitempty"`
AIOutput string `json:"ai_output,omitempty"`
RyshOutput string `json:"rysh_output,omitempty"`
ChatOutput string `json:"chat_output,omitempty"`
ExternalOutput string `json:"external_output,omitempty"`
// ModeOutputs holds output for dynamic per-humanoid modes, keyed by mode name
// (the humanoid's name). Rendered when the active input mode matches a key.
ModeOutputs map[string]string `json:"mode_outputs,omitempty"`
// Structured conversation buffers (new — per-mode ConversationMessage arrays).
// Keys: "shell", "ai", "rysh", "chat", "email", "slack", "chatbot".
Conversations map[string][]ConversationMessageSnapshot `json:"conversations,omitempty"`
MergedConv []ConversationMessageSnapshot `json:"merged_conv,omitempty"`
// Structured history buffers (new — per-mode ConversationMessage arrays).
ConvHistories map[string][]ConversationMessageSnapshot `json:"conv_histories,omitempty"`
MergedConvHistory []ConversationMessageSnapshot `json:"merged_conv_history,omitempty"`
// Listener state: which pane this pane is listening to (empty if not listening).
ListeningToID string `json:"listening_to_id,omitempty"`
// Copy state: content hopped from another pane via ##hop.
HoppedFromAlias string `json:"hopped_from_alias,omitempty"`
HoppedFromID string `json:"hopped_from_id,omitempty"`
HasHoppedContent bool `json:"has_hopped_content,omitempty"`
// Remote upstream sharing state.
Sharing bool `json:"sharing,omitempty"`
UpstreamURL string `json:"upstream_url,omitempty"`
UpstreamConnected bool `json:"upstream_connected,omitempty"`
// SnatEnabled reports whether SecretNAT / ReSet (reversible secret
// translation, ##snat) is active for this pane. Display flag only — the
// mapping table itself is never serialized.
SnatEnabled bool `json:"snat_enabled,omitempty"`
// Controller mode — when non-empty, this pane is controlling a remote share.
ControllingPaneAlias string `json:"controlling_pane_alias,omitempty"`
// Connected-to state: the remote pane ID this pane is subscribed to via upstream.
ConnectedToPaneID string `json:"connected_to_pane_id,omitempty"`
// Raw/interactive terminal mode state.
RawMode bool `json:"raw_mode,omitempty"`
// FullScreen carries the NARROW full-screen terminal signal from the child
// program (alt screen entered, or cursor hidden). RawMode is broader: it is
// also true for ANY foreground child (cat, ls, make) so the TUI shows the
// live VT screen in-pane while a command runs. Only a FullScreen pane may
// escalate to the full-terminal PTY relay — the relay writes raw PTY bytes
// straight to the real stdout, which must never happen for plain commands.
FullScreen bool `json:"full_screen,omitempty"`
MouseEnabled bool `json:"mouse_enabled,omitempty"` // child process has mouse tracking on
// MouseProto and MouseSGR describe HOW the child wants mouse events, not
// just whether it wants them: the tracking mode it asked for (one of the
// vterm.Mouse* constants) and whether it enabled SGR extended encoding
// (\x1b[?1006h). A child that enabled \x1b[?1000h alone expects the legacy
// X10 byte encoding and cannot parse an SGR report — sending the wrong one
// makes mouse events surface as literal text in the child's input line.
// Empty MouseProto with MouseEnabled set means the snapshot came from a peer
// that predates these fields; treat it as SGR, the previous behaviour.
MouseProto string `json:"mouse_proto,omitempty"`
MouseSGR bool `json:"mouse_sgr,omitempty"`
// Child process enabled DECCKM (application cursor keys): arrows must be
// sent as \x1bO[A-D], not \x1b[[A-D] (less ignores the CSI form).
AppCursorKeys bool `json:"app_cursor_keys,omitempty"`
VTScreen []string `json:"vt_screen,omitempty"`
VTCursorRow int `json:"vt_cursor_row,omitempty"`
VTCursorCol int `json:"vt_cursor_col,omitempty"`
// Remote interactive sharing: when subscribing to a remote share in interactive mode.
RemoteInteractive bool `json:"remote_interactive,omitempty"`
RemoteVTScreen []string `json:"remote_vt_screen,omitempty"`
RemoteVTCursorRow int `json:"remote_vt_cursor_row,omitempty"`
RemoteVTCursorCol int `json:"remote_vt_cursor_col,omitempty"`
ShareRestrictions *ShareRestrictions `json:"share_restrictions,omitempty"`
RemoteShareRestrictions *ShareRestrictions `json:"remote_share_restrictions,omitempty"`
// Approval pane groups configured for this pane.
ApprovalPaneGroups []string `json:"approval_pane_groups,omitempty"`
// Attention mechanism state.
AttentionEnabled bool `json:"attention_enabled,omitempty"`
AttentionCount int `json:"attention_count,omitempty"`
AttentionCategory string `json:"attention_category,omitempty"`
AttentionTitle string `json:"attention_title,omitempty"`
// Pane type discriminator. A pane type is what a pane is INSTEAD of a
// shell — it is a different axis from the pane's input mode
// (EnabledModes: shell/prompt/rysh/chat/external/email/web), which is how
// keystrokes are read on a pane that does have a shell.
//
// "" normal pane (the default; "normal" is never written)
// "approval" ephemeral approval pane (actors/approval_pane.go)
// "replay" recorded playback (actors/workspace_replay.go)
// "agents-board" threaded board of what every agent is doing (design 025)
//
// The previous comment here claimed the values were "normal" or
// "approval", omitting "replay" entirely — stale in both directions, and a
// stale comment on the field being extended is how the next reader gets it
// wrong. Use the PaneType* constants rather than string literals.
PaneType string `json:"pane_type,omitempty"`
// Hidden takes a pane OFF SCREEN without taking it out of the session
// (design 027 §5.1). It is a rendering state, not a lifecycle state: the
// pane keeps its PTY, its program keeps running, and it stays fully
// addressable — ANSA reaches it, `##pane` commands act on it, and it is
// still in the roster. What changes is that FlatLanes/FlatPanes stop
// emitting it, and the pane group skips it when cycling focus.
//
// This is deliberately NOT the stack's collapsed state. StackCollapsed
// below means "drawn as a one-line title row"; a collapsed pane is still on
// screen. Before this field there was no way to have a live pane that is
// not drawn at all, and the only off-screen-but-alive state was a
// non-active TAB — rejected for the board claude because revealing it would
// then mean switching the human's tab.
//
// Persisted, so a hidden pane comes back hidden rather than reappearing
// after a daemon restart.
Hidden bool `json:"hidden,omitempty"`
// ShellPID is the OS process id of the pane's shell process. The TUI uses it
// to resolve the shell's live working directory for tab-completion (so paths
// complete relative to the directory the user has cd'd into). 0 if unknown.
ShellPID int `json:"shell_pid,omitempty"`
// ShellCwd is the shell's live working directory as reported via OSC 7
// (push-based, exact after every prompt). Preferred over ShellPID+lsof
// resolution when non-empty; "" when the shell has not reported yet.
ShellCwd string `json:"shell_cwd,omitempty"`
// NativeMode: the pane is in ##native pass-through shell mode — always
// interactive, bash owns the line; double-Esc (TUI) exits to prompt mode.
NativeMode bool `json:"native_mode,omitempty"`
// StartupDir is the directory the pane's shell was launched in
// (cfg.WorkingDirectory, or empty when it fell back to the daemon cwd). The
// file-browse responder uses it as a fallback browse root when the live
// foreground/shell cwd cannot be resolved (e.g. on non-Linux without /proc).
StartupDir string `json:"startup_dir,omitempty"`
// Row flex weight (set by FlatPanes/FlatLanes from the pane group's RowFlex).
RowFlex int `json:"row_flex,omitempty"`
// Stacked pane metadata (set by FlatPanes/FlatLanes for the top pane in a stack).
StackedTitles []string `json:"stacked_titles,omitempty"` // titles of background stacked panes (legacy, unused in Zellij-style rendering)
StackPosition int `json:"stack_position,omitempty"` // 0-based position in stack (0 = top)
StackTotal int `json:"stack_total,omitempty"` // total panes in stack (0 or 1 = no stack)
StackCollapsed bool `json:"stack_collapsed,omitempty"` // true = render as collapsed single title line (Zellij-style)
}
PaneSnapshot carries the displayable state of a single pane.
func FindPaneInTab ¶ added in v0.2.3
func FindPaneInTab(t *TabSnapshot, paneID string) *PaneSnapshot
FindPaneInTab returns a pointer to the pane with this id, or nil. The pointer addresses the tab passed in, so it may be written through.
func FindPaneInWorkspace ¶ added in v0.2.3
func FindPaneInWorkspace(snap *WorkspaceSnapshot, paneID string) *PaneSnapshot
FindPaneInWorkspace returns a pointer to the pane with this id in any tab, or nil.
func ResolvePane ¶ added in v0.2.3
func ResolvePane(g *PaneGroupSnapshot, arg string) *PaneSnapshot
ResolvePane resolves a pane selector within a group. The empty selector picks the group's active pane, falling back to the first. A non-empty selector matches id, title or given name — but NOT an index, because a pane's position within its stack is not something listings number.
type PaneSnapshotResponse ¶
type PaneSnapshotResponse struct {
Snapshot PaneSnapshot `json:"snapshot"`
}
PaneSnapshotResponse is kept for backward compatibility with any code that still uses the old NATS request/reply pattern on rysh.pane.{id}.snapshot.
type ShareRestrictions ¶
type ShareRestrictions struct {
}
ShareRestrictions holds per-pane access restrictions for remote users.
type TabSnapshot ¶
type TabSnapshot struct {
ID string `json:"id"`
Title string `json:"title"`
// Partial marks a tab whose real snapshot never arrived — the request timed
// out and this is a stub carrying nothing but the id and title (`F-11`).
//
// IT EXISTS BECAUSE A STUB AND AN EMPTY TAB ARE OTHERWISE THE SAME VALUE.
// A reader that cannot tell them apart reports "lane not found" for a lane
// that is sitting right there, which is how 22 fleets lost their fan-out
// while every operator hunted a selector bug. Anything that concludes
// ABSENCE from an empty Lanes slice must check this first.
Partial bool `json:"partial,omitempty"`
Lanes []LaneSnapshot `json:"lanes"`
ActivePaneID string `json:"active_pane_id"`
PipelineOutput string `json:"pipeline_output,omitempty"`
PipelineActive bool `json:"pipeline_active,omitempty"`
PipelineEnabled bool `json:"pipeline_enabled,omitempty"`
PipelineName string `json:"pipeline_name,omitempty"`
}
TabSnapshot carries the state of a single tab. Tabs now contain lanes (columns), not pane groups directly.
func FindTabByID ¶ added in v0.2.3
func FindTabByID(snap *WorkspaceSnapshot, tabID string) *TabSnapshot
FindTabByID returns the tab with this id, or nil.
func ResolveTab ¶ added in v0.2.3
func ResolveTab(snap *WorkspaceSnapshot, arg string) *TabSnapshot
ResolveTab resolves a tab selector against the workspace. Returns nil when there are no tabs, or when a non-empty selector matches nothing.
func (TabSnapshot) FlatLanes ¶
func (ts TabSnapshot) FlatLanes() []LaneRenderInfo
FlatLanes returns lane rendering info for the TUI. For each pane group, ALL panes are emitted in their stable creation order. The active pane (identified by ActivePaneID) gets full rendering, while all other panes are marked StackCollapsed=true and rendered as single title-line rows in the Zellij-style stacked layout.
func (TabSnapshot) FlatPanes ¶
func (ts TabSnapshot) FlatPanes() []PaneSnapshot
FlatPanes returns all visible panes across all lanes as a flat list. This maintains backward compatibility with code that needs a flat pane list. Each pane gets Flex set from its lane for width calculation, and LaneID for identification. All stacked panes are included in stable creation order: the active pane (matching ActivePaneID) is expanded, others have StackCollapsed=true.
type WorkspaceSnapshot ¶
type WorkspaceSnapshot struct {
Tabs []TabSnapshot `json:"tabs"`
ActiveTabID string `json:"active_tab_id"`
ActivePaneID string `json:"active_pane_id"`
// Workspaces lists the names of every workspace in the session, in order,
// and ActiveWorkspace is the 0-based index of the one this snapshot belongs
// to. Populated by the active WorkspaceActor from data passed down by the
// WorkspaceFarmActor. Used by the TUI to render the workspace switcher.
Workspaces []string `json:"workspaces,omitempty"`
ActiveWorkspace int `json:"active_workspace,omitempty"`
// TabBarVertical renders the tab bar as a column down the left edge of the
// body instead of a strip under the workspace row. Per-workspace, set by
// `##tab orientation` (or ctrl+t v) and persisted with the layout. Omitted
// when false so the horizontal default stays the zero value.
TabBarVertical bool `json:"tab_bar_vertical,omitempty"`
// SpendMicroUSD is today's session spend (design 003 §3.5), for the status
// bar. SpendWarn is true when any active pane ceiling is at ≥80% of its
// limit, so the TUI colours the figure yellow. Populated by the WorkspaceActor
// from a TTL-cached usage query, so the status bar shows spend without a
// round-trip per frame.
SpendMicroUSD int64 `json:"spend_micro_usd,omitempty"`
SpendWarn bool `json:"spend_warn,omitempty"`
}
WorkspaceSnapshot is the top-level snapshot polled by the TUI.