tui

package
v1.24.2 Latest Latest
Warning

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

Go to latest
Published: May 15, 2026 License: MIT Imports: 45 Imported by: 0

Documentation

Overview

agents.go implements the agent management modal. It displays all agents in current display order with visibility, pin state, activity, and bead info. Rendered as a centered floating box over the live TUI. Opened via backtick+agents command or Alt+a shortcut.

Actions apply immediately and persist through the layout save path. Keybindings: Space (toggle visibility), Enter (grab/drop for reorder), p (toggle pin), A (reveal all), R (reset to config order).

Package tui — git branch detection for the status bar.

readBranch resolves the current branch of a repo (or git worktree) by reading .git/HEAD directly, avoiding a fork+exec on every poll. Returns "" when the directory is not a git repo or HEAD cannot be parsed.

control_mux.go multiplexes a single yamux control stream so multiple RemotePanes can send concurrent requests without interleaving responses. A single reader goroutine routes responses by ID to waiting callers, and routes unsolicited events to a broadcast channel.

daemon.go implements the headless initech daemon. It manages local agent panes without a TUI, listens on TCP, and streams PTY bytes to connected clients over yamux-multiplexed connections.

Protocol:

  1. Client connects via TCP, yamux server wraps the connection.
  2. Client opens stream 0 (control channel), sends hello.
  3. Server validates token, responds with hello_ok + agent list.
  4. Server sends stream_map (yamux stream ID -> agent name).
  5. Server opens one yamux stream per agent for bidirectional PTY bytes.
  6. Control channel accepts JSON commands (send, peek, resize).

daemon_agent_ctrl.go implements the configure_agent / stop_agent / restart_agent control commands used by zero-config remote daemons. The local TUI is the source of truth for agent configuration; the daemon receives concrete instructions and manages process lifecycle.

daemon_web_bridge.go adapts the headless Daemon to the web.* interfaces so the web companion can run alongside initech serve.

help.go renders the help reference card as a centered floating modal. Opened by typing "help" or "?" in the command modal. Closed by pressing Esc, backtick, or q.

ipc_dispatch.go provides a shared IPC action dispatch used by both the TUI and headless daemon. Adding a new IPC action requires one change here instead of maintaining parallel switch statements.

ipc_lifecycle.go contains IPC handlers for pane lifecycle operations: stop, start, restart, add, and remove. These handlers create, replace, or destroy panes in the running TUI session.

Separated from ipc.go (which owns the socket server, router, and message-oriented handlers) to reduce merge conflicts when lifecycle and messaging logic are edited concurrently.

logger.go provides structured application logging for the TUI. Writes to .initech/initech.log with automatic rotation at 10MB. Uses log/slog for leveled, structured output.

mcp_modal.go renders the MCP setup modal showing server status, bearer token, and pre-filled connection commands. Opened via the `mcp` command.

multisink.go implements a fan-out io.Writer that replicates writes to multiple downstream writers. Used by the daemon to stream PTY bytes to all connected clients plus the ring buffer simultaneously.

Package tui implements a terminal multiplexer with PTY management, VT emulation via charmbracelet/x/vt, and a tcell-based rendering engine.

pane_journal.go contains JSONL session file watching, entry parsing, activity state derivation, and event detection (bead claims, completions, stalls, stuck loops). The watchJSONL goroutine polls for new entries and feeds them into the pane's ring buffer and event detectors.

pane_render.go contains the Render method and visual conversion helpers for drawing a pane's terminal content and ribbon onto the tcell screen.

paste.go implements buffered paste handling for the TUI. When the terminal delivers a bracketed paste (EventPaste start, N x EventKey, EventPaste end), characters are accumulated in pasteBuf and written to the focused pane's PTY in a single call. This turns O(N) renders into O(1) for large pastes.

pid.go manages the .initech/initech.pid file and post-mortem crash detection.

On startup: write current PID. On clean exit: delete it. If the file exists at startup, the previous run exited uncleanly (signal, OOM, cgo crash). We log a warning and query the macOS system log and DiagnosticReports for evidence of what happened.

Package tui polling functions. These gather data on the render tick (tip rotation) or in background goroutines (battery). Separated from render.go so that file stays pure drawing.

reconnect.go manages persistent connections to remote daemon peers with automatic reconnection on failure. Each remote peer gets a background goroutine that handles the connect/reconnect lifecycle.

remote_conn.go manages outbound connections to headless daemon peers. On TUI startup, it dials each configured remote, performs the yamux+hello handshake, and returns RemotePane instances that the TUI adds to its pane list. Failures are logged and skipped (graceful degradation).

remote_pane.go implements PaneView for network-backed agent panes. A RemotePane connects to a headless daemon via yamux and presents the remote agent as a local pane in the TUI grid. PTY bytes flow downstream (daemon -> local emulator) for rendering, and keystrokes flow upstream (TUI -> daemon -> PTY) for input.

remote_push.go implements the TUI->daemon push protocol for zero-config remotes. After the hello handshake, the TUI computes a role-diff against the daemon's reported running agents and converges the daemon to the local config: pushes new/refresh roles via configure_agent, stops orphans via stop_agent.

render_common.go contains rendering helpers shared between Pane and RemotePane. These eliminate duplicate ribbon, cell, and cursor rendering logic.

Package tui resource management.

resource.go is the home for all resource-aware agent lifecycle code: memory monitoring, auto-suspend policy, and resume-on-message. All of this is gated behind the autoSuspend bool on the TUI struct.

When autoSuspend is false (the default), nothing in this file runs. The memory monitor goroutine is never started, the suspend policy never checks, and agents are never automatically suspended or resumed.

ringbuf.go implements a fixed-size circular byte buffer for PTY output replay on reconnect. When the buffer fills, new writes overwrite the oldest data. Snapshot returns the buffered content in chronological order.

signals_unix.go installs OS signal handlers so every external termination leaves a trace in initech.log before the process exits.

Without this, SIGTERM/SIGHUP/SIGKILL from the OS kill the process silently and leave the terminal in raw mode (screen.Fini never runs). The handlers here fix that for catchable signals. SIGKILL still can't be caught — for that case, the PID file + system log check in pid.go provides post-mortem evidence.

stderr_linux.go redirects os.Stderr (fd 2) to .initech/stderr.log at the OS file-descriptor level. This must happen before screen.Init() puts the terminal into raw mode, so that cgo/native crash stack traces are written to a file rather than into the garbled terminal buffer.

Go's own panic handler writes through os.Stderr (Go level), which also goes through fd 2, so this captures both Go panics and cgo crashes.

Uses syscall.Dup3 instead of syscall.Dup2 because Dup2 is not available on linux/arm64 (the kernel exposes only dup3 on that architecture). Dup3 with flags=0 is semantically identical to Dup2.

timer.go implements the timer data model and JSON persistence for scheduled sends ("initech at"). Timers survive restarts via .initech/timers.json.

watchdog.go implements a render watchdog that detects when the TUI main loop stops rendering (deadlock, infinite loop, blocked syscall). When no render completes within the timeout, the watchdog dumps all goroutine stacks to .initech/crash.log so the blocking path can be identified post-mortem.

web_modal.go renders the Web Companion modal showing server status, URL, and available endpoints. Opened via the `web` command.

webhook.go implements fire-and-forget HTTP POST of agent events to an external webhook URL. Each AgentEvent triggers a JSON POST with kind (dot-notation), agent, bead_id, detail, timestamp, and project fields.

