@osovv/vv-opencode 1.3.4 → 1.3.6-rc.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,299 @@
1
+ // FILE: src/plugins/peak-hours/index.ts
2
+ // VERSION: 1.0.0
3
+ // START_MODULE_CONTRACT
4
+ // PURPOSE: Warn or block chat.message requests whose model provider is in configured peak hours, with dynamic off-peak provider suggestions and session-age plus subagent grace.
5
+ // SCOPE: Startup vvoc snapshot resolution and peak-hours entry parsing, internal and subagent-like agent exemptions, persisted-session grace lookup, connected provider enumeration with bounded fallback, soft system-note injection, hard blocking errors, and fail-open degradation.
6
+ // DEPENDS: [@opencode-ai/plugin, src/lib/config-layers.ts, src/lib/plugin-toggle-config.ts, src/lib/managed-agents.ts, src/lib/peak-hours.ts]
7
+ // LINKS: [M-PLUGIN-PEAK-HOURS, M-PEAK-HOURS-SCHEDULES, M-PLUGIN-TOGGLE-CONFIG]
8
+ // ROLE: RUNTIME
9
+ // MAP_MODE: EXPORTS
10
+ // END_MODULE_CONTRACT
11
+ //
12
+ // START_MODULE_MAP
13
+ // PeakHoursPluginDependencies - Injectable clock, entry, session, provider, and logging dependencies for focused tests.
14
+ // SessionGraceInfo - Persisted session fields used for grace decisions.
15
+ // buildHardBlockMessage - Composes the blocking error text with window end, wait, and suggestions.
16
+ // buildSoftSystemNote - Composes the bounded model-facing peak notice.
17
+ // appendSystemNote - Appends the notice once without duplicating it.
18
+ // createPeakHoursPlugin - Builds the peak-hours server plugin with injectable dependencies.
19
+ // PeakHoursPlugin - Default production peak-hours server plugin.
20
+ // END_MODULE_MAP
21
+ //
22
+ // START_CHANGE_SUMMARY
23
+ // LAST_CHANGE: [C-PLUGIN-PEAK-HOURS - Added the peak-hours chat.message plugin with soft notes, hard blocks, suggestions, and grace.]
24
+ // END_CHANGE_SUMMARY
25
+ import { loadVvocConfig } from "../../lib/config-layers.js";
26
+ import { MANAGED_SUBAGENT_NAMES } from "../../lib/managed-agents.js";
27
+ import { isVvocPluginEnabled } from "../../lib/plugin-toggle-config.js";
28
+ import { findActivePeak, formatPeakEndTime, formatWaitMinutes, normalizeProviderId, parsePeakHoursEntry, suggestOffPeakProviders, } from "../../lib/peak-hours.js";
29
+ // OpenCode internal agents that must never be gated; blocking them breaks
30
+ // session machinery itself.
31
+ const INTERNAL_AGENTS = ["compaction", "title", "summary"];
32
+ const BUILT_IN_SUBAGENTS = ["general"];
33
+ const PLUGIN_MANAGED_SUBAGENTS = ["guardian"];
34
+ const PEAK_HOURS_SERVICE = "peak-hours";
35
+ const PEAK_HOURS_NOTE_TAG = "<peak_hours_notice>";
36
+ const PROVIDER_CACHE_TTL_MS = 10 * 60 * 1_000;
37
+ const PROVIDER_FETCH_TIMEOUT_MS = 5_000;
38
+ const MAX_LOG_TEXT_CHARS = 500;
39
+ // START_BLOCK_AGENT_EXEMPTIONS
40
+ function createKnownSubagentSet() {
41
+ return new Set([
42
+ ...BUILT_IN_SUBAGENTS,
43
+ ...PLUGIN_MANAGED_SUBAGENTS,
44
+ ...MANAGED_SUBAGENT_NAMES,
45
+ ]);
46
+ }
47
+ function syncConfiguredSubagents(configAgentNames, knownSubagents) {
48
+ for (const [name, definition] of Object.entries(configAgentNames)) {
49
+ if (definition &&
50
+ typeof definition === "object" &&
51
+ definition.mode === "subagent") {
52
+ knownSubagents.add(name);
53
+ }
54
+ }
55
+ }
56
+ function isInternalAgent(agentName) {
57
+ return !!agentName && INTERNAL_AGENTS.includes(agentName);
58
+ }
59
+ // END_BLOCK_AGENT_EXEMPTIONS
60
+ // START_CONTRACT: buildHardBlockMessage
61
+ // PURPOSE: Compose the hard-mode blocking error text with window end, wait time, and suggestions.
62
+ // INPUTS: { providerID: string - peak provider id; peak: ActivePeak - active window hit; suggestions: readonly string[] - connected off-peak provider ids }
63
+ // OUTPUTS: { string - complete blocking error message }
64
+ // SIDE_EFFECTS: none
65
+ // LINKS: formatPeakEndTime, formatWaitMinutes
66
+ // END_CONTRACT: buildHardBlockMessage
67
+ export function buildHardBlockMessage(providerID, peak, suggestions) {
68
+ const until = formatPeakEndTime(peak.endsAt);
69
+ const wait = formatWaitMinutes(peak.minutesRemaining);
70
+ const lines = [
71
+ `PEAK_HOURS_BLOCK: provider "${providerID}" is in peak hours until ${until} (about ${wait}).`,
72
+ "Requests to this provider cost more right now.",
73
+ ];
74
+ if (suggestions.length > 0) {
75
+ lines.push(`Connected providers outside peak hours right now: ${suggestions.join(", ")}. Switch the model to one of them, or wait.`);
76
+ }
77
+ else {
78
+ lines.push("Every connected provider is currently in peak hours or unscheduled; wait for the window to end or review the plugins[peak-hours] schedules in vvoc.json.");
79
+ }
80
+ return lines.join(" ");
81
+ }
82
+ // START_CONTRACT: buildSoftSystemNote
83
+ // PURPOSE: Compose the bounded model-facing notice for soft mode.
84
+ // INPUTS: { providerID: string - peak provider id; peak: ActivePeak - active window hit }
85
+ // OUTPUTS: { string - single-line tagged system notice }
86
+ // SIDE_EFFECTS: none
87
+ // LINKS: formatPeakEndTime
88
+ // END_CONTRACT: buildSoftSystemNote
89
+ export function buildSoftSystemNote(providerID, peak) {
90
+ const until = formatPeakEndTime(peak.endsAt);
91
+ return `${PEAK_HOURS_NOTE_TAG}Provider "${providerID}" is in peak hours until ${until}; pricing is elevated. Keep responses focused and avoid unnecessary verbosity.</${PEAK_HOURS_NOTE_TAG.slice(1)}`;
92
+ }
93
+ // START_CONTRACT: appendSystemNote
94
+ // PURPOSE: Append the peak notice to an existing system prompt exactly once.
95
+ // INPUTS: { existingSystem: string | undefined - current system prompt; note: string - composed notice }
96
+ // OUTPUTS: { string - system prompt with the notice appended at most once }
97
+ // SIDE_EFFECTS: none
98
+ // LINKS: buildSoftSystemNote
99
+ // END_CONTRACT: appendSystemNote
100
+ export function appendSystemNote(existingSystem, note) {
101
+ if (typeof existingSystem === "string" && existingSystem.includes(PEAK_HOURS_NOTE_TAG)) {
102
+ return existingSystem;
103
+ }
104
+ const parts = [];
105
+ if (typeof existingSystem === "string" && existingSystem.trim()) {
106
+ parts.push(existingSystem.trim());
107
+ }
108
+ parts.push(note);
109
+ return parts.join("\n\n");
110
+ }
111
+ // START_BLOCK_DEFAULT_DEPENDENCIES
112
+ function truncateLogText(value) {
113
+ return value.length > MAX_LOG_TEXT_CHARS ? `${value.slice(0, MAX_LOG_TEXT_CHARS)}...` : value;
114
+ }
115
+ async function lookupSessionGrace(client, directory, sessionID) {
116
+ try {
117
+ const response = (await client.session.get({
118
+ path: { id: sessionID },
119
+ query: directory ? { directory } : undefined,
120
+ }));
121
+ if (response.error || response.data === undefined)
122
+ return undefined;
123
+ const session = response.data;
124
+ return {
125
+ createdMs: typeof session.time?.created === "number" ? session.time.created : undefined,
126
+ parentID: typeof session.parentID === "string" ? session.parentID : undefined,
127
+ };
128
+ }
129
+ catch {
130
+ return undefined;
131
+ }
132
+ }
133
+ async function listConnectedProviders(client, serverUrl, directory) {
134
+ // Prefer a typed client surface when the host SDK exposes one.
135
+ const configClient = client.config;
136
+ if (typeof configClient?.providers === "function") {
137
+ try {
138
+ const response = await configClient.providers(directory ? { query: { directory } } : undefined);
139
+ const providers = response.data?.providers ?? [];
140
+ return providers
141
+ .map((provider) => provider.id)
142
+ .filter((id) => typeof id === "string");
143
+ }
144
+ catch {
145
+ // Fall through to the raw HTTP fallback.
146
+ }
147
+ }
148
+ try {
149
+ const url = new URL("/config/providers", serverUrl);
150
+ if (directory) {
151
+ url.searchParams.set("directory", directory);
152
+ }
153
+ const response = await fetch(url, {
154
+ signal: AbortSignal.timeout(PROVIDER_FETCH_TIMEOUT_MS),
155
+ });
156
+ if (!response.ok)
157
+ return [];
158
+ const body = (await response.json());
159
+ const providers = body.providers ?? [];
160
+ return providers
161
+ .map((provider) => provider.id)
162
+ .filter((id) => typeof id === "string");
163
+ }
164
+ catch {
165
+ return [];
166
+ }
167
+ }
168
+ // END_BLOCK_DEFAULT_DEPENDENCIES
169
+ // START_BLOCK_PLUGIN_ENTRY
170
+ /**
171
+ * Builds the peak-hours OpenCode server plugin.
172
+ *
173
+ * chat.message evaluates the message model's provider against the configured
174
+ * schedules. Soft mode appends a bounded system notice; hard mode throws before
175
+ * any LLM request with dynamic off-peak suggestions. Internal agents, subagent
176
+ * agents, sessions with a parentID, and sessions created before the active
177
+ * window start are never hard-blocked. Schedule and lookup failures degrade to
178
+ * soft or no-op and never block.
179
+ */
180
+ export function createPeakHoursPlugin(dependencies = {}) {
181
+ return async ({ client, directory, serverUrl }) => {
182
+ const providerCache = { at: 0, ids: [] };
183
+ const deps = {
184
+ now: () => new Date(),
185
+ loadEntry: async () => {
186
+ const vvoc = await loadVvocConfig({ cwd: directory });
187
+ if (!isVvocPluginEnabled(vvoc.config, "peak-hours")) {
188
+ return { entry: { ...parsePeakHoursEntry(false).entry }, warnings: [] };
189
+ }
190
+ return parsePeakHoursEntry(vvoc.config.plugins?.["peak-hours"]);
191
+ },
192
+ session: (sessionID) => lookupSessionGrace(client, directory, sessionID),
193
+ connectedProviders: async () => {
194
+ const now = Date.now();
195
+ if (now - providerCache.at < PROVIDER_CACHE_TTL_MS) {
196
+ return providerCache.ids;
197
+ }
198
+ const ids = await listConnectedProviders(client, serverUrl, directory);
199
+ providerCache.at = ids.length > 0 ? Date.now() : 0;
200
+ providerCache.ids = ids;
201
+ return ids;
202
+ },
203
+ log: async (level, message, extra) => {
204
+ try {
205
+ await client.app.log({
206
+ ...(directory ? { query: { directory } } : {}),
207
+ body: {
208
+ service: PEAK_HOURS_SERVICE,
209
+ level,
210
+ message: truncateLogText(message),
211
+ extra,
212
+ },
213
+ });
214
+ }
215
+ catch {
216
+ // Logging must never interfere with message handling.
217
+ }
218
+ },
219
+ ...dependencies,
220
+ };
221
+ const { entry, warnings } = await deps.loadEntry();
222
+ if (!entry.enabled)
223
+ return {};
224
+ const knownSubagents = createKnownSubagentSet();
225
+ await deps.log("info", "peak-hours plugin initialized", {
226
+ mode: entry.mode,
227
+ graceActiveSessions: entry.graceActiveSessions,
228
+ scheduledProviders: Object.keys(entry.schedules),
229
+ warningCount: warnings.length,
230
+ });
231
+ for (const warning of warnings) {
232
+ await deps.log("warn", `peak-hours config warning: ${warning}`);
233
+ }
234
+ return {
235
+ config: async (config) => {
236
+ syncConfiguredSubagents((config.agent ?? {}), knownSubagents);
237
+ },
238
+ "chat.message": async (input, output) => {
239
+ const agent = output.message.agent ?? input.agent;
240
+ if (isInternalAgent(agent))
241
+ return;
242
+ const providerID = input.model?.providerID ?? output.message.model?.providerID;
243
+ if (!providerID)
244
+ return;
245
+ const now = deps.now();
246
+ const peak = findActivePeak(now, entry.schedules, providerID);
247
+ if (!peak)
248
+ return;
249
+ const override = entry.schedules[peak.providerKey]?.mode;
250
+ let mode = override ?? entry.mode;
251
+ // Subagent-like agents are continuation of already-admitted work.
252
+ if (agent && knownSubagents.has(agent)) {
253
+ mode = "soft";
254
+ }
255
+ if (mode === "hard") {
256
+ const session = await deps.session(input.sessionID);
257
+ if (!session) {
258
+ // Fail-open: unknown session state never blocks.
259
+ mode = "soft";
260
+ }
261
+ else {
262
+ if (session.parentID) {
263
+ mode = "soft";
264
+ }
265
+ if (entry.graceActiveSessions &&
266
+ session.createdMs !== undefined &&
267
+ session.createdMs < peak.startedAt.getTime()) {
268
+ mode = "soft";
269
+ }
270
+ }
271
+ }
272
+ if (mode === "hard") {
273
+ const connected = await deps.connectedProviders();
274
+ const suggestions = suggestOffPeakProviders(now, entry.schedules, connected).filter((candidate) => normalizeProviderId(candidate) !== normalizeProviderId(providerID));
275
+ const message = buildHardBlockMessage(providerID, peak, suggestions);
276
+ await deps.log("info", "peak-hours hard block applied", {
277
+ providerID,
278
+ providerKey: peak.providerKey,
279
+ until: formatPeakEndTime(peak.endsAt),
280
+ waitMinutes: peak.minutesRemaining,
281
+ suggestions,
282
+ sessionID: input.sessionID,
283
+ });
284
+ throw new Error(message);
285
+ }
286
+ output.message.system = appendSystemNote(output.message.system, buildSoftSystemNote(providerID, peak));
287
+ await deps.log("info", "peak-hours soft notice applied", {
288
+ providerID,
289
+ providerKey: peak.providerKey,
290
+ until: formatPeakEndTime(peak.endsAt),
291
+ sessionID: input.sessionID,
292
+ });
293
+ },
294
+ };
295
+ };
296
+ }
297
+ export const PeakHoursPlugin = createPeakHoursPlugin();
298
+ // END_BLOCK_PLUGIN_ENTRY
299
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/peak-hours/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"indicator.js","sourceRoot":"","sources":["../../../src/tui/analytics/indicator.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indicator.js","sourceRoot":"","sources":["../../../src/tui/analytics/indicator.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { JSX } from "@opentui/solid";
2
+ import type { RGBA } from "@opentui/core";
3
+ import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
4
+ import { type ActivePeak, type PeakHoursClock, type PeakHoursEntryConfig } from "../../lib/peak-hours.js";
5
+ export type PeakBannerModelRef = {
6
+ providerID: string;
7
+ };
8
+ export type PeakHoursBannerDependencies = {
9
+ enabled: (api: TuiPluginApi) => Promise<boolean>;
10
+ now: PeakHoursClock;
11
+ entry: () => PeakHoursEntryConfig;
12
+ currentModel: (api: TuiPluginApi) => PeakBannerModelRef | undefined;
13
+ connectedProviders: (api: TuiPluginApi) => string[];
14
+ renderBanner: (text: string, color: RGBA) => JSX.Element;
15
+ };
16
+ export declare function buildPeakBannerText(providerID: string, peak: ActivePeak, suggestions: readonly string[]): string;
17
+ export declare function resolveBannerModelRef(api: TuiPluginApi): PeakBannerModelRef | undefined;
18
+ /**
19
+ * Registers the persistent peak-hours banner for the app_bottom host slot.
20
+ *
21
+ * The banner renders in warning colors only while the current model's provider
22
+ * is inside an active peak window, naming the provider, the window end, and
23
+ * connected providers that are currently outside peak. Disabled toggle or
24
+ * entry, missing slot API, or registration failure leave the TUI untouched.
25
+ */
26
+ export declare function registerPeakHoursBanner(api: TuiPluginApi, dependencies?: Partial<PeakHoursBannerDependencies>): Promise<void>;
@@ -0,0 +1,126 @@
1
+ import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
2
+ import { loadVvocConfigForRead } from "../../lib/config-layers.js";
3
+ import { isVvocPluginEnabled } from "../../lib/plugin-toggle-config.js";
4
+ import { findActivePeak, formatPeakEndTime, normalizeProviderId, parsePeakHoursEntry, suggestOffPeakProviders, } from "../../lib/peak-hours.js";
5
+ // START_CONTRACT: buildPeakBannerText
6
+ // PURPOSE: Compose the one-line banner label with window end and suggestions.
7
+ // INPUTS: { providerID: string - peak provider id; peak: ActivePeak - active window hit; suggestions: readonly string[] - connected off-peak provider ids }
8
+ // OUTPUTS: { string - banner label text }
9
+ // SIDE_EFFECTS: none
10
+ // LINKS: formatPeakEndTime
11
+ // END_CONTRACT: buildPeakBannerText
12
+ export function buildPeakBannerText(providerID, peak, suggestions) {
13
+ const until = formatPeakEndTime(peak.endsAt);
14
+ const suffix = suggestions.length > 0
15
+ ? ` · off-peak now: ${suggestions.join(", ")}`
16
+ : " · every connected provider is in peak or unscheduled";
17
+ return `⚠ PEAK ${providerID} until ${until} · elevated pricing${suffix}`;
18
+ }
19
+ // START_CONTRACT: resolveBannerModelRef
20
+ // PURPOSE: Resolve the current model reference from the open session's messages with a config default fallback.
21
+ // INPUTS: { api: TuiPluginApi - TUI plugin api }
22
+ // OUTPUTS: { PeakBannerModelRef | undefined - provider id of the most recent model-bearing message, the config default model, or undefined }
23
+ // SIDE_EFFECTS: none
24
+ // LINKS: currentSessionID
25
+ // END_CONTRACT: resolveBannerModelRef
26
+ export function resolveBannerModelRef(api) {
27
+ const sessionID = currentSessionID(api);
28
+ if (!sessionID)
29
+ return undefined;
30
+ const messages = api.state.session.messages(sessionID);
31
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
32
+ const message = messages[index];
33
+ if (!message)
34
+ continue;
35
+ if (message.role === "assistant" && typeof message.providerID === "string") {
36
+ return { providerID: message.providerID };
37
+ }
38
+ if (typeof message.model?.providerID === "string") {
39
+ return { providerID: message.model.providerID };
40
+ }
41
+ }
42
+ const configModel = api.state.config.model;
43
+ if (typeof configModel === "string" && configModel.includes("/")) {
44
+ const providerID = configModel.split("/")[0];
45
+ if (providerID)
46
+ return { providerID };
47
+ }
48
+ return undefined;
49
+ }
50
+ /** Reads the currently open sessionID from the TUI route, or "". */
51
+ function currentSessionID(api) {
52
+ const route = api.route.current;
53
+ if (route.name === "session" && "params" in route) {
54
+ const sessionID = route.params?.sessionID;
55
+ return typeof sessionID === "string" ? sessionID : "";
56
+ }
57
+ return "";
58
+ }
59
+ // START_BLOCK_REGISTER_BANNER
60
+ /**
61
+ * Registers the persistent peak-hours banner for the app_bottom host slot.
62
+ *
63
+ * The banner renders in warning colors only while the current model's provider
64
+ * is inside an active peak window, naming the provider, the window end, and
65
+ * connected providers that are currently outside peak. Disabled toggle or
66
+ * entry, missing slot API, or registration failure leave the TUI untouched.
67
+ */
68
+ export async function registerPeakHoursBanner(api, dependencies = {}) {
69
+ if (typeof api.slots?.register !== "function")
70
+ return;
71
+ // Populated by the default enabled() config read; kept for the sync entry().
72
+ let cachedEntry;
73
+ const deps = {
74
+ enabled: async (apiInstance) => {
75
+ const vvoc = await loadVvocConfigForRead({
76
+ scope: "effective",
77
+ allowDefault: true,
78
+ cwd: apiInstance.state.path.directory,
79
+ });
80
+ if (!isVvocPluginEnabled(vvoc.config, "peak-hours"))
81
+ return false;
82
+ const parsed = parsePeakHoursEntry(vvoc.config.plugins?.["peak-hours"]);
83
+ cachedEntry = parsed.entry;
84
+ return parsed.entry.enabled;
85
+ },
86
+ now: () => new Date(),
87
+ entry: () => cachedEntry ?? parsePeakHoursEntry(undefined).entry,
88
+ currentModel: (apiInstance) => resolveBannerModelRef(apiInstance),
89
+ connectedProviders: (apiInstance) => apiInstance.state.provider
90
+ .map((provider) => provider.id)
91
+ .filter((id) => typeof id === "string"),
92
+ renderBanner: (text, color) => (_jsx("text", { children: _jsx("span", { style: { fg: color }, children: text }) })),
93
+ ...dependencies,
94
+ };
95
+ if (!(await deps.enabled(api)))
96
+ return;
97
+ try {
98
+ // OpenCode's runtime requires a string plugin id on slot registrations, while
99
+ // the SDK's TuiSlotPlugin type still types id as never; cast bridges the two.
100
+ const plugin = {
101
+ id: "vvoc-peak-hours",
102
+ order: 100,
103
+ slots: {
104
+ app_bottom: (_ctx) => {
105
+ const entry = deps.entry();
106
+ if (!entry.enabled)
107
+ return undefined;
108
+ const modelRef = deps.currentModel(api);
109
+ if (!modelRef)
110
+ return undefined;
111
+ const now = deps.now();
112
+ const peak = findActivePeak(now, entry.schedules, modelRef.providerID);
113
+ if (!peak)
114
+ return undefined;
115
+ const suggestions = suggestOffPeakProviders(now, entry.schedules, deps.connectedProviders(api)).filter((candidate) => normalizeProviderId(candidate) !== normalizeProviderId(modelRef.providerID));
116
+ return deps.renderBanner(buildPeakBannerText(modelRef.providerID, peak, suggestions), api.theme.current.warning);
117
+ },
118
+ },
119
+ };
120
+ api.slots.register(plugin);
121
+ }
122
+ catch {
123
+ // Fail-soft: no banner for this session.
124
+ }
125
+ }
126
+ //# sourceMappingURL=banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banner.js","sourceRoot":"","sources":["../../../src/tui/peak-hours/banner.tsx"],"names":[],"mappings":"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"}
package/dist/tui.js CHANGED
@@ -1,25 +1,26 @@
1
1
  // FILE: src/tui.tsx
2
- // VERSION: 1.1.0
2
+ // VERSION: 1.2.0
3
3
  // START_MODULE_CONTRACT
4
- // PURPOSE: Publish the default @osovv/vv-opencode/tui module containing the managed context inspector, analytics indicator, and branding footer.
4
+ // PURPOSE: Publish the default @osovv/vv-opencode/tui module containing the managed context inspector, analytics indicator, branding footer, and peak-hours banner.
5
5
  // SCOPE: Stable TUI package entrypoint and module identity only.
6
- // DEPENDS: [@opencode-ai/plugin/tui, @opencode-ai/plugin, src/tui/context/plugin.ts, src/tui/analytics/indicator.tsx, src/tui/branding/footer.tsx]
7
- // LINKS: [M-PLUGIN-CONTEXT-TUI, M-TUI-ANALYTICS-INDICATOR, M-TUI-BRANDING-FOOTER, V-M-PLUGIN-CONTEXT-TUI]
6
+ // DEPENDS: [@opencode-ai/plugin/tui, @opencode-ai/plugin, src/tui/context/plugin.ts, src/tui/analytics/indicator.tsx, src/tui/branding/footer.tsx, src/tui/peak-hours/banner.tsx]
7
+ // LINKS: [M-PLUGIN-CONTEXT-TUI, M-TUI-ANALYTICS-INDICATOR, M-TUI-BRANDING-FOOTER, M-TUI-PEAK-HOURS-BANNER, V-M-PLUGIN-CONTEXT-TUI]
8
8
  // ROLE: BARREL
9
9
  // MAP_MODE: SUMMARY
10
10
  // END_MODULE_CONTRACT
11
11
  //
12
12
  // START_MODULE_MAP
13
- // default - OpenCode TUI plugin module registering /context, the analytics indicator, and the branding footer.
13
+ // default - OpenCode TUI plugin module registering /context, the analytics indicator, the branding footer, and the peak-hours banner.
14
14
  // ContextTuiPlugin - Named TUI plugin factory for direct consumers and tests.
15
15
  // END_MODULE_MAP
16
16
  //
17
17
  // START_CHANGE_SUMMARY
18
- // LAST_CHANGE: [2026-08-19-cache-hit-rate-analytics - Registered the live cache indicator and vvoc version footer alongside /context.]
18
+ // LAST_CHANGE: [C-PLUGIN-PEAK-HOURS - Registered the app_bottom peak-hours banner alongside the indicator and footer.]
19
19
  // END_CHANGE_SUMMARY
20
20
  import { ContextTuiPlugin } from "./tui/context/plugin.js";
21
21
  import { registerAnalyticsIndicator } from "./tui/analytics/indicator.js";
22
22
  import { registerBrandingFooter } from "./tui/branding/footer.js";
23
+ import { registerPeakHoursBanner } from "./tui/peak-hours/banner.js";
23
24
  export { ContextTuiPlugin };
24
25
  // START_BLOCK_TUI_MODULE
25
26
  const plugin = {
@@ -38,6 +39,12 @@ const plugin = {
38
39
  catch {
39
40
  // Fail-soft: footer unavailable for this session.
40
41
  }
42
+ try {
43
+ await registerPeakHoursBanner(api, options);
44
+ }
45
+ catch {
46
+ // Fail-soft: banner unavailable for this session.
47
+ }
41
48
  },
42
49
  };
43
50
  // END_BLOCK_TUI_MODULE
package/dist/tui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,iBAAiB;AACjB,wBAAwB;AACxB,mJAAmJ;AACnJ,mEAAmE;AACnE,qJAAqJ;AACrJ,4GAA4G;AAC5G,iBAAiB;AACjB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,iHAAiH;AACjH,gFAAgF;AAChF,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,yIAAyI;AACzI,qBAAqB;AAGrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,yBAAyB;AACzB,MAAM,MAAM,GAAqC;IAC/C,EAAE,EAAE,cAAc;IAClB,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAChC,MAAM,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,0BAA0B,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;QACD,IAAI,CAAC;YACH,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;IACH,CAAC;CACF,CAAC;AACF,uBAAuB;AAEvB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,iBAAiB;AACjB,wBAAwB;AACxB,sKAAsK;AACtK,mEAAmE;AACnE,oLAAoL;AACpL,qIAAqI;AACrI,iBAAiB;AACjB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,wIAAwI;AACxI,gFAAgF;AAChF,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,yHAAyH;AACzH,qBAAqB;AAGrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,yBAAyB;AACzB,MAAM,MAAM,GAAqC;IAC/C,EAAE,EAAE,cAAc;IAClB,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAChC,MAAM,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,0BAA0B,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;QACD,IAAI,CAAC;YACH,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;QACD,IAAI,CAAC;YACH,MAAM,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;IACH,CAAC;CACF,CAAC;AACF,uBAAuB;AAEvB,eAAe,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@osovv/vv-opencode",
3
- "version": "1.3.4",
4
- "description": "A curated, opinionated set of OpenCode plugins for spec-first, review-driven, safer agentic development.",
3
+ "version": "1.3.6-rc.0",
4
+ "description": "An opinionated agentic development layer for OpenCode — spec-first when it matters, review-driven execution, portable model roles, safer tools, and long-run safety.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -81,6 +81,10 @@
81
81
  "./plugins/analytics": {
82
82
  "types": "./dist/plugins/analytics/index.d.ts",
83
83
  "import": "./dist/plugins/analytics/index.js"
84
+ },
85
+ "./plugins/peak-hours": {
86
+ "types": "./dist/plugins/peak-hours/index.d.ts",
87
+ "import": "./dist/plugins/peak-hours/index.js"
84
88
  }
85
89
  },
