@herjarsa/omo-meta-governor 0.12.0 → 0.13.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.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Custom tools that omo-meta-governor registers for the LLM to invoke
3
+ * explicitly. v0.13.1 upgrade.
4
+ *
5
+ * Three tools:
6
+ * - `omo_search` — semantic code search via codegraph/graphify
7
+ * - `omo_recall` — search past lessons learned in this project's history
8
+ * - `omo_health` — show plugin runtime status + metrics
9
+ *
10
+ * Design:
11
+ * - Tools are registered via the `tool` field in the returned Hooks object
12
+ * - All tools have Zod-validated args (typed at compile time)
13
+ * - Each tool calls into the modules we already built (GraphRetrieval, SqliteBackend)
14
+ * - Failure modes return a friendly string so the LLM can recover
15
+ *
16
+ * The key insight: instead of fire-and-forget invocations in tool.execute.before
17
+ * (v0.13.0), the LLM now EXPLICITLY chooses when to call these tools via its
18
+ * tool schema. This makes governance visible and intentional rather than
19
+ * ambient and invisible.
20
+ */
21
+ import { type ToolContext, type ToolResult } from "@opencode-ai/plugin";
22
+ import type { SqliteBackend } from "./sqlite-backend";
23
+ import type { GraphRetrieval } from "./graph-retrieval";
24
+ import type { MetricsCollector } from "./metrics";
25
+ export interface OmoSearchDeps {
26
+ graphRetrieval: GraphRetrieval;
27
+ cwd: string;
28
+ }
29
+ /**
30
+ * Build the `omo_search` tool. The LLM sees it in its tool schema with a
31
+ * description that biases it toward graph-first queries.
32
+ */
33
+ export declare function buildOmoSearchTool(deps: OmoSearchDeps): {
34
+ description: string;
35
+ args: {
36
+ query: import("zod").ZodString;
37
+ maxResults: import("zod").ZodOptional<import("zod").ZodNumber>;
38
+ };
39
+ execute(args: {
40
+ query: string;
41
+ maxResults?: number | undefined;
42
+ }, context: ToolContext): Promise<ToolResult>;
43
+ };
44
+ export interface OmoRecallDeps {
45
+ sqlite: SqliteBackend;
46
+ }
47
+ /**
48
+ * Build the `omo_recall` tool. The LLM uses this to retrieve lessons learned
49
+ * in past sessions, enabling genuine cross-session learning.
50
+ */
51
+ export declare function buildOmoRecallTool(deps: OmoRecallDeps): {
52
+ description: string;
53
+ args: {
54
+ query: import("zod").ZodString;
55
+ limit: import("zod").ZodOptional<import("zod").ZodNumber>;
56
+ };
57
+ execute(args: {
58
+ query: string;
59
+ limit?: number | undefined;
60
+ }, context: ToolContext): Promise<ToolResult>;
61
+ };
62
+ export interface OmoHealthDeps {
63
+ metrics: MetricsCollector;
64
+ logFilePath: string;
65
+ healthFilePath: string;
66
+ }
67
+ /**
68
+ * Build the `omo_health` tool. Lets the agent (and the user) see exactly
69
+ * what the plugin is doing — closes the v0.10.0 "silent governance" complaint.
70
+ */
71
+ export declare function buildOmoHealthTool(deps: OmoHealthDeps): {
72
+ description: string;
73
+ args: {};
74
+ execute(args: Record<string, never>, context: ToolContext): Promise<ToolResult>;
75
+ };
@@ -1 +1,47 @@
1
+ /**
2
+ * JSONL structured file logger with size-based rotation. v0.13.0 upgrade
3
+ * of the original text-based logger.
4
+ *
5
+ * Output format: one JSON object per line, with fields:
6
+ * { timestamp, level, event, message, sessionID?, data? }
7
+ *
8
+ * Rotation: when the active log file exceeds MAX_FILE_SIZE_BYTES (10MB),
9
+ * the current file is renamed to .1, .1 to .2, etc. Files past MAX_ROTATED_FILES
10
+ * are deleted. Atomic on POSIX (rename is atomic for same-filesystem moves).
11
+ *
12
+ * Backwards compatibility: the existing `logToFile(level, message, data?)`
13
+ * API is preserved with 14 existing call sites. New code can use
14
+ * `logStructured({ level, event, message, ... })` for structured fields.
15
+ */
16
+ declare const LOG_PATH: string;
17
+ export type LogLevel = "info" | "warn" | "error";
18
+ export interface LogEntry {
19
+ /** ISO 8601 timestamp. Auto-filled if omitted. */
20
+ timestamp?: string;
21
+ level: LogLevel;
22
+ /** Optional structured event name (e.g. "plugin_loaded", "intervention_delivered"). */
23
+ event?: string;
24
+ message: string;
25
+ /** Optional sessionID for correlation across logs. */
26
+ sessionID?: string;
27
+ /** Optional structured data (JSON-serializable). */
28
+ data?: unknown;
29
+ }
30
+ /**
31
+ * Legacy API — preserves the signature of the pre-0.13.0 logger so all 14
32
+ * existing call sites in src/plugin.ts continue to work unchanged.
33
+ */
1
34
  export declare function logToFile(level: "info" | "warn" | "error", message: string, data?: unknown): void;