Index

Constants

View Source
const DefaultRingBufSize = 256 * 1024

DefaultRingBufSize is the per-pane ring buffer capacity. 256KB holds ~12 full screen rewrites of terminal output, enough to reconstruct the current screen state on reconnect.

View Source
const DefaultStallThreshold = 10 * time.Minute

DefaultStallThreshold is the duration of inactivity before an agent with an assigned bead is considered stalled.

View Source
const IPCScanBufSize = 256 * 1024

IPCScanBufSize is the buffer limit for all IPC and control-stream scanners. Must exceed maxSendTextLen plus JSON framing overhead so that a legal near-limit send is tokenized successfully before the explicit size check runs. 256 KB gives ~4x headroom over the 64 KB text limit (ini-piyb.2).

Variables

This section is empty.

Functions

func DeleteLayout

func DeleteLayout(projectRoot string) error

DeleteLayout removes .initech/layout.yaml. Returns nil if the file doesn't exist (idempotent).

func DialIPC added in v1.18.0

func DialIPC(socketPath string) (net.Conn, error)

DialIPC connects to the IPC endpoint at the given path.

func EmitEvent

func EmitEvent(ch chan<- AgentEvent, ev AgentEvent)

EmitEvent sends an event to the TUI's event channel without blocking. If the channel is full, the event is dropped (producers must not stall).

func GenerateToken added in v1.20.0

func GenerateToken() (string, error)

GenerateToken returns a cryptographically random 32-byte token encoded as base64.

func InitLogger

func InitLogger(projectRoot string, level slog.Level) func()

InitLogger sets up the application logger writing to .initech/initech.log. level is the minimum severity (slog.LevelDebug for verbose, slog.LevelInfo for normal). Safe to call multiple times; subsequent calls replace the logger. Returns a cleanup function that closes the log file.

func LANIPv4 added in v1.21.0

func LANIPv4() string

LANIPv4 returns the first non-loopback IPv4 address from the host's network interfaces, suitable for displaying in a connection snippet. Returns "0.0.0.0" if no usable address is found (e.g., disconnected machine).

func LogDebug

func LogDebug(component string, msg string, args ...any)

LogDebug logs at DEBUG level with a component tag.

func LogError

func LogError(component string, msg string, args ...any)

LogError logs at ERROR level with a component tag.

func LogInfo

func LogInfo(component string, msg string, args ...any)

LogInfo logs at INFO level with a component tag.

func LogWarn

func LogWarn(component string, msg string, args ...any)

LogWarn logs at WARN level with a component tag.

func NewIPCScanner added in v0.25.31

func NewIPCScanner(r io.Reader) *bufio.Scanner

NewIPCScanner creates a bufio.Scanner with a buffer large enough to handle the largest supported IPC/control message (maxSendTextLen + JSON framing).

func ReadOrCreateToken added in v1.20.0

func ReadOrCreateToken(dir string) (string, error)

ReadOrCreateToken reads a persisted token from dir/token, or generates a new one and writes it. The dir is created with 0700 if it doesn't exist. The token file has 0600 permissions.

func ReadOrCreateTokenStatus added in v1.21.0

func ReadOrCreateTokenStatus(dir string) (token string, created bool, err error)

ReadOrCreateTokenStatus is like ReadOrCreateToken but also reports whether the token was newly generated (true) or read from an existing file (false). Used by 'initech serve' to decide whether to print the connection snippet.

func Run

func Run(cfg Config) error

Run starts the TUI event loop. Blocks until the user quits.

func RunDaemon added in v0.23.18

func RunDaemon(cfg DaemonConfig) error

RunDaemon starts the headless daemon. Blocks until SIGINT/SIGTERM.

func SaveLayout

func SaveLayout(projectRoot string, state LayoutState) error

SaveLayout writes the layout state to .initech/layout.yaml using atomic write (temp file + rename) to prevent corruption. Creates .initech/ if it doesn't exist.

func SetConfigureAgentBuilder added in v1.20.0

func SetConfigureAgentBuilder(b configureAgentBuilder)

SetConfigureAgentBuilder registers the function used by pushRolesToPeer to construct configure_agent payloads. The cmd layer calls this once at startup with a builder that has access to role templates and project state.

func SocketPath

func SocketPath(projectRoot, projectName string) string

SocketPath returns the IPC endpoint path for a project. On Unix this is a domain socket inside .initech/; on Windows it is a named pipe.

Types

type ActivityState

type ActivityState int

ActivityState describes what an agent is doing based on JSONL session tailing.

const (
	StateRunning   ActivityState = iota // Claude is processing.
	StateIdle                           // Waiting for input.
	StateDead                           // Process has exited; pane is no longer alive.
	StateSuspended                      // Auto-suspended by resource policy. Eligible for resume.
)

func (ActivityState) String

func (s ActivityState) String() string

String returns a human-readable label for the state.

type AgentEvent

type AgentEvent struct {
	Type   EventType
	Pane   string    // Agent name (e.g., "eng1").
	BeadID string    // Relevant bead ID (empty if N/A).
	Detail string    // Human-readable description.
	Time   time.Time // When the event was detected.
}

AgentEvent represents a semantic event from an agent's activity. Emitted by JSONL watchers and consumed by the TUI main loop.

type AgentInfo

type AgentInfo struct {
	Name       string
	Status     string        // Display text: activity string or bead ID.
	Activity   ActivityState // Actual activity state for dot color.
	Visible    bool
	Protected  bool // True when agent is protected from auto-suspend.
	LivePinned bool // True when agent is pinned to a live mode slot.
	Remote     bool // True for agents on remote peers.
}

AgentInfo describes an agent for the status overlay.

type AgentStatus added in v0.23.18

type AgentStatus struct {
	Name     string `json:"name"`
	Alive    bool   `json:"alive"`
	Activity string `json:"activity"`
	Bead     string `json:"bead,omitempty"`
}

AgentStatus describes an agent's state for the hello handshake.

type Config

type Config struct {
	Agents            []PaneConfig                          // One entry per agent pane.
	ProjectName       string                                // Used for socket path.
	ProjectRoot       string                                // Project root for .initech/ layout persistence.
	ResetLayout       bool                                  // Ignore saved layout and start with defaults.
	Verbose           bool                                  // Enable DEBUG-level logging (default: INFO).
	Version           string                                // Build version for crash reports.
	AutoSuspend       bool                                  // Enable resource-aware auto-suspend/resume.
	PressureThreshold int                                   // RSS percentage threshold (0 uses default 85).
	PaneConfigBuilder func(name string) (PaneConfig, error) // Optional factory for hot-add. Nil disables add command.
	Project           *config.Project                       // Full project config. Used for remote peer connections.
	UpdateResult      <-chan string                         // Receives newer version string from background check. Nil = no check.
	WebPort           int                                   // Port for the web companion server. 0 = disabled.
	WebhookURL        string                                // HTTP endpoint for agent event POSTs. Empty = disabled.
	SlackAppToken     string                                // Slack app-level token for Socket Mode. Empty = disabled.
	SlackBotToken     string                                // Slack bot token for Web API calls. Empty = disabled.
	McpPort           int                                   // Port for the MCP server. 0 = disabled.
	McpToken          string                                // Bearer token for MCP auth. Empty = auto-generate.
	McpBind           string                                // Bind address for MCP server. Empty = "0.0.0.0".
}

