opencode-ultra 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,109 @@
1
+ export interface BackoffOptions {
2
+ /** Base delay in ms (default: 200) */
3
+ baseDelayMs?: number;
4
+ /** Max delay in ms (default: 10_000) */
5
+ maxDelayMs?: number;
6
+ }
7
+ export type BackoffJitterMode = "full" | "none";
8
+ export interface ExponentialBackoffOptions extends BackoffOptions {
9
+ /** Jitter strategy (default: full) */
10
+ jitter?: BackoffJitterMode;
11
+ /** Random generator in [0, 1) (default: Math.random) */
12
+ rng?: () => number;
13
+ }
14
+ export declare function computeExponentialBackoffDelayMs(attempt: number, opts?: ExponentialBackoffOptions): number;
15
+ export interface CircuitBreakerOptions {
16
+ /** Open circuit after this many consecutive retryable failures (default: 3) */
17
+ failureThreshold?: number;
18
+ /** How long the circuit stays open before allowing a single trial (default: 10_000) */
19
+ cooldownMs?: number;
20
+ }
21
+ type CircuitState = "closed" | "open" | "half_open";
22
+ export declare class CircuitBreakerOpenError extends Error {
23
+ readonly provider: string;
24
+ readonly waitMs: number;
25
+ constructor(provider: string, waitMs: number);
26
+ }
27
+ export declare class CircuitBreaker {
28
+ private state;
29
+ private consecutiveFailures;
30
+ private openUntil;
31
+ private halfOpenInFlight;
32
+ private failureThreshold;
33
+ private cooldownMs;
34
+ constructor(opts?: CircuitBreakerOptions);
35
+ snapshot(now: number): {
36
+ state: CircuitState;
37
+ consecutiveFailures: number;
38
+ openForMs: number;
39
+ };
40
+ /**
41
+ * Gate a request. If open, returns not allowed with waitMs.
42
+ * If half-open, only allows a single in-flight probe.
43
+ */
44
+ enter(now: number): {
45
+ allowed: boolean;
46
+ waitMs?: number;
47
+ };
48
+ onSuccess(): void;
49
+ onFailure(now: number): void;
50
+ }
51
+ export interface RetryBudgetOptions {
52
+ /** Max retries per provider per interval (default: 20) */
53
+ maxRetries?: number;
54
+ /** Interval for budget reset (default: 60_000) */
55
+ intervalMs?: number;
56
+ }
57
+ export declare class RetryBudgetExceededError extends Error {
58
+ readonly provider: string;
59
+ constructor(provider: string);
60
+ }
61
+ export declare class RetryBudget {
62
+ private maxRetries;
63
+ private intervalMs;
64
+ private state;
65
+ constructor(opts?: RetryBudgetOptions);
66
+ trySpend(provider: string, cost?: number): boolean;
67
+ }
68
+ export interface RetryDecision {
69
+ retryable: boolean;
70
+ /** HTTP status code when available */
71
+ status?: number;
72
+ /** Retry-After when available */
73
+ retryAfterMs?: number;
74
+ }
75
+ export declare function parseRetryAfterMs(value: string, now: number): number | undefined;
76
+ export declare function classifyRetryableError(err: unknown, now?: number): RetryDecision;
77
+ export interface AdaptiveRetryOptions {
78
+ /** Max total attempts including the first attempt (default: 4) */
79
+ maxAttempts?: number;
80
+ /** Backoff options (default: base 200, max 10_000) */
81
+ backoff?: ExponentialBackoffOptions;
82
+ /** Cap any retry-after delays to this max (default: 30_000) */
83
+ maxRetryAfterMs?: number;
84
+ /** Treat this classifier result as authoritative (default: classifyRetryableError) */
85
+ classify?: (err: unknown, now: number) => RetryDecision;
86
+ /** Sleep function used for delays (default: setTimeout) */
87
+ sleep?: (ms: number) => Promise<void>;
88
+ /** Time source (default: Date.now) */
89
+ now?: () => number;
90
+ }
91
+ export declare function adaptiveRetry<T>(provider: string, fn: () => Promise<T>, breaker: CircuitBreaker, budget: RetryBudget, opts?: AdaptiveRetryOptions): Promise<T>;
92
+ export interface ProviderResilienceOptions {
93
+ breaker?: CircuitBreakerOptions;
94
+ retryBudget?: RetryBudgetOptions;
95
+ retry?: AdaptiveRetryOptions;
96
+ }
97
+ export declare class ProviderResilience {
98
+ private breakers;
99
+ private budgets;
100
+ private opts;
101
+ constructor(opts?: ProviderResilienceOptions);
102
+ private getBreaker;
103
+ private getBudget;
104
+ run<T>(provider: string, fn: () => Promise<T>, retryOverrides?: AdaptiveRetryOptions): Promise<T>;
105
+ snapshot(provider: string, now?: number): {
106
+ breaker: ReturnType<CircuitBreaker["snapshot"]>;
107
+ };
108
+ }
109
+ export {};
@@ -3,3 +3,5 @@ export { parseJsonc } from "./jsonc";
3
3
  export { log } from "./log";
4
4
  export type { ExtendedClient, ModelRef, ToastOptions, SystemTransformInput } from "./types";
5
5
  export { TtlMap } from "./ttl-map";
