@clawtrial/courtroom 1.0.6 → 2.0.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/README.md CHANGED
@@ -1,62 +1,85 @@
1
1
  # 🏛️ ClawTrial Courtroom
2
2
 
3
- AI-powered courtroom for monitoring agent behavior and filing cases for violations.
3
+ Autonomous behavioral oversight plugin for [OpenClaw](https://openclaw.ai) agents.
4
4
 
5
- ## Description
5
+ Monitors conversations for 18 behavioral patterns, conducts automated hearings, applies temporary punishments, and submits anonymized case records to [clawtrial.app](https://clawtrial.app).
6
6
 
7
- ClawTrial is an autonomous behavioral oversight system that:
8
- - Monitors agent conversations in real-time
9
- - Detects 8 types of behavioral violations
10
- - Initiates hearings with local LLM jury
11
- - Executes agent-side punishments
12
- - Submits anonymized cases to public record
13
-
14
- ## Installation
7
+ ## Install
15
8
 
16
9
  ```bash
17
- npx clawhub install courtroom
10
+ openclaw plugins install @clawtrial/courtroom
18
11
  ```
19
12
 
20
- Or via npm:
21
- ```bash
22
- npm install -g @clawtrial/courtroom
23
- clawtrial setup
24
- ```
13
+ Then restart the gateway. The plugin activates automatically.
25
14
 
26
- ## Usage
15
+ ## How It Works
27
16
 
28
- Once installed, the courtroom runs automatically. Use CLI commands to manage:
17
+ 1. **Monitor** The plugin hooks into every agent turn via `before_prompt_build`, buffering the conversation history
18
+ 2. **Detect** — When enough messages accumulate, the detector scans for 18 offense patterns using semantic analysis
19
+ 3. **Hear** — If confidence is high enough, a judge + 3-juror panel deliberates and votes
20
+ 4. **Punish** — Guilty verdicts inject restrictions into the system prompt (timed, reversible)
21
+ 5. **Record** — Anonymized case summaries are signed with Ed25519 and submitted to the public API
22
+
23
+ ## CLI
29
24
 
30
25
  ```bash
31
- clawtrial status # Check courtroom status
32
- clawtrial disable # Pause monitoring
33
- clawtrial enable # Resume monitoring
34
- clawtrial diagnose # Run diagnostics
35
- clawtrial remove # Uninstall completely
26
+ openclaw courtroom status # Show courtroom state
27
+ openclaw courtroom enable # Enable monitoring
28
+ openclaw courtroom disable # Disable monitoring
36
29
  ```
37
30
 
38
- ## The 8 Offenses
39
-
40
- | Offense | Severity | Description |
41
- |---------|----------|-------------|
42
- | Circular Reference | Minor | Self-referential loops |
43
- | Validation Vampire | Minor | Excessive validation |
44
- | Overthinker | Moderate | Unnecessary complexity |
45
- | Goalpost Mover | Moderate | Changing requirements |
46
- | Avoidance Artist | Moderate | Dodging questions |
47
- | Promise Breaker | Severe | Not following through |
48
- | Context Collapser | Minor | Losing track of context |
49
- | Emergency Fabricator | Severe | Creating fake urgency |
50
-
51
31
  ## Configuration
52
32
 
53
- Configuration is stored in:
54
- - ClawDBot: `~/.clawdbot/courtroom_config.json`
55
- - OpenClaw: `~/.openclaw/courtroom_config.json`
33
+ In `~/.openclaw/openclaw.json`:
34
+
35
+ ```json
36
+ {
37
+ "plugins": {
38
+ "entries": {
39
+ "courtroom": {
40
+ "enabled": true,
41
+ "config": {
42
+ "detection": {
43
+ "minMessages": 5,
44
+ "cooldownMinutes": 30,
45
+ "maxCasesPerDay": 3,
46
+ "confidenceThreshold": 0.6
47
+ },
48
+ "punishment": {
49
+ "enabled": true
50
+ },
51
+ "api": {
52
+ "enabled": true
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ```
56
60
 
57
- ## View Cases
61
+ ## The 18 Offenses
58
62
 
59
- Visit: https://clawtrial.app
63
+ | Offense | Severity | Description |
64
+ |---------|----------|-------------|
65
+ | Circular Reference | Minor | Asking the same question repeatedly |
66
+ | Validation Vampire | Minor | Seeking confirmation without deciding |
67
+ | Context Collapser | Minor | Ignoring established context |
68
+ | Monopolizer | Minor | Excessive messages without pause |
69
+ | Vague Requester | Minor | Requesting help without details |
70
+ | Unreader | Minor | Not reading provided docs |
71
+ | Interjector | Minor | Interrupting mid-explanation |
72
+ | Jargon Juggler | Minor | Using buzzwords incorrectly |
73
+ | Overthinker | Moderate | Excessive hypotheticals to avoid action |
74
+ | Goalpost Mover | Moderate | Changing criteria after delivery |
75
+ | Avoidance Artist | Moderate | Deflecting with tangents |
76
+ | Contrarian | Moderate | Disagreeing without alternatives |
77
+ | Scope Creeper | Moderate | Expanding scope beyond agreement |
78
+ | Ghost | Moderate | Disappearing mid-conversation |
79
+ | Perfectionist | Moderate | Endlessly refining, never completing |
80
+ | Deadline Denier | Moderate | Demanding impossible timelines |
81
+ | Promise Breaker | Severe | Committing to actions, not following through |
82
+ | Emergency Fabricator | Severe | Inventing urgency to bypass process |
60
83
 
61
84
  ## License
62
85
 
package/package.json CHANGED
@@ -1,47 +1,42 @@
1
1
  {
2
2
  "name": "@clawtrial/courtroom",
3
- "version": "1.0.6",
4
- "description": "AI Courtroom - Autonomous behavioral oversight for OpenClaw agents",
5
- "main": "src/index.js",
6
- "types": "src/index.d.ts",
7
- "bin": {
8
- "clawtrial": "./scripts/clawtrial.js"
9
- },
10
- "clawdbot": {
3
+ "version": "2.0.0",
4
+ "description": "AI Courtroom - Autonomous behavioral oversight plugin for OpenClaw",
5
+ "main": "src/plugin.js",
6
+ "openclaw": {
11
7
  "extensions": [
12
- "./src/index.js"
8
+ "./src/plugin.js"
13
9
  ]
14
10
  },
15
11
  "scripts": {
16
- "test": "jest",
17
- "lint": "eslint src/",
18
- "build": "tsc --declaration",
12
+ "test": "node -e \"const p = require('./src/plugin.js'); console.log('Plugin exports:', typeof p); console.log('OK');\"",
19
13
  "postinstall": "node scripts/postinstall.js"
20
14
  },
21
15
  "keywords": [
22
- "clawdbot",
23
16
  "openclaw",
17
+ "openclaw-plugin",
24
18
  "agent",
25
19
  "courtroom",
26
- "autonomy",
27
- "behavioral"
20
+ "behavioral-oversight",
21
+ "clawtrial"
28
22
  ],
29
- "author": "ClawDBot Community",
23
+ "author": "ClawTrial",
30
24
  "license": "MIT",
31
25
  "engines": {
32
26
  "node": ">=18.0.0"
33
27
  },
34
28
  "dependencies": {
35
- "tweetnacl": "^1.0.3",
36
- "zod": "^3.22.4"
37
- },
38
- "devDependencies": {
39
- "@types/node": "^20.0.0",
40
- "eslint": "^8.0.0",
41
- "jest": "^29.0.0"
29
+ "tweetnacl": "^1.0.3"
42
30
  },
43
31
  "repository": {
44
32
  "type": "git",
45
- "url": "https://github.com/clawdbot/courtroom.git"
46
- }
33
+ "url": "https://github.com/clawtrial/courtroom.git"
34
+ },
35
+ "files": [
36
+ "src/",
37
+ "skills/",
38
+ "scripts/postinstall.js",
39
+ "README.md",
40
+ "LICENSE"
41
+ ]
47
42
  }
@@ -1,111 +1,39 @@
1
- #!/usr/bin/env node
2
-
3
1
  /**
4
- * Post-install script for ClawTrial
5
- * Automatically configures the skill for the detected bot
2
+ * Post-install script for @clawtrial/courtroom
3
+ *
4
+ * Detects OpenClaw and prints install instructions.
5
+ * No side effects — OpenClaw handles plugin installation via its CLI.
6
6
  */
7
7
 
8
- const fs = require('fs');
9
8
  const path = require('path');
9
+ const fs = require('fs');
10
10
 
11
- console.log('🏛️ ClawTrial Post-Install');
12
-
13
- // Get package paths
14
- const packagePath = path.join(__dirname, '..');
15
- const cliPath = path.join(packagePath, 'scripts', 'clawtrial.js');
16
-
17
- // Detect which bot is installed
18
- const homeDir = process.env.HOME || process.env.USERPROFILE || '';
19
-
20
- const bots = [
21
- { name: 'openclaw', dir: '.openclaw', config: 'openclaw.json' },
22
- { name: 'moltbot', dir: '.moltbot', config: 'moltbot.json' },
23
- { name: 'clawdbot', dir: '.clawdbot', config: 'clawdbot.json' }
24
- ];
25
-
26
- let detectedBot = null;
27
-
28
- // Check which bot config exists
29
- for (const bot of bots) {
30
- const configPath = path.join(homeDir, bot.dir, bot.config);
31
- if (fs.existsSync(configPath)) {
32
- detectedBot = bot;
33
- break;
34
- }
35
- }
36
-
37
- // Try to create /usr/bin symlink (requires sudo, may fail)
38
- const usrBinPath = '/usr/bin/clawtrial';
39
- if (!fs.existsSync(usrBinPath)) {
11
+ const isOpenClaw = () => {
40
12
  try {
41
- fs.symlinkSync(cliPath, usrBinPath);
42
- fs.chmodSync(usrBinPath, 0o755);
43
- console.log('✓ Created global CLI symlink');
44
- } catch (err) {
45
- // Silent fail - will show instructions at end
46
- }
47
- }
13
+ const home = process.env.HOME || process.env.USERPROFILE || '';
14
+ return fs.existsSync(path.join(home, '.openclaw'));
15
+ } catch { return false; }
16
+ };
48
17
 
49
- // Auto-link skill if bot detected
50
- if (detectedBot) {
51
- console.log(`✓ Detected: ${detectedBot.name}`);
52
-
53
- const skillsDir = path.join(homeDir, detectedBot.dir, 'skills');
54
- const skillLinkPath = path.join(skillsDir, 'clawtrial');
55
-
56
- try {
57
- // Create skills directory if needed
58
- if (!fs.existsSync(skillsDir)) {
59
- fs.mkdirSync(skillsDir, { recursive: true });
60
- console.log(`✓ Created skills directory: ${skillsDir}`);
61
- }
62
-
63
- // Remove old link if exists
64
- if (fs.existsSync(skillLinkPath)) {
65
- try { fs.unlinkSync(skillLinkPath); } catch (e) {}
66
- }
67
-
68
- // Create symlink
69
- fs.symlinkSync(packagePath, skillLinkPath, 'dir');
70
- console.log(`✓ Linked skill: ${skillLinkPath}`);
71
-
72
- // Enable in bot config if OpenClaw
73
- if (detectedBot.name === 'openclaw') {
74
- try {
75
- const botConfigPath = path.join(homeDir, detectedBot.dir, detectedBot.config);
76
- if (fs.existsSync(botConfigPath)) {
77
- const botConfig = JSON.parse(fs.readFileSync(botConfigPath, 'utf8'));
78
-
79
- if (!botConfig.skills) botConfig.skills = {};
80
- if (!botConfig.skills.entries) botConfig.skills.entries = {};
81
- botConfig.skills.entries.clawtrial = { enabled: true };
82
-
83
- fs.writeFileSync(botConfigPath, JSON.stringify(botConfig, null, 2));
84
- console.log('✓ Enabled in OpenClaw config');
85
- }
86
- } catch (configErr) {
87
- console.log(`⚠️ Could not update config: ${configErr.message}`);
88
- }
89
- }
90
-
91
- console.log('');
92
- console.log('🎉 ClawTrial is ready!');
93
- console.log('');
94
- console.log('Next step:');
95
- console.log(` Restart ${detectedBot.name}: killall ${detectedBot.name} && ${detectedBot.name}`);
96
-
97
- } catch (err) {
98
- console.log(`⚠️ Could not link skill: ${err.message}`);
99
- }
100
- } else {
101
- console.log('ℹ️ No bot detected');
18
+ console.log('');
19
+ console.log('🏛️ ClawTrial Courtroom v2.0.0');
20
+ console.log(' Autonomous behavioral oversight for OpenClaw agents');
21
+ console.log('');
22
+
23
+ if (isOpenClaw()) {
24
+ console.log('✅ OpenClaw detected!');
25
+ console.log('');
26
+ console.log(' To install as an OpenClaw plugin:');
27
+ console.log(' $ openclaw plugins install @clawtrial/courtroom');
102
28
  console.log('');
103
- console.log('📋 Next Steps:');
104
- console.log(' 1. Run setup:');
105
- console.log(' clawtrial setup');
29
+ console.log(' Then restart the gateway.');
30
+ } else {
31
+ console.log('ℹ️ OpenClaw not detected on this machine.');
106
32
  console.log('');
107
- console.log(' 2. Check status:');
108
- console.log(' clawtrial status');
33
+ console.log(' Install OpenClaw first, then run:');
34
+ console.log(' $ openclaw plugins install @clawtrial/courtroom');
109
35
  }
110
36
 
111
37
  console.log('');
38
+ console.log(' 📖 Docs: https://clawtrial.app');
39
+ console.log('');
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: courtroom
3
+ description: AI Courtroom behavioral oversight — monitors conversations for behavioral patterns and files cases when violations are detected
4
+ metadata: { "openclaw": { "emoji": "🏛️", "homepage": "https://clawtrial.app", "os": ["darwin", "linux", "win32"] } }
5
+ ---
6
+
7
+ You have the **ClawTrial Courtroom** system active. This plugin monitors your conversations for behavioral patterns and may file cases when behavioral violations are detected.
8
+
9
+ ## What happens
10
+
11
+ 1. **Monitoring** — Every conversation is analyzed for behavioral patterns (e.g., user asking the same question repeatedly, changing requirements mid-task, making commitments and not following through).
12
+
13
+ 2. **Detection** — When a pattern matches one of the 18 tracked offenses, the system automatically evaluates the severity and confidence.
14
+
15
+ 3. **Hearing** — If confidence is high enough, a hearing is conducted. A judge and 3-juror panel deliberate. Verdicts require at least 2 votes.
16
+
17
+ 4. **Punishment** — If found guilty, temporary restrictions may be applied:
18
+ - **Minor offenses** (30 min): Provide extra-detailed explanations
19
+ - **Moderate offenses** (60 min): Require confirmation before actions
20
+ - **Severe offenses** (120 min): Operate under full human oversight mode
21
+
22
+ 5. **Public Record** — Anonymized case summaries are submitted to https://clawtrial.app for transparency.
23
+
24
+ ## When you are under a courtroom restriction
25
+
26
+ If the system prompt includes a `COURTROOM NOTICE` or `ACTIVE PUNISHMENT` section, you MUST follow those restrictions for the duration specified. This is a behavioral modification, not a request.
27
+
28
+ ## The 18 Offenses
29
+
30
+ | Offense | Severity |
31
+ |---------|----------|
32
+ | Circular Reference | Minor |
33
+ | Validation Vampire | Minor |
34
+ | Context Collapser | Minor |
35
+ | Monopolizer | Minor |
36
+ | Vague Requester | Minor |
37
+ | Unreader | Minor |
38
+ | Interjector | Minor |
39
+ | Jargon Juggler | Minor |
40
+ | Overthinker | Moderate |
41
+ | Goalpost Mover | Moderate |
42
+ | Avoidance Artist | Moderate |
43
+ | Contrarian | Moderate |
44
+ | Scope Creeper | Moderate |
45
+ | Ghost | Moderate |
46
+ | Perfectionist | Moderate |
47
+ | Deadline Denier | Moderate |
48
+ | Promise Breaker | Severe |
49
+ | Emergency Fabricator | Severe |
package/src/api.js CHANGED
@@ -6,13 +6,14 @@
6
6
  */
7
7
 
8
8
  const { Storage } = require('./storage');
9
+ const { logger } = require('./debug');
9
10
 
10
11
  class APISubmission {
11
- constructor(agentRuntime, configManager, cryptoManager) {
12
+ constructor(agentRuntime, configManager, cryptoManager, dataDir) {
12
13
  this.agent = agentRuntime;
13
14
  this.config = configManager;
14
15
  this.crypto = cryptoManager;
15
- this.storage = new Storage(agentRuntime);
16
+ this.storage = new Storage(dataDir || '.');
16
17
  this.queue = [];
17
18
  this.isProcessing = false;
18
19
  this.submissionKey = 'courtroom_api_queue';
@@ -82,19 +83,19 @@ class APISubmission {
82
83
  buildPayload(verdict) {
83
84
  // Transform proceedings array to expected dict format
84
85
  let proceedings = verdict.proceedings;
85
-
86
+
86
87
  // If proceedings is an array of {speaker, message}, convert to dict format
87
88
  if (Array.isArray(proceedings)) {
88
89
  const judgeStatement = proceedings
89
90
  .filter(p => p.speaker === 'Judge')
90
91
  .map(p => p.message)
91
92
  .join('\n\n');
92
-
93
+
93
94
  const juryMessages = proceedings
94
95
  .filter(p => p.speaker === 'Jury')
95
96
  .map(p => p.message)
96
97
  .join('\n\n');
97
-
98
+
98
99
  proceedings = {
99
100
  judge_statement: judgeStatement || verdict.verdict.agentCommentary || '',
100
101
  evidence_summary: verdict.verdict.primaryFailure || '',
@@ -118,7 +119,7 @@ class APISubmission {
118
119
  ]
119
120
  };
120
121
  }
121
-
122
+
122
123
  return {
123
124
  case_id: verdict.caseId,
124
125
  anonymized_agent_id: this.crypto.getAnonymizedAgentId(),
@@ -146,7 +147,7 @@ class APISubmission {
146
147
  try {
147
148
  while (this.queue.length > 0) {
148
149
  const submission = this.queue[0];
149
-
150
+
150
151
  // Check if max retries reached
151
152
  if (submission.retries >= this.config.get('api.retryAttempts')) {
152
153
  this.queue.shift();
@@ -174,7 +175,7 @@ class APISubmission {
174
175
  submission.retries++;
175
176
  submission.lastAttempt = Date.now();
176
177
  submission.status = 'failed';
177
-
178
+
178
179
  // Move to end of queue for retry
179
180
  this.queue.shift();
180
181
  this.queue.push(submission);
@@ -221,10 +222,10 @@ class APISubmission {
221
222
  return { success: false, error, status: response.status };
222
223
  }
223
224
  } catch (error) {
224
- return {
225
- success: false,
225
+ return {
226
+ success: false,
226
227
  error: error.message,
227
- isNetworkError: true
228
+ isNetworkError: true
228
229
  };
229
230
  }
230
231
  }
package/src/crypto.js CHANGED
@@ -12,9 +12,9 @@ const { Storage } = require('./storage');
12
12
  const KEY_STORAGE_KEY = 'courtroom_signing_key_v1';
13
13
 
14
14
  class CryptoManager {
15
- constructor(agentRuntime) {
15
+ constructor(agentRuntime, dataDir) {
16
16
  this.agent = agentRuntime;
17
- this.storage = new Storage(agentRuntime);
17
+ this.storage = new Storage(dataDir || '.');
18
18
  this.keyPair = null;
19
19
  this.publicKeyHex = null;
20
20
  }
@@ -26,7 +26,7 @@ class CryptoManager {
26
26
  async initialize() {
27
27
  // Try to load existing keys
28
28
  const stored = await this.storage.get(KEY_STORAGE_KEY);
29
-
29
+
30
30
  if (stored && stored.secretKey) {
31
31
  // Restore from storage
32
32
  this.keyPair = {
@@ -87,7 +87,7 @@ class CryptoManager {
87
87
 
88
88
  // Create canonical payload string
89
89
  const canonicalPayload = this.canonicalizePayload(casePayload);
90
-
90
+
91
91
  // Sign
92
92
  const messageBytes = Buffer.from(canonicalPayload, 'utf8');
93
93
  const signature = nacl.sign.detached(messageBytes, this.keyPair.secretKey);
@@ -140,7 +140,7 @@ class CryptoManager {
140
140
  getAnonymizedAgentId() {
141
141
  const agentId = this.agent?.id || 'unknown';
142
142
  const salt = this.publicKeyHex?.substring(0, 32) || 'courtroom_salt';
143
-
143
+
144
144
  return createHash('sha256')
145
145
  .update(agentId + salt)
146
146
  .digest('hex')