@vainplex/openclaw-leuko 0.1.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +160 -0
  4. package/dist/src/check-runner.d.ts +65 -0
  5. package/dist/src/check-runner.d.ts.map +1 -0
  6. package/dist/src/check-runner.js +40 -0
  7. package/dist/src/check-runner.js.map +1 -0
  8. package/dist/src/check-utils.d.ts +21 -0
  9. package/dist/src/check-utils.d.ts.map +1 -0
  10. package/dist/src/check-utils.js +39 -0
  11. package/dist/src/check-utils.js.map +1 -0
  12. package/dist/src/checks/anomaly-detection.d.ts +3 -0
  13. package/dist/src/checks/anomaly-detection.d.ts.map +1 -0
  14. package/dist/src/checks/anomaly-detection.js +127 -0
  15. package/dist/src/checks/anomaly-detection.js.map +1 -0
  16. package/dist/src/checks/bootstrap-integrity.d.ts +3 -0
  17. package/dist/src/checks/bootstrap-integrity.d.ts.map +1 -0
  18. package/dist/src/checks/bootstrap-integrity.js +107 -0
  19. package/dist/src/checks/bootstrap-integrity.js.map +1 -0
  20. package/dist/src/checks/goal-quality.d.ts +3 -0
  21. package/dist/src/checks/goal-quality.d.ts.map +1 -0
  22. package/dist/src/checks/goal-quality.js +143 -0
  23. package/dist/src/checks/goal-quality.js.map +1 -0
  24. package/dist/src/checks/pipeline-correlation.d.ts +7 -0
  25. package/dist/src/checks/pipeline-correlation.d.ts.map +1 -0
  26. package/dist/src/checks/pipeline-correlation.js +118 -0
  27. package/dist/src/checks/pipeline-correlation.js.map +1 -0
  28. package/dist/src/checks/recommendations.d.ts +3 -0
  29. package/dist/src/checks/recommendations.d.ts.map +1 -0
  30. package/dist/src/checks/recommendations.js +132 -0
  31. package/dist/src/checks/recommendations.js.map +1 -0
  32. package/dist/src/checks/thread-health.d.ts +3 -0
  33. package/dist/src/checks/thread-health.d.ts.map +1 -0
  34. package/dist/src/checks/thread-health.js +129 -0
  35. package/dist/src/checks/thread-health.js.map +1 -0
  36. package/dist/src/config.d.ts +10 -0
  37. package/dist/src/config.d.ts.map +1 -0
  38. package/dist/src/config.js +301 -0
  39. package/dist/src/config.js.map +1 -0
  40. package/dist/src/index.d.ts +27 -0
  41. package/dist/src/index.d.ts.map +1 -0
  42. package/dist/src/index.js +198 -0
  43. package/dist/src/index.js.map +1 -0
  44. package/dist/src/llm-client.d.ts +7 -0
  45. package/dist/src/llm-client.d.ts.map +1 -0
  46. package/dist/src/llm-client.js +111 -0
  47. package/dist/src/llm-client.js.map +1 -0
  48. package/dist/src/status-reader.d.ts +6 -0
  49. package/dist/src/status-reader.d.ts.map +1 -0
  50. package/dist/src/status-reader.js +156 -0
  51. package/dist/src/status-reader.js.map +1 -0
  52. package/dist/src/status-writer.d.ts +14 -0
  53. package/dist/src/status-writer.d.ts.map +1 -0
  54. package/dist/src/status-writer.js +52 -0
  55. package/dist/src/status-writer.js.map +1 -0
  56. package/dist/src/tool.d.ts +9 -0
  57. package/dist/src/tool.d.ts.map +1 -0
  58. package/dist/src/tool.js +129 -0
  59. package/dist/src/tool.js.map +1 -0
  60. package/dist/src/types.d.ts +261 -0
  61. package/dist/src/types.d.ts.map +1 -0
  62. package/dist/src/types.js +5 -0
  63. package/dist/src/types.js.map +1 -0
  64. package/openclaw.plugin.json +22 -0
  65. package/package.json +51 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] — 2026-02-23
