job

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package job owns the on-disk representation of a monitored agent run.

Every run gets a directory under the codexmon home (default ~/.codexmon/jobs/<id>):

spec.json     immutable launch spec (agent, args, cwd, thresholds) read by the worker
status.json   live status, rewritten by the monitor at least once per second
events.jsonl  raw agent event-stream lines (when JSON monitoring is on)
output.log    merged human-readable stdout/stderr log
result.txt    final agent message / review output
cancel        marker file; its presence asks the monitor to stop

status.json is the contract `codexmon status/wait/list/tail` reads, so it is written atomically (temp file + rename) to avoid torn reads by a poller.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoJobs = errors.New("no codexmon jobs found")

ErrNoJobs is returned by Latest when no jobs exist.

Functions

func AutoPrune added in v0.4.0

func AutoPrune() (int, error)

AutoPrune applies DefaultPruneOptions. Launch paths call it best-effort so the jobs directory stays bounded (logs are capped per job, but the number of jobs would otherwise grow forever, and every list/status scan reads them all).

func CancelRequested

func CancelRequested(dir string) bool

CancelRequested reports whether the cancel marker exists.

func Dir

func Dir(id string) (string, error)

Dir returns (and creates) the directory for a job id. Directories are 0700: codex prompts, output, and review text may contain secrets.

func Home

func Home() (string, error)

Home returns the codexmon home directory ($CODEXMON_HOME or ~/.codexmon).

func NewID

func NewID() string

NewID returns a sortable, unique job id like "cdx-20260603-150405-9f3a1c".

func Paths

func Paths(dir string) (spec, status, eventsFile, logFile, resultFile, cancel string)

Paths returns the standard file paths within a job directory.

func Prune added in v0.4.0

func Prune(opts PruneOptions) (removed int, err error)

Prune deletes old terminal job directories and returns how many were removed. Active (queued/running) jobs are never removed — a status that *claims* active but whose worker is dead reconciles to terminal via ReadStatus first, so it ages out like any other finished job. A directory with no readable status (half-initialized or corrupt) is removed once its mtime is older than MaxAge, so a crashed launch cannot linger forever.

func RequestCancel

func RequestCancel(dir string) error

RequestCancel writes the cancel marker the monitor polls for.

func ValidID

func ValidID(id string) bool

ValidID reports whether id is a well-formed, traversal-safe job id.

func WriteSpec

func WriteSpec(dir string, s *Spec) error

WriteSpec persists the immutable launch spec.

func WriteStatus

func WriteStatus(dir string, s *Status) error

WriteStatus atomically writes status.json.

Types

type Health

type Health string

Health is the liveness verdict derived from idle time while running.

const (
	HealthStarting Health = "starting"
	HealthHealthy  Health = "healthy"
	HealthSlow     Health = "slow"
	HealthStalled  Health = "stalled"
	HealthDone     Health = "done"
	HealthDead     Health = "dead"
)

type PruneOptions added in v0.4.0

type PruneOptions struct {
	MaxAge   time.Duration // remove terminal jobs that ended longer ago than this
	MaxCount int           // keep at most this many terminal jobs (newest first)
	All      bool          // remove all terminal jobs
}

PruneOptions bound which terminal jobs Prune removes. A zero or negative limit disables that dimension; All removes every terminal job regardless.

func DefaultPruneOptions added in v0.4.0

func DefaultPruneOptions() PruneOptions

DefaultPruneOptions returns the retention policy: the defaults above, overridable via CODEXMON_KEEP_DAYS and CODEXMON_KEEP_JOBS (0 disables that limit; non-numeric or negative values are ignored).

type Spec

type Spec struct {
	ID           string     `json:"id"`
	Agent        string     `json:"agent"`     // which agent to run (codex/claude/cursor)
	AgentBin     string     `json:"agent_bin"` // resolved path to the agent binary
	Args         []string   `json:"args"`
	Cwd          string     `json:"cwd"`
	JSONMode     bool       `json:"json_mode"`
	ForwardStdin bool       `json:"forward_stdin"`
	Thresholds   Thresholds `json:"thresholds"`
	Title        string     `json:"title"`
	// Env, when non-empty, fully REPLACES the agent's environment (it is not
	// merged with the parent's). codexmon never sets it — the agent inherits the
	// launcher's environment — so it exists only for tests/advanced use; a
	// hand-edited spec.json that sets it must include PATH/HOME/etc.
	Env []string `json:"env,omitempty"`
}