Config controls what agents the TUI launches.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a config with standard shell-only agents.

type ConfigureAgentCmd added in v1.20.0

type ConfigureAgentCmd struct {
	ID               string   `json:"id,omitempty"`
	Action           string   `json:"action"` // "configure_agent"
	Name             string   `json:"name"`
	Command          []string `json:"command"`
	Dir              string   `json:"dir"`
	Env              []string `json:"env,omitempty"`
	AgentType        string   `json:"agent_type,omitempty"`
	AutoApprove      bool     `json:"auto_approve,omitempty"`
	NoBracketedPaste bool     `json:"no_bracketed_paste,omitempty"`
	SubmitKey        string   `json:"submit_key,omitempty"`
	ClaudeMD         string   `json:"claude_md,omitempty"`      // Role-level CLAUDE.md content.
	RootClaudeMD     string   `json:"root_claude_md,omitempty"` // Project-root CLAUDE.md content.
}

ConfigureAgentCmd is sent by a client to push an agent configuration to a zero-config daemon. The daemon creates the workspace, writes CLAUDE.md files, creates a Pane, and starts the process.

type ControlCmd added in v0.23.18

type ControlCmd struct {
	ID     string `json:"id,omitempty"` // Request ID for response correlation.
	Action string `json:"action"`       // "send", "peek", "resize", "schedule", etc.
	Target string `json:"target"`       // Agent name.
	Host   string `json:"host,omitempty"`
	Text   string `json:"text,omitempty"`
	Enter  bool   `json:"enter,omitempty"`
	Lines  int    `json:"lines,omitempty"`
	Rows   int    `json:"rows,omitempty"`
	Cols   int    `json:"cols,omitempty"`
	FireAt string `json:"fire_at,omitempty"` // RFC3339 for schedule command.
}

ControlCmd is a command sent on the control channel after handshake.

type ControlMux added in v0.23.27

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

ControlMux multiplexes a yamux control stream for concurrent request/response use by multiple RemotePanes. Each Request call gets a unique ID, writes the command, and waits for the response with that ID. A single readLoop goroutine reads all messages and dispatches by ID.

func NewControlMux added in v0.23.27

func NewControlMux(conn net.Conn) *ControlMux

NewControlMux creates a multiplexer for the given control stream connection and starts the background reader goroutine.

func (*ControlMux) Close added in v0.23.27

func (m *ControlMux) Close()

Close shuts down the multiplexer by closing the underlying connection.

func (*ControlMux) Done added in v0.23.27

func (m *ControlMux) Done() <-chan struct{}

Done returns a channel that is closed when the read loop exits (stream died).

func (*ControlMux) Events added in v0.23.27

func (m *ControlMux) Events() <-chan ControlResp

Events returns a channel that receives unsolicited server-pushed messages (responses with no ID, such as agent_died or timer_fired events).

func (*ControlMux) Request added in v0.23.27

func (m *ControlMux) Request(cmd ControlCmd) (ControlResp, error)

Request sends a command on the control stream and waits for the correlated response (matched by ID). Returns an error if the stream is closed or the request times out after 10 seconds.

func (*ControlMux) RequestRaw added in v1.20.0

func (m *ControlMux) RequestRaw(payload any) (ControlResp, error)

RequestRaw sends an arbitrary JSON-encodable payload on the control stream and waits for the correlated response. The payload must serialise to a JSON object with a top-level "id" field; if the field is empty, RequestRaw generates a fresh ID and overwrites it via a marshal-then-merge pass.

Used for control commands whose payload struct is not ControlCmd (for example, ConfigureAgentCmd and StopAgentCmd).

func (*ControlMux) SetRequestHandler added in v0.25.31

func (m *ControlMux) SetRequestHandler(h RequestHandler)

SetRequestHandler registers a callback for incoming commands (messages with both an ID and an Action field). The handler processes the command and returns a response that is written back with the same ID.

type ControlResp added in v0.23.18

type ControlResp struct {
	ID       string `json:"id,omitempty"` // Echoed from request for correlation.
	OK       bool   `json:"ok"`
	Error    string `json:"error,omitempty"`
	Data     string `json:"data,omitempty"`
	Action   string `json:"action,omitempty"`    // Set for unsolicited commands (e.g. "forward_send", "stream_added").
	Target   string `json:"target,omitempty"`    // Agent name for forward_send.
	Text     string `json:"text,omitempty"`      // Message text for forward_send.
	Enter    bool   `json:"enter,omitempty"`     // Append Enter for forward_send.
	StreamID uint32 `json:"stream_id,omitempty"` // yamux stream ID for stream_added.
	Name     string `json:"name,omitempty"`      // Agent name for stream_added.
}

ControlResp is the response to a control command. It also carries unsolicited server-pushed commands (e.g. forward_send, stream_added) when Action is set.

type Daemon added in v0.23.18

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

Daemon manages headless agent panes and streams them to a yamux client.

func (*Daemon) AllPanes added in v0.23.53

func (d *Daemon) AllPanes() ([]PaneInfo, bool)

func (*Daemon) FindPaneView added in v0.23.53

func (d *Daemon) FindPaneView(name string) (PaneView, bool)

func (*Daemon) HandleExtended added in v0.23.53

func (d *Daemon) HandleExtended(conn net.Conn, req IPCRequest, rawJSON []byte) bool

func (*Daemon) HandleSend added in v0.23.53

func (d *Daemon) HandleSend(conn net.Conn, req IPCRequest)

func (*Daemon) NotifyConfig added in v1.10.0

func (d *Daemon) NotifyConfig() (webhookURL, project string)

func (*Daemon) Timers added in v0.23.53

func (d *Daemon) Timers() *TimerStore

type DaemonConfig added in v0.23.18

type DaemonConfig struct {
	Project *config.Project
	Agents  []PaneConfig
	Version string
	Verbose bool
	WebPort int // Web companion port. 0 = disabled.
}

DaemonConfig holds the configuration for a headless daemon session.

type Divider

type Divider struct {
	X, Y     int
	Len      int
	Vertical bool
}

Divider describes a vertical or horizontal line between panes.

type ErrorMsg added in v0.23.18

type ErrorMsg struct {
	Action string `json:"action"` // "error"
	Error  string `json:"error"`
}

ErrorMsg is sent on handshake failure.

type EventType

type EventType int

EventType classifies semantic events from agent activity detection.

const (
	EventBeadCompleted     EventType = iota // Agent finished a bead (DONE comment, ready_for_qa).
	EventBeadClaimed                        // Agent claimed a bead (in_progress).
	EventBeadFailed                         // QA failed a bead or agent reported failure.
	EventBeadAssigned                       // Agent received work via initech assign.
	EventBeadDelivered                      // Agent completed work via initech deliver (pass or fail).
	EventAgentStalled                       // No output for configurable threshold (warning).
	EventAgentStuck                         // Extended inactivity or error loop detected.
	EventAgentIdleWithBead                  // Agent went running->idle while holding a bead.
	EventAgentSuspended                     // Agent auto-suspended by resource pressure policy.
	EventAgentResumed                       // Agent resumed from suspension (triggered by message).
	EventMessageSent                        // Message delivered to an agent via IPC send.
	EventAgentStarted                       // Agent pane started via IPC.
	EventAgentStopped                       // Agent pane stopped via IPC.
	EventAgentRestarted                     // Agent pane restarted via IPC.
	EventAgentAdded                         // New agent pane added to session.
	EventAgentRemoved                       // Agent pane removed from session.
	EventTimerFired                         // Scheduled timer delivered its message.
	EventPeerConnected                      // Remote daemon connected.
	EventPeerDisconnected                   // Remote daemon disconnected.
	EventLiveSwap                           // Live mode swapped an agent into/out of a slot.
)

