Documentation
¶
Overview ¶
Package activity implements tmux session activity detection using screen-delta hysteresis and tag-based output timestamps. It is extracted from the app god-package to decouple pure detection logic from App state.
Index ¶
- Constants
- Variables
- func ActiveWorkspaceIDsFromTagsWithRemoved(infoBySession map[string]SessionInfo, sessions []TaggedSession, ...) (map[string]bool, map[string]*SessionState, []string)
- func ClassifyWorkspaceStates(active map[string]bool, updated map[string]*SessionState, ...) map[string]AgentState
- func DecodeSnapshotWithStates(raw string) (map[string]bool, map[string]AgentState, int64, time.Time, bool)
- func EncodeSnapshot(active map[string]bool, epoch int64, now time.Time) string
- func EncodeSnapshotWithStates(active map[string]bool, states map[string]AgentState, epoch int64, ...) string
- func FetchRecentlyActiveByWindow(svc SessionFetcher, window time.Duration, opts tmux.Options) (map[string]bool, error)
- func FreshTagVisibleActivity(sessionName string, states map[string]*SessionState, ...) bool
- func HasRecentUserInput(snapshot TaggedSession, now time.Time) bool
- func HasRecentWindowActivity(sessionName string, recentActivityBySession map[string]bool) bool
- func IsChatSession(session tmux.SessionActivity, info SessionInfo, hasInfo bool) bool
- func IsLikelyUserEcho(snapshot TaggedSession) bool
- func IsRunningSession(info SessionInfo, hasInfo bool) bool
- func OwnerLeaseAlive(lease OwnerLease, now time.Time) bool
- func ParseLastOutputAtTag(raw string) (time.Time, bool)
- func PrepareStaleTagFallbackState(sessionName string, states map[string]*SessionState)
- func ReadSnapshotWithStates(opts tmux.Options, now time.Time, expectedEpoch int64) (map[string]bool, map[string]AgentState, bool, error)
- func RenewOwnerLeaseHeartbeat(opts tmux.Options, now time.Time) error
- func SeedFreshTagFallbackBaseline(sessionName string, states map[string]*SessionState, ...)
- func WorkspaceIDForSession(session tmux.SessionActivity, info SessionInfo, hasInfo bool) string
- func WorkspaceIDFromSessionName(name string) string
- func WriteOwnerLease(opts tmux.Options, ownerID string, epoch int64, now time.Time) error
- type AgentState
- type CaptureFn
- type HashFn
- type OwnerLease
- type SessionFetcher
- type SessionInfo
- type SessionState
- type TaggedSession
Constants ¶
const ( OwnerOption = "@amux_activity_owner" HeartbeatOption = "@amux_activity_owner_heartbeat_ms" EpochOption = "@amux_activity_owner_epoch" SnapshotOption = "@amux_activity_active_workspaces" )
tmux global option names backing the cross-instance activity-scan lease.
const ( // OwnerLeaseTTL controls how long an activity-scan owner lease stays valid // after its last heartbeat before another instance may claim ownership. OwnerLeaseTTL = 7 * time.Second // OwnerFutureSkewTolerance caps how far in the future a lease heartbeat may be // (clock skew) before it is treated as expired rather than alive. OwnerFutureSkewTolerance = 2 * time.Second // SnapshotStaleAfter controls how long a shared activity snapshot is trusted // after its timestamp before followers ignore it. SnapshotStaleAfter = 10 * time.Second // SnapshotFutureSkewTolerance caps how far in the future a shared snapshot // timestamp may be (clock skew) before it is treated as stale rather than // fresh, mirroring OwnerFutureSkewTolerance for the owner lease. SnapshotFutureSkewTolerance = 2 * time.Second )
const ( ScoreThreshold = 3 // Score needed to be considered active ScoreMax = 6 // Maximum score (prevents runaway accumulation) // OutputWindow is how recently output must have occurred to be "active". OutputWindow = 2 * time.Second // InputEchoWindow treats output immediately after input as likely local echo. InputEchoWindow = 400 * time.Millisecond // InputSuppressWindow suppresses fallback capture right after user input. InputSuppressWindow = 2 * time.Second // CaptureTail is the number of terminal lines captured for activity checks. CaptureTail = 50 // HoldDuration holds an active session state after the last observed change. HoldDuration = 6 * time.Second // DoneWindow is how long after going quiet a session is reported "done" // (recently finished) before decaying to idle. DoneWindow = 30 * time.Second )
Hysteresis / detection constants.
Variables ¶
ErrTmuxUnavailable is returned when the tmux service is nil or missing.
Functions ¶
func ActiveWorkspaceIDsFromTagsWithRemoved ¶ added in v0.0.20
func ActiveWorkspaceIDsFromTagsWithRemoved( infoBySession map[string]SessionInfo, sessions []TaggedSession, recentActivityBySession map[string]bool, states map[string]*SessionState, opts tmux.Options, captureFn CaptureFn, hashFn HashFn, ) (map[string]bool, map[string]*SessionState, []string)
ActiveWorkspaceIDsFromTagsWithRemoved is the production entry point. It returns the active workspace IDs, updated session states, and the names of session states pruned this scan (unseen beyond pruneAfterScans) so the caller can delete them from its persistent map.
func ClassifyWorkspaceStates ¶ added in v0.0.20
func ClassifyWorkspaceStates( active map[string]bool, updated map[string]*SessionState, infoBySession map[string]SessionInfo, sessions []TaggedSession, now time.Time, ) map[string]AgentState
ClassifyWorkspaceStates maps workspaces to a semantic AgentState. A workspace is Working if it is in `active`; otherwise Done if any of its sessions went quiet within DoneWindow (per ClassifyState); otherwise omitted. `updated` is keyed by session name. Workspace IDs are resolved from local tab metadata and the same tagged tmux session metadata used by active classification.
func DecodeSnapshotWithStates ¶ added in v0.0.20
func DecodeSnapshotWithStates(raw string) (map[string]bool, map[string]AgentState, int64, time.Time, bool)
DecodeSnapshotWithStates parses an encoded snapshot and optional semantic states, accepting both current and legacy active-only payloads.
func EncodeSnapshot ¶ added in v0.0.20
EncodeSnapshot serializes the active-workspace set with epoch and timestamp.
func EncodeSnapshotWithStates ¶ added in v0.0.20
func EncodeSnapshotWithStates(active map[string]bool, states map[string]AgentState, epoch int64, now time.Time) string
EncodeSnapshotWithStates serializes the active-workspace set plus optional per-workspace semantic states with epoch and timestamp.
func FetchRecentlyActiveByWindow ¶
func FetchRecentlyActiveByWindow(svc SessionFetcher, window time.Duration, opts tmux.Options) (map[string]bool, error)
FetchRecentlyActiveByWindow returns session names with recent tmux window activity.
func FreshTagVisibleActivity ¶ added in v0.0.15
func FreshTagVisibleActivity( sessionName string, states map[string]*SessionState, updated map[string]*SessionState, now time.Time, opts tmux.Options, captureFn CaptureFn, hashFn HashFn, ) bool
FreshTagVisibleActivity validates fresh output tags against visible pane changes for initialized sessions. This prevents control-sequence churn from keeping sessions active when captured content is unchanged.
Returns true when the fresh tag should count as active.
func HasRecentUserInput ¶
func HasRecentUserInput(snapshot TaggedSession, now time.Time) bool
HasRecentUserInput returns true if the session has had user input within the suppress window.
func HasRecentWindowActivity ¶ added in v0.0.15
HasRecentWindowActivity reports whether a session has recent tmux window activity. When the prefilter map is nil (data unavailable), it returns true to preserve accuracy by allowing the caller to proceed.
func IsChatSession ¶
func IsChatSession(session tmux.SessionActivity, info SessionInfo, hasInfo bool) bool
IsChatSession determines whether a tmux session represents an active AI agent.
Detection priority:
- Known-tab metadata marks chat sessions active even if tmux type is stale.
- Session tag (@amux_type == "agent") is authoritative for agent sessions.
- For known sessions with no explicit type, fall back to tab metadata.
func IsLikelyUserEcho ¶
func IsLikelyUserEcho(snapshot TaggedSession) bool
IsLikelyUserEcho returns true if the output timestamp is within the echo window after the input timestamp, suggesting the output is local echo rather than agent work.
func IsRunningSession ¶ added in v0.0.15
func IsRunningSession(info SessionInfo, hasInfo bool) bool
IsRunningSession reports whether a known session should be considered active-capable based on status metadata from app state.
func OwnerLeaseAlive ¶ added in v0.0.20
func OwnerLeaseAlive(lease OwnerLease, now time.Time) bool
OwnerLeaseAlive reports whether lease is a live owner lease at now, tolerating small forward clock skew but expiring stale or far-future heartbeats.
func ParseLastOutputAtTag ¶
ParseLastOutputAtTag parses a unix timestamp tag value in seconds, millis, or nanos.
func PrepareStaleTagFallbackState ¶
func PrepareStaleTagFallbackState(sessionName string, states map[string]*SessionState)
PrepareStaleTagFallbackState trims stale-tag hysteresis carryover so sessions stop appearing active promptly after output ceases.
func ReadSnapshotWithStates ¶ added in v0.0.20
func ReadSnapshotWithStates(opts tmux.Options, now time.Time, expectedEpoch int64) (map[string]bool, map[string]AgentState, bool, error)
ReadSnapshotWithStates reads the shared activity snapshot, including optional semantic agent states for newer owners. Legacy active-only snapshots decode with a nil states map.
func RenewOwnerLeaseHeartbeat ¶ added in v0.0.20
RenewOwnerLeaseHeartbeat refreshes only the heartbeat timestamp.
func SeedFreshTagFallbackBaseline ¶
func SeedFreshTagFallbackBaseline( sessionName string, states map[string]*SessionState, updated map[string]*SessionState, opts tmux.Options, captureFn CaptureFn, hashFn HashFn, )
SeedFreshTagFallbackBaseline initializes hysteresis state for sessions that are currently active via fresh tags, so stale fallback doesn't treat them as brand-new sessions and blip active on unchanged content.
func WorkspaceIDForSession ¶
func WorkspaceIDForSession(session tmux.SessionActivity, info SessionInfo, hasInfo bool) string
WorkspaceIDForSession resolves a workspace ID from known tab info, session tags, or session name.
func WorkspaceIDFromSessionName ¶
WorkspaceIDFromSessionName extracts a workspace ID from an amux session name pattern.
Types ¶
type AgentState ¶ added in v0.0.20
type AgentState int
AgentState is the semantic activity state of an agent session.
const ( StateIdle AgentState = iota // quiet, nothing pending StateWorking // actively producing output StateDone // recently finished (was working, now quiet) )
func ClassifyState ¶ added in v0.0.20
func ClassifyState(state *SessionState, now time.Time) AgentState
ClassifyState maps a session's hysteresis state to a semantic AgentState as of `now`. Working = currently active; Done = not active but went quiet within DoneWindow; Idle otherwise. This is deterministic — no screen parsing.
The isActive derivation is kept identical to the one in activeWorkspaceIDsWithHysteresisWithSeen so "Working" exactly matches the existing active bit. If the hysteresis logic changes, update both in lockstep (or refactor to a shared predicate — a good Phase 2 cleanup).
func (AgentState) String ¶ added in v0.0.20
func (s AgentState) String() string
String returns a human-readable label for the AgentState.
type OwnerLease ¶ added in v0.0.20
OwnerLease is the cross-instance activity-scan owner lease stored in tmux global options.
func ReadOwnerLease ¶ added in v0.0.20
func ReadOwnerLease(opts tmux.Options) (OwnerLease, error)
ReadOwnerLease reads the current owner lease from tmux global options.
type SessionFetcher ¶
type SessionFetcher interface {
SessionsWithTags(match map[string]string, keys []string, opts tmux.Options) ([]tmux.SessionTagValues, error)
ActiveAgentSessionsByActivity(window time.Duration, opts tmux.Options) ([]tmux.SessionActivity, error)
}
SessionFetcher is the subset of tmux operations needed by activity detection.
type SessionInfo ¶
SessionInfo maps a tmux session name to known tab metadata.
type SessionState ¶
type SessionState struct {
LastHash [16]byte // Hash of last captured pane content
Score int // Activity score (0 to ScoreMax)
LastActiveAt time.Time // Last time this session was considered active
Initialized bool // Whether we have a baseline hash
// UnseenScans counts consecutive scans in which this session was not seen.
// Reset to 0 whenever the session is observed; once it exceeds
// pruneAfterScans the state is dropped so deleted-workspace sessions do not
// accumulate as zombie states that are deep-copied every scan forever.
UnseenScans int
// LastWorkingAt is the last time this session was classified active; used to
// derive the transient "done" state after it goes quiet.
LastWorkingAt time.Time
}
SessionState tracks per-session activity using screen-delta hysteresis.
type TaggedSession ¶
type TaggedSession struct {
Session tmux.SessionActivity
LastOutputAt time.Time
HasLastOutput bool
LastInputAt time.Time
HasLastInput bool
}
TaggedSession pairs a tmux session with parsed tag timestamps.
func FetchTaggedSessions ¶
func FetchTaggedSessions(svc SessionFetcher, infoBySession map[string]SessionInfo, opts tmux.Options) ([]TaggedSession, error)
FetchTaggedSessions retrieves tmux sessions with amux tags and known-tab metadata.