Documentation
¶
Overview ¶
Package background implements a background task manager for long-running commands and subagent work. Tasks can be started asynchronously, observed via live UI events, waited on by tools, and queried for their final results.
Index ¶
- Constants
- Variables
- type AdoptResult
- type AgentTaskResult
- type ExecFunc
- type ManagedRun
- type Manager
- func (m *Manager) AgentTaskStopRequested(taskID string) bool
- func (m *Manager) Cleanup(maxAge time.Duration)
- func (m *Manager) CompleteAgentTask(taskID string, result AgentTaskResult)
- func (m *Manager) CompleteSpawnTask(taskID string, branches []SpawnBranch)
- func (m *Manager) CompleteVideoTask(taskID string, status TaskStatus, result map[string]any, errorMessage string)
- func (m *Manager) Get(taskID string) *Task
- func (m *Manager) GetForSession(botID, sessionID, taskID string) *Task
- func (m *Manager) Kill(taskID string) error
- func (m *Manager) KillForSession(botID, sessionID, taskID string) error
- func (m *Manager) ListForSession(botID, sessionID string) []*Task
- func (m *Manager) ListSnapshotsForSession(botID, sessionID string) []TaskSnapshot
- func (m *Manager) MarkAgentTaskRunning(parentCtx context.Context, taskID string) (context.Context, bool, error)
- func (m *Manager) RecordOutput(taskID, stream, chunk string)
- func (m *Manager) RecordVideoTaskProgress(taskID string, result map[string]any, outputLine string) bool
- func (m *Manager) RunningTasksSummary(botID, sessionID string) string
- func (m *Manager) SetEventFunc(fn func(TaskEvent))
- func (m *Manager) Spawn(parentCtx context.Context, ...) (taskID, outputFile string)
- func (m *Manager) SpawnAdopt(parentCtx context.Context, ...) (taskID, outputFile string)
- func (m *Manager) SpawnManaged(parentCtx context.Context, botID, sessionID, description string, ...) (taskID string)
- func (m *Manager) StartAgentTask(parentCtx context.Context, ...) (string, context.Context, error)
- func (m *Manager) StartCleanupLoop(done <-chan struct{}, interval, maxAge time.Duration)
- func (m *Manager) StartSpawnTask(parentCtx context.Context, botID, sessionID, description string) (string, context.Context, error)
- func (m *Manager) StartVideoTask(parentCtx context.Context, botID, sessionID, description string) (string, context.Context, error)
- func (m *Manager) WaitForSessionTask(ctx context.Context, botID, sessionID, taskID string, ...) (TaskSnapshot, WaitOutcome, error)
- type ReadFileFunc
- type SpawnBranch
- type Task
- type TaskEvent
- type TaskEventType
- type TaskKind
- type TaskSnapshot
- type TaskStatus
- type WaitOutcome
- type WriteFileFunc
Constants ¶
const ( // DefaultExecTimeout is the default timeout for foreground exec calls. DefaultExecTimeout int32 = 30 // MaxExecTimeout is the maximum allowed timeout (10 minutes). MaxExecTimeout int32 = 600 // BackgroundExecTimeout is the timeout for background tasks (30 minutes). BackgroundExecTimeout int32 = 1800 // DefaultWaitTimeout bounds a single wait_until call. The task keeps // running afterwards; callers re-wait to keep observing. DefaultWaitTimeout = 120 * time.Second // DefaultIdleThreshold is how long an exec task's output must stay quiet // before a waiter is woken with WaitIdle. DefaultIdleThreshold = 20 * time.Second // DefaultCleanupInterval is how often the manager prunes old completed tasks. DefaultCleanupInterval = time.Hour // DefaultTaskRetention is how long completed tasks are retained in memory. DefaultTaskRetention = 24 * time.Hour // OutputLogDir is the default directory where background task output logs // are written. OutputLogDir = "/tmp/memoh-bg" )
const MaxRunningSpawnTasks = 3
MaxRunningSpawnTasks caps concurrently running background spawn tasks per bot+session to prevent subagent storms across agent runs.
const SpawnTaskTimeout = 30 * time.Minute
SpawnTaskTimeout is the safety ceiling for a background spawn task, mirroring BackgroundExecTimeout for exec tasks.
Variables ¶
var ErrManagedOutcomeUnknown = errors.New("managed operation outcome is unknown")
ErrManagedOutcomeUnknown marks a lost execution connection without proof that the underlying process stopped or committed. Callers must reconcile its state.
Functions ¶
This section is empty.
Types ¶
type AdoptResult ¶
type AdoptResult struct {
Stdout string
Stderr string
ExitCode int32
ExitReceived bool
Err error
OutputRecorded bool
}
AdoptResult carries the outcome of a command whose execution was started externally (e.g. via ExecStream) and then handed off to the Manager.
ExitReceived distinguishes "the bridge actually sent us an EXIT frame" (so ExitCode is the real value the process returned, even if the gRPC stream errored out afterwards) from "we never saw an EXIT frame, ExitCode is just its zero value". Without this flag, downstream code can't tell "the command finished with exit 0 right before the stream died" from "we have no idea what the exit code was".
type AgentTaskResult ¶
type AgentTaskResult struct {
AgentID string
AgentSessionID string
Message string
ModelID string
Provider string
Fork bool
Status TaskStatus
Report string
Error string
}
AgentTaskResult is the terminal output for one managed subagent run.
type ExecFunc ¶
type ExecFunc func(ctx context.Context, command, workDir string, timeout int32) (*bridge.ExecResult, error)
ExecFunc executes a command in a container and returns the result. This is the signature that bridge.Client.Exec satisfies.
type ManagedRun ¶ added in v0.20.0
ManagedRun is the body of a managed task. Text passed to log is appended to the task output and emitted as an output event, exactly like RecordOutput.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager tracks background tasks and emits live task events.
func (*Manager) AgentTaskStopRequested ¶
AgentTaskStopRequested reports whether Kill has asked a running managed agent to stop. The status intentionally remains running until its runtime publishes the authoritative terminal outcome.
func (*Manager) CompleteAgentTask ¶
func (m *Manager) CompleteAgentTask(taskID string, result AgentTaskResult)
CompleteAgentTask finalises a managed agent task. Running task cancellation remains nonterminal until this call records the runtime's resolved outcome.
func (*Manager) CompleteSpawnTask ¶
func (m *Manager) CompleteSpawnTask(taskID string, branches []SpawnBranch)
CompleteSpawnTask finalises a spawn task with its branch outcomes and records the join result. The task is completed when every branch completed and failed when any branch failed. Branch outcomes are recorded even for killed tasks.
func (*Manager) CompleteVideoTask ¶
func (m *Manager) CompleteVideoTask(taskID string, status TaskStatus, result map[string]any, errorMessage string)
CompleteVideoTask finalises a video generation task unless it was killed before the provider goroutine returned.
func (*Manager) GetForSession ¶
GetForSession returns a task by ID only if it belongs to the provided bot+session.
func (*Manager) Kill ¶
Kill cancels a running background task. A running agent task records the request without publishing a terminal state: its runtime outcome is the single authority that wakes waiters as killed, failed, or completed.
func (*Manager) KillForSession ¶
KillForSession cancels a running background task only when it belongs to the provided bot+session.
func (*Manager) ListForSession ¶
ListForSession returns all tasks for a given bot+session, most recent first.
func (*Manager) ListSnapshotsForSession ¶
func (m *Manager) ListSnapshotsForSession(botID, sessionID string) []TaskSnapshot
ListSnapshotsForSession returns lock-safe snapshots for all tasks in a bot+session, most recent first.
func (*Manager) MarkAgentTaskRunning ¶
func (m *Manager) MarkAgentTaskRunning(parentCtx context.Context, taskID string) (context.Context, bool, error)
MarkAgentTaskRunning transitions a queued managed agent task into running and returns the cancelable run context. If the task was killed while queued, ok is false and no run should start.
func (*Manager) RecordOutput ¶
RecordOutput appends live output for a running task and emits a UI event.
func (*Manager) RecordVideoTaskProgress ¶
func (m *Manager) RecordVideoTaskProgress(taskID string, result map[string]any, outputLine string) bool
RecordVideoTaskProgress records the latest provider-visible video job state. The result map is merged into the current task result; outputLine is appended to the compact UI tail when non-empty.
func (*Manager) RunningTasksSummary ¶
RunningTasksSummary returns a text summary of currently running tasks for a given bot+session. This is injected into the system prompt so the agent knows about ongoing background work.
func (*Manager) SetEventFunc ¶
SetEventFunc registers a callback for live background task events.
func (*Manager) Spawn ¶
func (m *Manager) Spawn( parentCtx context.Context, botID, sessionID, command, workDir, description string, execFn ExecFunc, writeFn WriteFileFunc, readFn ReadFileFunc, ) (taskID, outputFile string)
Spawn starts a command in the background. It returns the task ID immediately. The command runs asynchronously and can be observed through task status tools.
execFn should call bridge.Client.Exec (or equivalent). writeFn should call bridge.Client.WriteFile to persist output logs.
func (*Manager) SpawnAdopt ¶
func (m *Manager) SpawnAdopt( parentCtx context.Context, botID, sessionID, command, workDir, description, outputDir string, resultCh <-chan AdoptResult, writeFn WriteFileFunc, ) (taskID, outputFile string)
SpawnAdopt registers a background task for a command that is already running externally (e.g. via ExecStream). Instead of re-executing the command, it waits for the result on the provided channel. This enables "flip to background" where a foreground stream is handed off without killing the process.
func (*Manager) SpawnManaged ¶ added in v0.20.0
func (m *Manager) SpawnManaged(parentCtx context.Context, botID, sessionID, description string, run ManagedRun) (taskID string)
SpawnManaged runs an in-process job as a background task. It reuses the Task/TaskEvent lifecycle so the UI can show progress and completion.
The job receives a context detached from parentCtx (a finished request must not abort it) and cancelled by Kill. The operation owns its deadline (for dependency scripts this is the confirmed manifest timeout), so an unrelated background exec limit cannot interrupt a valid longer install. A nil error completes the task; a non-nil error (or a panic) fails it and stores the error text on the task.
func (*Manager) StartAgentTask ¶
func (m *Manager) StartAgentTask(parentCtx context.Context, botID, sessionID, agentID, agentSessionID, message, description string, queued bool) (string, context.Context, error)
StartAgentTask registers a managed subagent task. Queued tasks are visible to background task status immediately but do not get a cancelable run context until MarkAgentTaskRunning is called.
func (*Manager) StartCleanupLoop ¶
StartCleanupLoop periodically removes old completed tasks until done is closed.
func (*Manager) StartSpawnTask ¶
func (m *Manager) StartSpawnTask(parentCtx context.Context, botID, sessionID, description string) (string, context.Context, error)
StartSpawnTask registers a background task for a spawn (subagent batch) whose execution is driven by the spawn tool. It returns the task ID and a detached, cancelable context that subagent branches must derive from so Kill can stop in-flight work.
func (*Manager) StartVideoTask ¶
func (m *Manager) StartVideoTask(parentCtx context.Context, botID, sessionID, description string) (string, context.Context, error)
StartVideoTask registers an asynchronous video generation task and returns a detached, cancelable context for the provider polling goroutine.
func (*Manager) WaitForSessionTask ¶
func (m *Manager) WaitForSessionTask(ctx context.Context, botID, sessionID, taskID string, idleThreshold time.Duration) (TaskSnapshot, WaitOutcome, error)
WaitForSessionTask waits until a task reaches a terminal state or needs attention, and reports why the wait ended. Besides completed/failed/killed/ stalled, a running exec task whose output stays quiet for idleThreshold returns WaitIdle — the signal that a server-style command has settled and its ready banner is in the output tail. idleThreshold <= 0 disables idle wake-ups; non-exec kinds (agent, spawn, video) have no output stream, so idle never applies to them.
type ReadFileFunc ¶
ReadFileFunc reads content from a file in the container.
type SpawnBranch ¶
type SpawnBranch struct {
Task string
ChildSessionID string
Status TaskStatus
Report string
Error string
}
SpawnBranch is the join-record entry for one subagent in a spawn batch. ChildSessionID points at the persisted subagent session so the parent agent can read the full transcript via history tools when needed.
type Task ¶
type Task struct {
ID string
Kind TaskKind
BotID string
SessionID string
Command string
Description string
AgentID string
AgentSessionID string
AgentMessage string
AgentReport string
AgentError string
AgentModelID string
AgentProvider string
AgentFork bool
WorkDir string
Status TaskStatus
ExitCode int32
OutputFile string // path inside the workspace where output is being written
Result map[string]any
Error string
StartedAt time.Time
CompletedAt time.Time
// contains filtered or unexported fields
}
Task represents a single background task (a container command execution or a spawn subagent batch, per Kind).
func (*Task) AppendOutput ¶
AppendOutput appends text to the buffered output tail. Only the last maxTailBytes are kept.
func (*Task) MarkStalled ¶
MarkStalled atomically marks the task as stalled and wakes any waiters.
func (*Task) OutputTail ¶
OutputTail returns the last portion of collected output.
func (*Task) Snapshot ¶
func (t *Task) Snapshot() TaskSnapshot
Snapshot returns a consistent view of the task without exposing its mutex.
type TaskEvent ¶
type TaskEvent struct {
Event TaskEventType `json:"event"`
TaskID string `json:"task_id"`
Kind TaskKind `json:"kind,omitempty"`
BotID string `json:"bot_id,omitempty"`
SessionID string `json:"session_id,omitempty"`
Command string `json:"command,omitempty"`
AgentID string `json:"agent_id,omitempty"`
AgentSessionID string `json:"agent_session_id,omitempty"`
Status TaskStatus `json:"status,omitempty"`
Stream string `json:"stream,omitempty"`
Chunk string `json:"chunk,omitempty"`
Tail string `json:"tail,omitempty"`
OutputFile string `json:"output_file,omitempty"`
ExitCode int32 `json:"exit_code,omitempty"`
Duration string `json:"duration,omitempty"`
Stalled bool `json:"stalled,omitempty"`
}
TaskEvent is emitted for live UI updates. Output events are intentionally lightweight and non-persistent; task snapshots remain the source of truth for tool-visible state.
type TaskEventType ¶
type TaskEventType string
TaskEventType identifies a UI-facing background task event.
const ( TaskEventQueued TaskEventType = "queued" TaskEventStarted TaskEventType = "started" TaskEventOutput TaskEventType = "output" TaskEventCompleted TaskEventType = "completed" TaskEventFailed TaskEventType = "failed" TaskEventKilled TaskEventType = "killed" TaskEventUnknown TaskEventType = "unknown" TaskEventStalled TaskEventType = "stalled" )
type TaskKind ¶
type TaskKind string
TaskKind identifies what kind of work a background task tracks.
const ( // KindExec is a background container command execution. KindExec TaskKind = "exec" // KindSpawn is a background subagent batch run by the spawn tool. KindSpawn TaskKind = "spawn" // KindAgent is a single managed subagent task. KindAgent TaskKind = "agent" // KindVideo is an asynchronous video generation task. KindVideo TaskKind = "video" // KindDependency is an in-process workspace dependency install or update // started by SpawnManaged. KindDependency TaskKind = "dependency" )
type TaskSnapshot ¶
type TaskSnapshot struct {
TaskID string
Kind TaskKind
BotID string
SessionID string
Command string
Description string
AgentID string
AgentSessionID string
AgentMessage string
AgentReport string
AgentError string
AgentModelID string
AgentProvider string
AgentFork bool
WorkDir string
Status TaskStatus
ExitCode int32
OutputFile string
OutputTail string
Result map[string]any
Error string
Branches []SpawnBranch
StartedAt time.Time
CompletedAt time.Time
LastOutputAt time.Time
Duration time.Duration
Stalled bool
}
TaskSnapshot is a lock-safe, immutable view of a task for handler/UI code.
type TaskStatus ¶
type TaskStatus string
TaskStatus represents the lifecycle state of a background task.
const ( TaskQueued TaskStatus = "queued" TaskRunning TaskStatus = "running" TaskCompleted TaskStatus = "completed" TaskFailed TaskStatus = "failed" TaskKilled TaskStatus = "killed" // TaskUnknown means supervision ended without proof of process exit. TaskUnknown TaskStatus = "unknown" )
type WaitOutcome ¶
type WaitOutcome string
WaitOutcome explains why a wait on a task returned.
const ( WaitCompleted WaitOutcome = "completed" WaitFailed WaitOutcome = "failed" WaitKilled WaitOutcome = "killed" WaitUnknown WaitOutcome = "unknown" WaitStalled WaitOutcome = "stalled" // WaitIdle means the command is still running but produced no new output // for the idle threshold — for server-style commands this usually means // startup is done and the ready banner is already in the output tail. WaitIdle WaitOutcome = "idle" // WaitTimeout is never returned by the manager itself; the tool layer uses // it when the caller-supplied wait budget elapses before any other outcome. WaitTimeout WaitOutcome = "timeout" )