func (EventType) String

func (e EventType) String() string

String returns a human-readable label for the event type.

type HelloMsg added in v0.23.18

type HelloMsg struct {
	Action   string `json:"action"`    // "hello"
	Version  int    `json:"version"`   // Protocol version (1).
	Token    string `json:"token"`     // Auth token.
	PeerName string `json:"peer_name"` // Client's peer name.
}

HelloMsg is sent by the client to initiate the handshake.

type HelloOKMsg added in v0.23.18

type HelloOKMsg struct {
	Action   string        `json:"action"`    // "hello_ok"
	Version  int           `json:"version"`   // Protocol version (1).
	PeerName string        `json:"peer_name"` // Server's peer name.
	Agents   []AgentStatus `json:"agents"`    // Current agent states.
}

HelloOKMsg is the server's response to a successful hello.

type IPCHost added in v0.23.53

type IPCHost interface {
	// FindPaneView looks up a pane by name. Returns (nil, true) if not found.
	// Returns (nil, false) if the host is shutting down and the lookup could
	// not be performed.
	FindPaneView(name string) (PaneView, bool)

	// AllPanes returns status info for all managed panes. The bool is false
	// if the host is shutting down.
	AllPanes() ([]PaneInfo, bool)

	// HandleSend processes the "send" action. Each runtime has different
	// send semantics (TUI: suspended pane resume, remote forwarding;
	// Daemon: client routing, auto-forward).
	HandleSend(conn net.Conn, req IPCRequest)

	// Timers returns the timer store, or nil if timers are not available.
	Timers() *TimerStore

	// NotifyConfig returns the webhook URL and project name for posting
	// notifications. Returns empty strings if webhook is not configured.
	NotifyConfig() (webhookURL, project string)

	// HandleExtended processes runtime-specific actions not covered by
	// the shared dispatch (e.g. TUI lifecycle commands). Returns true if
	// the action was handled, false if it should fall through to "unknown".
	HandleExtended(conn net.Conn, req IPCRequest, rawJSON []byte) bool
}

IPCHost provides the runtime-specific capabilities needed by the shared IPC dispatch. Both TUI and Daemon implement this interface.

type IPCRequest

type IPCRequest struct {
	Action string `json:"action"` // "send", "peek", "list", "peers_query"
	Target string `json:"target"` // Role name (for send/peek).
	Host   string `json:"host"`   // Remote peer name (for cross-machine send). Empty = local.
	Text   string `json:"text"`   // Text to inject (for send).
	Lines  int    `json:"lines"`  // Number of lines to return (for peek, 0 = all).
	Enter  bool   `json:"enter"`  // Append Enter after text (for send).
}

IPCRequest is the JSON structure sent by CLI commands to the TUI socket.

type IPCResponse

type IPCResponse struct {
	OK    bool   `json:"ok"`
	Error string `json:"error,omitempty"`
	Data  string `json:"data,omitempty"` // Pane content for peek, pane list for list.
}

IPCResponse is the JSON structure returned by the TUI socket.

type JournalEntry

type JournalEntry struct {
	Type      string    // "user", "assistant", "progress", "system", "last-prompt", etc.
	Content   string    // Text content (assistant message, tool output). Capped at 4KB.
	ToolName  string    // For tool_use/tool_result: which tool was called.
	ExitCode  int       // For Bash tool results: exit code if available.
	Timestamp time.Time // When this entry was written.
}

JournalEntry represents a parsed JSONL entry from a Claude Code session.

type LayoutMode

type LayoutMode int

LayoutMode determines how panes are arranged on screen.

const (
	LayoutFocus LayoutMode = iota // Single pane, full screen.
	LayoutGrid                    // Arbitrary NxM grid.
	Layout2Col                    // Main pane left, stacked right.
	LayoutLive                    // Dynamic pane rotation by activity conviction score.
)

type LayoutState

type LayoutState struct {
	Mode      LayoutMode      `yaml:"mode"`
	GridCols  int             `yaml:"grid_cols"`
	GridRows  int             `yaml:"grid_rows"`
	Zoomed    bool            `yaml:"zoomed,omitempty"`
	Focused   string          `yaml:"focused"`             // Pane key, not index.
	Hidden    map[string]bool `yaml:"hidden,omitempty"`    // Pane keys that are hidden.
	Protected map[string]bool `yaml:"protected,omitempty"` // Pane keys protected from auto-suspend.
	Order     []string        `yaml:"order,omitempty"`     // Pane keys in display order (from show command).
	Overlay   bool            `yaml:"overlay"`

	// GridExplicit is true when the user chose grid dimensions via :grid CxR
	// or Alt+2/Alt+3. When set, recalcGrid skips auto-recalculation so peer
	// updates and hot-adds don't overwrite the user's choice.
	GridExplicit bool `yaml:"grid_explicit,omitempty"`

	// Per-column and per-row proportional sizing (future).
	// nil means uniform. Values are relative weights (e.g., [60, 40] = 60%/40%).
	ColWeights []int `yaml:"col_weights,omitempty"`
	RowWeights []int `yaml:"row_weights,omitempty"`

	// Live mode: dynamic pane rotation by conviction score.
	LivePinned map[string]int `yaml:"live_pinned,omitempty"` // Agent name -> slot index.
	LiveSlots  []string       `yaml:"live_slots,omitempty"`  // Current agent name per slot (updated by live engine).
	LiveAuto   bool           `yaml:"live_auto,omitempty"`   // True = auto-size grid from active agent count.
}

LayoutState captures the complete layout intent. It is the single authority on what the screen should look like. Trivially serializable to YAML for persistent layout.

func DefaultLayoutState

func DefaultLayoutState(paneNames []string) LayoutState

DefaultLayoutState creates a LayoutState with auto-calculated grid dimensions for the given pane names.

func LoadLayout

func LoadLayout(projectRoot string, paneKeys []string) (LayoutState, bool)

LoadLayout reads .initech/layout.yaml and merges it into a LayoutState. The caller passes the currently known pane keys. Unknown remote pane keys (host:name) are preserved so delayed peer reconnects can reuse saved hidden and order preferences, while stale local-only names are filtered out. Returns false if the file doesn't exist, is empty, contains invalid YAML, or would result in all currently known panes hidden.

type LiveEngine added in v1.13.0

type LiveEngine struct {
	Slots      []string       // Current agent per slot.
	Pinned     map[string]int // Agent name -> fixed slot index.
	RolesOrder []string       // Config roles list for stable ordering in auto mode.
	// contains filtered or unexported fields
}

LiveEngine manages dynamic slot assignments for Live Mode. It ranks agents by conviction score and assigns them to slots, respecting pinned assignments. Anti-thrashing mechanisms (hold time, hysteresis, one-swap-per-tick) prevent flickering when agent scores change rapidly.

func NewLiveEngine added in v1.13.0

func NewLiveEngine(numSlots int, pinned map[string]int, rolesOrder []string) *LiveEngine

