oh-my-opencode-slim 2.1.0 → 2.1.1
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.
- package/README.ja-JP.md +18 -18
- package/README.ko-KR.md +18 -18
- package/README.md +29 -18
- package/README.zh-CN.md +16 -16
- package/dist/cli/index.js +42 -11
- package/dist/cli/providers.d.ts +7 -7
- package/dist/companion/manager.d.ts +3 -0
- package/dist/config/constants.d.ts +2 -1
- package/dist/config/council-schema.d.ts +37 -12
- package/dist/config/schema.d.ts +13 -6
- package/dist/council/council-manager.d.ts +7 -3
- package/dist/hooks/foreground-fallback/index.d.ts +35 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/phase-reminder/index.d.ts +3 -1
- package/dist/hooks/post-file-tool-nudge/index.d.ts +15 -9
- package/dist/hooks/session-lifecycle.d.ts +11 -0
- package/dist/hooks/task-session-manager/index.d.ts +14 -2
- package/dist/index.js +972 -514
- package/dist/interview/service.d.ts +2 -0
- package/dist/multiplexer/herdr/index.d.ts +5 -3
- package/dist/multiplexer/session-manager.d.ts +5 -5
- package/dist/multiplexer/shared.d.ts +24 -0
- package/dist/multiplexer/tmux/index.d.ts +0 -1
- package/dist/multiplexer/zellij/index.d.ts +0 -1
- package/dist/tools/cancel-task.d.ts +2 -2
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +45 -10
- package/dist/utils/background-job-board.d.ts +5 -1
- package/dist/utils/background-job-coordinator.d.ts +72 -0
- package/dist/utils/background-job-store.d.ts +46 -0
- package/dist/utils/councillor-models.d.ts +20 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/internal-initiator.d.ts +6 -1
- package/dist/utils/session.d.ts +1 -1
- package/oh-my-opencode-slim.schema.json +13 -1
- package/package.json +1 -1
- package/src/skills/oh-my-opencode-slim/SKILL.md +3 -3
- package/src/skills/release-smoke-test/SKILL.md +2 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { type CouncillorModelEntry } from '../utils/councillor-models';
|
|
3
|
+
export type { CouncillorModelEntry };
|
|
2
4
|
/**
|
|
3
5
|
* Configuration for a single councillor within a preset.
|
|
4
6
|
* Each councillor is an independent LLM that processes the same prompt.
|
|
@@ -6,12 +8,31 @@ import { z } from 'zod';
|
|
|
6
8
|
* Councillors run as agent sessions with read-only codebase access
|
|
7
9
|
* (read, glob, grep, lsp, list). They can examine the codebase but
|
|
8
10
|
* cannot modify files or spawn subagents.
|
|
11
|
+
*
|
|
12
|
+
* `model` accepts a single ID or an ordered fallback chain. The parsed config
|
|
13
|
+
* exposes `models` (the normalized chain) plus `model` (the primary, for
|
|
14
|
+
* backward compatibility).
|
|
9
15
|
*/
|
|
10
|
-
export declare const CouncillorConfigSchema: z.ZodObject<{
|
|
11
|
-
model: z.ZodString
|
|
16
|
+
export declare const CouncillorConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
17
|
+
model: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
18
|
+
id: z.ZodString;
|
|
19
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, z.core.$strip>]>>]>;
|
|
12
21
|
variant: z.ZodOptional<z.ZodString>;
|
|
13
22
|
prompt: z.ZodOptional<z.ZodString>;
|
|
14
|
-
}, z.core.$strip
|
|
23
|
+
}, z.core.$strip>, z.ZodTransform<{
|
|
24
|
+
model: string;
|
|
25
|
+
variant: string | undefined;
|
|
26
|
+
prompt: string | undefined;
|
|
27
|
+
models: CouncillorModelEntry[];
|
|
28
|
+
}, {
|
|
29
|
+
model: string | (string | {
|
|
30
|
+
id: string;
|
|
31
|
+
variant?: string | undefined;
|
|
32
|
+
})[];
|
|
33
|
+
variant?: string | undefined;
|
|
34
|
+
prompt?: string | undefined;
|
|
35
|
+
}>>;
|
|
15
36
|
export type CouncillorConfig = z.infer<typeof CouncillorConfigSchema>;
|
|
16
37
|
/**
|
|
17
38
|
* A named preset grouping several councillors.
|
|
@@ -22,8 +43,9 @@ export type CouncillorConfig = z.infer<typeof CouncillorConfigSchema>;
|
|
|
22
43
|
*/
|
|
23
44
|
export declare const CouncilPresetSchema: z.ZodPipe<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>, z.ZodTransform<Record<string, {
|
|
24
45
|
model: string;
|
|
25
|
-
variant
|
|
26
|
-
prompt
|
|
46
|
+
variant: string | undefined;
|
|
47
|
+
prompt: string | undefined;
|
|
48
|
+
models: CouncillorModelEntry[];
|
|
27
49
|
}>, Record<string, Record<string, unknown>>>>;
|
|
28
50
|
export type CouncilPreset = z.infer<typeof CouncilPresetSchema>;
|
|
29
51
|
/**
|
|
@@ -44,7 +66,7 @@ export declare const CouncillorExecutionModeSchema: z.ZodDefault<z.ZodEnum<{
|
|
|
44
66
|
* "council": {
|
|
45
67
|
* "presets": {
|
|
46
68
|
* "default": {
|
|
47
|
-
* "alpha": { "model": "openai/gpt-5.
|
|
69
|
+
* "alpha": { "model": "openai/gpt-5.6-luna" },
|
|
48
70
|
* "beta": { "model": "openai/gpt-5.3-codex" },
|
|
49
71
|
* "gamma": { "model": "google/gemini-3-pro" }
|
|
50
72
|
* }
|
|
@@ -58,8 +80,9 @@ export declare const CouncillorExecutionModeSchema: z.ZodDefault<z.ZodEnum<{
|
|
|
58
80
|
export declare const CouncilConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
59
81
|
presets: z.ZodRecord<z.ZodString, z.ZodPipe<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>, z.ZodTransform<Record<string, {
|
|
60
82
|
model: string;
|
|
61
|
-
variant
|
|
62
|
-
prompt
|
|
83
|
+
variant: string | undefined;
|
|
84
|
+
prompt: string | undefined;
|
|
85
|
+
models: CouncillorModelEntry[];
|
|
63
86
|
}>, Record<string, Record<string, unknown>>>>>;
|
|
64
87
|
timeout: z.ZodDefault<z.ZodNumber>;
|
|
65
88
|
default_preset: z.ZodDefault<z.ZodString>;
|
|
@@ -72,8 +95,9 @@ export declare const CouncilConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
|
72
95
|
}, z.core.$strip>, z.ZodTransform<{
|
|
73
96
|
presets: Record<string, Record<string, {
|
|
74
97
|
model: string;
|
|
75
|
-
variant
|
|
76
|
-
prompt
|
|
98
|
+
variant: string | undefined;
|
|
99
|
+
prompt: string | undefined;
|
|
100
|
+
models: CouncillorModelEntry[];
|
|
77
101
|
}>>;
|
|
78
102
|
timeout: number;
|
|
79
103
|
default_preset: string;
|
|
@@ -84,8 +108,9 @@ export declare const CouncilConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
|
84
108
|
}, {
|
|
85
109
|
presets: Record<string, Record<string, {
|
|
86
110
|
model: string;
|
|
87
|
-
variant
|
|
88
|
-
prompt
|
|
111
|
+
variant: string | undefined;
|
|
112
|
+
prompt: string | undefined;
|
|
113
|
+
models: CouncillorModelEntry[];
|
|
89
114
|
}>>;
|
|
90
115
|
timeout: number;
|
|
91
116
|
default_preset: string;
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -177,7 +177,9 @@ export declare const FailoverConfigSchema: z.ZodObject<{
|
|
|
177
177
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
178
178
|
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
179
179
|
retryDelayMs: z.ZodDefault<z.ZodNumber>;
|
|
180
|
+
maxRetries: z.ZodDefault<z.ZodNumber>;
|
|
180
181
|
retry_on_empty: z.ZodDefault<z.ZodBoolean>;
|
|
182
|
+
runtimeOverride: z.ZodDefault<z.ZodBoolean>;
|
|
181
183
|
}, z.core.$strict>;
|
|
182
184
|
export type FailoverConfig = z.infer<typeof FailoverConfigSchema>;
|
|
183
185
|
export declare const CompanionConfigSchema: z.ZodObject<{
|
|
@@ -337,13 +339,16 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
337
339
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
338
340
|
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
339
341
|
retryDelayMs: z.ZodDefault<z.ZodNumber>;
|
|
342
|
+
maxRetries: z.ZodDefault<z.ZodNumber>;
|
|
340
343
|
retry_on_empty: z.ZodDefault<z.ZodBoolean>;
|
|
344
|
+
runtimeOverride: z.ZodDefault<z.ZodBoolean>;
|
|
341
345
|
}, z.core.$strict>>;
|
|
342
346
|
council: z.ZodOptional<z.ZodPipe<z.ZodObject<{
|
|
343
347
|
presets: z.ZodRecord<z.ZodString, z.ZodPipe<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>, z.ZodTransform<Record<string, {
|
|
344
348
|
model: string;
|
|
345
|
-
variant
|
|
346
|
-
prompt
|
|
349
|
+
variant: string | undefined;
|
|
350
|
+
prompt: string | undefined;
|
|
351
|
+
models: import("./council-schema").CouncillorModelEntry[];
|
|
347
352
|
}>, Record<string, Record<string, unknown>>>>>;
|
|
348
353
|
timeout: z.ZodDefault<z.ZodNumber>;
|
|
349
354
|
default_preset: z.ZodDefault<z.ZodString>;
|
|
@@ -356,8 +361,9 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
356
361
|
}, z.core.$strip>, z.ZodTransform<{
|
|
357
362
|
presets: Record<string, Record<string, {
|
|
358
363
|
model: string;
|
|
359
|
-
variant
|
|
360
|
-
prompt
|
|
364
|
+
variant: string | undefined;
|
|
365
|
+
prompt: string | undefined;
|
|
366
|
+
models: import("./council-schema").CouncillorModelEntry[];
|
|
361
367
|
}>>;
|
|
362
368
|
timeout: number;
|
|
363
369
|
default_preset: string;
|
|
@@ -368,8 +374,9 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
368
374
|
}, {
|
|
369
375
|
presets: Record<string, Record<string, {
|
|
370
376
|
model: string;
|
|
371
|
-
variant
|
|
372
|
-
prompt
|
|
377
|
+
variant: string | undefined;
|
|
378
|
+
prompt: string | undefined;
|
|
379
|
+
models: import("./council-schema").CouncillorModelEntry[];
|
|
373
380
|
}>>;
|
|
374
381
|
timeout: number;
|
|
375
382
|
default_preset: string;
|
|
@@ -41,9 +41,13 @@ export declare class CouncilManager {
|
|
|
41
41
|
private runAgentSession;
|
|
42
42
|
private runCouncillors;
|
|
43
43
|
/**
|
|
44
|
-
* Run a single councillor
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* Run a single councillor across its configured model chain.
|
|
45
|
+
*
|
|
46
|
+
* For each model in the chain, empty responses are retried up to
|
|
47
|
+
* `maxRetries` times (providers that silently rate-limit). Any other
|
|
48
|
+
* failure or timeout advances to the next model in the chain. The
|
|
49
|
+
* councillor only fails once every model has been exhausted; the reported
|
|
50
|
+
* `model` and `error` reflect the last model tried.
|
|
47
51
|
*/
|
|
48
52
|
private runCouncillorWithRetry;
|
|
49
53
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* try/catch, which is not possible for interactive (foreground) sessions.
|
|
15
15
|
*/
|
|
16
16
|
import type { PluginInput } from '@opencode-ai/plugin';
|
|
17
|
+
import type { SessionLifecycle } from '../session-lifecycle';
|
|
17
18
|
type OpencodeClient = PluginInput['client'];
|
|
18
19
|
export declare function isRateLimitError(error: unknown): boolean;
|
|
19
20
|
/**
|
|
@@ -31,6 +32,15 @@ export declare class ForegroundFallbackManager {
|
|
|
31
32
|
*/
|
|
32
33
|
private readonly chains;
|
|
33
34
|
private readonly enabled;
|
|
35
|
+
/** Consecutive 429s tolerated on the same model before swap/abort. */
|
|
36
|
+
private readonly maxRetries;
|
|
37
|
+
/**
|
|
38
|
+
* When true (default), a runtime model outside the configured chain
|
|
39
|
+
* still triggers fallback on rate-limit errors. When false, out-of-chain
|
|
40
|
+
* runtime picks are respected and the error surfaces instead. Models
|
|
41
|
+
* that are members of the chain always fall back regardless.
|
|
42
|
+
*/
|
|
43
|
+
private readonly runtimeOverride;
|
|
34
44
|
/** sessionID → last observed model string ("providerID/modelID") */
|
|
35
45
|
private readonly sessionModel;
|
|
36
46
|
/** sessionID → agent name (populated from message.updated info.agent field) */
|
|
@@ -45,18 +55,42 @@ export declare class ForegroundFallbackManager {
|
|
|
45
55
|
* when the model has changed, allowing the cascade to continue when a
|
|
46
56
|
* new fallback model also fails within the dedup window. */
|
|
47
57
|
private readonly lastTriggerModel;
|
|
58
|
+
/** sessionID → consecutive 429 count for the current model.
|
|
59
|
+
* Reset on model swap or session deletion. */
|
|
60
|
+
private readonly sessionRetries;
|
|
61
|
+
/** Exposed for task-session-manager: prevents idle reconciliation
|
|
62
|
+
* while a fallback abort/re-prompt is in flight for this session. */
|
|
63
|
+
isFallbackInProgress(sessionID: string): boolean;
|
|
48
64
|
constructor(client: OpencodeClient,
|
|
49
65
|
/**
|
|
50
66
|
* Ordered fallback chains per agent.
|
|
51
67
|
* e.g. { orchestrator: ['anthropic/claude-opus-4-5', 'openai/gpt-4o'] }
|
|
52
68
|
* The first model that hasn't been tried yet is selected on each fallback.
|
|
53
69
|
*/
|
|
54
|
-
chains: Record<string, string[]>, enabled: boolean
|
|
70
|
+
chains: Record<string, string[]>, enabled: boolean,
|
|
71
|
+
/** Consecutive 429s tolerated on the same model before swap/abort. */
|
|
72
|
+
maxRetries?: number, coordinator?: SessionLifecycle,
|
|
73
|
+
/**
|
|
74
|
+
* When true (default), a runtime model outside the configured chain
|
|
75
|
+
* still triggers fallback on rate-limit errors. When false, out-of-chain
|
|
76
|
+
* runtime picks are respected and the error surfaces instead. Models
|
|
77
|
+
* that are members of the chain always fall back regardless.
|
|
78
|
+
*/
|
|
79
|
+
runtimeOverride?: boolean);
|
|
55
80
|
/**
|
|
56
81
|
* Process an OpenCode plugin event.
|
|
57
82
|
* Call this from the plugin's `event` hook for every event received.
|
|
58
83
|
*/
|
|
59
84
|
handleEvent(rawEvent: unknown): Promise<void>;
|
|
85
|
+
/** Increment retry counter and return true when the budget is exhausted.
|
|
86
|
+
* Used by the session.status retry path — each retry counts toward the
|
|
87
|
+
* budget and only triggers fallback after maxRetries - 1 absorptions.
|
|
88
|
+
* Non-retry paths (session.error / message.updated) use shouldIntervene(),
|
|
89
|
+
* which bypasses the counter on first occurrence. */
|
|
90
|
+
private checkRetryBudget;
|
|
91
|
+
/** For non-retry paths (session.error, message.updated): intervene immediately
|
|
92
|
+
* unless the session is already in a retry window (has prior retries). */
|
|
93
|
+
private shouldIntervene;
|
|
60
94
|
private tryFallback;
|
|
61
95
|
/**
|
|
62
96
|
* Determine the fallback chain to use for a session.
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -12,4 +12,5 @@ export { createLoopCommandHook } from './loop-command';
|
|
|
12
12
|
export { createPhaseReminderHook } from './phase-reminder';
|
|
13
13
|
export { createPostFileToolNudgeHook } from './post-file-tool-nudge';
|
|
14
14
|
export { createReflectCommandHook } from './reflect';
|
|
15
|
+
export { SessionLifecycle } from './session-lifecycle';
|
|
15
16
|
export { createTaskSessionManagerHook } from './task-session-manager';
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* of the user's actual turn.
|
|
7
7
|
*/
|
|
8
8
|
import { PHASE_REMINDER } from '../../config/constants';
|
|
9
|
+
import type { SessionLifecycle } from '../session-lifecycle';
|
|
9
10
|
export { PHASE_REMINDER };
|
|
11
|
+
export declare const PHASE_REMINDER_METADATA_KEY = "oh-my-opencode-slim.phaseReminder";
|
|
10
12
|
/**
|
|
11
13
|
* Creates the experimental.chat.messages.transform hook for phase reminder injection.
|
|
12
14
|
* This hook runs right before sending to API, so it doesn't affect UI display.
|
|
13
15
|
* Only injects for the orchestrator agent.
|
|
14
16
|
*/
|
|
15
|
-
export declare function createPhaseReminderHook(): {
|
|
17
|
+
export declare function createPhaseReminderHook(coordinator?: SessionLifecycle): {
|
|
16
18
|
'experimental.chat.messages.transform': (_input: Record<string, never>, output: {
|
|
17
19
|
messages?: unknown;
|
|
18
20
|
}) => Promise<void>;
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Post-tool nudge - queues a delegation reminder after file reads/writes.
|
|
3
3
|
* Catches the "inspect/edit files → implement myself" anti-pattern.
|
|
4
|
+
*
|
|
5
|
+
* The reminder is ephemeral: recorded on tool execution, injected via
|
|
6
|
+
* system.transform, and consumed once. File tool output stays clean.
|
|
4
7
|
*/
|
|
5
|
-
|
|
6
|
-
tool: string;
|
|
7
|
-
sessionID?: string;
|
|
8
|
-
callID?: string;
|
|
9
|
-
}
|
|
10
|
-
interface ToolExecuteAfterOutput {
|
|
11
|
-
output?: unknown;
|
|
12
|
-
}
|
|
8
|
+
import type { SessionLifecycle } from '../session-lifecycle';
|
|
13
9
|
interface PostFileToolNudgeOptions {
|
|
14
10
|
shouldInject?: (sessionID: string) => boolean;
|
|
11
|
+
coordinator?: SessionLifecycle;
|
|
15
12
|
}
|
|
16
13
|
export declare function createPostFileToolNudgeHook(options?: PostFileToolNudgeOptions): {
|
|
17
|
-
'tool.execute.after': (input:
|
|
14
|
+
'tool.execute.after': (input: {
|
|
15
|
+
tool: string;
|
|
16
|
+
sessionID?: string;
|
|
17
|
+
callID?: string;
|
|
18
|
+
}, _output: unknown) => Promise<void>;
|
|
19
|
+
'experimental.chat.system.transform': (input: {
|
|
20
|
+
sessionID?: string;
|
|
21
|
+
}, output: {
|
|
22
|
+
system: string[];
|
|
23
|
+
}) => Promise<void>;
|
|
18
24
|
};
|
|
19
25
|
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class SessionLifecycle {
|
|
2
|
+
#private;
|
|
3
|
+
constructor(log: (msg: string, meta?: Record<string, unknown>) => void);
|
|
4
|
+
onSessionDeleted(callback: (sessionId: string) => void): void;
|
|
5
|
+
dispatchSessionDeleted(sessionId: string): void;
|
|
6
|
+
markPending(sessionId: string): void;
|
|
7
|
+
/** Atomic — only one caller gets true per markPending call. */
|
|
8
|
+
consumePending(sessionId: string): boolean;
|
|
9
|
+
hasPendingSession(sessionId: string): boolean;
|
|
10
|
+
clearSession(sessionId: string): void;
|
|
11
|
+
}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import type { PluginInput } from '@opencode-ai/plugin';
|
|
2
|
-
import {
|
|
2
|
+
import { type BackgroundJobStore } from '../../utils';
|
|
3
|
+
import type { SessionLifecycle } from '../session-lifecycle';
|
|
4
|
+
export declare const BACKGROUND_JOB_BOARD_METADATA_KEY = "oh-my-opencode-slim.backgroundJobBoard";
|
|
3
5
|
export declare function createTaskSessionManagerHook(_ctx: PluginInput, options: {
|
|
4
6
|
maxSessionsPerAgent: number;
|
|
5
7
|
readContextMinLines?: number;
|
|
6
8
|
readContextMaxFiles?: number;
|
|
7
|
-
backgroundJobBoard?:
|
|
9
|
+
backgroundJobBoard?: BackgroundJobStore;
|
|
8
10
|
shouldManageSession: (sessionID: string) => boolean;
|
|
11
|
+
/** Register a session as orchestrator when the transform hook detects
|
|
12
|
+
* an orchestrator message but the session isn't in the agent map yet. */
|
|
13
|
+
registerSessionAsOrchestrator?: (sessionID: string) => void;
|
|
14
|
+
/** Optional guard: when provided, idle events for a session that is
|
|
15
|
+
* currently undergoing a foreground-fallback abort/re-prompt cycle
|
|
16
|
+
* will NOT trigger idle reconciliation. prevents marking a still-
|
|
17
|
+
* active child job as completed when the session was aborted for
|
|
18
|
+
* model fallback rather than natural completion. */
|
|
19
|
+
isFallbackInProgress?: (sessionID: string) => boolean;
|
|
20
|
+
coordinator?: SessionLifecycle;
|
|
9
21
|
}): {
|
|
10
22
|
'tool.execute.before': (input: {
|
|
11
23
|
tool: string;
|