Spec is the immutable launch description persisted for the detached worker.

func ReadSpec

func ReadSpec(dir string) (*Spec, error)

ReadSpec loads a launch spec.

type State

type State string

State is the lifecycle of a job. queued/running are active; the rest terminal.

const (
	StateQueued    State = "queued"
	StateRunning   State = "running"
	StateCompleted State = "completed"
	StateFailed    State = "failed"
	StateStalled   State = "stalled"
	StateTimeout   State = "timeout"
	StateCancelled State = "cancelled"
)

func (State) Active

func (s State) Active() bool

Active reports whether the state is non-terminal.

type Status

type Status struct {
	ID     string `json:"id"`
	State  State  `json:"state"`
	Health Health `json:"health"`
	Phase  string `json:"phase"`

	Agent    string   `json:"agent"`     // which agent ran (codex/claude/cursor)
	AgentBin string   `json:"agent_bin"` // resolved path to the agent binary
	Args     []string `json:"args"`      // args passed to the agent
	Cwd      string   `json:"cwd"`
	JSONMode bool     `json:"json_mode"` // true when monitoring the JSON event stream

	WorkerPID int `json:"worker_pid"` // process that owns the agent child
	AgentPID  int `json:"agent_pid"`  // agent process group leader

	StartedAt   time.Time  `json:"started_at"`
	UpdatedAt   time.Time  `json:"updated_at"`
	EndedAt     *time.Time `json:"ended_at,omitempty"`      // nil until terminal
	LastEventAt *time.Time `json:"last_event_at,omitempty"` // nil until first event

	ElapsedSec float64 `json:"elapsed_sec"`
	IdleSec    float64 `json:"idle_sec"`

	EventCount int          `json:"event_count"`
	LastEvent  string       `json:"last_event"`
	ThreadID   string       `json:"thread_id,omitempty"`
	Usage      *agent.Usage `json:"usage,omitempty"`

	ExitCode *int   `json:"exit_code,omitempty"`
	Error    string `json:"error,omitempty"`

	// ResultPreview is a truncated copy of the final output for at-a-glance
	// status; the full text lives in result.txt (ResultFile).
	ResultPreview string `json:"result_preview,omitempty"`

	// Result is the final output, capped at 4 MiB — past the cap it ends with
	// a truncation notice naming ResultFile, which always holds the full text.
	// It is never persisted in status.json (the file is rewritten every
	// second; ResultFile holds the text once) — the CLI fills it from
	// ResultFile when emitting terminal JSON for wait/run, so a consumer gets
	// the whole answer in one call.
	Result string `json:"result,omitempty"`

	Thresholds Thresholds `json:"thresholds"`

	Dir        string `json:"dir"`
	EventsFile string `json:"events_file,omitempty"`
	LogFile    string `json:"log_file"`
	ResultFile string `json:"result_file"`

	// Title is a short human label (e.g. "codex exec review").
	Title string `json:"title"`
}

Status is the full, serialized state of a job. It is both the live status file and the structure emitted by `--json`.

func Latest

func Latest() (*Status, error)

Latest returns the most recently started job, preferring active ones.

func List

func List() ([]*Status, error)

List returns all known job statuses, newest first.

func ReadStatus

func ReadStatus(dir string) (*Status, error)

ReadStatus reads status.json for a job directory. If the status still claims to be active but the owning worker process is gone, it is reconciled to a terminal failed state — otherwise a crashed worker would leave the job reported as "running"/"healthy" forever, the exact misread codexmon must avoid. Reconciliation is applied to the returned value only; callers that want it persisted should WriteStatus it back.

func ReadStatusByID

func ReadStatusByID(id string) (*Status, error)

ReadStatusByID resolves a job id to its status.

func Resolve

func Resolve(id string) (*Status, error)

Resolve returns the status for an explicit id, or the latest job if id == "".

type Thresholds

type Thresholds struct {
	HeartbeatSec float64 `json:"heartbeat_sec"`
	SlowAfterSec float64 `json:"slow_after_sec"`
	StalledSec   float64 `json:"stalled_sec"`    // idle ceiling when nothing is in flight
	ToolStuckSec float64 `json:"tool_stuck_sec"` // max time a single MCP/tool call may run
	WallSec      float64 `json:"wall_sec"`
}

Thresholds are the watchdog limits, in seconds. A zero value disables that check.

Jump to

Keyboard shortcuts

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