NewLiveEngine creates a LiveEngine with the given number of slots and pinned assignments. rolesOrder provides stable ordering for auto mode; pass nil for fixed-slot mode where order is determined by slot index.

func (*LiveEngine) Tick added in v1.13.0

func (le *LiveEngine) Tick(panes []PaneView, now time.Time) []string

Tick computes slot assignments from the current pane states.

Pinned agents are placed in their fixed slots unconditionally. Dynamic slots use anti-thrashing rules:

  • Hold time: a slot keeps its agent for at least liveHoldDuration after assignment.
  • Hysteresis: displacing a strong occupant (>= keepThreshold) requires score >= claimThreshold and beating it by claimMargin. Displacing a weak occupant (< keepThreshold or dead) only requires score >= keepThreshold. Keeping a slot requires >= keepThreshold.
  • One-swap-per-tick: at most one displacement per call, preventing full-screen flashes.

Filling an empty slot is not a displacement and is always allowed. Returns the ordered list of pane names, one per slot. Empty strings indicate unfilled slots.

func (*LiveEngine) TickAuto added in v1.14.0

func (le *LiveEngine) TickAuto(panes []PaneView, now time.Time) []string

TickAuto computes the visible agent list for Live Auto mode. Unlike Tick (fixed slot count), TickAuto dynamically grows and shrinks the visible set based on conviction scores:

  • Pinned agents are always visible regardless of score.
  • Other agents are visible if score >= liveKeepThreshold.
  • Hold time: a newly visible agent stays visible for liveHoldDuration even if its score drops below threshold.
  • One change per tick: at most one agent added or removed per call, preventing the grid from jumping sizes instantly.

Returns the ordered list of visible agent names (pinned first, then by score descending).

type MultiSink added in v0.23.22

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

MultiSink writes to all registered writers. Dead writers (those that return errors) are automatically removed. All methods are safe for concurrent use.

func NewMultiSink added in v0.23.22

func NewMultiSink() *MultiSink

NewMultiSink creates an empty MultiSink. Add writers with Add().

func (*MultiSink) Add added in v0.23.22

func (ms *MultiSink) Add(w io.Writer)

Add registers a writer to receive future writes.

func (*MultiSink) Len added in v0.23.22

func (ms *MultiSink) Len() int

Len returns the current number of registered writers.

func (*MultiSink) Remove added in v0.23.22

func (ms *MultiSink) Remove(w io.Writer)

Remove unregisters a writer. No-op if not found.

func (*MultiSink) Write added in v0.23.22

func (ms *MultiSink) Write(p []byte) (int, error)

Write sends p to all registered writers. Writers that return errors are removed automatically (dead client cleanup). Returns len(p), nil to satisfy io.Writer (the caller should not stall on downstream failures).

The writer list is snapshot'd under lock, then writes happen lock-free. This prevents a slow/blocked writer from holding the lock and stalling Add/Remove or other concurrent Write calls.

type Pane

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

Pane represents a terminal pane backed by a PTY process. It uses a SafeEmulator from charmbracelet/x/vt for terminal emulation.

func NewPane

func NewPane(cfg PaneConfig, rows, cols int) (*Pane, error)

NewPane creates a terminal pane running the configured command (or $SHELL).

func (*Pane) ActiveRunBytes added in v1.13.0

func (p *Pane) ActiveRunBytes() int64

ActiveRunBytes returns bytes received during the current running streak.

func (*Pane) ActiveRunStart added in v1.13.0

func (p *Pane) ActiveRunStart() time.Time

ActiveRunStart returns when the current running streak began.

func (*Pane) Activity

func (p *Pane) Activity() ActivityState

Activity returns the current activity state based on JSONL session tailing.

func (*Pane) AgentType added in v0.25.21

func (p *Pane) AgentType() string

AgentType returns the configured semantic agent type for this pane.

func (*Pane) BeadID

func (p *Pane) BeadID() string

BeadID returns the first (primary) bead ID, or empty string.

func (*Pane) BeadIDs added in v1.16.0

func (p *Pane) BeadIDs() []string

BeadIDs returns all assigned bead IDs.

func (*Pane) BeadTitle added in v1.16.2

func (p *Pane) BeadTitle() string

BeadTitle returns the title of the primary bead, or empty string.

func (*Pane) ClearNetworkSink added in v0.23.21

func (p *Pane) ClearNetworkSink()

ClearNetworkSink removes the network sink. Safe to call if no sink is set.

func (*Pane) Close

func (p *Pane) Close()

Close terminates the PTY, kills the process, and signals goroutines to exit.

func (*Pane) DrainQueue

func (p *Pane) DrainQueue() []QueuedMessage

DrainQueue returns all queued messages in FIFO order and clears the queue. Called on resume to deliver buffered messages.

Caller must be on the main goroutine (via runOnMain).

func (*Pane) Emulator added in v0.23.13

func (p *Pane) Emulator() *vt.SafeEmulator

Emulator returns the pane's terminal emulator for cell-level access.

func (*Pane) EnqueueMessage

func (p *Pane) EnqueueMessage(text string, enter bool) bool

EnqueueMessage appends a message to the pane's queue. If the queue is at capacity (maxMessageQueue), the oldest message is dropped. Returns true if a message was dropped to make room.

Caller must be on the main goroutine (via runOnMain).

func (*Pane) FlushPaste added in v1.12.2

func (p *Pane) FlushPaste(content []byte)

FlushPaste writes the entire paste content to the PTY in a single operation, wrapped in bracketed paste markers (unless noBracketedPaste is set). Uses sendMu to serialize with concurrent IPC sends and prevent interleaving. For large pastes (>64KB), the content is written in chunks to avoid blocking the PTY write buffer for extended periods.

func (*Pane) ForwardMouse

func (p *Pane) ForwardMouse(ev uv.MouseEvent)

ForwardMouse sends a mouse event to the emulator with pane-local content coordinates translated to emulator coordinates. The emulator silently drops the event if the child process hasn't enabled mouse reporting.

func (*Pane) GetRegion added in v0.23.14

func (p *Pane) GetRegion() Region

GetRegion returns the pane's screen region.

func (*Pane) Host added in v0.23.13

func (p *Pane) Host() string

Host returns the hostname for this pane. Local panes always return "".

func (*Pane) InResumeGrace

func (p *Pane) InResumeGrace() bool

InResumeGrace returns true if the pane is within the post-resume grace period. During this window the pane is exempt from auto-suspend and idle-with-bead notifications.

func (*Pane) InScrollback

func (p *Pane) InScrollback() bool

InScrollback returns true when the pane is viewing scrollback history.

func (*Pane) IsAlive

func (p *Pane) IsAlive() bool

IsAlive returns whether the pane's shell process is still running.

func (*Pane) IsProtected added in v1.17.0

func (p *Pane) IsProtected() bool

IsProtected reports whether the operator has protected this pane from auto-suspension. Protected panes are always kept running.

func (*Pane) IsSuspended

func (p *Pane) IsSuspended() bool

IsSuspended returns whether the pane has been stopped by the auto-suspend policy. A suspended pane is distinct from dead (crashed) and will auto-resume when a message arrives.

func (*Pane) LastEventTime added in v1.13.0

func (p *Pane) LastEventTime() time.Time

LastEventTime returns when an AgentEvent last fired for this pane.

func (*Pane) LastMessageReceived added in v1.13.0

func (p *Pane) LastMessageReceived() time.Time