35
+ /**
36
+ * Structured API for new code. Adds `event` and `sessionID` fields.
37
+ */
38
+ export declare function logStructured(entry: LogEntry): void;
39
+ /**
40
+ * Returns the current log file size in bytes (for health monitoring).
41
+ */
42
+ export declare function getLogFileSize(): number;
43
+ /**
44
+ * Returns the number of rotated log files (e.g. 0-5).
45
+ */
46
+ export declare function getRotatedLogCount(): number;
47
+ export { LOG_PATH };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * GraphRetrieval — invokes codegraph/graphify and caches results for
3
+ * injection into the agent's context. v0.13.0 fix for C2: the plugin
4
+ * previously only told the agent to use graph tools via prompt text; now
5
+ * it actually invokes them and injects the results.
6
+ *
7
+ * Design:
8
+ * - Re-detects graph directories on EVERY call (fixes the race condition
9
+ * in src/plugin.ts:74-81 where static booleans were set once at load time
10
+ * before async `runGraphSync()` could create the directories).
11
+ * - Async with timeout (5s default) — never blocks tool.execute.before.
12
+ * - Per-session cache keyed by (sessionID, queryHash) with 5min TTL.
13
+ * - LRU eviction at 10 entries per session.
14
+ * - Graceful degradation: missing CLI → null result, errors are swallowed.
15
+ *
16
+ * Invocation strategy:
17
+ * - If `.codegraph/` exists and `codegraph` CLI is available: invoke `codegraph explore <query>`
18
+ * - Else if `graphify-out/` exists and `graphify` is available: invoke `graphify query <query>`
19
+ * - Else: return null
20
+ *
21
+ * The plugin can override CLI paths via the `invoke()` options for testing
22
+ * and for users who have the tools in non-standard locations.
23
+ */
24
+ import { existsSync } from "node:fs";
25
+ export type GraphToolKind = "codegraph" | "graphify" | null;
26
+ export interface GraphInvocationResult {
27
+ /** Which tool was actually invoked. */
28
+ kind: GraphToolKind;
29
+ /** The query that was executed. */
30
+ query: string;
31
+ /** Result text from the tool, or null on failure / no-tool. */
32
+ result: string | null;
33
+ /** True if the subprocess was killed by timeout. */
34
+ timedOut: boolean;
35
+ /** Wall-clock duration in ms. */
36
+ durationMs: number;
37
+ }
38
+ export interface GraphRetrievalConfig {
39
+ /** Subprocess timeout in ms. Default: 5000. */
40
+ timeoutMs?: number;
41
+ /** Cache entry TTL in ms. Default: 300000 (5min). */
42
+ cacheTtlMs?: number;
43
+ /** Max cache entries per session. Default: 10. */
44
+ maxEntriesPerSession?: number;
45
+ }
46
+ export interface InvokeOptions {
47
+ /** Override codegraph CLI path (default: lookup in PATH). */
48
+ codegraphBin?: string;
49
+ /** Override graphify CLI path (default: lookup in PATH). */
50
+ graphifyBin?: string;
51
+ /** Override timeout for this call. */
52
+ timeoutMs?: number;
53
+ }
54
+ /** Deterministic hash for a query string. Used as cache key suffix. */
55
+ export declare function hashQuery(query: string): string;
56
+ export declare class GraphRetrieval {
57
+ private readonly timeoutMs;
58
+ private readonly cacheTtlMs;
59
+ private readonly maxEntriesPerSession;
60
+ private readonly cache;
61
+ constructor(config?: GraphRetrievalConfig);
62
+ /** Returns true if .codegraph/ exists in the project dir. */
63
+ hasCodegraphDir(projectDir: string): boolean;
64
+ /** Returns true if graphify-out/ exists in the project dir. */
65
+ hasGraphifyDir(projectDir: string): boolean;
66
+ /**
67
+ * Cache graph context for a session. The most recent entry per session is
68
+ * returned by `getCachedContext()` with no query arg (used by
69
+ * system.transform to inject the latest graph context).
70
+ */
71
+ cacheContext(sessionID: string, query: string, content: string): void;
72
+ /**
73
+ * Get cached context for a session.
74
+ * - If `query` is provided, returns the exact match (or null).
75
+ * - If `query` is omitted, returns the most recent non-expired entry.
76
+ */
77
+ getCachedContext(sessionID: string, query?: string): string | null;
78
+ clear(): void;
79
+ clearSession(sessionID: string): void;
80
+ /**
81
+ * Invoke a graph tool for the given query. Returns structured result.
82
+ * Never throws — all errors are caught and returned as `result: null`.
83
+ *
84
+ * Selection logic:
85
+ * 1. If `.codegraph/` exists and `codegraph` CLI is found → invoke codegraph
86
+ * 2. Else if `graphify-out/` exists and `graphify` CLI is found → invoke graphify
87
+ * 3. Else → return null result
88
+ */
89
+ invoke(projectDir: string, query: string, options?: InvokeOptions): Promise<GraphInvocationResult>;
90
+ private spawnWithTimeout;
91
+ }
92
+ /** Returns the process-wide singleton, creating it on first use. */
93
+ export declare function getDefaultGraphRetrieval(): GraphRetrieval;
94
+ export { existsSync };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Plugin health state — observable proof that omo-meta-governor is doing work.
3
+ * Closes the C3/C4 invisibility gap: the user can `cat` the health JSON to
4
+ * see exactly what the plugin is doing, instead of wondering "is this thing
5
+ * alive?".
6
+ *
7
+ * Design:
8
+ * - Atomic writes (write to .tmp, then rename) — never produces partial reads
9
+ * - Pure functions: writeHealthToFile() and readHealthFromFile() take an
10
+ * explicit path so they're testable without environment setup
11
+ * - Returns null on missing/malformed file (not throwing) — the health file
12
+ * is observability, not a control plane; failure is non-fatal
13
+ */
14
+ export interface PluginHealth {
15
+ version: string;
16
+ status: "healthy" | "degraded" | "error";
17
+ enabled: boolean;
18
+ startedAtISO: string;
19
+ uptimeMs: number;
20
+ metrics: {
21
+ decisionsTaken: number;
22
+ decisionsStored: number;
23
+ interventionsDelivered: number;
24
+ orchestratorRuns: number;
25
+ orchestratorErrors: number;
26
+ lastDecisionISO: string | null;
27
+ lastInterventionISO: string | null;
28
+ };
29
+ logFile: {
30
+ path: string;
31
+ sizeBytes: number;
32
+ rotatedFiles: number;
33
+ };
34
+ session: {
35
+ id: string;
36
+ toolCallsObserved: number;
37
+ violationsDetected: number;
38
+ interventionsSkipped: number;
39
+ firstSeenISO: string;
40
+ lastSeenISO: string;
41
+ };
42
+ }
43
+ /**
44
+ * Atomically write the health state to the given path. Writes to a
45
+ * .tmp sibling first, then renames. Creates parent directory if needed.
46
+ */
47
+ export declare function writeHealthToFile(health: PluginHealth, path: string): void;
48
+ /**
49
+ * Read the health state from the given path. Returns null on missing or
50
+ * malformed file. Never throws — the health file is observability, not
51
+ * a control plane.
52
+ */
53
+ export declare function readHealthFromFile(path: string): PluginHealth | null;
54
+ /**
55
+ * Compute the current log file size and rotated file count. Used to
56
+ * populate the `logFile` field in PluginHealth.
57
+ */
58
+ export declare function describeLogFile(logPath: string): {
59
+ path: string;
60
+ sizeBytes: number;
61
+ rotatedFiles: number;
62
+ };
package/dist/index.d.ts CHANGED
@@ -36,5 +36,7 @@ export { loadProtocol, buildSystemInjection, auditToolCall, DEFAULT_PROTOCOL_PAT
36
36
  export { stripJsoncComments, parseJsonc, loadJsoncFile, deepMerge, loadMetaGovernorConfig, getUserConfigPath, getProjectConfigPath, type ConfigFileSources, type ConfigFileResult, } from "./config-file";
37
37
  export { runGraphSync, stopWatches, resetInitializedProjects, type GraphSyncConfig, type GraphSyncResult, type GraphSyncCode, type ToolAvailability, } from "./graph-sync";
38
38
  export { generateSchema, writeSchemaFile, type JsonSchema, type JsonSchemaProperty } from "./generate-schema";
39
+ export { SqliteBackend, getDefaultSqliteBackend } from "./sqlite-backend";
40
+ export { GraphRetrieval, getDefaultGraphRetrieval, hashQuery, type GraphToolKind, type GraphInvocationResult, type GraphRetrievalConfig, type InvokeOptions } from "./graph-retrieval";
39
41
  export { loadOrchestratorConfigFromSources } from "./config";
40
42
  export type { ProtocolViolation, ProtocolEnforcementSessionState } from "./types";