bgproc

package
v1.41.0 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package bgproc manages background processes started by the agent.

The manager is deliberately process-scoped, not agent-scoped: agents are created per run (serve headless), per message (Telegram), and per REPL session, while background jobs must outlive the turn that started them. One Manager lives for the lifetime of the odek process; jobs are keyed by session id so concurrent sessions never see each other's jobs.

Lifecycle contract (v1):

  • A job runs until it exits, its timeout fires, or it is stopped.
  • StopAll(session) runs at session teardown; Shutdown() at process exit.
  • There is no detach mode: every job is killed when its session or the process ends. Detached processes are an explicit non-goal (v1).

Security contract:

  • Output is captured into a bounded in-memory ring — never spilled to disk, never persisted, never included in events (arg-hash discipline lives in the events package).
  • All addressing calls take the session id; a foreign session gets the same "not found" answer as a stale id (no existence oracle).
  • Stop signals the process GROUP (SIGTERM, then SIGKILL after a grace window) so shell-spawned children die with the job; sandbox mode wraps the command via the caller-supplied SandboxWrap (the shell tool's pidfile mechanism) and runs the follow-up kill after a forced stop.

Index

Constants

This section is empty.

Variables

View Source
var ErrTooManyJobs = errors.New("background job limit reached for session")

ErrTooManyJobs is returned by Start when the session already has MaxJobsPerSession jobs running.

Functions

This section is empty.

Types

type Config

type Config struct {
	// MaxJobsPerSession caps concurrently running jobs per session.
	// <= 0 means unlimited (not recommended; callers should clamp).
	MaxJobsPerSession int
	// MaxOutputBytes caps the per-job output ring. <= 0 means 1 MiB.
	MaxOutputBytes int
	// MaxTimeout, when > 0, clamps the per-job timeout passed to Start.
	MaxTimeout time.Duration
	// SandboxWrap, when set, rewrites the command into a sandboxed argv
	// (e.g. the shell tool's docker-exec pidfile wrapper) and returns a
	// follow-up func invoked after the job is forcibly stopped. When nil,
	// commands run on the host via "sh -c".
	SandboxWrap func(command string) (argv []string, followUp func(), err error)
}

Config bounds the manager.

type Job

type Job struct {
	ID          string
	SessionID   string
	Command     string
	Status      Status
	ExitCode    int    // meaningful once ended (bash semantics; -1 when signaled)
	Err         string // terminal error detail, when applicable
	StartedAt   time.Time
	EndedAt     time.Time
	Timeout     time.Duration // 0 = until session end
	OutputBytes int64         // logical output size (dropped-front + retained); populated by Get snapshots
}

Job is a snapshot of a background job's state.

type Manager

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

Manager owns every background job of the process.

func NewManager

func NewManager(cfg Config, obs Observer) *Manager

NewManager returns a manager with the given bounds and observer (both may be zero/nil, applying defaults).

func (*Manager) DrainNotices

func (m *Manager) DrainNotices(sessionID string) []Notice

DrainNotices pops all pending completion notices for the session.

func (*Manager) Get

func (m *Manager) Get(sessionID, jobID string) (Job, bool)

Get returns the job snapshot; ok is false for unknown or foreign ids. The snapshot carries the job's logical output size so callers can judge whether reading the output is worthwhile (B3-TOOLS-3).

func (*Manager) List

func (m *Manager) List(sessionID string) []Job

List returns a snapshot of the session's jobs in creation order.

func (*Manager) Output

func (m *Manager) Output(sessionID, jobID string, since int64, limit int) (string, int64, error)

Output returns up to limit bytes of output recorded after the absolute byte offset since (limit <= 0 = no cap), plus the cursor for the next read. When the ring has dropped bytes from the front, a truncation marker precedes the retained window. Foreign or unknown ids yield an error identical in shape for both cases.

func (*Manager) Shutdown

func (m *Manager) Shutdown() []Job

Shutdown stops every running job across all sessions (process exit).

func (*Manager) Start

func (m *Manager) Start(sessionID, command, cwd string, timeout time.Duration) (*Job, error)

Start spawns command in the given session and returns immediately. timeout > 0 kills the job (StatusTimeout) when it elapses; 0 means the job runs until session end. The returned snapshot has Status running.

func (*Manager) Stop

func (m *Manager) Stop(sessionID, jobID string) (Job, bool)

Stop stops the job if it is running and owned by sessionID. It returns the job snapshot and whether the job was found under that session. Stopping an already-finished job is not an error and does not change its status.

func (*Manager) StopAll

func (m *Manager) StopAll(sessionID string) []Job

StopAll stops every running job of the session and returns the killed jobs. Jobs of other sessions are untouched.

type Notice

type Notice struct {
	JobID       string
	SessionID   string
	Command     string
	Status      Status
	ExitCode    int
	Duration    time.Duration
	OutputBytes int64
	Tail        string // last output, rune-boundary-safe
}

Notice reports a job that reached a terminal state. It is delivered to the session's completion-notice queue (drained by the agent loop at the top of the next iteration) and to the Observer.

type Observer

type Observer interface {
	BGStarted(Job)
	BGExited(Notice)
}

Observer receives job lifecycle callbacks. Implementations must be non-blocking; they are invoked from the manager's waiter goroutines without the manager lock held.

type Status

type Status string

Status is the lifecycle state of a background job.

const (
	StatusRunning Status = "running"
	StatusExited  Status = "exited" // exit code 0
	StatusFailed  Status = "failed" // non-zero exit or external kill
	StatusTimeout Status = "timeout"
	StatusKilled  Status = "killed" // stopped via Stop/StopAll/Shutdown
)

Jump to

Keyboard shortcuts

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