LastMessageReceived returns when a message was last delivered to this pane.

func (*Pane) LastOutputTime

func (p *Pane) LastOutputTime() time.Time

LastOutputTime returns the last time PTY output was received.

func (*Pane) MemoryRSS

func (p *Pane) MemoryRSS() int64

MemoryRSS returns the pane's last polled RSS in kilobytes. Returns 0 if the memory monitor has not yet polled or the process is dead.

func (*Pane) Name added in v0.23.13

func (p *Pane) Name() string

Name returns the pane's display name (role name).

func (*Pane) QueueLen

func (p *Pane) QueueLen() int

QueueLen returns the number of messages waiting in the queue.

func (*Pane) RecentEntries

func (p *Pane) RecentEntries() []JournalEntry

RecentEntries returns a copy of the recent JSONL entries ring buffer.

func (*Pane) Render

func (p *Pane) Render(screen tcell.Screen, focused bool, dimmed bool, index int, sel Selection)

Render draws the pane's bottom ribbon and terminal content onto the tcell screen. When dimmed is true, foreground colors are reduced to ~70% brightness. The index parameter is the 1-based pane number shown in the ribbon badge. All writes are clamped to the pane's region to prevent bleed-through.

func (*Pane) Resize

func (p *Pane) Resize(rows, cols int)

Resize updates the PTY and emulator dimensions. Resizes the PTY first so the child process receives the size change before the emulator buffer reorganizes. Holds renderMu across both operations to prevent readLoop from writing old-geometry PTY output into a new-geometry emulator buffer (ini-yah).

func (*Pane) ScrollDown

func (p *Pane) ScrollDown(n int)

ScrollDown moves the viewport down (toward live output) by n rows. When scrollOffset reaches 0, the pane returns to live view.

func (*Pane) ScrollUp

func (p *Pane) ScrollUp(n int)

ScrollUp moves the viewport up (into scrollback history) by n rows.

func (*Pane) SendKey

func (p *Pane) SendKey(ev *tcell.EventKey)

SendKey translates a tcell key event into a charmbracelet KeyPressEvent and sends it through the emulator, which encodes it for the PTY.

func (*Pane) SendPaste

func (p *Pane) SendPaste(start bool)

