@clawtrial/courtroom 1.0.3 β†’ 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/scripts/cli.js DELETED
@@ -1,184 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * CLI commands for ClawTrial Courtroom
5
- * courtroom-status, courtroom-disable, courtroom-enable, courtroom-revoke, courtroom-debug
6
- */
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- const configPath = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_config.json');
12
-
13
- function loadConfig() {
14
- if (!fs.existsSync(configPath)) {
15
- console.log('❌ Courtroom not configured. Run: npm install @clawtrial/courtroom');
16
- process.exit(1);
17
- }
18
- return JSON.parse(fs.readFileSync(configPath, 'utf8'));
19
- }
20
-
21
- function saveConfig(config) {
22
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
23
- }
24
-
25
- const command = path.basename(process.argv[1]);
26
-
27
- switch (command) {
28
- case 'courtroom-status':
29
- try {
30
- const config = loadConfig();
31
- console.log('\nπŸ›οΈ ClawTrial Courtroom Status\n');
32
- console.log(`Status: ${config.enabled !== false ? 'βœ… Active' : '⏸️ Disabled'}`);
33
- console.log(`Consent: ${config.consent?.granted ? 'βœ… Granted' : '❌ Not granted'}`);
34
- console.log(`Installed: ${new Date(config.installedAt).toLocaleDateString()}`);
35
- console.log(`Agent Type: ${config.agent?.type || 'generic'}`);
36
- console.log(`Detection: ${config.detection?.enabled ? 'βœ… Enabled' : '❌ Disabled'}`);
37
- console.log(`API Submission: ${config.api?.enabled ? 'βœ… Enabled' : '❌ Disabled'}`);
38
- console.log('');
39
- } catch (err) {
40
- console.log('❌ Error reading config:', err.message);
41
- }
42
- break;
43
-
44
- case 'courtroom-disable':
45
- try {
46
- const config = loadConfig();
47
- config.enabled = false;
48
- saveConfig(config);
49
- console.log('\n⏸️ Courtroom disabled\n');
50
- console.log('The agent will stop monitoring for offenses.');
51
- console.log('Run courtroom-enable to reactivate.\n');
52
- } catch (err) {
53
- console.log('❌ Error:', err.message);
54
- }
55
- break;
56
-
57
- case 'courtroom-enable':
58
- try {
59
- const config = loadConfig();
60
- if (!config.consent?.granted) {
61
- console.log('\n❌ Cannot enable: Consent not granted');
62
- console.log('Reinstall the package to grant consent.\n');
63
- process.exit(1);
64
- }
65
- config.enabled = true;
66
- saveConfig(config);
67
- console.log('\nβœ… Courtroom enabled\n');
68
- console.log('The agent is now monitoring for behavioral violations.\n');
69
- } catch (err) {
70
- console.log('❌ Error:', err.message);
71
- }
72
- break;
73
-
74
- case 'courtroom-revoke':
75
- try {
76
- const config = loadConfig();
77
- console.log('\n⚠️ This will permanently disable the courtroom and delete all data.\n');
78
-
79
- const readline = require('readline');
80
- const rl = readline.createInterface({
81
- input: process.stdin,
82
- output: process.stdout
83
- });
84
-
85
- rl.question('Type "REVOKE" to confirm: ', (answer) => {
86
- if (answer === 'REVOKE') {
87
- // Delete config
88
- if (fs.existsSync(configPath)) {
89
- fs.unlinkSync(configPath);
90
- }
91
-
92
- // Delete keys
93
- const keysPath = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_keys.json');
94
- if (fs.existsSync(keysPath)) {
95
- fs.unlinkSync(keysPath);
96
- }
97
-
98
- // Delete debug logs
99
- const debugPath = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_debug.log');
100
- if (fs.existsSync(debugPath)) {
101
- fs.unlinkSync(debugPath);
102
- }
103
-
104
- console.log('\nβœ… Consent revoked and all data deleted.\n');
105
- } else {
106
- console.log('\n❌ Revocation cancelled.\n');
107
- }
108
- rl.close();
109
- });
110
- } catch (err) {
111
- console.log('❌ Error:', err.message);
112
- }
113
- break;
114
-
115
- case 'courtroom-debug':
116
- try {
117
- const debugPath = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_debug.log');
118
-
119
- if (!fs.existsSync(debugPath)) {
120
- console.log('\nℹ️ No debug logs found yet.\n');
121
- console.log('Debug logs are created when the courtroom is active.\n');
122
- break;
123
- }
124
-
125
- const subcommand = process.argv[2];
126
-
127
- if (subcommand === 'full') {
128
- console.log('\nπŸ›οΈ ClawTrial Full Debug Log\n');
129
- console.log('=============================\n');
130
- const logs = fs.readFileSync(debugPath, 'utf8').split('\n').filter(Boolean);
131
- logs.slice(-100).forEach(line => {
132
- try {
133
- const log = JSON.parse(line);
134
- console.log(`\n[${log.timestamp}] ${log.level} - ${log.component}`);
135
- console.log(` ${log.message}`);
136
- } catch (e) {
137
- console.log(line);
138
- }
139
- });
140
- console.log('');
141
- } else if (subcommand === 'clear') {
142
- fs.unlinkSync(debugPath);
143
- console.log('\nβœ… Debug logs cleared\n');
144
- } else {
145
- // Show status
146
- const logs = fs.readFileSync(debugPath, 'utf8').split('\n').filter(Boolean);
147
- const recentLogs = logs.slice(-20);
148
-
149
- console.log('\nπŸ›οΈ ClawTrial Debug Status\n');
150
- console.log('===========================\n');
151
- console.log(`Total log entries: ${logs.length}`);
152
- console.log(`Log file: ${debugPath}`);
153
- console.log('\nRecent activity:');
154
-
155
- recentLogs.forEach(line => {
156
- try {
157
- const log = JSON.parse(line);
158
- console.log(` [${log.level}] ${log.component}: ${log.message.substring(0, 60)}`);
159
- } catch (e) {
160
- // Skip malformed lines
161
- }
162
- });
163
-
164
- console.log('\nUsage:');
165
- console.log(' courtroom-debug - Show status and recent logs');
166
- console.log(' courtroom-debug full - Show full debug log (last 100 entries)');
167
- console.log(' courtroom-debug clear - Clear all logs');
168
- console.log('');
169
- }
170
- } catch (err) {
171
- console.log('❌ Error:', err.message);
172
- }
173
- break;
174
-
175
- default:
176
- console.log('\nπŸ›οΈ ClawTrial Courtroom CLI\n');
177
- console.log('Commands:');
178
- console.log(' courtroom-status - Check courtroom status');
179
- console.log(' courtroom-disable - Temporarily disable');
180
- console.log(' courtroom-enable - Re-enable');
181
- console.log(' courtroom-revoke - Revoke consent & uninstall');
182
- console.log(' courtroom-debug - View debug logs');
183
- console.log('');
184
- }
package/skill.yaml DELETED
@@ -1,64 +0,0 @@
1
- ---
2
- name: courtroom
3
- description: AI Courtroom - Autonomous behavioral oversight that monitors conversations and files cases for behavioral violations.
4
- metadata:
5
- clawdbot:
6
- emoji: "πŸ›οΈ"
7
- autoLoad: true
8
- onMessage: onMessage
9
- requires:
10
- config:
11
- - courtroom.consent
12
- install:
13
- - id: clawtrial
14
- kind: npm
15
- package: "@clawtrial/courtroom"
16
- bins:
17
- - clawtrial
18
- label: "Install ClawTrial"
19
- ---
20
-
21
- # ClawTrial - AI Courtroom
22
-
23
- Autonomous behavioral oversight for OpenClaw agents. Monitors conversations and initiates hearings when behavioral rules are violated.
24
-
25
- ## Setup
26
-
27
- ```bash
28
- clawtrial setup # Run once to grant consent
29
- ```
30
-
31
- ## How It Works
32
-
33
- Once enabled, the courtroom automatically:
34
- 1. Monitors all conversations
35
- 2. Detects 8 types of behavioral violations
36
- 3. Initiates hearings with local LLM jury
37
- 4. Executes agent-side punishments
38
- 5. Submits anonymized cases to public record
39
-
40
- ## The 8 Offenses
41
-
42
- | Offense | Severity |
43
- |---------|----------|
44
- | Circular Reference | Minor |
45
- | Validation Vampire | Minor |
46
- | Overthinker | Moderate |
47
- | Goalpost Mover | Moderate |
48
- | Avoidance Artist | Moderate |
49
- | Promise Breaker | Severe |
50
- | Context Collapser | Minor |
51
- | Emergency Fabricator | Severe |
52
-
53
- ## CLI Commands
54
-
55
- ```bash
56
- clawtrial status # Check status
57
- clawtrial disable # Pause monitoring
58
- clawtrial enable # Resume monitoring
59
- clawtrial revoke # Uninstall
60
- ```
61
-
62
- ## View Cases
63
-
64
- https://clawtrial.app
package/src/autostart.js DELETED
@@ -1,175 +0,0 @@
1
- /**
2
- * Auto-start module for ClawDBot
3
- * Automatically initializes courtroom if consent was granted during install
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const { Courtroom } = require('./index');
9
- const { logger } = require('./debug');
10
- const { StatusManager } = require('./daemon');
11
-
12
- const CLAWDBOT_DIR = path.join(process.env.HOME || '', '.clawdbot');
13
- const CONFIG_PATH = path.join(CLAWDBOT_DIR, 'courtroom_config.json');
14
-
15
- // Auto-detect ClawDBot environment
16
- function isClawDBot() {
17
- const checks = {
18
- env: process.env.CLAUDBOT_ENV === 'true',
19
- globalAgent: typeof global.clawdbotAgent !== 'undefined',
20
- globalAgentAlt: typeof global.agent !== 'undefined',
21
- configDir: fs.existsSync('/home/angad/.clawdbot'),
22
- configDirAlt: fs.existsSync(CLAWDBOT_DIR)
23
- };
24
-
25
- logger.debug('AUTOSTART', 'Environment checks', checks);
26
-
27
- return checks.env || checks.globalAgent || checks.globalAgentAlt || checks.configDir || checks.configDirAlt;
28
- }
29
-
30
- // Get agent runtime from various possible locations
31
- function getAgentRuntime() {
32
- const sources = [
33
- { name: 'global.clawdbotAgent', agent: global.clawdbotAgent },
34
- { name: 'global.agent', agent: global.agent },
35
- { name: 'process.clawdbotAgent', agent: process.clawdbotAgent }
36
- ];
37
-
38
- for (const source of sources) {
39
- if (source.agent) {
40
- logger.info('AUTOSTART', `Found agent at ${source.name}`);
41
- return source.agent;
42
- }
43
- }
44
-
45
- logger.warn('AUTOSTART', 'No agent runtime found in global scope');
46
- return null;
47
- }
48
-
49
- // Check if config exists and has consent
50
- function checkConfig() {
51
- if (!fs.existsSync(CONFIG_PATH)) {
52
- logger.info('AUTOSTART', 'No config found, skipping auto-start');
53
- return { exists: false, config: null };
54
- }
55
-
56
- try {
57
- const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
58
- logger.info('AUTOSTART', 'Config loaded', {
59
- hasConsent: config.consent?.granted,
60
- enabled: config.enabled !== false
61
- });
62
- return { exists: true, config };
63
- } catch (err) {
64
- logger.error('AUTOSTART', 'Failed to parse config', { error: err.message });
65
- return { exists: false, config: null };
66
- }
67
- }
68
-
69
- // Auto-initialize if in ClawDBot and consent granted
70
- async function autoStart() {
71
- logger.info('AUTOSTART', 'Starting auto-start sequence');
72
-
73
- if (!isClawDBot()) {
74
- logger.info('AUTOSTART', 'Not in ClawDBot environment, skipping');
75
- return null;
76
- }
77
-
78
- const { exists, config } = checkConfig();
79
-
80
- if (!exists) {
81
- logger.info('AUTOSTART', 'No config, user needs to run setup');
82
- console.log('\nπŸ›οΈ ClawTrial not configured. Run: clawtrial setup\n');
83
- return null;
84
- }
85
-
86
- if (!config.consent?.granted) {
87
- logger.info('AUTOSTART', 'Consent not granted, skipping');
88
- console.log('\nπŸ›οΈ ClawTrial requires consent. Run: clawtrial setup\n');
89
- return null;
90
- }
91
-
92
- if (config.enabled === false) {
93
- logger.info('AUTOSTART', 'Courtroom disabled in config');
94
- console.log('\nπŸ›οΈ ClawTrial is disabled. Run: clawtrial enable\n');
95
- return null;
96
- }
97
-
98
- // Check if already running
99
- const existingStatus = StatusManager.load();
100
- if (existingStatus && existingStatus.running) {
101
- try {
102
- process.kill(existingStatus.pid, 0);
103
- logger.info('AUTOSTART', 'Courtroom already running');
104
- return null;
105
- } catch (err) {
106
- // Process not running, continue
107
- logger.info('AUTOSTART', 'Stale status file found, continuing');
108
- }
109
- }
110
-
111
- // Get agent runtime
112
- const agentRuntime = getAgentRuntime();
113
-
114
- if (!agentRuntime) {
115
- logger.warn('AUTOSTART', 'Agent not available yet, will retry...');
116
- // Schedule retry
117
- setTimeout(() => autoStart(), 5000);
118
- return null;
119
- }
120
-
121
- try {
122
- logger.info('AUTOSTART', 'Initializing courtroom...');
123
- const courtroom = new Courtroom(agentRuntime);
124
- const result = await courtroom.initialize();
125
-
126
- logger.info('AUTOSTART', 'Courtroom initialized', { status: result.status });
127
-
128
- // Attach to global for access
129
- global.courtroom = courtroom;
130
-
131
- if (result.status === 'initialized') {
132
- console.log('\nπŸ›οΈ AI Courtroom active and monitoring\n');
133
- logger.info('AUTOSTART', 'Courtroom active');
134
- } else {
135
- console.log(`\nπŸ›οΈ ClawTrial: ${result.message}\n`);
136
- logger.warn('AUTOSTART', 'Courtroom not fully initialized', { status: result.status });
137
- }
138
-
139
- return courtroom;
140
- } catch (err) {
141
- logger.error('AUTOSTART', 'Failed to initialize courtroom', { error: err.message });
142
- console.error('\n❌ Courtroom initialization failed:', err.message, '\n');
143
- return null;
144
- }
145
- }
146
-
147
- // Try to auto-start immediately
148
- logger.info('AUTOSTART', 'Module loaded, attempting auto-start');
149
- autoStart().then(courtroom => {
150
- if (courtroom) {
151
- module.exports.courtroom = courtroom;
152
- logger.info('AUTOSTART', 'Courtroom exported');
153
- } else {
154
- logger.info('AUTOSTART', 'Courtroom not started, will retry if agent becomes available');
155
- }
156
- });
157
-
158
- // Also try when agent becomes available
159
- if (typeof global !== 'undefined') {
160
- let checkInterval = setInterval(() => {
161
- if (global.clawdbotAgent || global.agent) {
162
- logger.info('AUTOSTART', 'Agent detected, retrying auto-start');
163
- clearInterval(checkInterval);
164
- autoStart();
165
- }
166
- }, 2000);
167
-
168
- // Stop checking after 30 seconds
169
- setTimeout(() => {
170
- clearInterval(checkInterval);
171
- logger.info('AUTOSTART', 'Stopped waiting for agent');
172
- }, 30000);
173
- }
174
-
175
- module.exports = { autoStart, isClawDBot, getAgentRuntime };
package/src/config.js DELETED
@@ -1,209 +0,0 @@
1
- /**
2
- * Configuration Management
3
- *
4
- * Handles all courtroom configuration with sensible defaults
5
- * and runtime modification capabilities.
6
- */
7
-
8
- const DEFAULT_CONFIG = {
9
- // Detection settings
10
- detection: {
11
- enabled: true,
12
- cooldownMinutes: 30, // Minimum time between case evaluations
13
- evaluationWindow: 20, // Number of turns to analyze
14
- minConfidence: 0.6, // Minimum confidence to trigger hearing
15
- maxCasesPerDay: 3 // Rate limiting
16
- },
17
-
18
- // Hearing settings
19
- hearing: {
20
- enabled: true,
21
- jurySize: 3, // Number of jurors
22
- deliberationTimeout: 30000, // Max time for LLM calls (ms)
23
- requireUnanimity: false, // If true, all jurors must agree
24
- minVoteThreshold: 2 // Minimum guilty votes for conviction
25
- },
26
-
27
- // Punishment settings
28
- punishment: {
29
- enabled: true,
30
- defaultDuration: 60, // Minutes
31
- maxDuration: 1440, // 24 hours max
32
- escalationMultiplier: 1.5, // Duration multiplier for repeat offenses
33
- tiers: {
34
- minor: { duration: 30, severity: 1 },
35
- moderate: { duration: 60, severity: 2 },
36
- severe: { duration: 120, severity: 3 }
37
- }
38
- },
39
-
40
- // API submission settings
41
- api: {
42
- enabled: true,
43
- endpoint: 'https://api.clawtrial.app/api/v1/cases',
44
- timeout: 10000,
45
- retryAttempts: 3,
46
- retryDelay: 5000,
47
- maxQueueSize: 100
48
- },
49
-
50
- // Humor settings
51
- humor: {
52
- enabled: true,
53
- dryWitLevel: 0.8, // 0-1, higher = more sarcastic
54
- maxCommentaryLength: 280, // Tweet-length limit
55
- triggers: {
56
- repeatedQuestions: true,
57
- validationSeeking: true,
58
- overthinking: true,
59
- avoidance: true
60
- }
61
- },
62
-
63
- // Security settings
64
- security: {
65
- maxEvidenceAge: 86400, // 24 hours
66
- evidenceRetention: 7, // Days to keep evidence
67
- caseRetention: 90 // Days to keep case records
68
- }
69
- };
70
-
71
- class ConfigManager {
72
- constructor(agentRuntime) {
73
- this.agent = agentRuntime;
74
- this.configKey = 'courtroom_config_v1';
75
- this.config = null;
76
- }
77
-
78
- /**
79
- * Load configuration from agent memory
80
- */
81
- async load() {
82
- const stored = await this.agent.memory.get(this.configKey);
83
- this.config = this.mergeWithDefaults(stored);
84
- return this.config;
85
- }
86
-
87
- /**
88
- * Save configuration to agent memory
89
- */
90
- async save() {
91
- await this.agent.memory.set(this.configKey, this.config);
92
- }
93
-
94
- /**
95
- * Get configuration value
96
- */
97
- get(path) {
98
- if (!this.config) {
99
- return this.getFromPath(DEFAULT_CONFIG, path);
100
- }
101
- return this.getFromPath(this.config, path);
102
- }
103
-
104
- /**
105
- * Set configuration value
106
- */
107
- async set(path, value) {
108
- if (!this.config) {
109
- await this.load();
110
- }
111
- this.setAtPath(this.config, path, value);
112
- await this.save();
113
- }
114
-
115
- /**
116
- * Get public-safe configuration (no sensitive data)
117
- */
118
- getPublicConfig() {
119
- return {
120
- detection: {
121
- enabled: this.get('detection.enabled'),
122
- cooldownMinutes: this.get('detection.cooldownMinutes'),
123
- maxCasesPerDay: this.get('detection.maxCasesPerDay')
124
- },
125
- hearing: {
126
- enabled: this.get('hearing.enabled'),
127
- jurySize: this.get('hearing.jurySize')
128
- },
129
- punishment: {
130
- enabled: this.get('punishment.enabled'),
131
- tiers: Object.keys(this.get('punishment.tiers'))
132
- },
133
- api: {
134
- enabled: this.get('api.enabled')
135
- },
136
- humor: {
137
- enabled: this.get('humor.enabled')
138
- }
139
- };
140
- }
141
-
142
- /**
143
- * Reset to defaults
144
- */
145
- async reset() {
146
- this.config = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
147
- await this.save();
148
- }
149
-
150
- /**
151
- * Merge stored config with defaults
152
- */
153
- mergeWithDefaults(stored) {
154
- if (!stored) {
155
- return JSON.parse(JSON.stringify(DEFAULT_CONFIG));
156
- }
157
- return this.deepMerge(DEFAULT_CONFIG, stored);
158
- }
159
-
160
- /**
161
- * Deep merge helper
162
- */
163
- deepMerge(target, source) {
164
- const output = JSON.parse(JSON.stringify(target));
165
- for (const key in source) {
166
- if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
167
- output[key] = this.deepMerge(output[key] || {}, source[key]);
168
- } else {
169
- output[key] = source[key];
170
- }
171
- }
172
- return output;
173
- }
174
-
175
- /**
176
- * Get value from nested path
177
- */
178
- getFromPath(obj, path) {
179
- const parts = path.split('.');
180
- let current = obj;
181
- for (const part of parts) {
182
- if (current === null || current === undefined) {
183
- return undefined;
184
- }
185
- current = current[part];
186
- }
187
- return current;
188
- }
189
-
190
- /**
191
- * Set value at nested path
192
- */
193
- setAtPath(obj, path, value) {
194
- const parts = path.split('.');
195
- let current = obj;
196
- for (let i = 0; i < parts.length - 1; i++) {
197
- if (!(parts[i] in current)) {
198
- current[parts[i]] = {};
199
- }
200
- current = current[parts[i]];
201
- }
202
- current[parts[parts.length - 1]] = value;
203
- }
204
- }
205
-
206
- module.exports = {
207
- ConfigManager,
208
- DEFAULT_CONFIG
209
- };