Manages V2 background job-board state for task execution and injected completion messages, enabling the orchestrator to track active jobs and reuse only completed, reconciled child sessions by short aliases (e.g., exp-1, ora-2). The implementation is split into focused submodules to improve separation of concerns and maintainability.
The directory follows a Facade + Strategy pattern where index.ts acts as the facade that composes and orchestrates behavior across specialized strategy modules:
tool.execute.before, tool.execute.after, experimental.chat.messages.transform, event) and exposes beginUserWait() to the wait_for_user tool.hasInputWait() seam used by idle reconciliation and continuation evaluation. It combines local question/permission waits with the process-global explicit user-wait latch.attempts sentinel so pre-upgrade #856 hooks sharing the store also fail closed. Distinct external user-message identity rearms both states.MAX_PENDING_TASK_CALLS) to correlate launch output safely. Provides call ID generation, storage, retrieval, and cleanup for pending task invocations.All modules depend on BackgroundJobBoard from src/utils/background-job-board.ts as the single source of truth for active jobs, terminal unreconciled jobs, reusable completed sessions, aliases, read context, and LRU caps.
wait_for_user and released by a distinct real external user message.Before Execution (tool.execute.before)
task tool calls on managed sessionsdescription/prompt via deriveTaskSessionLabelPendingTaskCall record with call ID, parent session ID, agent type, and labelTask Launch (tool.execute.after)
Context Tracking
read tool outputs using extractReadFilesMessage Injection (experimental.chat.messages.transform)
<system-reminder> part containing the ### Background Job Board section into user messages for managed sessionsLifecycle Events (event)
session.created: Adds new task IDs to pending managed setsession.idle / session.status (idle): Reconciles injected terminal jobs for the parent sessionsession.status (busy): Marks sessions as running from live session statesession.deleted: Clears job state, child jobs, and pending call records for the sessionHuman-in-the-loop Waits
wait_for_user calls the facade's beginUserWait() only after tool validationUser task call → tool.execute.before → PendingTaskCall created → task ID resolved/reused
→ tool.execute.after → BackgroundJobBoard.registerLaunch() → context extracted/added
→ Message transform → BackgroundJobBoard.formatForPrompt() injected as a system-reminder message part
→ session.idle → reconcileInjectedTerminalJobs() → BackgroundJobBoard.markReconciled()
src/index.ts): Wires the task session manager hook into OpenCode's lifecycle via createTaskSessionManagerHook().src/utils/background-job-board.ts): Central state store for task sessions and context.src/utils/index.ts): parseTaskIdFromTaskOutput, parseTaskLaunchOutput, parseTaskStatusOutput, deriveTaskSessionLabel.isRecord utility and log for diagnostics.maxSessionsPerAgent: Limits reusable sessions per agent typereadContextMinLines: Minimum lines to include in read contextreadContextMaxFiles: Maximum files to include in read contextshouldManageSession: Predicate to determine which sessions are managed by this hooktool.execute.before / tool.execute.after: Intercept task tool calls and register launches/statusexperimental.chat.messages.transform: Inject background job board status into user messagesevent: Handle session lifecycle events (created, idle, busy, error, deleted)The original monolithic module was split to improve:
Each submodule adheres to the Single Responsibility Principle while collaborating through the facade to provide a cohesive user experience.