harness

package
v0.47.0 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package harness detects which agent harness (claude/codex/...) is running in a wrapped PTY and whether it is working or idle, so the terminal-share heartbeat can stamp live agent info instead of relying on launch-time env vars. Detection is pure and platform-free: the wrapper feeds in foreground-process snapshots and byte-activity timestamps; this package never touches the PTY itself.

The design (and the timing constants) are ported from herdr's pane detection: identification is process-tree based, working/idle is PTY byte-causality based. See docs/specs/agent-detection.md.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Identify

func Identify(comm string, argv []string) string

Identify maps a foreground process (comm + full argv, argv[0] included) to a canonical harness name, or "" when the process is not a known harness. It normalizes names (case, .exe/.cmd/.bat/.ps1/.js suffixes, whitespace) and sees through generic runtimes (node, python, sh/bash/zsh, pwsh) by scanning argv for a known binary name.

Types

type ActivityTracker

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

ActivityTracker classifies a wrapped harness as working or idle from PTY byte causality alone — no screen parsing. Output observed while no recent user input is in flight marks the harness working for outputActivityWindow; quiescence past the window reads as idle. Callers pass `now` explicitly so tests never sleep.

Not safe for concurrent use; the wrapper serializes observations onto its detection goroutine.

func NewActivityTracker

func NewActivityTracker(start time.Time) *ActivityTracker

NewActivityTracker starts tracking for a harness identified at `start`. The first startupGraceWindow after start reads as idle regardless of output.

func (*ActivityTracker) ObserveInput

func (t *ActivityTracker) ObserveInput(now time.Time)

ObserveInput records user-originated input at `now` (local stdin, remote .stdin messages, alert auto-submission).

func (*ActivityTracker) ObserveOutput

func (t *ActivityTracker) ObserveOutput(now time.Time)

ObserveOutput records PTY output bytes from the child at `now`. Output inside the startup grace is boot noise, and output inside the input-taint window is presumed local echo / prompt redraw; neither counts as agent work.

func (*ActivityTracker) State

func (t *ActivityTracker) State(now time.Time) State

State returns StateWorking or StateIdle as of `now`: working while agent-attributed output is fresher than outputActivityWindow, idle otherwise. The window doubles as the working→idle hysteresis.

type Detection

type Detection struct {
	Harness  string // canonical harness name, "" when none
	ChildPID int    // foreground pid when a harness is identified
	State    State  // StateUnknown when Harness is ""
}

Detection is the point-in-time snapshot the heartbeat loop stamps into payloads. Zero value means "no harness in the foreground".

type Detector

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

Detector owns harness detection for one wrapped PTY: it polls the foreground process via the injected inspector and folds byte observations into an ActivityTracker. Inspect is the platform seam — TIOCGPGRP + process lookup in production, a fake in tests.

Inspect errors retain the previous identification (a transient ps failure must not flap `ppz who`); a successful inspect of a non-harness clears it. A harness change resets the activity tracker so each identification gets its own startup grace.

Safe for concurrent use: the wrapper calls Observe* from its PTY I/O goroutines, Poll from the detection ticker, and Snapshot from the heartbeat loop.

func NewDetector

func NewDetector(inspect func() (ForegroundProc, error), start time.Time) *Detector

NewDetector creates a Detector for a PTY whose child spawned at `start`.

func (*Detector) ObserveInput

func (d *Detector) ObserveInput(now time.Time)

ObserveInput forwards user input activity to the tracker.

func (*Detector) ObserveOutput

func (d *Detector) ObserveOutput(now time.Time)

ObserveOutput forwards PTY output activity to the tracker.

func (*Detector) Poll

func (d *Detector) Poll(now time.Time)

Poll re-inspects the foreground process and updates identification.

func (*Detector) Snapshot

func (d *Detector) Snapshot(now time.Time) Detection

Snapshot returns the current detection for heartbeat stamping.

type ForegroundProc

type ForegroundProc struct {
	PID  int
	Comm string
	Argv []string
}

ForegroundProc is what the platform inspector reports about the PTY's foreground process group: the group leader's pid, its comm (binary name), and full argv (argv[0] included).

type Spec

type Spec struct {
	Name        string
	BinaryNames []string
}

Spec describes one known harness. Extending detection to a new harness is one new row here. Names are the canonical ids `ppz agent --harness` already uses, so heartbeat consumers see one vocabulary regardless of whether the value came from env or detection.

func Specs

func Specs() []Spec

Specs returns the registry of known harnesses.

type State

type State string

State of a wrapped agent harness, as stamped into heartbeat payloads.

const (
	StateUnknown State = ""        // no harness identified in the PTY foreground
	StateIdle    State = "idle"    // harness at rest, prompt waiting
	StateWorking State = "working" // harness actively producing output
	StateBlocked State = "blocked" // harness waiting on human input (phase 3)
)

Jump to

Keyboard shortcuts

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