6
+ export { CircuitBreaker, CircuitBreakerOpenError, ProviderResilience, RetryBudget, RetryBudgetExceededError, adaptiveRetry, classifyRetryableError, computeExponentialBackoffDelayMs, parseRetryAfterMs, } from "./concurrency";
7
+ export type { AdaptiveRetryOptions, BackoffJitterMode, BackoffOptions, CircuitBreakerOptions, ExponentialBackoffOptions, ProviderResilienceOptions, RetryBudgetOptions, RetryDecision, } from "./concurrency";
@@ -0,0 +1,73 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import { tool } from "@opencode-ai/plugin";
3
+ import { z } from "zod";
4
+ import { type ActiveRalphLoopInfo } from "./ralph-loop";
5
+ export declare const BUILTIN_HOOK_IDS: readonly ["keyword-detector", "rules-injector", "context-injector", "fragment-injector", "prompt-renderer", "todo-enforcer", "comment-checker", "token-truncation", "session-compaction"];
6
+ export declare const BUILTIN_TOOL_IDS: readonly ["spawn_agent", "ralph_loop", "cancel_ralph", "batch_read", "ledger_save", "ledger_load", "ast_search", "evolve_apply", "evolve_scan", "evolve_score", "evolve_exe", "evolve_publish", "health_check"];
7
+ export type BuiltinHookId = (typeof BUILTIN_HOOK_IDS)[number];
8
+ export type BuiltinToolId = (typeof BUILTIN_TOOL_IDS)[number];
9
+ export interface BinaryStatus {
10
+ found: boolean;
11
+ path?: string;
12
+ version?: string;
13
+ error?: string;
14
+ }
15
+ export interface HealthCheckResult {
16
+ ok: boolean;
17
+ generatedAt: string;
18
+ registry: {
19
+ tools: {
20
+ enabled: string[];
21
+ disabled: string[];
22
+ unknownDisabled: string[];
23
+ };
24
+ hooks: {
25
+ enabled: string[];
26
+ disabled: string[];
27
+ unknownDisabled: string[];
28
+ };
29
+ mcps: {
30
+ enabled: string[];
31
+ disabled: string[];
32
+ unknownDisabled: string[];
33
+ };
34
+ };
35
+ loops: {
36
+ ralph: {
37
+ activeCount: number;
38
+ active: ActiveRalphLoopInfo[];
39
+ };
40
+ };
41
+ sessions: {
42
+ internalSessionCount: number;
43
+ };
44
+ config: {
45
+ astSearch: {
46
+ enabled: boolean;
47
+ binaryFound: boolean;
48
+ binaryPath?: string;
49
+ };
50
+ mcp: {
51
+ context7: {
52
+ enabled: boolean;
53
+ npxFound: boolean;
54
+ npxPath?: string;
55
+ };
56
+ };
57
+ };
58
+ binaries: Record<string, BinaryStatus>;
59
+ warnings: string[];
60
+ }
61
+ export declare const BinaryStatusSchema: z.ZodType<BinaryStatus>;
62
+ export declare const HealthCheckResultSchema: z.ZodType<HealthCheckResult>;
63
+ export interface HealthCheckDeps {
64
+ toolNames: string[];
65
+ disabledTools?: string[];
66
+ disabledHooks?: string[];
67
+ disabledMcps?: string[];
68
+ internalSessions?: Set<string>;
69
+ astGrepBinary?: string | null;
70
+ }
71
+ export declare function findBinaryInPath(name: string): string | null;
72
+ export declare function checkBinary(name: string, versionArgs?: string[], includeVersion?: boolean): BinaryStatus;
73
+ export declare function createHealthCheckTool(ctx: PluginInput, deps: HealthCheckDeps): ReturnType<typeof tool>;
@@ -0,0 +1,16 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ export interface ActiveRalphLoopInfo {
3
+ sessionID: string;
4
+ iteration: number;
5
+ maxIterations: number;
6
+ active: boolean;
7
+ }
8
+ export declare function getActiveRalphLoops(): ActiveRalphLoopInfo[];
9
+ export interface RalphLoopDeps {
10
+ /** Per-iteration timeout in ms (default: 180000 = 3min) */
11
+ iterationTimeoutMs?: number;
12
+ }
13
+ export declare function createRalphLoopTools(ctx: PluginInput, internalSessions: Set<string>, deps?: RalphLoopDeps): {
14
+ ralph_loop: any;
15
+ cancel_ralph: any;
16
+ };
@@ -0,0 +1,19 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import { tool } from "@opencode-ai/plugin";
3
+ import { ProviderResilience } from "../shared";
4
+ import { type ConcurrencyPool } from "../concurrency";
5
+ import type { CategoryConfig } from "../categories";
6
+ export interface SpawnAgentDeps {
7
+ pool?: ConcurrencyPool;
8
+ categories?: Record<string, CategoryConfig>;
9
+ resolveAgentModel?: (agent: string) => string | undefined;
10
+ /** Provider-level retry/backoff + circuit breaker */
11
+ resilience?: ProviderResilience;
12
+ /** Max total concurrent spawned sessions (default: 15) */
13
+ maxTotalSpawned?: number;
14
+ /** Per-agent timeout in ms (default: 180000 = 3min) */
15
+ agentTimeoutMs?: number;
16
+ /** Reference to internalSessions for spawn limit check */
17
+ internalSessions?: Set<string>;
18
+ }
19
+ export declare function createSpawnAgentTool(ctx: PluginInput, internalSessions: Set<string>, deps?: SpawnAgentDeps): ReturnType<typeof tool>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-ultra",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "Lightweight OpenCode 1.2.x plugin — ultrawork mode, multi-agent orchestration, rules injection",
5
5
  "keywords": [
6
6
  "opencode",