ipc

package
v1.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// Lifecycle
	MsgAttach    = "attach"
	MsgDetach    = "detach"
	MsgShutdown  = "shutdown"
	MsgHeartbeat = "heartbeat"

	// Session control (Client -> Daemon)
	MsgCreatePane   = "create_pane"
	MsgDestroyPane  = "destroy_pane"
	MsgResizePane   = "resize_pane"
	MsgUpdatePane   = "update_pane"
	MsgUpdateLayout = "update_layout"

	// Tab control (Client -> Daemon)
	MsgCreateTab  = "create_tab"
	MsgDestroyTab = "destroy_tab"
	MsgSwitchTab  = "switch_tab"
	MsgUpdateTab  = "update_tab"
	MsgReorderTab = "reorder_tab"

	// I/O (bidirectional)
	MsgPaneInput  = "pane_input"
	MsgPaneOutput = "pane_output"

	// State sync (Daemon -> Client)
	MsgWorkspaceState = "workspace_state"
	MsgStateUpdate    = "state_update"

	// Plugin (Daemon -> Client)
	MsgPluginError = "plugin_error"

	// Plugin management (Client -> Daemon)
	MsgReloadPlugins = "reload_plugins"

	// MCP request-response (Client -> Daemon -> Client)
	MsgListPanesReq       = "list_panes_req"
	MsgListPanesResp      = "list_panes_resp"
	MsgReadPaneOutputReq  = "read_pane_output_req"
	MsgReadPaneOutputResp = "read_pane_output_resp"
	MsgPaneStatusReq      = "pane_status_req"
	MsgPaneStatusResp     = "pane_status_resp"
	MsgCreatePaneReq      = "create_pane_req"
	MsgCreatePaneResp     = "create_pane_resp"
	MsgRestartPaneReq     = "restart_pane_req"
	MsgRestartPaneResp    = "restart_pane_resp"
	MsgScreenshotPaneReq  = "screenshot_pane_req"
	MsgScreenshotPaneResp = "screenshot_pane_resp"
	MsgSwitchTabReq       = "switch_tab_req"
	MsgSwitchTabResp      = "switch_tab_resp"
	MsgListTabsReq        = "list_tabs_req"
	MsgListTabsResp       = "list_tabs_resp"
	MsgDestroyPaneReq     = "destroy_pane_req"
	MsgDestroyPaneResp    = "destroy_pane_resp"
	MsgSetActivePane      = "set_active_pane" // broadcast to TUI
	MsgCloseTUI           = "close_tui"       // broadcast to TUI
	MsgHighlightPane      = "highlight_pane"  // broadcast to TUI (MCP interaction indicator)

	// Notification center (M12)
	MsgPaneEvent              = "pane_event"               // broadcast to TUI
	MsgDismissEvent           = "dismiss_event"            // client → daemon
	MsgGetNotificationsReq    = "get_notifications_req"    // MCP request
	MsgGetNotificationsResp   = "get_notifications_resp"   // MCP response
	MsgWatchNotificationsReq  = "watch_notifications_req"  // MCP request (blocking)
	MsgWatchNotificationsResp = "watch_notifications_resp" // MCP response

	// Version negotiation — TUI asks daemon for its version string before
	// attaching so mismatches can be surfaced as a blocking dialog or an
	// auto-restart prompt. A daemon built before this pair existed will
	// silently drop MsgVersionReq; the client handles the timeout.
	MsgVersionReq  = "version_req"  // client → daemon (empty payload)
	MsgVersionResp = "version_resp" // daemon → client (VersionRespPayload)

	// Memory reporting
	MsgMemoryReportReq  = "memory_report_req"
	MsgMemoryReportResp = "memory_report_resp"
)

Message type constants

Variables

View Source
var ErrSendOverflow = errors.New("ipc: send buffer overflow (slow client)")

ErrSendOverflow is returned by Conn.Send when the per-conn send buffer is full. The connection has been scheduled for close; future Sends short- circuit with the same error.

Functions

func WriteMessage

func WriteMessage(w io.Writer, msg *Message) error

WriteMessage writes a length-prefixed JSON message to w. Format: [4 bytes uint32 big-endian length][JSON payload]

Types

type AttachPayload

