chore: include pre-built dist for github install

This commit is contained in:
Robin Mordasiewicz
2026-03-14 04:56:50 +00:00
parent a7f0a4cf46
commit bce8ff3a75
1011 changed files with 150081 additions and 0 deletions
@@ -0,0 +1,10 @@
import type { BackgroundTask } from "./types";
export type BackgroundTaskNotificationStatus = "COMPLETED" | "CANCELLED" | "INTERRUPTED";
export declare function buildBackgroundTaskNotificationText(input: {
task: BackgroundTask;
duration: string;
statusText: BackgroundTaskNotificationStatus;
allComplete: boolean;
remainingCount: number;
completedTasks: BackgroundTask[];
}): string;
@@ -0,0 +1,18 @@
import type { StoredMessage } from "../hook-message-injector";
type SessionMessage = {
info?: {
agent?: string;
model?: {
providerID?: string;
modelID?: string;
variant?: string;
};
providerID?: string;
modelID?: string;
tools?: StoredMessage["tools"];
};
};
export declare function isCompactionAgent(agent: string | undefined): boolean;
export declare function resolvePromptContextFromSessionMessages(messages: SessionMessage[], sessionID?: string): StoredMessage | null;
export declare function findNearestMessageExcludingCompaction(messageDir: string, sessionID?: string): StoredMessage | null;
export {};
+27
View File
@@ -0,0 +1,27 @@
import type { BackgroundTaskConfig } from "../../config/schema";
export declare class ConcurrencyManager {
private config?;
private counts;
private queues;
constructor(config?: BackgroundTaskConfig);
getConcurrencyLimit(model: string): number;
acquire(model: string): Promise<void>;
release(model: string): void;
/**
* Cancel all waiting acquires for a model. Used during cleanup.
*/
cancelWaiters(model: string): void;
/**
* Clear all state. Used during manager cleanup/shutdown.
* Cancels all pending waiters.
*/
clear(): void;
/**
* Get current count for a model (for testing/debugging)
*/
getCount(model: string): number;
/**
* Get queue length for a model (for testing/debugging)
*/
getQueueLength(model: string): number;
}
+45
View File
@@ -0,0 +1,45 @@
import type { PluginInput } from "@opencode-ai/plugin";
import type { BackgroundTask, LaunchInput } from "./types";
export declare const TASK_TTL_MS: number;
export declare const MIN_STABILITY_TIME_MS: number;
export declare const DEFAULT_STALE_TIMEOUT_MS = 180000;
export declare const DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
export declare const MIN_RUNTIME_BEFORE_STALE_MS = 30000;
export declare const MIN_IDLE_TIME_MS = 5000;
export declare const POLLING_INTERVAL_MS = 3000;
export declare const TASK_CLEANUP_DELAY_MS: number;
export declare const TMUX_CALLBACK_DELAY_MS = 200;
export type ProcessCleanupEvent = NodeJS.Signals | "beforeExit" | "exit";
export type OpencodeClient = PluginInput["client"];
export interface MessagePartInfo {
sessionID?: string;
type?: string;
tool?: string;
}
export interface EventProperties {
sessionID?: string;
info?: {
id?: string;
};
[key: string]: unknown;
}
export interface BackgroundEvent {
type: string;
properties?: EventProperties;
}
export interface Todo {
content: string;
status: string;
priority: string;
id?: string;
}
export interface QueueItem {
task: BackgroundTask;
input: LaunchInput;
}
export interface SubagentSessionCreatedEvent {
sessionID: string;
parentID: string;
title: string;
}
export type OnSubagentSessionCreated = (event: SubagentSessionCreatedEvent) => Promise<void>;
@@ -0,0 +1 @@
export declare function formatDuration(start: Date, end?: Date): string;
+10
View File
@@ -0,0 +1,10 @@
export declare function isRecord(value: unknown): value is Record<string, unknown>;
export declare function isAbortedSessionError(error: unknown): boolean;
export declare function getErrorText(error: unknown): string;
export declare function extractErrorName(error: unknown): string | undefined;
export declare function extractErrorMessage(error: unknown): string | undefined;
interface EventPropertiesLike {
[key: string]: unknown;
}
export declare function getSessionErrorMessage(properties: EventPropertiesLike): string | undefined;
export {};
@@ -0,0 +1,16 @@
import type { BackgroundTask } from "./types";
import type { ConcurrencyManager } from "./concurrency";
import type { OpencodeClient, QueueItem } from "./constants";
export declare function tryFallbackRetry(args: {
task: BackgroundTask;
errorInfo: {
name?: string;
message?: string;
};
source: string;
concurrencyManager: ConcurrencyManager;
client: OpencodeClient;
idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>>;
queuesByKey: Map<string, QueueItem[]>;
processKey: (key: string) => void;
}): boolean;
+2
View File
@@ -0,0 +1,2 @@
export * from "./types";
export { BackgroundManager, type SubagentSessionCreatedEvent, type OnSubagentSessionCreated } from "./manager";
+156
View File
@@ -0,0 +1,156 @@
import type { PluginInput } from "@opencode-ai/plugin";
import type { BackgroundTask, LaunchInput, ResumeInput } from "./types";
import { TaskHistory } from "./task-history";
import type { BackgroundTaskConfig, TmuxConfig } from "../../config/schema";
import { type SubagentSpawnContext } from "./subagent-spawn-limits";
interface EventProperties {
sessionID?: string;
info?: {
id?: string;
};
[key: string]: unknown;
}
interface Event {
type: string;
properties?: EventProperties;
}
export interface SubagentSessionCreatedEvent {
sessionID: string;
parentID: string;
title: string;
}
export type OnSubagentSessionCreated = (event: SubagentSessionCreatedEvent) => Promise<void>;
export declare class BackgroundManager {
private tasks;
private notifications;
private pendingNotifications;
private pendingByParent;
private client;
private directory;
private pollingInterval?;
private pollingInFlight;
private concurrencyManager;
private shutdownTriggered;
private config?;
private tmuxEnabled;
private onSubagentSessionCreated?;
private onShutdown?;
private queuesByKey;
private processingKeys;
private completionTimers;
private completedTaskSummaries;
private idleDeferralTimers;
private notificationQueueByParent;
private rootDescendantCounts;
private preStartDescendantReservations;
private enableParentSessionNotifications;
readonly taskHistory: TaskHistory;
constructor(ctx: PluginInput, config?: BackgroundTaskConfig, options?: {
tmuxConfig?: TmuxConfig;
onSubagentSessionCreated?: OnSubagentSessionCreated;
onShutdown?: () => void | Promise<void>;
enableParentSessionNotifications?: boolean;
});
assertCanSpawn(parentSessionID: string): Promise<SubagentSpawnContext>;
reserveSubagentSpawn(parentSessionID: string): Promise<{
spawnContext: SubagentSpawnContext;
descendantCount: number;
commit: () => number;
rollback: () => void;
}>;
private registerRootDescendant;
private unregisterRootDescendant;
private markPreStartDescendantReservation;
private settlePreStartDescendantReservation;
private rollbackPreStartDescendantReservation;
launch(input: LaunchInput): Promise<BackgroundTask>;
private processKey;
private startTask;
getTask(id: string): BackgroundTask | undefined;
getTasksByParentSession(sessionID: string): BackgroundTask[];
getAllDescendantTasks(sessionID: string): BackgroundTask[];
findBySession(sessionID: string): BackgroundTask | undefined;
private getConcurrencyKeyFromInput;
/**
* Track a task created elsewhere (e.g., from task) for notification tracking.
* This allows tasks created by other tools to receive the same toast/prompt notifications.
*/
trackTask(input: {
taskId: string;
sessionID: string;
parentSessionID: string;
description: string;
agent?: string;
parentAgent?: string;
concurrencyKey?: string;
}): Promise<BackgroundTask>;
resume(input: ResumeInput): Promise<BackgroundTask>;
private checkSessionTodos;
handleEvent(event: Event): void;
private tryFallbackRetry;
markForNotification(task: BackgroundTask): void;
getPendingNotifications(sessionID: string): BackgroundTask[];
clearNotifications(sessionID: string): void;
queuePendingNotification(sessionID: string | undefined, notification: string): void;
injectPendingNotificationsIntoChatMessage(output: {
parts: Array<{
type: string;
text?: string;
[key: string]: unknown;
}>;
}, sessionID: string): void;
/**
* Validates that a session has actual assistant/tool output before marking complete.
* Prevents premature completion when session.idle fires before agent responds.
*/
private validateSessionHasOutput;
private clearNotificationsForTask;
/**
* Remove task from pending tracking for its parent session.
* Cleans up the parent entry if no pending tasks remain.
*/
private cleanupPendingByParent;
private clearTaskHistoryWhenParentTasksGone;
private scheduleTaskRemoval;
cancelTask(taskId: string, options?: {
source?: string;
reason?: string;
abortSession?: boolean;
skipNotification?: boolean;
}): Promise<boolean>;
/**
* Cancels a pending task by removing it from queue and marking as cancelled.
* Does NOT abort session (no session exists yet) or release concurrency slot (wasn't acquired).
*/
cancelPendingTask(taskId: string): boolean;
private startPolling;
private stopPolling;
private registerProcessCleanup;
private unregisterProcessCleanup;
/**
* Get all running tasks (for compaction hook)
*/
getRunningTasks(): BackgroundTask[];
/**
* Get all non-running tasks still in memory (for compaction hook)
*/
getNonRunningTasks(): BackgroundTask[];
/**
* Safely complete a task with race condition protection.
* Returns true if task was successfully completed, false if already completed by another path.
*/
private tryCompleteTask;
private notifyParentSession;
private hasRunningTasks;
private pruneStaleTasksAndNotifications;
private checkAndInterruptStaleTasks;
private pollRunningTasks;
/**
* Shutdown the manager gracefully.
* Cancels all pending concurrency waiters and clears timers.
* Should be called when the plugin is unloaded.
*/
shutdown(): Promise<void>;
private enqueueNotificationForParent;
}
export {};
+2
View File
@@ -0,0 +1,2 @@
import type { PluginInput } from "@opencode-ai/plugin";
export type OpencodeClient = PluginInput["client"];
+8
View File
@@ -0,0 +1,8 @@
interface CleanupTarget {
shutdown(): void | Promise<void>;
}
export declare function registerManagerForCleanup(manager: CleanupTarget): void;
export declare function unregisterManagerForCleanup(manager: CleanupTarget): void;
/** @internal — test-only reset for module-level singleton state */
export declare function _resetForTesting(): void;
export {};
@@ -0,0 +1 @@
export declare function removeTaskToastTracking(taskId: string): void;
@@ -0,0 +1,10 @@
import type { BackgroundTask } from "./types";
export declare function handleSessionIdleBackgroundEvent(args: {
properties: Record<string, unknown>;
findBySession: (sessionID: string) => BackgroundTask | undefined;
idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>>;
validateSessionHasOutput: (sessionID: string) => Promise<boolean>;
checkSessionTodos: (sessionID: string) => Promise<boolean>;
tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean>;
emitIdleEvent: (sessionID: string) => void;
}): void;
+14
View File
@@ -0,0 +1,14 @@
import type { BackgroundTask, LaunchInput, ResumeInput } from "./types";
import type { OpencodeClient, OnSubagentSessionCreated, QueueItem } from "./constants";
import type { ConcurrencyManager } from "./concurrency";
export interface SpawnerContext {
client: OpencodeClient;
directory: string;
concurrencyManager: ConcurrencyManager;
tmuxEnabled: boolean;
onSubagentSessionCreated?: OnSubagentSessionCreated;
onTaskError: (task: BackgroundTask, error: Error) => void;
}
export declare function createTask(input: LaunchInput): BackgroundTask;
export declare function startTask(item: QueueItem, ctx: SpawnerContext): Promise<void>;
export declare function resumeTask(task: BackgroundTask, input: ResumeInput, ctx: Pick<SpawnerContext, "client" | "concurrencyManager" | "onTaskError">): Promise<void>;
@@ -0,0 +1,6 @@
import type { OpencodeClient } from "../constants";
export declare function resolveParentDirectory(options: {
client: OpencodeClient;
parentSessionID: string;
defaultDirectory: string;
}): Promise<string>;
+35
View File
@@ -0,0 +1,35 @@
import type { BackgroundTask, LaunchInput } from "./types";
import type { QueueItem } from "./constants";
export declare class TaskStateManager {
readonly tasks: Map<string, BackgroundTask>;
readonly notifications: Map<string, BackgroundTask[]>;
readonly pendingByParent: Map<string, Set<string>>;
readonly queuesByKey: Map<string, QueueItem[]>;
readonly processingKeys: Set<string>;
readonly completionTimers: Map<string, ReturnType<typeof setTimeout>>;
getTask(id: string): BackgroundTask | undefined;
findBySession(sessionID: string): BackgroundTask | undefined;
getTasksByParentSession(sessionID: string): BackgroundTask[];
getAllDescendantTasks(sessionID: string): BackgroundTask[];
getRunningTasks(): BackgroundTask[];
getNonRunningTasks(): BackgroundTask[];
hasRunningTasks(): boolean;
getConcurrencyKeyFromInput(input: LaunchInput): string;
getConcurrencyKeyFromTask(task: BackgroundTask): string;
addTask(task: BackgroundTask): void;
removeTask(taskId: string): void;
trackPendingTask(parentSessionID: string, taskId: string): void;
cleanupPendingByParent(task: BackgroundTask): void;
markForNotification(task: BackgroundTask): void;
getPendingNotifications(sessionID: string): BackgroundTask[];
clearNotifications(sessionID: string): void;
clearNotificationsForTask(taskId: string): void;
addToQueue(key: string, item: QueueItem): void;
getQueue(key: string): QueueItem[] | undefined;
removeFromQueue(key: string, taskId: string): boolean;
setCompletionTimer(taskId: string, timer: ReturnType<typeof setTimeout>): void;
clearCompletionTimer(taskId: string): void;
clearAllCompletionTimers(): void;
clear(): void;
cancelPendingTask(taskId: string): boolean;
}
@@ -0,0 +1,23 @@
import type { BackgroundTaskConfig } from "../../config/schema";
import type { OpencodeClient } from "./constants";
export declare const DEFAULT_MAX_SUBAGENT_DEPTH = 3;
export declare const DEFAULT_MAX_ROOT_SESSION_SPAWN_BUDGET = 50;
export interface SubagentSpawnContext {
rootSessionID: string;
parentDepth: number;
childDepth: number;
}
export declare function getMaxSubagentDepth(config?: BackgroundTaskConfig): number;
export declare function getMaxRootSessionSpawnBudget(config?: BackgroundTaskConfig): number;
export declare function resolveSubagentSpawnContext(client: OpencodeClient, parentSessionID: string): Promise<SubagentSpawnContext>;
export declare function createSubagentDepthLimitError(input: {
childDepth: number;
maxDepth: number;
parentSessionID: string;
rootSessionID: string;
}): Error;
export declare function createSubagentDescendantLimitError(input: {
rootSessionID: string;
descendantCount: number;
maxDescendants: number;
}): Error;
+19
View File
@@ -0,0 +1,19 @@
import type { BackgroundTaskStatus } from "./types";
export interface TaskHistoryEntry {
id: string;
sessionID?: string;
agent: string;
description: string;
status: BackgroundTaskStatus;
category?: string;
startedAt?: Date;
completedAt?: Date;
}
export declare class TaskHistory {
private entries;
record(parentSessionID: string | undefined, entry: TaskHistoryEntry): void;
getByParentSession(parentSessionID: string): TaskHistoryEntry[];
clearSession(parentSessionID: string): void;
clearAll(): void;
formatForCompaction(parentSessionID: string): string | null;
}
+21
View File
@@ -0,0 +1,21 @@
import type { BackgroundTaskConfig } from "../../config/schema";
import type { BackgroundTask } from "./types";
import type { ConcurrencyManager } from "./concurrency";
import type { OpencodeClient } from "./opencode-client";
export declare function pruneStaleTasksAndNotifications(args: {
tasks: Map<string, BackgroundTask>;
notifications: Map<string, BackgroundTask[]>;
onTaskPruned: (taskId: string, task: BackgroundTask, errorMessage: string) => void;
}): void;
export type SessionStatusMap = Record<string, {
type: string;
}>;
export declare function checkAndInterruptStaleTasks(args: {
tasks: Iterable<BackgroundTask>;
client: OpencodeClient;
config: BackgroundTaskConfig | undefined;
concurrencyManager: ConcurrencyManager;
notifyParentSession: (task: BackgroundTask) => Promise<void>;
sessionStatuses?: SessionStatusMap;
onTaskInterrupted?: (task: BackgroundTask) => void;
}): Promise<void>;
+94
View File
@@ -0,0 +1,94 @@
import type { FallbackEntry } from "../../shared/model-requirements";
import type { SessionPermissionRule } from "../../shared/question-denied-session-permission";
export type BackgroundTaskStatus = "pending" | "running" | "completed" | "error" | "cancelled" | "interrupt";
export interface TaskProgress {
toolCalls: number;
lastTool?: string;
lastUpdate: Date;
lastMessage?: string;
lastMessageAt?: Date;
}
export interface BackgroundTask {
id: string;
sessionID?: string;
rootSessionID?: string;
parentSessionID: string;
parentMessageID: string;
description: string;
prompt: string;
agent: string;
spawnDepth?: number;
status: BackgroundTaskStatus;
queuedAt?: Date;
startedAt?: Date;
completedAt?: Date;
result?: string;
error?: string;
progress?: TaskProgress;
parentModel?: {
providerID: string;
modelID: string;
};
model?: {
providerID: string;
modelID: string;
variant?: string;
};
/** Fallback chain for runtime retry on model errors */
fallbackChain?: FallbackEntry[];
/** Number of fallback retry attempts made */
attemptCount?: number;
/** Active concurrency slot key */
concurrencyKey?: string;
/** Persistent key for re-acquiring concurrency on resume */
concurrencyGroup?: string;
/** Parent session's agent name for notification */
parentAgent?: string;
/** Parent session's tool restrictions for notification prompts */
parentTools?: Record<string, boolean>;
/** Marks if the task was launched from an unstable agent/category */
isUnstableAgent?: boolean;
/** Category used for this task (e.g., 'quick', 'visual-engineering') */
category?: string;
/** Last message count for stability detection */
lastMsgCount?: number;
/** Number of consecutive polls with stable message count */
stablePolls?: number;
}
export interface LaunchInput {
description: string;
prompt: string;
agent: string;
parentSessionID: string;
parentMessageID: string;
parentModel?: {
providerID: string;
modelID: string;
};
parentAgent?: string;
parentTools?: Record<string, boolean>;
model?: {
providerID: string;
modelID: string;
variant?: string;
};
/** Fallback chain for runtime retry on model errors */
fallbackChain?: FallbackEntry[];
isUnstableAgent?: boolean;
skills?: string[];
skillContent?: string;
category?: string;
sessionPermission?: SessionPermissionRule[];
}
export interface ResumeInput {
sessionId: string;
prompt: string;
parentSessionID: string;
parentMessageID: string;
parentModel?: {
providerID: string;
modelID: string;
};
parentAgent?: string;
parentTools?: Record<string, boolean>;
}