SendPaste writes a bracketed paste marker to the PTY. On start=true it writes \x1b[200~ (paste start); on start=false it writes \x1b[201~ (paste end). The child process uses these delimiters to distinguish pasted content from typed keystrokes. No-op if the PTY is not open.

func (*Pane) SendText added in v0.23.13

func (p *Pane) SendText(text string, enter bool)

SendText injects text into the pane using the harness-appropriate local delivery path. Claude panes use bracketed paste; raw-input panes like Codex write the body directly to the PTY and delay submit to avoid paste-burst suppression. The Codex ready-wait runs before acquiring sendMu so it does not block concurrent sends.

func (*Pane) SessionDesc

func (p *Pane) SessionDesc() string

SessionDesc returns the session description extracted from Claude's cursor row.

func (*Pane) SetBead

func (p *Pane) SetBead(id, title string)

SetBead sets a single bead ID (backward compat). Pass "" to clear. When assigning a non-empty bead, resets the idle-with-bead grace window so the notification doesn't fire on stale lastOutputTime (ini-t42).

func (*Pane) SetBeads added in v1.16.0

func (p *Pane) SetBeads(ids []string)

SetBeads sets multiple bead IDs. Pass nil to clear. When assigning non-empty beads, resets the idle-with-bead grace window so the notification doesn't fire on stale lastOutputTime (ini-t42).

func (*Pane) SetNetworkSink added in v0.23.21

func (p *Pane) SetNetworkSink(w io.Writer)

SetNetworkSink sets the writer that receives a copy of all PTY output. Used by the daemon to stream bytes to a connected client. The sink receives bytes after the emulator, so network backpressure cannot stall local rendering.

func (*Pane) SetProtected added in v1.17.0

func (p *Pane) SetProtected(v bool)

SetProtected marks the pane as protected (true) or unprotected (false).

func (*Pane) SetResumeGrace

func (p *Pane) SetResumeGrace(until time.Time)

SetResumeGrace sets the post-resume grace period expiration.

func (*Pane) SetSuspended

func (p *Pane) SetSuspended(v bool)

SetSuspended marks the pane as suspended or resumed by the auto-suspend policy.

func (*Pane) SetVisible

func (p *Pane) SetVisible(v bool)

SetVisible controls whether the pane appears in the layout. Hiding a pane does not stop its process or resize its emulator.

func (*Pane) Start

func (p *Pane) Start()

Start launches the pane's background goroutines (PTY reader, response loop, JSONL watcher). Must be called after safeGo and eventCh are wired. If safeGo is nil, falls back to bare goroutine launches.

func (*Pane) SubmitKey added in v0.25.9

func (p *Pane) SubmitKey() string

SubmitKey returns the configured submit key sequence for this pane.

func (*Pane) Subscribe added in v1.0.0

func (p *Pane) Subscribe(id string) chan []byte

Subscribe registers a new subscriber for PTY byte fan-out and returns a buffered channel that receives copies of all bytes read from the PTY. The channel has a 64KB buffer; if a slow consumer falls behind, the oldest entry is dropped to prevent blocking the readLoop. Callers must eventually call Unsubscribe to release resources.

func (*Pane) Unsubscribe added in v1.0.0

func (p *Pane) Unsubscribe(id string)

Unsubscribe removes a subscriber and closes its channel. Safe to call with an ID that was never subscribed or was already unsubscribed.

func (*Pane) Visible

func (p *Pane) Visible() bool

Visible returns whether the pane is included in the current layout. Hidden panes keep their emulator running at their last visible size.

type PaneConfig

type PaneConfig struct {
	Name             string   // Display name (role name).
	Command          []string // Command + args. Empty means use $SHELL.
	Dir              string   // Working directory. Empty means inherit.
	Env              []string // Extra env vars (KEY=VALUE). TERM is always set.
	AgentType        string   // Semantic agent type: claude-code (default), codex, or generic.
	AutoApprove      bool     // When true, auto-approve matching permission prompts.
	NoBracketedPaste bool     // Final resolved injection mode. True uses typed input instead of bracketed paste.
	BeadsEnabled     bool     // When false, skip bead detection (detectBeadClaim, detectCompletion, detectStall).
	SubmitKey        string   // Key sequence to submit input: "enter" (default) or "ctrl+enter".
}

PaneConfig describes how to launch a pane's process.

type PaneInfo added in v0.23.53

type PaneInfo struct {
	Name     string `json:"name"`
	Host     string `json:"host,omitempty"`
	Activity string `json:"activity"`
	Alive    bool   `json:"alive"`
	Visible  bool   `json:"visible"`
}

PaneInfo is the JSON structure returned by the "list" IPC action.

type PaneRender

type PaneRender struct {
	Pane    PaneView
	Region  Region
	Index   int  // 1-based pane number (position in full pane list).
	Focused bool // Receives keyboard input.
	Dimmed  bool // Render with reduced contrast.
}

PaneRender describes how to render a single pane.

type PaneView added in v0.23.13

type PaneView interface {
	Name() string
	Host() string // "" for local panes.
	IsAlive() bool
	IsSuspended() bool
	IsProtected() bool
	Activity() ActivityState
	LastOutputTime() time.Time
	BeadID() string
	BeadIDs() []string
	SessionDesc() string
	Emulator() *vt.SafeEmulator
	GetRegion() Region
	SetBead(id, title string)
	SetBeads(ids []string)
	SendKey(ev *tcell.EventKey)
	SendText(text string, enter bool)
	AgentType() string
	SubmitKey() string // "" or "enter" (default), "ctrl+enter".
	ActiveRunStart() time.Time
	ActiveRunBytes() int64
	LastMessageReceived() time.Time
	LastEventTime() time.Time
	Render(screen tcell.Screen, focused bool, dimmed bool, index int, sel Selection)
	Resize(rows, cols int)
	Close()
}

PaneView abstracts pane behavior so both local panes (Pane) and future network-backed panes (RemotePane) can be used interchangeably by the TUI.

type PeerInfo added in v0.23.20

type PeerInfo struct {
	Name   string   `json:"name"`
	Agents []string `json:"agents"`
}

PeerInfo describes a peer and its agents for the peers_query response.

type PersistentLayout

type PersistentLayout struct {
	Grid         string         `yaml:"grid"`                    // e.g. "3x2"
	GridExplicit bool           `yaml:"grid_explicit,omitempty"` // True = user chose CxR explicitly; don't auto-resize.
	Hidden       []string       `yaml:"hidden,omitempty"`        // Pane keys: name for local, host:name for remote.
	Protected    []string       `yaml:"protected,omitempty"`     // Pane keys protected from auto-suspend.
	DepPinned    []string       `yaml:"pinned,omitempty"`        // Deprecated: migration shim for old layout.yaml files.
	Order        []string       `yaml:"order,omitempty"`         // Pane keys in display order (from show command).
	Mode         string         `yaml:"mode"`                    // "grid", "focus", "main", "live"
	LivePinned   map[string]int `yaml:"live_pinned,omitempty"`   // Agent name -> fixed slot index for live mode.
	LiveAuto     bool           `yaml:"live_auto,omitempty"`     // True = auto-size grid from active agent count.
}

PersistentLayout is the subset of LayoutState that survives sessions. Focused pane is deliberately excluded (momentary choice, not a preference). Overlay and weights are excluded (not layout-changing from the user's perspective).

type QueuedMessage

type QueuedMessage struct {
	Text  string
	Enter bool
	Time  time.Time
}

QueuedMessage is a message waiting to be delivered to a suspended pane. On resume, the queue is drained in FIFO order via injectText.

type Region

type Region struct {
	X, Y, W, H int
}

Region defines a rectangular area on screen (outer bounds including border).

func (Region) InnerSize

func (r Region) InnerSize() (cols, rows int)

InnerSize returns the renderable content area (full width, minus 1 row for bottom ribbon).

func (Region) TerminalSize added in v1.17.12

func (r Region) TerminalSize() (cols, rows int)

TerminalSize returns the dimensions given to the VT emulator and PTY. Subtracts 2 rows from region height: 1 for the bottom ribbon, 1 for the top activity bar. The child process sees these dimensions via LINES and SIGWINCH, so content it renders fits entirely within the visible area.

type RemotePane added in v0.23.19

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

RemotePane is a PaneView backed by a yamux stream to a headless daemon. The local VT emulator receives PTY bytes from the stream for rendering. Keystrokes are forwarded upstream to the daemon for injection into the PTY.

func NewRemotePane added in v0.23.19

func NewRemotePane(name, host string, stream net.Conn, mux *ControlMux, cols, rows int) *RemotePane

NewRemotePane creates a RemotePane connected to a remote agent. The mux is shared across all RemotePanes from the same peer connection. The caller must call Start() to begin the readLoop goroutine.

func (*RemotePane) ActiveRunBytes added in v1.13.0

func (rp *RemotePane) ActiveRunBytes() int64

func (*RemotePane) ActiveRunStart added in v1.13.0

func (rp *RemotePane) ActiveRunStart() time.Time

func (*RemotePane) Activity added in v0.23.19

func (rp *RemotePane) Activity() ActivityState

func (*RemotePane) AgentType added in v0.25.21

func (rp *RemotePane) AgentType() string

func (*RemotePane) BeadID added in v0.23.19

func (rp *RemotePane) BeadID() string

func (*RemotePane) BeadIDs added in v1.16.0

func (rp *RemotePane) BeadIDs() []string

func (*RemotePane) Close added in v0.23.19

func (rp *RemotePane) Close()

Close terminates the yamux stream and stops background goroutines. Uses a timeout to prevent hanging on dead yamux streams during shutdown.

func (*RemotePane) DrainData added in v0.23.53

func (rp *RemotePane) DrainData()

DrainData moves pending byte chunks from dataCh into the emulator. Called by the TUI main loop for ALL remote panes (visible or hidden) so hidden panes don't accumulate stale data. Budget limits bytes per call to prevent stalls when the ring buffer replays megabytes into a new pane.

func (*RemotePane) Emulator added in v0.23.19

func (rp *RemotePane) Emulator() *vt.SafeEmulator

func (*RemotePane) GetRegion added in v0.23.19

func (rp *RemotePane) GetRegion() Region

func (*RemotePane) Host added in v0.23.19

func (rp *RemotePane) Host() string

func (*RemotePane) IsAlive added in v0.23.19

func (rp *RemotePane) IsAlive() bool

func (*RemotePane) IsProtected added in v1.17.0

func (rp *RemotePane) IsProtected() bool

func (*RemotePane) IsSuspended added in v0.23.19

func (rp *RemotePane) IsSuspended() bool

func (*RemotePane) LastEventTime added in v1.13.0

func (rp *RemotePane) LastEventTime() time.Time

func (*RemotePane) LastMessageReceived added in v1.13.0

func (rp *RemotePane) LastMessageReceived() time.Time

func (*RemotePane) LastOutputTime added in v0.23.19

func (rp *RemotePane) LastOutputTime() time.Time

func (*RemotePane) Mux added in v1.20.0

func (rp *RemotePane) Mux() *ControlMux

Mux exposes the shared control multiplexer for this RemotePane's peer connection. Callers (e.g. :remote-stop) use it to send control commands such as stop_agent that target the remote daemon directly.

func (*RemotePane) Name added in v0.23.19

func (rp *RemotePane) Name() string

func (*RemotePane) Render added in v0.23.19

func (rp *RemotePane) Render(screen tcell.Screen, focused bool, dimmed bool, index int, sel Selection)

Render draws the remote pane with [R] badge in the ribbon title.

func (*RemotePane) Resize added in v0.23.19

func (rp *RemotePane) Resize(rows, cols int)

Resize updates the local emulator immediately and debounces the control command to the remote daemon. Rapid resize events (SIGWINCH storms from dragging the terminal edge) are collapsed: only the final geometry is sent after a 50ms quiet period.

func (*RemotePane) SendKey added in v0.23.19

func (rp *RemotePane) SendKey(ev *tcell.EventKey)

SendKey encodes a tcell key event as raw ANSI bytes and writes them upstream to the daemon, which injects them into the remote PTY.

func (*RemotePane) SendText added in v0.23.19

func (rp *RemotePane) SendText(text string, enter bool)

SendText sends text to the remote agent via the control channel. This uses the daemon's "send" command which injects text through the emulator path (same as initech send), handling Ctrl+S stash and paste detection.

func (*RemotePane) SessionDesc added in v0.23.19

func (rp *RemotePane) SessionDesc() string

func (*RemotePane) SetBead added in v0.23.19

func (rp *RemotePane) SetBead(id, title string)

func (*RemotePane) SetBeads added in v1.16.0

func (rp *RemotePane) SetBeads(ids []string)

func (*RemotePane) SetVisible added in v0.26.2

func (rp *RemotePane) SetVisible(v bool)

SetVisible controls whether the remote pane appears in the layout.

func (*RemotePane) Start added in v0.23.19

func (rp *RemotePane) Start()

Start launches background goroutines: readLoop (stream -> dataCh) and responseLoop (drains emulator responses so Write never blocks on the internal io.Pipe).

func (*RemotePane) SubmitKey added in v0.25.9

func (rp *RemotePane) SubmitKey() string

func (*RemotePane) Visible added in v0.26.2

func (rp *RemotePane) Visible() bool

Visible returns whether the remote pane should appear in the layout.

type RenderPlan

type RenderPlan struct {
	Panes    []PaneRender // One entry per pane to draw (ordered).
	Dividers []Divider    // Vertical lines between pane columns.
	ScreenW  int
	ScreenH  int

	// ValidatedFocus is the name of the pane that actually receives input.
	// May differ from LayoutState.Focused if that pane is hidden.
	ValidatedFocus string
}

RenderPlan is the complete set of instructions for one frame. Produced by computeLayout, consumed by the render loop.

type RequestHandler added in v0.25.31

type RequestHandler func(resp ControlResp) ControlResp

RequestHandler is called when the remote end sends a command (a message with both an ID and an Action). The handler processes the command and returns a response. Used by the TUI to handle forward_send from the daemon.

type RestartAgentCmd added in v1.20.0

type RestartAgentCmd struct {
	ID     string `json:"id,omitempty"`
	Action string `json:"action"` // "restart_agent"
	Name   string `json:"name"`
}

RestartAgentCmd kills the existing process and starts a new one with the same config (command/dir/env/etc.).

type RingBuf added in v0.23.21

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

RingBuf is a fixed-size circular byte buffer. It implements io.Writer. All methods are safe for concurrent use.

func NewRingBuf added in v0.23.21

func NewRingBuf(size int) *RingBuf

NewRingBuf creates a ring buffer with the given capacity in bytes.

func (*RingBuf) Len added in v0.23.21

func (r *RingBuf) Len() int

Len returns the number of bytes currently stored.

func (*RingBuf) Snapshot added in v0.23.21

func (r *RingBuf) Snapshot() []byte

Snapshot returns the buffered content in chronological order (oldest first). Returns nil if the buffer is empty.

func (*RingBuf) Write added in v0.23.21

func (r *RingBuf) Write(p []byte) (int, error)

Write appends p to the buffer, overwriting oldest data when full. Always returns len(p), nil (io.Writer contract).

type Selection

type Selection struct {
	Active         bool
	StartX, StartY int
	EndX, EndY     int
}

Selection describes a text selection range in pane-local content coordinates.

type StopAgentCmd added in v1.20.0

type StopAgentCmd struct {
	ID     string `json:"id,omitempty"`
	Action string `json:"action"` // "stop_agent"
	Name   string `json:"name"`
}

StopAgentCmd stops a previously-pushed agent. Workspace files are preserved.

type StreamAddedMsg added in v1.21.0

type StreamAddedMsg struct {
	Action   string `json:"action"`    // "stream_added"
	StreamID uint32 `json:"stream_id"` // yamux stream ID for this agent.
	Name     string `json:"name"`      // Agent name.
}

StreamAddedMsg announces a new agent stream created mid-session by a configure_agent push. The client opens a RemotePane bound to StreamID and adds it to the displayed pane list.

type StreamMapMsg added in v0.23.18

type StreamMapMsg struct {
	Action  string            `json:"action"`  // "stream_map"
	Streams map[uint32]string `json:"streams"` // Stream ID -> agent name.
}

StreamMapMsg tells the client which yamux stream ID maps to which agent.

type TUI

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

TUI is the main terminal multiplexer. It owns the tcell screen, a set of terminal panes, and handles input routing, layout, and rendering.

func (*TUI) AllPanes added in v0.23.53

func (t *TUI) AllPanes() ([]PaneInfo, bool)

func (*TUI) FindPaneView added in v0.23.53

func (t *TUI) FindPaneView(name string) (PaneView, bool)

func (*TUI) HandleExtended added in v0.23.53

func (t *TUI) HandleExtended(conn net.Conn, req IPCRequest, rawJSON []byte) bool

func (*TUI) HandleSend added in v0.23.53

func (t *TUI) HandleSend(conn net.Conn, req IPCRequest)

func (*TUI) InterruptAgent added in v1.15.0

func (t *TUI) InterruptAgent(name string, hard bool) error

InterruptAgent sends a raw control byte to the named agent's PTY. Used by the MCP server. hard=true sends Ctrl+C, false sends Escape.

func (*TUI) NotifyConfig added in v1.10.0

func (t *TUI) NotifyConfig() (webhookURL, project string)

func (*TUI) PressureThreshold

func (t *TUI) PressureThreshold() int

PressureThreshold returns the configured RSS percentage above which agents may be auto-suspended. Returns 85 (the default) when not explicitly set.

func (*TUI) Timers added in v0.23.53

func (t *TUI) Timers() *TimerStore

type Timer added in v0.23.23

type Timer struct {
	ID        string    `json:"id"`
	Target    string    `json:"target"`
	Host      string    `json:"host,omitempty"`
	Text      string    `json:"text"`
	Enter     bool      `json:"enter"`
	FireAt    time.Time `json:"fire_at"`
	CreatedAt time.Time `json:"created_at"`
}

Timer represents a scheduled send: deliver Text to Target at FireAt.

type TimerStore added in v0.23.23

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

TimerStore manages scheduled timers with JSON persistence. All methods are safe for concurrent use.

func NewTimerStore added in v0.23.23

func NewTimerStore(path string) *TimerStore

NewTimerStore creates a store backed by the given file path. If the file exists, timers and the ID counter are loaded from it. Missing files are treated as empty (first run). Corrupt files log a warning and start empty so the operator is aware of data loss.

func (*TimerStore) Add added in v0.23.23

func (ts *TimerStore) Add(target, host, text string, enter bool, fireAt time.Time) (Timer, error)

Add creates a new timer and persists to disk. Returns the created timer and any persistence error. On save failure, the in-memory state is rolled back so it stays consistent with disk.

func (*TimerStore) Cancel added in v0.23.23

func (ts *TimerStore) Cancel(id string) (Timer, error)

Cancel removes a timer by ID and persists. Returns the canceled timer and any error (not-found or persistence failure). On save failure, the in-memory removal is rolled back.

func (*TimerStore) FireDue added in v0.23.23

func (ts *TimerStore) FireDue(now time.Time) ([]Timer, error)

FireDue returns and removes all timers where FireAt <= now. Persists after removal. Returns due timers and any save error. Even on save failure, the due timers are returned so they can still be fired (but the caller should log the error since the timers may resurrect after restart).

func (*TimerStore) List added in v0.23.23

func (ts *TimerStore) List() []Timer

List returns all pending timers sorted by FireAt (earliest first).

func (*TimerStore) Pending added in v0.23.23

func (ts *TimerStore) Pending() int

Pending returns the count of pending timers.

Jump to

Keyboard shortcuts

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