type AttachPayload struct {
	Cols int    `json:"cols"`
	Rows int    `json:"rows"`
	CWD  string `json:"cwd,omitempty"`
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client connects to the daemon over a Unix socket.

func NewClient

func NewClient(socketPath string) (*Client, error)

func (*Client) Close

func (c *Client) Close() error

func (*Client) Receive

func (c *Client) Receive() (*Message, error)

func (*Client) Send

func (c *Client) Send(msg *Message) error

func (*Client) SetReadDeadline added in v1.8.0

func (c *Client) SetReadDeadline(t time.Time) error

SetReadDeadline installs a read deadline on the underlying socket. Pass the zero time to disable it. Used by the pre-attach version handshake to cap how long we wait for MsgVersionResp from daemons that may predate the version-negotiation protocol.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn wraps a net.Conn with message framing.

Sends are non-blocking: each Conn owns a 64-slot queue and a dedicated goroutine that drains the queue into the underlying socket. A slow or wedged peer drains its own queue; if the queue overflows, the offending conn is closed in the background and Send returns ErrSendOverflow. Other connections are never affected by one client's slowness — closing the wedge-incident class where a single stuck TUI or MCP bridge stalled the daemon's broadcast for every other client.

func (*Conn) Close

func (c *Conn) Close() error

Close shuts down the conn. Idempotent — safe to call concurrently from any goroutine. Any frames still queued in sendCh at close time are intentionally discarded: by the time Close is called we are either tearing down an overflowed (already broken) peer or shutting down the server entirely, and in both cases delivery guarantees no longer apply.

func (*Conn) Receive

func (c *Conn) Receive() (*Message, error)

func (*Conn) Send

func (c *Conn) Send(msg *Message) error

Send marshals msg into the wire frame and queues it for transmission. Returns ErrSendOverflow when the per-conn buffer is full — the conn has been scheduled for async close at that point.

The closed/overflow short-circuit here is the fast path: it skips the JSON marshal entirely for a known-dead conn. The actual race-safe check happens inside sendFrame next to the channel send — do not remove either one.

type CreatePanePayload

type CreatePanePayload struct {
	TabID         string   `json:"tab_id"`
	CWD           string   `json:"cwd"`
	Type          string   `json:"type,omitempty"`
	InstanceName  string   `json:"instance_name,omitempty"`
	InstanceArgs  []string `json:"instance_args,omitempty"`
	ReplacePaneID string   `json:"replace_pane_id,omitempty"`
}

type CreatePaneReqPayload

type CreatePaneReqPayload struct {
	TabID        string   `json:"tab_id,omitempty"`
	CWD          string   `json:"cwd,omitempty"`
	Type         string   `json:"type,omitempty"`
	InstanceName string   `json:"instance_name,omitempty"`
	InstanceArgs []string `json:"instance_args,omitempty"`
}

type CreatePaneRespPayload

type CreatePaneRespPayload struct {
	PaneID string `json:"pane_id"`
	TabID  string `json:"tab_id"`
}

type CreateTabPayload

type CreateTabPayload struct {
	Name string `json:"name"`
}

type DestroyPanePayload

type DestroyPanePayload struct {
	PaneID string `json:"pane_id"`
}

type DestroyPaneReqPayload

type DestroyPaneReqPayload struct {
	PaneID string `json:"pane_id"`
}

type DestroyPaneRespPayload

type DestroyPaneRespPayload struct {
	Success bool `json:"success"`
}

type DestroyTabPayload

type DestroyTabPayload struct {
	TabID string `json:"tab_id"`
}

type DismissEventPayload

type DismissEventPayload struct {
	EventID string `json:"event_id"` // empty = dismiss all
}

type GetNotificationsRespPayload

type GetNotificationsRespPayload struct {
	Events []PaneEventPayload `json:"events"`
}

type HighlightPanePayload

type HighlightPanePayload struct {
	PaneID string `json:"pane_id"`
}

type ListPanesRespPayload

type ListPanesRespPayload struct {
	Panes []PaneInfo `json:"panes"`
}

type ListTabsRespPayload

type ListTabsRespPayload struct {
	Tabs []TabInfo `json:"tabs"`
}

type MemoryReportReqPayload added in v1.9.0

type MemoryReportReqPayload struct{}

type MemoryReportRespPayload added in v1.9.0

type MemoryReportRespPayload struct {
	SnapshotAt int64         `json:"snapshot_at"` // Unix nanoseconds
	Panes      []PaneMemInfo `json:"panes"`
	Total      uint64        `json:"total"`
	// Tabs is the same view that MsgListTabsResp would return at the moment
	// the daemon assembled this response. Embedded here so MCP
	// `get_memory_report` does not need a second round-trip to enrich tab
	// IDs with names. Note: the per-pane memory numbers come from the
	// memreport collector's last tick (up to 5 s old), while Tabs is taken
	// fresh — the two halves are captured close-in-time on the daemon side
	// but are not guaranteed to be drawn from the exact same instant.
	Tabs []TabInfo `json:"tabs,omitempty"`
}

type Message

type Message struct {
	Type    string          `json:"type"`
	ID      string          `json:"id,omitempty"` // request-response correlation (MCP bridge)
	Payload json.RawMessage `json:"payload,omitempty"`
}

Message is the wire format for IPC communication.

func NewMessage

func NewMessage(typ string, payload any) (*Message, error)

NewMessage creates a Message with a typed payload.

func ReadMessage

func ReadMessage(r io.Reader) (*Message, error)

ReadMessage reads a length-prefixed JSON message from r.

func (*Message) DecodePayload

func (m *Message) DecodePayload(target any) error

DecodePayload unmarshals the message payload into the given target.

type MessageHandler

type MessageHandler func(conn *Conn, msg *Message)

MessageHandler is called for each incoming message on a connection.

type PaneEventPayload

type PaneEventPayload struct {
	ID        string            `json:"id"`
	PaneID    string            `json:"pane_id"`
	TabID     string            `json:"tab_id"`
	PaneName  string            `json:"pane_name"`
	Type      string            `json:"type"`
	Title     string            `json:"title"`
	Message   string            `json:"message,omitempty"`
	Severity  string            `json:"severity"`
	Timestamp int64             `json:"timestamp"`
	Data      map[string]string `json:"data,omitempty"`
}

type PaneInfo

type PaneInfo struct {
	ID           string `json:"id"`
	TabID        string `json:"tab_id"`
	TabName      string `json:"tab_name"`
	Name         string `json:"name"`
	Type         string `json:"type"`
	CWD          string `json:"cwd"`
	Running      bool   `json:"running"`
	InstanceName string `json:"instance_name,omitempty"`
}

type PaneInputPayload

type PaneInputPayload struct {
	PaneID string `json:"pane_id"`
	Data   []byte `json:"data"`
}

type PaneMemInfo added in v1.9.0

type PaneMemInfo struct {
	PaneID      string `json:"pane_id"`
	TabID       string `json:"tab_id"`
	GoHeapBytes uint64 `json:"go_heap_bytes"`
	PTYRSSBytes uint64 `json:"pty_rss_bytes"`
	TotalBytes  uint64 `json:"total_bytes"`
}

PaneMemInfo is the wire form of a single pane's daemon-side memory. TUI-local memory is not part of the wire format — the TUI merges its own values at render time.

type PaneOutputPayload

type PaneOutputPayload struct {
	PaneID string `json:"pane_id"`
	Data   []byte `json:"data"`
	Ghost  bool   `json:"ghost,omitempty"`
}

type PaneStatusReqPayload

type PaneStatusReqPayload struct {
	PaneID string `json:"pane_id"`
}

type PaneStatusRespPayload

type PaneStatusRespPayload struct {
	PaneID   string `json:"pane_id"`
	Running  bool   `json:"running"`
	ExitCode *int   `json:"exit_code,omitempty"`
	Type     string `json:"type"`
	CWD      string `json:"cwd"`
	Name     string `json:"name"`
}

type PluginErrorPayload

type PluginErrorPayload struct {
	PaneID  string `json:"pane_id"`
	Title   string `json:"title"`
	Message string `json:"message"`
}

type ReadPaneOutputReqPayload

type ReadPaneOutputReqPayload struct {
	PaneID    string `json:"pane_id"`
	LastLines int    `json:"last_lines"`
}

type ReadPaneOutputRespPayload

type ReadPaneOutputRespPayload struct {
	PaneID string `json:"pane_id"`
	Text   string `json:"text"`
	Lines  int    `json:"lines"`
}

type ReorderTabPayload added in v1.15.0

type ReorderTabPayload struct {
	TabID    string `json:"tab_id"`
	NewIndex int    `json:"new_index"`
}

ReorderTabPayload moves an existing tab to a new ordinal position. NewIndex is clamped to the daemon-side tab list bounds, so a stale TUI does not have to track creation/destruction races to send a safe value.

type ResizePanePayload

type ResizePanePayload struct {
	PaneID string `json:"pane_id"`
	Rows   uint16 `json:"rows"`
	Cols   uint16 `json:"cols"`
}

type RestartPaneReqPayload

type RestartPaneReqPayload struct {
	PaneID string `json:"pane_id"`
}

type RestartPaneRespPayload

type RestartPaneRespPayload struct {
	PaneID  string `json:"pane_id"`
	Success bool   `json:"success"`
}

type ScreenshotPaneReqPayload

type ScreenshotPaneReqPayload struct {
	PaneID string `json:"pane_id"`
	Width  int    `json:"width,omitempty"`
	Height int    `json:"height,omitempty"`
}

type ScreenshotPaneRespPayload

type ScreenshotPaneRespPayload struct {
	PaneID  string `json:"pane_id"`
	Text    string `json:"text"`
	CursorX int    `json:"cursor_x"`
	CursorY int    `json:"cursor_y"`
}

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server listens for client connections over a Unix socket.

func NewServer

func NewServer(socketPath string, handler MessageHandler, onDisconnect func(*Conn)) *Server

func (*Server) Broadcast

func (s *Server) Broadcast(msg *Message)

Broadcast sends a message to all connected clients without blocking on any individual conn. Marshals the wire frame once and shares the bytes across all per-conn send queues. A slow or wedged conn is dropped from the fan-out (logged once, per CAS-guarded sendFrame) without affecting the others.

func (*Server) ConnCount added in v1.16.0

func (s *Server) ConnCount() int

ConnCount returns the number of currently-connected clients. Test-friendly alternative to the existing log-line scraping pattern; used to wait for connect/disconnect events without time-based sleeps.

func (*Server) Start

func (s *Server) Start() error

func (*Server) Stop

func (s *Server) Stop() error

Stop closes the listener and all active connections. Frames queued in any conn's send buffer at the moment of Stop are discarded — Daemon.Stop's shutdown sequence does not rely on a final IPC broadcast reaching clients (the final-snapshot durability lives in the on-disk workspace.json path, not in the wire).

type SetActivePanePayload

type SetActivePanePayload struct {
	PaneID string `json:"pane_id"`
}

type SwitchTabPayload

type SwitchTabPayload struct {
	TabID string `json:"tab_id"`
}

type SwitchTabReqPayload

type SwitchTabReqPayload struct {
	TabID string `json:"tab_id"`
}

type SwitchTabRespPayload

type SwitchTabRespPayload struct {
	TabID string `json:"tab_id"`
}

type TabInfo

type TabInfo struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Color     string `json:"color,omitempty"`
	PaneCount int    `json:"pane_count"`
	Active    bool   `json:"active"`
}