86
90
  "scripts": {
@@ -93,7 +97,7 @@
93
97
  "grace:check": "bun scripts/check-grace-markup.ts",
94
98
  "test": "bun test",
95
99
  "check": "bun run typecheck && bun run lint && bun run fmt:check && bun run grace:check && bun test",
96
- "pack:check": "bun run build && bun -e \"const root = await import('./dist/index.js'); if (!('GuardianPlugin' in root)) throw new Error('dist root export missing GuardianPlugin'); if (!('HashlineEditPlugin' in root)) throw new Error('dist root export missing HashlineEditPlugin'); if (!('ModelRolesPlugin' in root)) throw new Error('dist root export missing ModelRolesPlugin'); if (!('SystemContextInjectionPlugin' in root)) throw new Error('dist root export missing SystemContextInjectionPlugin'); if (!('WorkflowPlugin' in root)) throw new Error('dist root export missing WorkflowPlugin'); if (!('SecretsRedactionPlugin' in root)) throw new Error('dist root export missing SecretsRedactionPlugin'); if (!('WebToolsPlugin' in root)) throw new Error('dist root export missing WebToolsPlugin'); if (!('AnalyticsPlugin' in root)) throw new Error('dist root export missing AnalyticsPlugin'); const tui = await import('./dist/tui.js'); if (!tui.default || typeof tui.default.tui !== 'function') throw new Error('dist TUI export missing default tui module'); await import('./dist/plugins/guardian/index.js'); await import('./dist/plugins/hashline-edit/index.js'); await import('./dist/plugins/model-roles/index.js'); await import('./dist/plugins/system-context-injection/index.js'); await import('./dist/plugins/workflow/index.js'); await import('./dist/plugins/secrets-redaction/index.js'); await import('./dist/plugins/web-tools/index.js'); await import('./dist/plugins/analytics/index.js')\" && npm pack --dry-run",
100
+ "pack:check": "bun run build && bun -e \"const root = await import('./dist/index.js'); if (!('GuardianPlugin' in root)) throw new Error('dist root export missing GuardianPlugin'); if (!('HashlineEditPlugin' in root)) throw new Error('dist root export missing HashlineEditPlugin'); if (!('ModelRolesPlugin' in root)) throw new Error('dist root export missing ModelRolesPlugin'); if (!('SystemContextInjectionPlugin' in root)) throw new Error('dist root export missing SystemContextInjectionPlugin'); if (!('WorkflowPlugin' in root)) throw new Error('dist root export missing WorkflowPlugin'); if (!('SecretsRedactionPlugin' in root)) throw new Error('dist root export missing SecretsRedactionPlugin'); if (!('WebToolsPlugin' in root)) throw new Error('dist root export missing WebToolsPlugin'); if (!('AnalyticsPlugin' in root)) throw new Error('dist root export missing AnalyticsPlugin'); if (!('PeakHoursPlugin' in root)) throw new Error('dist root export missing PeakHoursPlugin'); const tui = await import('./dist/tui.js'); if (!tui.default || typeof tui.default.tui !== 'function') throw new Error('dist TUI export missing default tui module'); await import('./dist/plugins/guardian/index.js'); await import('./dist/plugins/hashline-edit/index.js'); await import('./dist/plugins/model-roles/index.js'); await import('./dist/plugins/system-context-injection/index.js'); await import('./dist/plugins/workflow/index.js'); await import('./dist/plugins/secrets-redaction/index.js'); await import('./dist/plugins/web-tools/index.js'); await import('./dist/plugins/analytics/index.js'); await import('./dist/plugins/peak-hours/index.js')\" && npm pack --dry-run",
97
101
  "tui:local": "bun scripts/tui-local.ts",
98
102
  "release:check": "bun scripts/release-check.ts",
99
103
  "release:bump": "bun scripts/release-bump.ts",