@herjarsa/omo-meta-governor 0.40.0 → 0.43.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.
package/dist/config.d.ts CHANGED
@@ -130,6 +130,69 @@ export interface MetaGovernorPluginConfig {
130
130
  };
131
131
  };
132
132
  /** Sisyphus protocol enforcement config. */
133
+ /**
134
+ * v0.41.0: Tier 1 governance - active policy enforcement via OpenCode hooks.
135
+ *
136
+ * Before v0.41.0 the plugin could only OBSERVE tool calls. This block adds
137
+ * the ability to BLOCK and REWRITE: permission.ask denies dangerous ops,
138
+ * tool.definition rewrites tool descriptions, command.execute.before filters
139
+ * destructive shell commands, and experimental.provider.small_model forces
140
+ * cheap models on subagents.
141
+ *
142
+ * All sub-blocks default to no-op (no behavior change vs v0.40.0) - features
143
+ * require explicit opt-in via the respective flags.
144
+ */
145
+ governance?: {
146
+ /** permission.ask policy. Default "allow" + empty patterns = no-op (preserves v0.40.0). */
147
+ permissionPolicy?: {
148
+ /**
149
+ * @default "allow"
150
+ * - "allow": plugin only denies when a pattern matches (default).
151
+ * - "deny-on-match": same as "allow" - kept for future semantic.
152
+ * - "ask-on-match": only escalate to user prompt on pattern match.
153
+ */
154
+ mode?: "allow" | "deny-on-match" | "ask-on-match";
155
+ /** Bash command regex patterns to deny (matched anywhere in command). */
156
+ bashDenyPatterns?: string[];
157
+ /** Bash command patterns that require user confirmation. */
158
+ bashAskPatterns?: string[];
159
+ /** Edit path glob patterns to deny (matched against file path). */
160
+ editDenyPaths?: string[];
161
+ /** Edit path patterns that require user confirmation. */
162
+ editAskPaths?: string[];
163
+ /** webfetch URL host patterns to deny (matched against host). */
164
+ webfetchDenyHosts?: string[];
165
+ };
166
+ /** tool.definition policy. Default disabled (no behavior change). */
167
+ toolRewrite?: {
168
+ /** @default false */
169
+ enabled?: boolean;
170
+ /** Suffix to append to every tool description visible to LLM. */
171
+ descriptionSuffix?: string;
172
+ /** Hide specific tools entirely from LLM by clearing description. */
173
+ hideToolIDs?: string[];
174
+ /** Override parameter descriptions per tool. */
175
+ parameterOverrides?: Record<string, Record<string, string>>;
176
+ };
177
+ /** command.execute.before policy. Default disabled. */
178
+ commandFilter?: {
179
+ /** @default false */
180
+ enabled?: boolean;
181
+ /** Dangerous command regex patterns - throw on match to block execution. */
182
+ denyPatterns?: string[];
183
+ /** Prefix injected as warning text part (does not block, only warns). */
184
+ replacementPrefix?: string;
185
+ };
186
+ /** experimental.provider.small_model override for subagent cost control. Default disabled. */
187
+ smallModelOverride?: {
188
+ /** @default false */
189
+ enabled?: boolean;
190
+ /** Force this model ID for subagents (e.g. "claude-3-5-haiku-20241022"). */
191
+ modelID?: string;
192
+ /** Provider ID (e.g. "anthropic"). */
193
+ providerID?: string;
194
+ };
195
+ };
133
196
  protocolEnforcement?: {
134
197
  enabled?: boolean;
135
198
  path?: string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * v0.41.0: command.execute.before hook handler.
3
+ *
4
+ * Blocks destructive shell commands (the `!`-prefixed palette). When enabled,
5
+ * every command is checked against the denyPatterns list (regex). On match,
6
+ * the handler throws — OpenCode surfaces the error to the agent.
7
+ *
8
+ * The optional replacementPrefix is appended as a warning text part; it is
9
+ * informative only (does not block).
10
+ */
11
+ import type { MetricsCollector } from "../metrics";
12
+ export interface GovernanceCommandFilterPolicy {
13
+ readonly enabled?: boolean;
14
+ readonly denyPatterns?: readonly string[];
15
+ readonly replacementPrefix?: string;
16
+ }
17
+ export interface CommandInput {
18
+ command: string;
19
+ sessionID: string;
20
+ arguments: string;
21
+ }
22
+ export interface CommandOutput {
23
+ parts: Array<{
24
+ type: string;
25
+ text: string;
26
+ synthetic?: boolean;
27
+ }>;
28
+ }
29
+ /**
30
+ * v0.41.0: Handle a command.execute.before invocation.
31
+ * - If not enabled or no patterns: pass through.
32
+ * - If a deny pattern matches: increment counter, throw (blocks execution).
33
+ */
34
+ export declare function handleCommandFilter(input: CommandInput, _output: CommandOutput, policy: GovernanceCommandFilterPolicy, metrics: MetricsCollector): Promise<void>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * v0.41.0: permission.ask hook handler.
3
+ *
4
+ * Routes permission requests through the governance policy. When the policy
5
+ * matches a deny/ask pattern, the handler mutates output.status to block or
6
+ * prompt the user, and increments the appropriate counter.
7
+ *
8
+ * SAFETY: policy.mode === undefined or empty patterns = pure pass-through
9
+ * (preserves v0.40.0 behavior - OpenCode prompts user by default).
10
+ */
11
+ import type { MetricsCollector } from "../metrics";
12
+ import { type GovernancePermissionPolicySubset } from "./permission-rules";
13
+ /**
14
+ * OpenCode Permission shape (subset we care about - the SDK defines more types).
15
+ * Discriminated by `type`.
16
+ */
17
+ export type Permission = {
18
+ type: "bash";
19
+ command: string;
20
+ [k: string]: unknown;
21
+ } | {
22
+ type: "edit";
23
+ pattern: string;
24
+ [k: string]: unknown;
25
+ } | {
26
+ type: "webfetch";
27
+ url: string;
28
+ [k: string]: unknown;
29
+ } | {
30
+ type: string;
31
+ [k: string]: unknown;
32
+ };
33
+ export interface PermissionOutput {
34
+ status: "ask" | "deny" | "allow";
35
+ }
36
+ /**
37
+ * v0.41.0: Handle a permission.ask invocation.
38
+ * - If policy is undefined/empty: pass through (no override of status).
39
+ * - If a deny pattern matches: set status="deny" + increment governance_blocks.
40
+ * - If an ask pattern matches: set status="ask" + increment governance_asks.
41
+ */
42
+ export declare function handlePermissionAsk(input: Permission, output: PermissionOutput, policy: GovernancePermissionPolicySubset, metrics: MetricsCollector): Promise<void>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * v0.41.0: Pure-function regex matchers for permission.ask policy.
3
+ *
4
+ * No I/O, no plugin deps - these are the building blocks the permission-gate
5
+ * handler calls. Each function returns a PermissionMatchResult with the
6
+ * governance decision ("deny" / "ask") and a human-readable reason
7
+ * for the audit log.
8
+ *
9
+ * SAFETY: callers MUST treat null as "do nothing" (preserve OpenCode's
10
+ * default permission flow). "deny" blocks silently, "ask" prompts the user.
11
+ */
12
+ export type PermissionDecision = "allow" | "deny" | "ask";
13
+ export interface PermissionMatchResult {
14
+ decision: Exclude<PermissionDecision, "allow">;
15
+ reason: string;
16
+ }
17
+ /**
18
+ * v0.41.0: Governance policy shape - subset of MetaGovernorPluginConfig that
19
+ * the matchers care about. Defined here so this file has no dependency on
20
+ * the full config schema.
21
+ */
22
+ export interface GovernancePermissionPolicySubset {
23
+ mode?: "allow" | "deny-on-match" | "ask-on-match";
24
+ bashDenyPatterns?: string[];
25
+ bashAskPatterns?: string[];
26
+ editDenyPaths?: string[];
27
+ editAskPaths?: string[];
28
+ webfetchDenyHosts?: string[];
29
+ }
30
+ /**
31
+ * Evaluate bash permission: returns the FIRST matching rule (deny beats ask).
32
+ * If no match: returns null - caller should treat as "allow" (no override).
33
+ */
34
+ export declare function evaluateBashPolicy(command: string, policy: GovernancePermissionPolicySubset): PermissionMatchResult | null;
35
+ /**
36
+ * Evaluate edit permission against file path patterns.
37
+ */
38
+ export declare function evaluateEditPolicy(filepath: string, policy: GovernancePermissionPolicySubset): PermissionMatchResult | null;
39
+ /**
40
+ * Evaluate webfetch permission against URL host patterns.
41
+ */
42
+ export declare function evaluateWebfetchPolicy(url: string, policy: GovernancePermissionPolicySubset): PermissionMatchResult | null;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * v0.41.0: experimental.provider.small_model hook handler — subagent cost control.
3
+ *
4
+ * When enabled, the plugin overrides which model OpenCode uses for the
5
+ * cheap/fast path (TaskTool/Oracle subagents). The provider hook receives the
6
+ * provider context with the provider's bundled model list; if the configured
7
+ * modelID exists in that list, the handler replaces output.model so OpenCode
8
+ * will schedule the subagent on the cheaper model.
9
+ *
10
+ * Graceful degradation: the handler is a pure no-op if the policy is empty
11
+ * or the model cannot be found — OpenCode keeps its default choice.
12
+ */
13
+ export interface GovernanceSmallModelPolicy {
14
+ readonly enabled?: boolean;
15
+ readonly modelID?: string;
16
+ readonly providerID?: string;
17
+ }
18
+ export interface SmallModelInput {
19
+ provider: {
20
+ models?: Record<string, unknown>;
21
+ [k: string]: unknown;
22
+ };
23
+ }
24
+ export interface SmallModelOutput {
25
+ model?: {
26
+ id?: string;
27
+ providerID?: string;
28
+ [k: string]: unknown;
29
+ };
30
+ }
31
+ /**
32
+ * v0.41.0: Handle a provider.small_model invocation.
33
+ * - If not enabled or modelID/providerID missing: pass through.
34
+ * - Finds the configured model in the provider's model list and assigns it.
35
+ */
36
+ export declare function handleSmallModel(input: SmallModelInput, output: SmallModelOutput, policy: GovernanceSmallModelPolicy): Promise<void>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * v0.41.0: tool.definition hook handler.
3
+ *
4
+ * Routes tool definition output through the governance policy. When enabled,
5
+ * the handler can hide specific tools or suffix every tool description with a
6
+ * governance marker. Parameter description overrides are per-tool and per-param.
7
+ *
8
+ * SAFETY: when policy.enabled is falsy, this is a pure pass-through.
9
+ */
10
+ import type { MetricsCollector } from "../metrics";
11
+ export interface GovernanceToolRewritePolicy {
12
+ readonly enabled?: boolean;
13
+ readonly descriptionSuffix?: string;
14
+ readonly hideToolIDs?: readonly string[];
15
+ readonly parameterOverrides?: Record<string, Record<string, string>>;
16
+ }
17
+ export interface ToolDefinitionInput {
18
+ toolID: string;
19
+ }
20
+ export interface ToolDefinitionOutput {
21
+ description: string;
22
+ parameters: {
23
+ properties?: Record<string, {
24
+ description?: string;
25
+ [k: string]: unknown;
26
+ }>;
27
+ };
28
+ }
29
+ /**
30
+ * v0.41.0: Handle a tool.definition invocation.
31
+ * - If not enabled: pass through.
32
+ * - If tool is in hideToolIDs: set description to empty string (hidden from LLM).
33
+ * - Otherwise: append descriptionSuffix if set, and apply parameterOverrides.
34
+ */
35
+ export declare function handleToolDefinition(input: ToolDefinitionInput, output: ToolDefinitionOutput, policy: GovernanceToolRewritePolicy, metrics: MetricsCollector): Promise<void>;