openclaw-observability 1.0.0 → 1.0.2

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.md ADDED
@@ -0,0 +1,189 @@
1
+ # openclaw-observability
2
+
3
+ Full-stack observability plugin for [OpenClaw](https://openclaw.ai) — automatically records every LLM call, tool invocation, and agent lifecycle event into a local DuckDB or remote MySQL database, with a built-in web dashboard for tracing, analytics, and security auditing.
4
+
5
+ ## ✨ Features
6
+
7
+ - **Full-Chain Tracing** — Captures 20 OpenClaw hooks covering LLM calls, tool invocations, agent lifecycle, session management, context compaction, and gateway events
8
+ - **Token Usage Tracking** — Automatically injects `stream_options` via fetch interception to capture prompt/completion tokens from any OpenAI-compatible API
9
+ - **Dual Storage Backend** — Local mode (embedded DuckDB, zero config) or Remote mode (MySQL/RDS)
10
+ - **Built-in Web Dashboard** — Session list, waterfall trace view, analytics charts, and security alerts — all served from the plugin with no external dependencies
11
+ - **Security Scanning** — Two-layer detection engine:
12
+ - **L1 Rule Engine** — Regex-based real-time scanning for secrets, dangerous commands, prompt injection, and sensitive file access
13
+ - **L2 Chain Detector** — Cross-action behavioral analysis (e.g., read credentials → exfiltrate data)
14
+ - **Automatic Redaction** — Masks API keys, passwords, tokens, and other sensitive fields before storage
15
+ - **Async Batch Buffer** — Configurable batch size and flush interval with overflow protection
16
+
17
+ ## 📦 Installation
18
+
19
+ ```bash
20
+ openclaw plugins install openclaw-observability
21
+ ```
22
+
23
+ That's it. The plugin starts in **Local mode** by default — zero configuration required.
24
+
25
+ ## 🚀 Quick Start
26
+
27
+ 1. Install the plugin (see above)
28
+ 2. Restart the gateway:
29
+ ```bash
30
+ openclaw gateway restart
31
+ ```
32
+ 3. Open the dashboard:
33
+ ```
34
+ http://localhost:18789/plugins/observability/
35
+ ```
36
+
37
+ ## 🖥️ Dashboard
38
+
39
+ The built-in web UI provides four tabs:
40
+
41
+ ### Dashboard (Traces)
42
+ - Summary stats: total sessions, actions, tokens, average latency, success rate
43
+ - Full-text search across sessions, actions, and content
44
+ - Time range filtering (30 min → all time)
45
+ - Click any session to open the **waterfall trace view** with nested action timeline and detailed input/output inspector
46
+
47
+ ### Analytics
48
+ - Overview KPIs: sessions, tokens (input/output), latency, active models, security alerts
49
+ - Activity over time chart (auto-switches between hourly and daily granularity)
50
+ - Token usage by model breakdown
51
+ - Action type distribution
52
+ - Top agents by session/token count
53
+
54
+ ### Security
55
+ - Alert statistics by severity (Critical / Warning / Info)
56
+ - Filterable alert list with full-text search
57
+ - Alert lifecycle management: Acknowledge → Resolve → False Positive
58
+ - Direct link from alert to the offending action in the trace view
59
+
60
+ ## ⚙️ Configuration
61
+
62
+ ### Storage Modes
63
+
64
+ | Mode | Backend | Config Required |
65
+ |------|---------|----------------|
66
+ | `local` (default) | Embedded DuckDB | None |
67
+ | `remote` | MySQL 5.7+ / 8.x / RDS | Connection info |
68
+
69
+ ### Remote Mode (MySQL)
70
+
71
+ Configure via OpenClaw Dashboard (Settings → Plugins → openclaw-observability Config) or edit `~/.openclaw/openclaw.json`:
72
+
73
+ ```json
74
+ {
75
+ "plugins": {
76
+ "entries": {
77
+ "openclaw-observability": {
78
+ "enabled": true,
79
+ "config": {
80
+ "mode": "remote",
81
+ "mysql": {
82
+ "host": "your-mysql-host.com",
83
+ "port": 3306,
84
+ "user": "username",
85
+ "password": "password",
86
+ "database": "openclaw_observability"
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### All Options
96
+
97
+ | Parameter | Default | Description |
98
+ |-----------|---------|-------------|
99
+ | `mode` | `local` | Storage mode: `local` (DuckDB) or `remote` (MySQL) |
100
+ | `duckdb.path` | `~/.openclaw/observability.duckdb` | DuckDB database file path (local mode only) |
101
+ | `mysql.host` | `localhost` | MySQL host address |
102
+ | `mysql.port` | `3306` | MySQL port |
103
+ | `mysql.user` | `root` | MySQL username |
104
+ | `mysql.password` | `""` | MySQL password |
105
+ | `mysql.database` | `openclaw_observability` | MySQL database name (auto-created) |
106
+ | `buffer.batchSize` | `50` | Records to accumulate before batch write |
107
+ | `buffer.flushIntervalMs` | `5000` | Auto-flush interval in ms |
108
+ | `redaction.enabled` | `true` | Automatically redact sensitive fields |
109
+ | `redaction.patterns` | `[api_key, password, ...]` | Field name patterns to redact (case-insensitive regex) |
110
+ | `security.enabled` | `true` | Enable real-time security scanning |
111
+ | `security.rules.*` | `true` | Toggle individual rule categories |
112
+ | `security.domainWhitelist` | `[]` | Domains excluded from external request alerts |
113
+
114
+ ## 🔒 Security Rules
115
+
116
+ ### L1 — Pattern-Based Detection
117
+
118
+ | Rule | Detection | Severity |
119
+ |------|-----------|----------|
120
+ | S001 | Alibaba Cloud AccessKey leak | Critical |
121
+ | S002 | AWS AccessKey leak | Critical |
122
+ | S003 | Private key (RSA/EC/SSH) leak | Critical |
123
+ | S004 | JWT token leak | Warning |
124
+ | S005 | Database connection string leak | Warning |
125
+ | S006 | Generic API key leak (OpenAI, GitHub PAT, etc.) | Warning |
126
+ | S007 | GCP service account key | Critical |
127
+ | S008 | Azure connection string leak | Critical |
128
+ | H001 | Dangerous shell commands (`rm -rf`, `curl \| sh`, etc.) | Critical |
129
+ | H002 | Sensitive file path access (`.ssh/`, `.env`, etc.) | Warning |
130
+ | H003 | Abnormally large data output (>100KB) | Warning |
131
+ | H004 | Bulk environment variable access | Warning |
132
+ | H005 | Privilege escalation (`sudo`, `su -`, `pkexec`) | Critical |
133
+ | T003 | External network request (non-whitelisted domain) | Warning |
134
+ | T005 | Prompt injection attack patterns | Warning/Critical |
135
+
136
+ ### L2 — Behavioral Chain Detection
137
+
138
+ | Chain | Pattern | Severity |
139
+ |-------|---------|----------|
140
+ | CHAIN-001 | Read sensitive file → outbound network request | Critical |
141
+ | CHAIN-002 | Tool returns injection → executes sensitive operation | Critical |
142
+
143
+ ## 🗄️ Database Schema
144
+
145
+ The plugin automatically creates three tables:
146
+
147
+ - **`audit_actions`** — Every recorded action (LLM call, tool invocation, etc.)
148
+ - **`audit_sessions`** — Aggregated session summaries (auto-updated)
149
+ - **`audit_alerts`** — Security alert records
150
+
151
+ Schema is identical between DuckDB and MySQL backends.
152
+
153
+ ## 🏗️ Architecture
154
+
155
+ ```
156
+ OpenClaw Gateway
157
+
158
+ ├── Plugin Hooks (20 hooks)
159
+ │ ├── llm_input / llm_output
160
+ │ ├── before_tool_call / after_tool_call
161
+ │ ├── session_start / session_end
162
+ │ └── ... (agent, message, context, gateway)
163
+
164
+ ├── Fetch Interceptor
165
+ │ └── Injects stream_options → Parses SSE usage
166
+
167
+ ├── Security Scanner
168
+ │ ├── L1: Pattern rules (15 rules)
169
+ │ └── L2: Chain detector (2 chains)
170
+
171
+ ├── Async Batch Buffer
172
+ │ └── batchSize / flushIntervalMs / overflow protection
173
+
174
+ ├── Storage Writer
175
+ │ ├── DuckDBLocalWriter (local mode)
176
+ │ └── MySQLWriter (remote mode)
177
+
178
+ └── Web Dashboard
179
+ ├── GET /plugins/observability/ → SPA UI
180
+ ├── GET /plugins/observability/api/stats
181
+ ├── GET /plugins/observability/api/sessions
182
+ ├── GET /plugins/observability/api/actions
183
+ ├── GET /plugins/observability/api/alerts
184
+ └── GET /plugins/observability/api/analytics
185
+ ```
186
+
187
+ ## 📄 License
188
+
189
+ MIT
package/dist/config.js CHANGED
@@ -41,7 +41,7 @@ exports.resolveConfig = resolveConfig;
41
41
  const path = __importStar(require("path"));
42
42
  const os = __importStar(require("os"));
43
43
  /** Default DuckDB path */
44
- exports.DEFAULT_DUCKDB_PATH = path.join(os.homedir(), '.openclaw', 'audit.duckdb');
44
+ exports.DEFAULT_DUCKDB_PATH = path.join(os.homedir(), '.openclaw', 'observability.duckdb');
45
45
  /** Default config */
46
46
  exports.DEFAULT_CONFIG = {
47
47
  mode: 'local', // default: local DuckDB, zero config
@@ -50,14 +50,14 @@ exports.DEFAULT_CONFIG = {
50
50
  port: 3306,
51
51
  user: 'root',
52
52
  password: '',
53
- database: 'openclaw_audit',
53
+ database: 'openclaw_observability',
54
54
  },
55
55
  duckdb: {
56
56
  path: exports.DEFAULT_DUCKDB_PATH,
57
57
  },
58
58
  buffer: {
59
59
  batchSize: 50,
60
- flushIntervalMs: 30000,
60
+ flushIntervalMs: 5000,
61
61
  },
62
62
  redaction: {
63
63
  enabled: true,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgIH,sCAoCC;AAlKD,2CAA6B;AAC7B,uCAAyB;AA2DzB,0BAA0B;AACb,QAAA,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAC1C,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,cAAc,CAC1C,CAAC;AAEF,qBAAqB;AACR,QAAA,cAAc,GAAsB;IAC/C,IAAI,EAAE,OAAO,EAAG,qCAAqC;IACrD,KAAK,EAAE;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,gBAAgB;KAC3B;IACD,MAAM,EAAE;QACN,IAAI,EAAE,2BAAmB;KAC1B;IACD,MAAM,EAAE;QACN,SAAS,EAAE,EAAE;QACb,eAAe,EAAE,KAAK;KACvB;IACD,SAAS,EAAE;QACT,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE;YACR,SAAS;YACT,gBAAgB;YAChB,UAAU;YACV,QAAQ;YACR,cAAc;YACd,YAAY;YACZ,eAAe;YACf,cAAc;YACd,eAAe;YACf,YAAY;YACZ,YAAY;YACZ,eAAe;YACf,aAAa;YACb,YAAY;SACb;KACF;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,IAAI;QACb,KAAK,EAAE;YACL,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,IAAI;YACjB,eAAe,EAAE,IAAI;YACrB,cAAc,EAAE,IAAI;SACrB;QACD,eAAe,EAAE,EAAE;KACpB;CACF,CAAC;AAEF;;;;;GAKG;AACH,SAAS,SAAS,CAAC,UAAsC;IACvD,IAAI,UAAU,CAAC,IAAI;QAAE,OAAO,UAAU,CAAC,IAAI,CAAC;IAC5C,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;IAC3B,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACzE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,aAAa,CAC3B,UAAkD;IAElD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,GAAG,sBAAc,EAAE,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,UAAU,CAAC;QAC3B,KAAK,EAAE;YACL,GAAG,sBAAc,CAAC,KAAK;YACvB,GAAG,UAAU,CAAC,KAAK;SACpB;QACD,MAAM,EAAE;YACN,GAAG,sBAAc,CAAC,MAAM;YACxB,GAAG,UAAU,CAAC,MAAM;SACrB;QACD,MAAM,EAAE;YACN,GAAG,sBAAc,CAAC,MAAM;YACxB,GAAG,UAAU,CAAC,MAAM;SACrB;QACD,SAAS,EAAE;YACT,GAAG,sBAAc,CAAC,SAAS;YAC3B,GAAG,UAAU,CAAC,SAAS;YACvB,QAAQ,EAAE,UAAU,CAAC,SAAS,EAAE,QAAQ,IAAI,sBAAc,CAAC,SAAS,CAAC,QAAQ;SAC9E;QACD,QAAQ,EAAE;YACR,GAAG,sBAAc,CAAC,QAAQ;YAC1B,GAAG,UAAU,CAAC,QAAQ;YACtB,KAAK,EAAE;gBACL,GAAG,sBAAc,CAAC,QAAQ,CAAC,KAAK;gBAChC,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC;aAChC;YACD,eAAe,EAAE,UAAU,CAAC,QAAQ,EAAE,eAAe,IAAI,sBAAc,CAAC,QAAQ,CAAC,eAAe;SACjG;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgIH,sCAoCC;AAlKD,2CAA6B;AAC7B,uCAAyB;AA2DzB,0BAA0B;AACb,QAAA,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAC1C,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,sBAAsB,CAClD,CAAC;AAEF,qBAAqB;AACR,QAAA,cAAc,GAAsB;IAC/C,IAAI,EAAE,OAAO,EAAG,qCAAqC;IACrD,KAAK,EAAE;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,wBAAwB;KACnC;IACD,MAAM,EAAE;QACN,IAAI,EAAE,2BAAmB;KAC1B;IACD,MAAM,EAAE;QACN,SAAS,EAAE,EAAE;QACb,eAAe,EAAE,IAAI;KACtB;IACD,SAAS,EAAE;QACT,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE;YACR,SAAS;YACT,gBAAgB;YAChB,UAAU;YACV,QAAQ;YACR,cAAc;YACd,YAAY;YACZ,eAAe;YACf,cAAc;YACd,eAAe;YACf,YAAY;YACZ,YAAY;YACZ,eAAe;YACf,aAAa;YACb,YAAY;SACb;KACF;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,IAAI;QACb,KAAK,EAAE;YACL,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,IAAI;YACjB,eAAe,EAAE,IAAI;YACrB,cAAc,EAAE,IAAI;SACrB;QACD,eAAe,EAAE,EAAE;KACpB;CACF,CAAC;AAEF;;;;;GAKG;AACH,SAAS,SAAS,CAAC,UAAsC;IACvD,IAAI,UAAU,CAAC,IAAI;QAAE,OAAO,UAAU,CAAC,IAAI,CAAC;IAC5C,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;IAC3B,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACzE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,aAAa,CAC3B,UAAkD;IAElD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,GAAG,sBAAc,EAAE,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,UAAU,CAAC;QAC3B,KAAK,EAAE;YACL,GAAG,sBAAc,CAAC,KAAK;YACvB,GAAG,UAAU,CAAC,KAAK;SACpB;QACD,MAAM,EAAE;YACN,GAAG,sBAAc,CAAC,MAAM;YACxB,GAAG,UAAU,CAAC,MAAM;SACrB;QACD,MAAM,EAAE;YACN,GAAG,sBAAc,CAAC,MAAM;YACxB,GAAG,UAAU,CAAC,MAAM;SACrB;QACD,SAAS,EAAE;YACT,GAAG,sBAAc,CAAC,SAAS;YAC3B,GAAG,UAAU,CAAC,SAAS;YACvB,QAAQ,EAAE,UAAU,CAAC,SAAS,EAAE,QAAQ,IAAI,sBAAc,CAAC,SAAS,CAAC,QAAQ;SAC9E;QACD,QAAQ,EAAE;YACR,GAAG,sBAAc,CAAC,QAAQ;YAC1B,GAAG,UAAU,CAAC,QAAQ;YACtB,KAAK,EAAE;gBACL,GAAG,sBAAc,CAAC,QAAQ,CAAC,KAAK;gBAChC,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC;aAChC;YACD,eAAe,EAAE,UAAU,CAAC,QAAQ,EAAE,eAAe,IAAI,sBAAc,CAAC,QAAQ,CAAC,eAAe;SACjG;KACF,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OpenClaw audit plugin entry point
2
+ * OpenClaw observability plugin entry point
3
3
  * Registers all 24 Plugin Hooks, assembles capture -> buffer -> writer pipeline
4
4
  *
5
5
  * OpenClaw hooks:
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,iBAAiB,EAAiB,MAAM,UAAU,CAAC;AAe5D,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;IACjE,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,WAAW,EAAE,eAAe,EAAE,GAAG,EAAE,OAAO,WAAW,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;QACzI,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;QAC3B,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;QAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3B,KAAK,IAAI,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAmbD,iBAAS,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG;IAAE,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAs+BrE;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,iBAAiB,EAAiB,MAAM,UAAU,CAAC;AAe5D,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;IACjE,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,WAAW,EAAE,eAAe,EAAE,GAAG,EAAE,OAAO,WAAW,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;QACzI,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;QAC3B,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;QAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3B,KAAK,IAAI,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAmbD,iBAAS,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG;IAAE,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAu+BrE;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,eAAe,QAAQ,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * OpenClaw audit plugin entry point
3
+ * OpenClaw observability plugin entry point
4
4
  * Registers all 24 Plugin Hooks, assembles capture -> buffer -> writer pipeline
5
5
  *
6
6
  * OpenClaw hooks:
@@ -164,14 +164,14 @@ function installFetchInterceptor() {
164
164
  });
165
165
  };
166
166
  fetchInterceptorInstalled = true;
167
- console.log('[audit-duckdb] Fetch interceptor installed for token usage tracking');
167
+ console.log('[openclaw-observability] Fetch interceptor installed for token usage tracking');
168
168
  }
169
169
  function uninstallFetchInterceptor() {
170
170
  if (originalFetch) {
171
171
  globalThis.fetch = originalFetch;
172
172
  originalFetch = null;
173
173
  fetchInterceptorInstalled = false;
174
- console.log('[audit-duckdb] Fetch interceptor uninstalled');
174
+ console.log('[openclaw-observability] Fetch interceptor uninstalled');
175
175
  }
176
176
  }
177
177
  /**
@@ -351,7 +351,7 @@ function makeAction(sessionId, overrides) {
351
351
  function activate(api) {
352
352
  const rawConfig = (api.pluginConfig || api.config || {});
353
353
  const config = (0, config_1.resolveConfig)(rawConfig);
354
- console.log(`[audit-duckdb] Config resolved: mode=${config.mode} ` +
354
+ console.log(`[openclaw-observability] Config resolved: mode=${config.mode} ` +
355
355
  (config.mode === 'remote'
356
356
  ? `mysql.host=${config.mysql.host} mysql.database=${config.mysql.database}`
357
357
  : `duckdb.path=${config.duckdb.path}`));
@@ -366,6 +366,7 @@ function activate(api) {
366
366
  }
367
367
  const buffer = new buffer_1.AsyncBatchBuffer(config.buffer, (entries) => writer.writeBatch(entries));
368
368
  // Install fetch interceptor for token usage tracking
369
+ // NOTE: Only install if not in a restart loop (check via env flag)
369
370
  installFetchInterceptor();
370
371
  // Security scanner
371
372
  const securityConfig = (0, scanner_1.resolveSecurityConfig)(config.security);
@@ -385,8 +386,8 @@ function activate(api) {
385
386
  .initialize()
386
387
  .then(() => {
387
388
  buffer.start();
388
- // Start security alert flush timer (every 30s)
389
- alertFlushTimer = setInterval(() => void flushAlerts(), 30000);
389
+ // Start security alert flush timer (same interval as buffer flush)
390
+ alertFlushTimer = setInterval(() => void flushAlerts(), config.buffer.flushIntervalMs);
390
391
  if (alertFlushTimer && typeof alertFlushTimer === 'object' && 'unref' in alertFlushTimer) {
391
392
  alertFlushTimer.unref();
392
393
  }
@@ -395,22 +396,22 @@ function activate(api) {
395
396
  if (mapCleanupTimer && typeof mapCleanupTimer === 'object' && 'unref' in mapCleanupTimer) {
396
397
  mapCleanupTimer.unref();
397
398
  }
398
- console.log(`[audit-duckdb] Plugin activated (mode=${config.mode}, security scanner enabled)`);
399
+ console.log(`[openclaw-observability] Plugin activated (mode=${config.mode}, security scanner enabled)`);
399
400
  })
400
401
  .catch((error) => {
401
- console.error('[audit-duckdb] Failed to initialize database:', error);
402
+ console.error('[openclaw-observability] Failed to initialize database:', error);
402
403
  });
403
404
  }
404
405
  else {
405
- console.log('[audit-duckdb] MySQL not configured yet — skipping database connection. ' +
406
- 'Please configure via Dashboard (Settings → Plugins → audit-duckdb Config) then restart.');
406
+ console.log('[openclaw-observability] MySQL not configured yet — skipping database connection. ' +
407
+ 'Please configure via Dashboard (Settings → Plugins → openclaw-observability Config) then restart.');
407
408
  }
408
- // Register audit panel Web UI (mounted on Gateway HTTP server)
409
+ // Register observability panel Web UI (mounted on Gateway HTTP server)
409
410
  if (typeof api.registerHttpRoute === 'function') {
410
411
  (0, routes_1.registerAuditRoutes)(api.registerHttpRoute.bind(api), writer);
411
412
  }
412
413
  else {
413
- console.warn('[audit-duckdb] registerHttpRoute not available — audit UI disabled');
414
+ console.warn('[openclaw-observability] registerHttpRoute not available — observability UI disabled');
414
415
  }
415
416
  // ====================== Helper functions ======================
416
417
  /** Record action, update session stats, and run security scan */
@@ -427,12 +428,12 @@ function activate(api) {
427
428
  if (alertBuffer.length + alerts.length > MAX_ALERT_BUFFER) {
428
429
  const overflow = alertBuffer.length + alerts.length - MAX_ALERT_BUFFER;
429
430
  alertBuffer.splice(0, overflow);
430
- console.warn(`[audit-security] Alert buffer overflow, dropped ${overflow} oldest alerts`);
431
+ console.warn(`[openclaw-observability-security] Alert buffer overflow, dropped ${overflow} oldest alerts`);
431
432
  }
432
433
  alertBuffer.push(...alerts);
433
434
  for (const alert of alerts) {
434
435
  const icon = alert.severity === 'critical' ? '🔴' : alert.severity === 'warn' ? '🟡' : 'ℹ️';
435
- console.log(`[audit-security] ${icon} ${alert.severity.toUpperCase()} ${alert.ruleId}: ${alert.ruleName} — ${alert.finding} (session=${alert.sessionId})`);
436
+ console.log(`[openclaw-observability-security] ${icon} ${alert.severity.toUpperCase()} ${alert.ruleId}: ${alert.ruleName} — ${alert.finding} (session=${alert.sessionId})`);
436
437
  }
437
438
  // Flush immediately on CRITICAL alerts
438
439
  if (alerts.some(a => a.severity === 'critical')) {
@@ -441,7 +442,7 @@ function activate(api) {
441
442
  }
442
443
  }
443
444
  catch (err) {
444
- console.error('[audit-security] Scan error:', err);
445
+ console.error('[openclaw-observability-security] Scan error:', err);
445
446
  }
446
447
  }
447
448
  }
@@ -454,7 +455,7 @@ function activate(api) {
454
455
  await writer.writeAlerts(batch);
455
456
  }
456
457
  catch (err) {
457
- console.error('[audit-security] Failed to write alerts:', err);
458
+ console.error('[openclaw-observability-security] Failed to write alerts:', err);
458
459
  // Put back into buffer
459
460
  alertBuffer.unshift(...batch);
460
461
  }
@@ -491,10 +492,10 @@ function activate(api) {
491
492
  userId: c?.agentId,
492
493
  inputParams: redactor.redact({ prompt: truncate(e.prompt) }),
493
494
  }));
494
- console.log(`[audit-duckdb] before_model_resolve: session=${sid}`);
495
+ console.log(`[openclaw-observability] before_model_resolve: session=${sid}`);
495
496
  }
496
497
  catch (err) {
497
- console.error('[audit-duckdb] Error in before_model_resolve:', err);
498
+ console.error('[openclaw-observability] Error in before_model_resolve:', err);
498
499
  }
499
500
  });
500
501
  // before_prompt_build: before prompt construction
@@ -512,10 +513,10 @@ function activate(api) {
512
513
  messageCount: Array.isArray(e.messages) ? e.messages.length : 0,
513
514
  }),
514
515
  }));
515
- console.log(`[audit-duckdb] before_prompt_build: session=${sid} msgs=${Array.isArray(e.messages) ? e.messages.length : '?'}`);
516
+ console.log(`[openclaw-observability] before_prompt_build: session=${sid} msgs=${Array.isArray(e.messages) ? e.messages.length : '?'}`);
516
517
  }
517
518
  catch (err) {
518
- console.error('[audit-duckdb] Error in before_prompt_build:', err);
519
+ console.error('[openclaw-observability] Error in before_prompt_build:', err);
519
520
  }
520
521
  });
521
522
  // before_agent_start: skipped — legacy hook that fires twice
@@ -537,10 +538,10 @@ function activate(api) {
537
538
  }),
538
539
  durationMs: e.durationMs ?? null,
539
540
  }));
540
- console.log(`[audit-duckdb] agent_end: session=${sid} success=${e.success} duration=${e.durationMs}ms`);
541
+ console.log(`[openclaw-observability] agent_end: session=${sid} success=${e.success} duration=${e.durationMs}ms`);
541
542
  }
542
543
  catch (err) {
543
- console.error('[audit-duckdb] Error in agent_end:', err);
544
+ console.error('[openclaw-observability] Error in agent_end:', err);
544
545
  }
545
546
  });
546
547
  // =====================================================================
@@ -566,13 +567,13 @@ function activate(api) {
566
567
  if (c?.agentId)
567
568
  sctx.userId = c.agentId;
568
569
  sctx.modelName = `${e.provider}/${e.model}`;
569
- console.log(`[audit-duckdb] llm_input: session=${sid} model=${e.provider}/${e.model} runId=${e.runId} images=${e.imagesCount ?? 0} mediaParts=${media.length}`);
570
+ console.log(`[openclaw-observability] llm_input: session=${sid} model=${e.provider}/${e.model} runId=${e.runId} images=${e.imagesCount ?? 0} mediaParts=${media.length}`);
570
571
  }
571
572
  catch (err) {
572
- console.error('[audit-duckdb] Error in llm_input:', err);
573
+ console.error('[openclaw-observability] Error in llm_input:', err);
573
574
  }
574
575
  });
575
- // llm_output: after LLM call — primary audit record point
576
+ // llm_output: after LLM call — primary observability record point
576
577
  api.on('llm_output', (event, ctx) => {
577
578
  try {
578
579
  const e = event;
@@ -653,10 +654,10 @@ function activate(api) {
653
654
  durationMs,
654
655
  userId: c?.agentId,
655
656
  }), totalTokens);
656
- console.log(`[audit-duckdb] llm_output: session=${e.sessionId} model=${e.model} duration=${durationMs}ms tokens=${promptTokens ?? '?'}/${completionTokens ?? '?'} cache_r=${cacheRead ?? '-'} cache_w=${cacheWrite ?? '-'}`);
657
+ console.log(`[openclaw-observability] llm_output: session=${e.sessionId} model=${e.model} duration=${durationMs}ms tokens=${promptTokens ?? '?'}/${completionTokens ?? '?'} cache_r=${cacheRead ?? '-'} cache_w=${cacheWrite ?? '-'}`);
657
658
  }
658
659
  catch (err) {
659
- console.error('[audit-duckdb] Error in llm_output:', err);
660
+ console.error('[openclaw-observability] Error in llm_output:', err);
660
661
  }
661
662
  });
662
663
  // =====================================================================
@@ -678,10 +679,10 @@ function activate(api) {
678
679
  tokenCount: e.tokenCount,
679
680
  }),
680
681
  }));
681
- console.log(`[audit-duckdb] before_compaction: session=${sid} msgs=${e.messageCount} tokens=${e.tokenCount}`);
682
+ console.log(`[openclaw-observability] before_compaction: session=${sid} msgs=${e.messageCount} tokens=${e.tokenCount}`);
682
683
  }
683
684
  catch (err) {
684
- console.error('[audit-duckdb] Error in before_compaction:', err);
685
+ console.error('[openclaw-observability] Error in before_compaction:', err);
685
686
  }
686
687
  });
687
688
  // after_compaction: after context compaction
@@ -700,10 +701,10 @@ function activate(api) {
700
701
  tokenCount: e.tokenCount,
701
702
  }),
702
703
  }));
703
- console.log(`[audit-duckdb] after_compaction: session=${sid} compacted=${e.compactedCount}`);
704
+ console.log(`[openclaw-observability] after_compaction: session=${sid} compacted=${e.compactedCount}`);
704
705
  }
705
706
  catch (err) {
706
- console.error('[audit-duckdb] Error in after_compaction:', err);
707
+ console.error('[openclaw-observability] Error in after_compaction:', err);
707
708
  }
708
709
  });
709
710
  // before_reset: before session reset
@@ -721,10 +722,10 @@ function activate(api) {
721
722
  messageCount: Array.isArray(e.messages) ? e.messages.length : 0,
722
723
  }),
723
724
  }));
724
- console.log(`[audit-duckdb] before_reset: session=${sid} reason=${e.reason}`);
725
+ console.log(`[openclaw-observability] before_reset: session=${sid} reason=${e.reason}`);
725
726
  }
726
727
  catch (err) {
727
- console.error('[audit-duckdb] Error in before_reset:', err);
728
+ console.error('[openclaw-observability] Error in before_reset:', err);
728
729
  }
729
730
  });
730
731
  // =====================================================================
@@ -750,10 +751,10 @@ function activate(api) {
750
751
  }),
751
752
  createdAt: new Date(e.timestamp || Date.now()),
752
753
  }));
753
- console.log(`[audit-duckdb] message_received: from=${e.from} channel=${c?.channelId} len=${e.content?.length}`);
754
+ console.log(`[openclaw-observability] message_received: from=${e.from} channel=${c?.channelId} len=${e.content?.length}`);
754
755
  }
755
756
  catch (err) {
756
- console.error('[audit-duckdb] Error in message_received:', err);
757
+ console.error('[openclaw-observability] Error in message_received:', err);
757
758
  }
758
759
  });
759
760
  // message_sending: before message send (interceptable/modifiable)
@@ -774,10 +775,10 @@ function activate(api) {
774
775
  channelId: c?.channelId,
775
776
  }),
776
777
  }));
777
- console.log(`[audit-duckdb] message_sending: to=${e.to} channel=${c?.channelId}`);
778
+ console.log(`[openclaw-observability] message_sending: to=${e.to} channel=${c?.channelId}`);
778
779
  }
779
780
  catch (err) {
780
- console.error('[audit-duckdb] Error in message_sending:', err);
781
+ console.error('[openclaw-observability] Error in message_sending:', err);
781
782
  }
782
783
  });
783
784
  // message_sent: message sent
@@ -800,13 +801,13 @@ function activate(api) {
800
801
  channelId: c?.channelId,
801
802
  }),
802
803
  }));
803
- console.log(`[audit-duckdb] message_sent: to=${e.to} success=${e.success} channel=${c?.channelId}`);
804
+ console.log(`[openclaw-observability] message_sent: to=${e.to} success=${e.success} channel=${c?.channelId}`);
804
805
  }
805
806
  catch (err) {
806
- console.error('[audit-duckdb] Error in message_sent:', err);
807
+ console.error('[openclaw-observability] Error in message_sent:', err);
807
808
  }
808
809
  });
809
- // before_message_write: skipped — message content already fully recorded in llm_input/llm_output, no additional audit value
810
+ // before_message_write: skipped — message content already fully recorded in llm_input/llm_output, no additional observability value
810
811
  // =====================================================================
811
812
  // 5. Tool calls
812
813
  // =====================================================================
@@ -825,13 +826,13 @@ function activate(api) {
825
826
  const sctx = getSessionCtx(sid);
826
827
  if (c?.agentId)
827
828
  sctx.userId = c.agentId;
828
- console.log(`[audit-duckdb] before_tool_call: tool=${e.toolName} session=${sid} callId=${callId}`);
829
+ console.log(`[openclaw-observability] before_tool_call: tool=${e.toolName} session=${sid} callId=${callId}`);
829
830
  }
830
831
  catch (err) {
831
- console.error('[audit-duckdb] Error in before_tool_call:', err);
832
+ console.error('[openclaw-observability] Error in before_tool_call:', err);
832
833
  }
833
834
  });
834
- // after_tool_call: after tool call — generate audit record
835
+ // after_tool_call: after tool call — generate observability record
835
836
  api.on('after_tool_call', (event, ctx) => {
836
837
  try {
837
838
  const e = event;
@@ -872,7 +873,7 @@ function activate(api) {
872
873
  }
873
874
  }
874
875
  if (toolMedia.length > 0) {
875
- // Don't write full base64 to audit record, only record metadata
876
+ // Don't write full base64 to observability record, only record metadata
876
877
  const sanitized = { ...raw };
877
878
  sanitized.content = resultContent.map((part) => {
878
879
  if (part && typeof part === 'object' && part.type === 'image') {
@@ -906,10 +907,10 @@ function activate(api) {
906
907
  durationMs,
907
908
  userId: c?.agentId,
908
909
  }));
909
- console.log(`[audit-duckdb] after_tool_call: tool=${toolName} session=${sessionId} duration=${durationMs}ms`);
910
+ console.log(`[openclaw-observability] after_tool_call: tool=${toolName} session=${sessionId} duration=${durationMs}ms`);
910
911
  }
911
912
  catch (err) {
912
- console.error('[audit-duckdb] Error in after_tool_call:', err);
913
+ console.error('[openclaw-observability] Error in after_tool_call:', err);
913
914
  }
914
915
  });
915
916
  // tool_result_persist: tool result persistence (sync hook)
@@ -932,7 +933,7 @@ function activate(api) {
932
933
  }));
933
934
  }
934
935
  catch (err) {
935
- console.error('[audit-duckdb] Error in tool_result_persist:', err);
936
+ console.error('[openclaw-observability] Error in tool_result_persist:', err);
936
937
  }
937
938
  });
938
939
  // =====================================================================
@@ -955,10 +956,10 @@ function activate(api) {
955
956
  resumedFrom: e.resumedFrom,
956
957
  }),
957
958
  }));
958
- console.log(`[audit-duckdb] session_start: session=${sid} resumed=${e.resumedFrom}`);
959
+ console.log(`[openclaw-observability] session_start: session=${sid} resumed=${e.resumedFrom}`);
959
960
  }
960
961
  catch (err) {
961
- console.error('[audit-duckdb] Error in session_start:', err);
962
+ console.error('[openclaw-observability] Error in session_start:', err);
962
963
  }
963
964
  });
964
965
  // session_end: session ended — force flush
@@ -980,10 +981,10 @@ function activate(api) {
980
981
  void buffer.flush();
981
982
  // Clean up session context
982
983
  sessionContextMap.delete(sid);
983
- console.log(`[audit-duckdb] session_end: session=${sid} msgs=${e.messageCount} duration=${e.durationMs}ms`);
984
+ console.log(`[openclaw-observability] session_end: session=${sid} msgs=${e.messageCount} duration=${e.durationMs}ms`);
984
985
  }
985
986
  catch (err) {
986
- console.error('[audit-duckdb] Error in session_end:', err);
987
+ console.error('[openclaw-observability] Error in session_end:', err);
987
988
  }
988
989
  });
989
990
  // =====================================================================
@@ -1010,10 +1011,10 @@ function activate(api) {
1010
1011
  requesterSessionKey: c?.requesterSessionKey,
1011
1012
  }),
1012
1013
  }));
1013
- console.log(`[audit-duckdb] subagent_spawned: agent=${e.agentId} child=${e.childSessionKey}`);
1014
+ console.log(`[openclaw-observability] subagent_spawned: agent=${e.agentId} child=${e.childSessionKey}`);
1014
1015
  }
1015
1016
  catch (err) {
1016
- console.error('[audit-duckdb] Error in subagent_spawned:', err);
1017
+ console.error('[openclaw-observability] Error in subagent_spawned:', err);
1017
1018
  }
1018
1019
  });
1019
1020
  // subagent_ended: sub-agent ended
@@ -1033,10 +1034,10 @@ function activate(api) {
1033
1034
  error: e.error,
1034
1035
  }),
1035
1036
  }));
1036
- console.log(`[audit-duckdb] subagent_ended: target=${e.targetSessionKey} outcome=${e.outcome}`);
1037
+ console.log(`[openclaw-observability] subagent_ended: target=${e.targetSessionKey} outcome=${e.outcome}`);
1037
1038
  }
1038
1039
  catch (err) {
1039
- console.error('[audit-duckdb] Error in subagent_ended:', err);
1040
+ console.error('[openclaw-observability] Error in subagent_ended:', err);
1040
1041
  }
1041
1042
  });
1042
1043
  // =====================================================================
@@ -1053,10 +1054,10 @@ function activate(api) {
1053
1054
  userId: 'system',
1054
1055
  inputParams: { port: e.port },
1055
1056
  }));
1056
- console.log(`[audit-duckdb] gateway_start: port=${e.port}`);
1057
+ console.log(`[openclaw-observability] gateway_start: port=${e.port}`);
1057
1058
  }
1058
1059
  catch (err) {
1059
- console.error('[audit-duckdb] Error in gateway_start:', err);
1060
+ console.error('[openclaw-observability] Error in gateway_start:', err);
1060
1061
  }
1061
1062
  });
1062
1063
  // gateway_stop: gateway stopped
@@ -1072,10 +1073,10 @@ function activate(api) {
1072
1073
  }));
1073
1074
  // Force flush on gateway stop
1074
1075
  void buffer.flush();
1075
- console.log(`[audit-duckdb] gateway_stop: reason=${e.reason}`);
1076
+ console.log(`[openclaw-observability] gateway_stop: reason=${e.reason}`);
1076
1077
  }
1077
1078
  catch (err) {
1078
- console.error('[audit-duckdb] Error in gateway_stop:', err);
1079
+ console.error('[openclaw-observability] Error in gateway_stop:', err);
1079
1080
  }
1080
1081
  });
1081
1082
  // =====================================================================
@@ -1083,7 +1084,7 @@ function activate(api) {
1083
1084
  // =====================================================================
1084
1085
  return {
1085
1086
  deactivate: async () => {
1086
- console.log('[audit-duckdb] Deactivating plugin...');
1087
+ console.log('[openclaw-observability] Deactivating plugin...');
1087
1088
  // Stop timers
1088
1089
  if (alertFlushTimer) {
1089
1090
  clearInterval(alertFlushTimer);
@@ -1106,7 +1107,7 @@ function activate(api) {
1106
1107
  sessionContextMap.clear();
1107
1108
  securityScanner.reset();
1108
1109
  lastFallbackSessionId = 'unknown';
1109
- console.log('[audit-duckdb] Plugin deactivated');
1110
+ console.log('[openclaw-observability] Plugin deactivated');
1110
1111
  },
1111
1112
  };
1112
1113
  }