4
+
5
+ ### Added
6
+ - Initial release of `@vainplex/openclaw-leuko` — Cognitive Immune System Plugin
7
+ - 6 Cognitive Checks (CK-01 through CK-06):
8
+ - CK-01: Goal Quality Assessment (LLM) — with pre-filter for expired/stale goals
9
+ - CK-02: Thread Health Assessment (LLM) — with pre-filter for stale threads
10
+ - CK-03: Pipeline Correlation (deterministic) — NATS/thread/cron cross-referencing
11
+ - CK-04: Anomaly Detection (deterministic) — directory size and metric trend analysis
12
+ - CK-05: Bootstrap Integrity (LLM) — BOOTSTRAP.md factual verification
13
+ - CK-06: Recommendations (LLM) — proactive housekeeping suggestions
14
+ - `leuko_status` tool for agent health queries (sections: summary, daemon, cognitive, recommendations, all)
15
+ - `/leuko` command (subcommands: refresh, detail, config)
16
+ - `before_agent_start` hook for health context injection
17
+ - External config pattern (`~/.openclaw/plugins/openclaw-leuko/config.json`)
18
+ - LLM client with primary (Ollama) + fallback (LiteLLM) support
19
+ - Atomic write protocol for `leuko-status.json` (preserves daemon fields)
20
+ - Consecutive-critical tracking with `escalation_needed` flag
21
+ - Fail-open behavior: LLM failures default to `severity: "ok"`
22
+ - Adopted Sitrep `errors` and `custom` collector config (implementation deferred to v0.2.0)
23
+
24
+ ### Replaces
25
+ - `@vainplex/openclaw-sitrep` (deprecated) — absorbed goals/threads pre-filtering, dropped redundant collectors
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Albert Hild / Vainplex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # @vainplex/openclaw-leuko
2
+
3
+ **Cognitive immune system for OpenClaw** — L2 semantic health checks with LLM analysis, tool exposure for agent queries, and Sitrep replacement.
4
+
5
+ Part of the [Vainplex OpenClaw Plugin Suite](https://github.com/alberthild/vainplex-openclaw).
6
+
7
+ ## Architecture
8
+
9
+ Leuko operates as a **two-tier hybrid system**:
10
+
11
+ - **Level 1 (Python daemon):** Heuristic checks — file freshness, JSON validity, service connectivity, cron health. Runs every 15min, zero API cost.
12
+ - **Level 2 (This plugin):** Cognitive checks — semantic analysis via LLM + deterministic correlation. Runs every 2h or on-demand.
13
+
14
+ ```
15
+ Plugin (L2) Agents
16
+ │ │
17
+ ┌─ Read daemon_checks[] from leuko-status.json │
18
+ ├─ Run CK-01..CK-06 (4 LLM + 2 deterministic) │
19
+ ├─ Write cognitive_checks[] to leuko-status.json │
20
+ │ │
21
+ ├── leuko_status tool ────────────────────────────┤
22
+ ├── before_agent_start hook ──────────────────────┤
23
+ └── /leuko command ──────────────────────────────→│
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```bash
29
+ npm install @vainplex/openclaw-leuko
30
+ ```
31
+
32
+ Add to `openclaw.json`:
33
+ ```json
34
+ {
35
+ "plugins": {
36
+ "entries": {
37
+ "openclaw-leuko": { "enabled": true }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ The plugin will auto-create a default config at `~/.openclaw/plugins/openclaw-leuko/config.json` on first run.
44
+
45
+ ## Cognitive Checks
46
+
47
+ | Check | ID | Type | Purpose |
48
+ |-------|-----|------|---------|
49
+ | Goal Quality | CK-01 | LLM | Are pending goals specific, actionable, non-redundant? |
50
+ | Thread Health | CK-02 | LLM | Are threads stale, duplicate, or accumulating? |
51
+ | Pipeline Correlation | CK-03 | Deterministic | Are inputs flowing to outputs? (NATS → threads) |
52
+ | Anomaly Detection | CK-04 | Deterministic | Statistical deviations in metrics and directory sizes |
53
+ | Bootstrap Integrity | CK-05 | LLM | Is BOOTSTRAP.md factually current? |
54
+ | Recommendations | CK-06 | LLM | Proactive housekeeping suggestions |
55
+
56
+ ### Severity Levels
57
+
58
+ - **`ok`** — All checks pass, no issues detected
59
+ - **`warn`** — Issues found that should be reviewed
60
+ - **`critical`** — Significant problems requiring attention
61
+
62
+ ## Tool: `leuko_status`
63
+
64
+ Agents can query system health:
65
+
66
+ ```
67
+ leuko_status({ section: "summary" })
68
+ leuko_status({ section: "cognitive", severity_filter: "warn" })
69
+ leuko_status({ section: "recommendations" })
70
+ ```
71
+
72
+ Sections: `summary` | `daemon` | `cognitive` | `recommendations` | `all`
73
+
74
+ ## Command: `/leuko`
75
+
76
+ ```
77
+ /leuko — Health summary
78
+ /leuko refresh — Trigger immediate L2 check cycle
79
+ /leuko detail — All checks with findings
80
+ /leuko config — Active configuration
81
+ ```
82
+
83
+ ## Hook: `before_agent_start`
84
+
85
+ When system health is degraded, injects a one-liner into the agent's context:
86
+
87
+ ```
88
+ ⚕️ Leuko Health: WARN — 2 issues: goal_quality (3/9 vague goals), facts (stale 48h)
89
+ ```
90
+
91
+ Configurable: only injected when issues exist, respects maxLength.
92
+
93
+ ## Configuration
94
+
95
+ Full config lives at `~/.openclaw/plugins/openclaw-leuko/config.json`:
96
+
97
+ ```json
98
+ {
99
+ "enabled": true,
100
+ "statusPath": "~/clawd/memory/leuko-status.json",
101
+ "intervalMinutes": 120,
102
+ "llm": {
103
+ "primary": {
104
+ "provider": "ollama",
105
+ "model": "qwen3:14b",
106
+ "baseUrl": "http://localhost:11434",
107
+ "timeoutSec": 30
108
+ },
109
+ "fallback": {
110
+ "provider": "litellm",
111
+ "model": "gemini/gemini-2.0-flash-lite",
112
+ "baseUrl": "http://localhost:4000",
113
+ "timeoutSec": 30
114
+ }
115
+ },
116
+ "checks": {
117
+ "goal_quality": { "enabled": true, "usesLlm": true },
118
+ "thread_health": { "enabled": true, "staleDays": 5 },
119
+ "pipeline_correlation": { "enabled": true },
120
+ "anomaly_detection": { "enabled": true },
121
+ "bootstrap_integrity": { "enabled": true },
122
+ "recommendations": { "enabled": true, "maxRecommendations": 5 }
123
+ }
124
+ }
125
+ ```
126
+
127
+ ## LLM Integration
128
+
129
+ - **Primary:** `ollama/qwen3:14b` (local, $0.00/run)
130
+ - **Fallback:** `gemini/gemini-2.0-flash-lite` via LiteLLM (~$0.002/run)
131
+ - **Budget:** ≤ $0.05 per run (30x margin)
132
+ - **Max 4 LLM calls per run** (CK-01, CK-02, CK-05, CK-06)
133
+ - **Fail-open:** If LLM unavailable, severity defaults to `ok` with explanatory detail
134
+
135
+ ## Sitrep Deprecation
136
+
137
+ This plugin replaces `@vainplex/openclaw-sitrep`. Sitrep's `errors` and `custom` collectors are adopted; `systemd_timers`, `nats`, `goals`, `threads`, and `calendar` collectors are either absorbed into cognitive checks or dropped (covered by L1).
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ npm install
143
+ npm run build # TypeScript compilation
144
+ npm run test # Run all tests
145
+ npm run test:coverage # Coverage report (>80% lines)
146
+ npm run typecheck # Type checking only
147
+ ```
148
+
149
+ ## Standards
150
+
151
+ - TypeScript strict mode, `noUncheckedIndexedAccess`
152
+ - 0 `any` types
153
+ - ESM (`type: module`)
154
+ - 0 runtime dependencies (only `node:*` builtins)
155
+ - vitest for tests
156
+ - kebab-case filenames
157
+
158
+ ## License
159
+
160
+ MIT
@@ -0,0 +1,65 @@
1
+ import type { CognitiveCheckResult, LlmClient, PluginLogger, Severity } from "./types.js";
2
+ /**
3
+ * Options for the generic LLM check runner.
4
+ *
5
+ * TInput: the shape read from disk (goals, threads, bootstrap text, etc.)
6
+ * TResponse: the expected LLM JSON response shape
7
+ */
8
+ export interface LlmCheckOpts<TInput, TResponse> {
9
+ /** Check name (e.g. "cognitive:goal_quality") */
10
+ name: string;
11
+ /** LLM system prompt */
12
+ systemPrompt: string;
13
+ /** LLM client instance */
14
+ llm: LlmClient;
15
+ /** Logger */
16
+ logger: PluginLogger;
17
+ /** Read input data; return null to skip with a given result */
18
+ readInput(timestamp: string, startMs: number): ReadInputResult<TInput>;
19
+ /** Run deterministic pre-filter; returns pre-severity and partial data */
20
+ preFilter(input: TInput): PreFilterResult;
21
+ /** Build the LLM user prompt from input */
22
+ buildPrompt(input: TInput): string;
23
+ /** Build fail-open result when LLM fails */
24
+ buildFailOpen(opts: FailOpenOpts): CognitiveCheckResult;
25
+ /** Parse and merge LLM response + pre-filter into final result */
26
+ mergeResults(opts: MergeOpts<TResponse>): CognitiveCheckResult;
27
+ }
28
+ export type ReadInputResult<T> = {
29
+ ok: true;
30
+ input: T;
31
+ } | {
32
+ ok: false;
33
+ skip: CognitiveCheckResult;
34
+ };
35
+ export interface PreFilterResult {
36
+ severity: Severity;
37
+ findingCount: number;
38
+ /** Arbitrary data passed through to mergeResults / buildFailOpen */
39
+ data: Record<string, unknown>;
40
+ }
41
+ export interface FailOpenOpts {
42
+ name: string;
43
+ pre: PreFilterResult;
44
+ message: string;
45
+ llmModel: string;
46
+ llmTokens: number;
47
+ timestamp: string;
48
+ startMs: number;
49
+ }
50
+ export interface MergeOpts<TResponse> {
51
+ parsed: TResponse;
52
+ pre: PreFilterResult;
53
+ llmModel: string;
54
+ llmTokens: number;
55
+ timestamp: string;
56
+ startMs: number;
57
+ }
58
+ /**
59
+ * Generic runner for all LLM-based cognitive checks.
60
+ *
61
+ * Flow: readInput → preFilter → LLM call → parse → mergeResults
62
+ * On LLM failure: fail-open via buildFailOpen.
63
+ */
64
+ export declare function runLlmCheck<TInput, TResponse>(opts: LlmCheckOpts<TInput, TResponse>): Promise<CognitiveCheckResult>;
65
+ //# sourceMappingURL=check-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-runner.d.ts","sourceRoot":"","sources":["../../src/check-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,SAAS,EACT,YAAY,EACZ,QAAQ,EACT,MAAM,YAAY,CAAC;AAGpB;;;;;GAKG;AACH,MAAM,WAAW,YAAY,CAAC,MAAM,EAAE,SAAS;IAC7C,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,GAAG,EAAE,SAAS,CAAC;IACf,aAAa;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,+DAA+D;IAC/D,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACvE,0EAA0E;IAC1E,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC;IAC1C,2CAA2C;IAC3C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,4CAA4C;IAC5C,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,oBAAoB,CAAC;IACxD,kEAAkE;IAClE,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,oBAAoB,CAAC;CAChE;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,IACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,eAAe,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS,CAAC,SAAS;IAClC,MAAM,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,eAAe,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,SAAS,EACjD,IAAI,EAAE,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,GACpC,OAAO,CAAC,oBAAoB,CAAC,CAmC/B"}
@@ -0,0 +1,40 @@
1
+ import { parseLlmJson } from "./check-utils.js";
2
+ /**
3
+ * Generic runner for all LLM-based cognitive checks.
4
+ *
5
+ * Flow: readInput → preFilter → LLM call → parse → mergeResults
6
+ * On LLM failure: fail-open via buildFailOpen.
7
+ */
8
+ export async function runLlmCheck(opts) {
9
+ const startMs = Date.now();
10
+ const timestamp = new Date().toISOString();
11
+ const readResult = opts.readInput(timestamp, startMs);
12
+ if (!readResult.ok)
13
+ return readResult.skip;
14
+ const pre = opts.preFilter(readResult.input);
15
+ const userPrompt = opts.buildPrompt(readResult.input);
16
+ const llmResult = await opts.llm.generate(opts.systemPrompt, userPrompt, 30000);
17
+ if (llmResult.content === null) {
18
+ const msg = llmResult.error
19
+ ? `LLM unavailable (${llmResult.error})`
20
+ : "LLM timeout";
21
+ return opts.buildFailOpen({
22
+ name: opts.name, pre, message: msg,
23
+ llmModel: llmResult.model, llmTokens: 0, timestamp, startMs,
24
+ });
25
+ }
26
+ const parsed = parseLlmJson(llmResult.content);
27
+ if (!parsed) {
28
+ return opts.buildFailOpen({
29
+ name: opts.name, pre, message: "LLM response parsing failed",
30
+ llmModel: llmResult.model, llmTokens: llmResult.tokens,
31
+ timestamp, startMs,
32
+ });
33
+ }
34
+ return opts.mergeResults({
35
+ parsed, pre,
36
+ llmModel: llmResult.model, llmTokens: llmResult.tokens,
37
+ timestamp, startMs,
38
+ });
39
+ }
40
+ //# sourceMappingURL=check-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-runner.js","sourceRoot":"","sources":["../../src/check-runner.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA2DhD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAqC;IAErC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE3C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACtD,IAAI,CAAC,UAAU,CAAC,EAAE;QAAE,OAAO,UAAU,CAAC,IAAI,CAAC;IAE3C,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;IAEhF,IAAI,SAAS,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK;YACzB,CAAC,CAAC,oBAAoB,SAAS,CAAC,KAAK,GAAG;YACxC,CAAC,CAAC,aAAa,CAAC;QAClB,OAAO,IAAI,CAAC,aAAa,CAAC;YACxB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG;YAClC,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO;SAC5D,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAY,SAAS,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC,aAAa,CAAC;YACxB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,6BAA6B;YAC5D,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,MAAM;YACtD,SAAS,EAAE,OAAO;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC,YAAY,CAAC;QACvB,MAAM,EAAE,GAAG;QACX,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,MAAM;QACtD,SAAS,EAAE,OAAO;KACnB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { Severity } from "./types.js";
2
+ /**
3
+ * Parse a string (typically from LLM response) into a valid Severity.
4
+ * Returns "ok" for unrecognized values.
5
+ */
6
+ export declare function parseSeverityString(v: unknown): Severity;
7
+ /**
8
+ * Return the most severe of the given severities.
9
+ * Order: ok < warn < critical.
10
+ */
11
+ export declare function worstSeverity(...severities: Severity[]): Severity;
12
+ /**
13
+ * Type guard: is value a plain object (non-null, non-array)?
14
+ */
15
+ export declare function isRecord(v: unknown): v is Record<string, unknown>;
16
+ /**
17
+ * Parse a JSON string (typically raw LLM output) into a typed object.
18
+ * Returns null on parse failure.
19
+ */
20
+ export declare function parseLlmJson<T>(raw: string): T | null;
21
+ //# sourceMappingURL=check-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-utils.d.ts","sourceRoot":"","sources":["../../src/check-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,GAAG,QAAQ,CAGxD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,UAAU,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAIjE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAMrD"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Parse a string (typically from LLM response) into a valid Severity.
3
+ * Returns "ok" for unrecognized values.
4
+ */
5
+ export function parseSeverityString(v) {
6
+ if (v === "ok" || v === "warn" || v === "critical")
7
+ return v;
8
+ return "ok";
9
+ }
10
+ /**
11
+ * Return the most severe of the given severities.
12
+ * Order: ok < warn < critical.
13
+ */
14
+ export function worstSeverity(...severities) {
15
+ if (severities.includes("critical"))
16
+ return "critical";
17
+ if (severities.includes("warn"))
18
+ return "warn";
19
+ return "ok";
20
+ }
21
+ /**
22
+ * Type guard: is value a plain object (non-null, non-array)?
23
+ */
24
+ export function isRecord(v) {
25
+ return typeof v === "object" && v !== null && !Array.isArray(v);
26
+ }
27
+ /**
28
+ * Parse a JSON string (typically raw LLM output) into a typed object.
29
+ * Returns null on parse failure.
30
+ */
31
+ export function parseLlmJson(raw) {
32
+ try {
33
+ return JSON.parse(raw);
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ //# sourceMappingURL=check-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-utils.js","sourceRoot":"","sources":["../../src/check-utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAU;IAC5C,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,UAAU;QAAE,OAAO,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,GAAG,UAAsB;IACrD,IAAI,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACvD,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC/C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAI,GAAW;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CognitiveCheckResult, AnomalyDetectionCheckConfig, LeukoHistory, PluginLogger } from "../types.js";
2
+ export declare function runAnomalyDetectionCheck(config: AnomalyDetectionCheckConfig, history: LeukoHistory | null, logger: PluginLogger): CognitiveCheckResult;
3
+ //# sourceMappingURL=anomaly-detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anomaly-detection.d.ts","sourceRoot":"","sources":["../../../src/checks/anomaly-detection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EAEpB,2BAA2B,EAC3B,YAAY,EACZ,YAAY,EAEb,MAAM,aAAa,CAAC;AAqGrB,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,2BAA2B,EACnC,OAAO,EAAE,YAAY,GAAG,IAAI,EAC5B,MAAM,EAAE,YAAY,GACnB,oBAAoB,CAsBtB"}
@@ -0,0 +1,127 @@
1
+ import { statSync, existsSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const CHECK_NAME = "cognitive:anomaly_detection";
4
+ function getDirSizeMb(dirPath) {
5
+ try {
6
+ if (!existsSync(dirPath))
7
+ return null;
8
+ let totalBytes = 0;
9
+ const entries = readdirSync(dirPath, { withFileTypes: true });
10
+ for (const entry of entries) {
11
+ try {
12
+ if (entry.isFile())
13
+ totalBytes += statSync(join(dirPath, entry.name)).size;
14
+ }
15
+ catch { /* skip inaccessible */ }
16
+ }
17
+ return Math.round((totalBytes / (1024 * 1024)) * 100) / 100;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ function detectTrends(history, metricName) {
24
+ if (!history || history.snapshots.length < 3)
25
+ return { consecutive: 0, direction: "stable" };
26
+ const values = history.snapshots
27
+ .filter((s) => typeof s.metrics[metricName] === "number")
28
+ .map((s) => s.metrics[metricName]);
29
+ if (values.length < 3)
30
+ return { consecutive: 0, direction: "stable" };
31
+ return computeConsecutiveTrend(values);
32
+ }
33
+ function computeConsecutiveTrend(values) {
34
+ let up = 0;
35
+ let down = 0;
36
+ for (let i = values.length - 1; i > 0; i--) {
37
+ const prev = values[i - 1];
38
+ const curr = values[i];
39
+ if (curr > prev) {
40
+ up++;
41
+ down = 0;
42
+ }
43
+ else if (curr < prev) {
44
+ down++;
45
+ up = 0;
46
+ }
47
+ else
48
+ break;
49
+ }
50
+ if (down >= 3)
51
+ return { consecutive: down, direction: "shrinking" };
52
+ if (up >= 3)
53
+ return { consecutive: up, direction: "growing" };
54
+ return { consecutive: 0, direction: "stable" };
55
+ }
56
+ function checkDirSizes(config, history, baselines, anomalies) {
57
+ for (const dir of config.monitoredDirs) {
58
+ const resolved = dir.path.replace(/^~/, process.env["HOME"] ?? "/tmp");
59
+ const currentMb = getDirSizeMb(resolved);
60
+ if (currentMb === null)
61
+ continue;
62
+ baselines[`${dir.label}_dir_mb`] = currentMb;
63
+ checkGrowthAnomaly(dir.label, currentMb, history, anomalies);
64
+ }
65
+ }
66
+ function checkGrowthAnomaly(label, currentMb, history, anomalies) {
67
+ if (!history || history.snapshots.length === 0)
68
+ return;
69
+ const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
70
+ const snap = history.snapshots.find((s) => {
71
+ const ts = new Date(s.timestamp).getTime();
72
+ return !isNaN(ts) && ts < weekAgo;
73
+ });
74
+ if (!snap)
75
+ return;
76
+ const baselineMb = snap.metrics[`${label}_dir_mb`];
77
+ if (typeof baselineMb !== "number" || baselineMb <= 0)
78
+ return;
79
+ const ratio = currentMb / baselineMb;
80
+ if (ratio > 5) {
81
+ anomalies.push({ metric: `${label}_dir_mb`, current: currentMb, baseline: baselineMb, deviation: `${ratio.toFixed(1)}x growth in 7 days`, severity: "critical" });
82
+ }
83
+ else if (ratio > 2) {
84
+ anomalies.push({ metric: `${label}_dir_mb`, current: currentMb, baseline: baselineMb, deviation: `${ratio.toFixed(1)}x growth in 7 days`, severity: "warn" });
85
+ }
86
+ }
87
+ function checkMetricTrends(history, anomalies) {
88
+ const tracked = ["fact_count", "goal_count", "thread_count"];
89
+ for (const metric of tracked) {
90
+ const trend = detectTrends(history, metric);
91
+ if (trend.direction !== "shrinking" || trend.consecutive < 3)
92
+ continue;
93
+ const severity = trend.consecutive >= 5 ? "critical" : "warn";
94
+ const deviation = trend.consecutive >= 5
95
+ ? `${trend.consecutive} consecutive decreases — possible data loss`
96
+ : `${trend.consecutive} consecutive decreases`;
97
+ anomalies.push({ metric, current: trend.consecutive, baseline: 0, deviation, severity });
98
+ }
99
+ }
100
+ function overallSeverity(anomalies) {
101
+ if (anomalies.some((a) => a.severity === "critical"))
102
+ return "critical";
103
+ if (anomalies.some((a) => a.severity === "warn"))
104
+ return "warn";
105
+ return "ok";
106
+ }
107
+ export function runAnomalyDetectionCheck(config, history, logger) {
108
+ const startMs = Date.now();
109
+ const timestamp = new Date().toISOString();
110
+ const anomalies = [];
111
+ const baselines = {};
112
+ checkDirSizes(config, history, baselines, anomalies);
113
+ checkMetricTrends(history, anomalies);
114
+ const detail = anomalies.length === 0
115
+ ? "All metrics within normal range"
116
+ : `${anomalies.length} anomaly(s) detected`;
117
+ return {
118
+ check_name: CHECK_NAME,
119
+ severity: overallSeverity(anomalies),
120
+ detail,
121
+ anomalies,
122
+ baselines,
123
+ timestamp,
124
+ duration_ms: Date.now() - startMs,
125
+ };
126
+ }
127
+ //# sourceMappingURL=anomaly-detection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anomaly-detection.js","sourceRoot":"","sources":["../../../src/checks/anomaly-detection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { CognitiveCheckResult, BootstrapIntegrityCheckConfig, LlmClient, LeukoStatus, PluginLogger } from "../types.js";
2
+ export declare function runBootstrapIntegrityCheck(config: BootstrapIntegrityCheckConfig, llm: LlmClient, status: LeukoStatus | null, logger: PluginLogger): Promise<CognitiveCheckResult>;
3
+ //# sourceMappingURL=bootstrap-integrity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap-integrity.d.ts","sourceRoot":"","sources":["../../../src/checks/bootstrap-integrity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EAEpB,6BAA6B,EAC7B,SAAS,EACT,WAAW,EACX,YAAY,EACb,MAAM,aAAa,CAAC;AAqErB,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,6BAA6B,EACrC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,WAAW,GAAG,IAAI,EAC1B,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,oBAAoB,CAAC,CA2D/B"}
@@ -0,0 +1,107 @@
1
+ import { readTextInput } from "../status-reader.js";
2
+ import { parseSeverityString } from "../check-utils.js";
3
+ import { runLlmCheck } from "../check-runner.js";
4
+ const CHECK_NAME = "cognitive:bootstrap_integrity";
5
+ const SYSTEM_PROMPT = `You are a system health evaluator. Verify that the BOOTSTRAP.md file is factually current and complete.
6
+ Respond ONLY with valid JSON matching this schema:
7
+ {
8
+ "severity": "ok" | "warn" | "critical",
9
+ "detail": "single line summary",
10
+ "findings": [
11
+ {
12
+ "issue": "stale_reference" | "missing_subsystem" | "factual_error" | "outdated_state",
13
+ "line": "the problematic text from BOOTSTRAP.md",
14
+ "detail": "explanation of what's wrong",
15
+ "recommendation": "what to fix"
16
+ }
17
+ ]
18
+ }
19
+
20
+ Evaluation rules:
21
+ - Does it reference services/crons that no longer exist?
22
+ - Are file paths correct?
23
+ - Is "current state" aligned with actual system status?
24
+ - Are key subsystems mentioned (NATS, Membrane, Cortex, Leuko, Governance)?
25
+ - Content aligns with system state → "ok"
26
+ - Minor omissions or stale references → "warn"
27
+ - Major factual errors or missing critical subsystems → "critical"`;
28
+ function buildSystemContext(status) {
29
+ if (!status)
30
+ return "System status: unavailable";
31
+ const daemonSummary = status.daemon_checks
32
+ .filter((c) => c.severity !== "ok")
33
+ .map((c) => `${c.check_name}: ${c.severity} — ${c.detail}`)
34
+ .join("\n");
35
+ return [
36
+ `Last check: ${status.last_check}`,
37
+ `Overall severity: ${status.overall_severity}`,
38
+ `Daemon checks: ${status.daemon_checks.length} total`,
39
+ daemonSummary ? `Issues:\n${daemonSummary}` : "All daemon checks OK",
40
+ ].join("\n");
41
+ }
42
+ function parseFindings(parsed) {
43
+ if (!Array.isArray(parsed.findings))
44
+ return [];
45
+ return parsed.findings.map((f) => ({
46
+ issue: typeof f.issue === "string" ? f.issue : "unknown",
47
+ line: typeof f.line === "string" ? f.line : undefined,
48
+ detail: typeof f.detail === "string" ? f.detail : "",
49
+ recommendation: typeof f.recommendation === "string" ? f.recommendation : undefined,
50
+ }));
51
+ }
52
+ export async function runBootstrapIntegrityCheck(config, llm, status, logger) {
53
+ return runLlmCheck({
54
+ name: CHECK_NAME,
55
+ systemPrompt: SYSTEM_PROMPT,
56
+ llm,
57
+ logger,
58
+ readInput(timestamp, startMs) {
59
+ const content = readTextInput(config.inputPath, 4000, logger);
60
+ if (content === null) {
61
+ return { ok: false, skip: { check_name: CHECK_NAME, severity: "warn", detail: "BOOTSTRAP.md not found — cannot verify integrity", timestamp, duration_ms: Date.now() - startMs } };
62
+ }
63
+ return { ok: true, input: { content, systemContext: buildSystemContext(status) } };
64
+ },
65
+ preFilter() {
66
+ return { severity: "ok", findingCount: 0, data: {} };
67
+ },
68
+ buildPrompt(input) {
69
+ return [
70
+ `Current date: ${new Date().toISOString().split("T")[0]}`,
71
+ "",
72
+ "=== System State ===",
73
+ input.systemContext,
74
+ "",
75
+ "=== BOOTSTRAP.md Content ===",
76
+ input.content,
77
+ ].join("\n");
78
+ },
79
+ buildFailOpen({ message, llmModel, llmTokens, timestamp, startMs }) {
80
+ return {
81
+ check_name: CHECK_NAME,
82
+ severity: "ok",
83
+ detail: `${message} — check skipped`,
84
+ timestamp,
85
+ model_used: llmModel,
86
+ tokens_used: llmTokens,
87
+ duration_ms: Date.now() - startMs,
88
+ };
89
+ },
90
+ mergeResults(opts) {
91
+ const findings = parseFindings(opts.parsed);
92
+ return {
93
+ check_name: CHECK_NAME,
94
+ severity: parseSeverityString(opts.parsed.severity),
95
+ detail: typeof opts.parsed.detail === "string"
96
+ ? opts.parsed.detail
97
+ : `${findings.length} findings`,
98
+ findings,
99
+ timestamp: opts.timestamp,
100
+ model_used: opts.llmModel,
101
+ tokens_used: opts.llmTokens,
102
+ duration_ms: Date.now() - opts.startMs,
103
+ };
104
+ },
105
+ });
106
+ }
107
+ //# sourceMappingURL=bootstrap-integrity.js.map