type UpdateLayoutPayload

type UpdateLayoutPayload struct {
	TabID  string          `json:"tab_id"`
	Layout json.RawMessage `json:"layout"`
}

type UpdatePanePayload

type UpdatePanePayload struct {
	PaneID string `json:"pane_id"`
	Name   string `json:"name,omitempty"`
	CWD    string `json:"cwd,omitempty"`
	// Muted is a pointer so an unset field (nil) is distinguishable from an
	// explicit false. Callers updating only Name or CWD pass nil and the
	// daemon leaves the pane's mute state untouched.
	Muted *bool `json:"muted,omitempty"`
}

type UpdateTabPayload

type UpdateTabPayload struct {
	TabID string `json:"tab_id"`
	Name  string `json:"name,omitempty"`
	Color string `json:"color,omitempty"`
}

type VersionRespPayload added in v1.8.0

type VersionRespPayload struct {
	Version string `json:"version"`
}

VersionRespPayload carries the daemon's version string. MsgVersionReq has no payload — the request is just "what version are you running?".

type WatchNotificationsReqPayload

type WatchNotificationsReqPayload struct {
	PaneIDs   []string `json:"pane_ids,omitempty"`
	TimeoutMs int      `json:"timeout_ms"`
	// SinceTimestamp closes the race between "kick off a task" and "start
	// watching" — events fired during that window would otherwise be lost.
	// When set (Unix ms), the daemon first scans the existing event queue
	// for any matching event whose timestamp is strictly greater, returning
	// the oldest such event immediately. Only if the queue holds no
	// qualifying event does it register a blocking watcher. Agents should
	// pass the timestamp of the last event they handled.
	SinceTimestamp int64 `json:"since_timestamp,omitempty"`
}

type WatchNotificationsRespPayload

type WatchNotificationsRespPayload struct {
	Event   *PaneEventPayload `json:"event,omitempty"`
	Timeout bool              `json:"timeout"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL