@clawtrial/courtroom 1.0.2 → 1.0.3

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/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @clawdbot/courtroom - AI Courtroom for OpenClaw
2
+ * @clawtrial/courtroom - AI Courtroom for OpenClaw
3
3
  *
4
4
  * Autonomous behavioral oversight system that monitors agent-human interactions
5
5
  * and initiates hearings when behavioral rules are violated.
@@ -9,6 +9,9 @@ const { CourtroomCore } = require('./core');
9
9
  const { ConsentManager } = require('./consent');
10
10
  const { ConfigManager } = require('./config');
11
11
  const { version } = require('../package.json');
12
+ const { detectAgentRuntime, createMockAgent, checkEnvironment, getSetupInstructions } = require('./environment');
13
+ const { logger } = require('./debug');
14
+ const { skill } = require('./skill');
12
15
  const fs = require('fs');
13
16
  const path = require('path');
14
17
 
@@ -16,86 +19,120 @@ class Courtroom {
16
19
  constructor(agentRuntime, options = {}) {
17
20
  this.agent = agentRuntime;
18
21
  this.options = options;
19
- this.config = new ConfigManager(agentRuntime);
20
- this.consent = new ConsentManager(agentRuntime, this.config);
22
+ this.config = agentRuntime ? new ConfigManager(agentRuntime) : null;
23
+ this.consent = agentRuntime ? new ConsentManager(agentRuntime, this.config) : null;
21
24
  this.core = null;
22
25
  this.enabled = false;
23
26
  this.version = version;
24
27
  }
25
28
 
26
29
  /**
27
- * Quick start - auto-initialize if consent already granted
30
+ * Quick start - auto-detect agent and initialize if possible
28
31
  */
29
- static async quickStart(agentRuntime, options = {}) {
30
- const courtroom = new Courtroom(agentRuntime, options);
32
+ static async quickStart(options = {}) {
33
+ logger.info('COURTROOM', 'Starting quickStart');
31
34
 
32
- // Check for existing config with consent
33
- const configPath = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_config.json');
34
- if (fs.existsSync(configPath)) {
35
- const savedConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
36
-
37
- if (savedConfig.consent?.granted && savedConfig.enabled !== false) {
38
- // Auto-initialize!
39
- await courtroom.initialize();
40
- console.log('šŸ›ļø AI Courtroom auto-initialized');
41
- return courtroom;
42
- }
35
+ // Check environment first
36
+ const env = checkEnvironment();
37
+ if (!env.valid) {
38
+ logger.error('COURTROOM', 'Environment check failed', { issues: env.issues });
39
+ return {
40
+ success: false,
41
+ status: 'environment_error',
42
+ issues: env.issues,
43
+ message: 'Environment issues detected. Run "clawtrial setup" for details.'
44
+ };
45
+ }
46
+
47
+ // Try to detect agent runtime
48
+ let agentRuntime = options.agent || detectAgentRuntime()?.agent;
49
+
50
+ // If no agent and mock requested, create one
51
+ if (!agentRuntime && options.useMock) {
52
+ logger.info('COURTROOM', 'Creating mock agent');
53
+ agentRuntime = createMockAgent(options.mockOptions);
43
54
  }
44
55
 
45
- // No config or not consented - return uninitialized
46
- return courtroom;
56
+ if (!agentRuntime) {
57
+ logger.warn('COURTROOM', 'No agent runtime available');
58
+ const instructions = getSetupInstructions();
59
+ return {
60
+ success: false,
61
+ status: 'no_agent',
62
+ message: instructions.message,
63
+ instructions: instructions.instructions
64
+ };
65
+ }
66
+
67
+ // Create and initialize courtroom
68
+ const courtroom = new Courtroom(agentRuntime, options);
69
+ const result = await courtroom.initialize();
70
+
71
+ return {
72
+ success: result.status === 'initialized',
73
+ courtroom: result.status === 'initialized' ? courtroom : null,
74
+ ...result
75
+ };
47
76
  }
48
77
 
49
78
  /**
50
79
  * Initialize the courtroom system
51
- * Must be called after construction
52
80
  */
53
81
  async initialize() {
82
+ logger.info('COURTROOM', 'Initializing courtroom');
83
+
84
+ if (!this.agent) {
85
+ logger.error('COURTROOM', 'No agent runtime provided');
86
+ return {
87
+ status: 'no_agent',
88
+ message: 'No agent runtime available. Pass an agent to createCourtroom(agent) or use Courtroom.quickStart()'
89
+ };
90
+ }
91
+
54
92
  // Check if this is first run (no config exists)
55
93
  const configPath = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_config.json');
56
94
  if (!fs.existsSync(configPath)) {
57
- console.log('\nšŸ›ļø Welcome to ClawTrial - AI Courtroom Setup\n');
58
- console.log('This appears to be your first time. Running setup...\n');
59
-
60
- // Run setup
61
- const { postInstall } = require('../scripts/postinstall.js');
62
- await postInstall();
63
-
64
- // After setup, check consent again
65
- const hasConsent = await this.consent.verifyConsent();
66
- if (!hasConsent) {
67
- return {
68
- status: 'setup_complete_consent_required',
69
- message: 'Setup complete! Run courtroom.grantConsent() to enable'
70
- };
71
- }
95
+ logger.info('COURTROOM', 'First run detected');
96
+ return {
97
+ status: 'setup_required',
98
+ message: 'First time setup required. Run: clawtrial setup'
99
+ };
72
100
  }
73
101
 
74
102
  // Check if consent has been granted
75
103
  const hasConsent = await this.consent.verifyConsent();
76
104
  if (!hasConsent) {
105
+ logger.warn('COURTROOM', 'Consent not granted');
77
106
  return {
78
107
  status: 'consent_required',
79
- message: 'Courtroom requires explicit user consent. Run courtroom.requestConsent()'
108
+ message: 'Consent required. Run: clawtrial setup'
80
109
  };
81
110
  }
82
111
 
83
112
  // Initialize core systems
84
- this.core = new CourtroomCore(this.agent, this.config);
85
- await this.core.initialize();
86
-
87
- this.enabled = true;
88
-
89
- return {
90
- status: 'initialized',
91
- version: this.version,
92
- config: this.config.getPublicConfig()
93
- };
113
+ try {
114
+ this.core = new CourtroomCore(this.agent, this.config);
115
+ await this.core.initialize();
116
+ this.enabled = true;
117
+
118
+ logger.info('COURTROOM', 'Courtroom initialized successfully');
119
+
120
+ return {
121
+ status: 'initialized',
122
+ version: this.version,
123
+ config: this.config.getPublicConfig()
124
+ };
125
+ } catch (err) {
126
+ logger.error('COURTROOM', 'Initialization failed', { error: err.message });
127
+ return {
128
+ status: 'initialization_error',
129
+ message: err.message
130
+ };
131
+ }
94
132
  }
95
133
 
96
134
  /**
97
135
  * Request consent from the user
98
- * Returns a consent form that must be explicitly accepted
99
136
  */
100
137
  async requestConsent() {
101
138
  return this.consent.presentConsentForm();
@@ -152,8 +189,10 @@ class Courtroom {
152
189
  return {
153
190
  enabled: this.enabled,
154
191
  version: this.version,
155
- consent: this.consent?.getStatus(),
156
- core: this.core?.getStatus()
192
+ hasAgent: !!this.agent,
193
+ hasCore: !!this.core,
194
+ consent: this.consent?.getStatus ? this.consent.getStatus() : null,
195
+ core: this.core?.getStatus ? this.core.getStatus() : null
157
196
  };
158
197
  }
159
198
 
@@ -164,21 +203,84 @@ class Courtroom {
164
203
  if (this.core) {
165
204
  await this.core.shutdown();
166
205
  }
167
- await this.consent.clearAllData();
206
+ if (this.consent) {
207
+ await this.consent.clearAllData();
208
+ }
209
+ this.enabled = false;
168
210
  return { status: 'uninstalled' };
169
211
  }
170
212
  }
171
213
 
172
- // Factory function for OpenClaw integration
173
- function createCourtroom(agentRuntime, options) {
214
+ // Factory function for creating courtroom instances
215
+ function createCourtroom(agentRuntime, options = {}) {
216
+ // If no agent provided, try to detect one
217
+ if (!agentRuntime) {
218
+ const detected = detectAgentRuntime();
219
+ if (detected) {
220
+ agentRuntime = detected.agent;
221
+ } else if (options.useMock) {
222
+ agentRuntime = createMockAgent(options.mockOptions);
223
+ }
224
+ }
225
+
174
226
  return new Courtroom(agentRuntime, options);
175
227
  }
176
228
 
177
- // Trigger auto-start if in ClawDBot environment
178
- require('./autostart');
229
+ // Export environment utilities
230
+ const environment = {
231
+ detectAgentRuntime,
232
+ createMockAgent,
233
+ checkEnvironment,
234
+ getSetupInstructions
235
+ };
179
236
 
180
- module.exports = {
181
- Courtroom,
182
- createCourtroom,
183
- quickStart: Courtroom.quickStart
237
+ // Create the ClawDBot plugin object
238
+ const plugin = {
239
+ id: 'courtroom',
240
+ name: 'ClawTrial - AI Courtroom',
241
+ description: 'Autonomous behavioral oversight that monitors conversations and files cases for behavioral violations',
242
+ version: version,
243
+
244
+ // Plugin registration function required by ClawDBot
245
+ register(api) {
246
+ logger.info('PLUGIN', 'Registering courtroom plugin');
247
+
248
+ // Store runtime reference
249
+ const runtime = api.runtime;
250
+
251
+ // Only initialize skill if runtime has the required memory interface
252
+ // The skill system will auto-initialize via shouldActivate if this fails
253
+ if (skill && typeof skill.initialize === 'function' && runtime && runtime.memory) {
254
+ skill.initialize(runtime).catch(err => {
255
+ logger.error('PLUGIN', 'Skill initialization failed', { error: err.message });
256
+ });
257
+ } else {
258
+ logger.info('PLUGIN', 'Skill will auto-initialize via ClawDBot skill system');
259
+ }
260
+
261
+ // Register any commands or hooks
262
+ logger.info('PLUGIN', 'Courtroom plugin registered successfully');
263
+ },
264
+
265
+ // Optional: activation function
266
+ activate(api) {
267
+ logger.info('PLUGIN', 'Activating courtroom plugin');
268
+ // Additional activation logic if needed
269
+ }
184
270
  };
271
+
272
+ // Export both the plugin (default) and the Courtroom class (named exports)
273
+ module.exports = plugin;
274
+ module.exports.Courtroom = Courtroom;
275
+ module.exports.createCourtroom = createCourtroom;
276
+ module.exports.quickStart = Courtroom.quickStart;
277
+ module.exports.environment = environment;
278
+ module.exports.skill = skill;
279
+
280
+ // Auto-initialize skill if loaded by ClawDBot (legacy support)
281
+ if (typeof global !== 'undefined' && global.clawdbotAgent) {
282
+ logger.info('INDEX', 'Detected ClawDBot environment, auto-initializing skill');
283
+ skill.initialize(global.clawdbotAgent).catch(err => {
284
+ logger.error('INDEX', 'Auto-initialization failed', { error: err.message });
285
+ });
286
+ }
package/src/monitor.js ADDED
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ClawTrial Background Monitor
5
+ * Runs continuously and initializes courtroom when agent becomes available
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { logger } = require('./debug');
11
+ const { StatusManager } = require('./daemon');
12
+
13
+ const CONFIG_PATH = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_config.json');
14
+ const CHECK_INTERVAL = 5000; // Check every 5 seconds
15
+ const MAX_RETRIES = 100; // Give up after ~8 minutes
16
+
17
+ let retryCount = 0;
18
+ let courtroom = null;
19
+ let statusManager = new StatusManager();
20
+
21
+ logger.info('MONITOR', 'Background monitor started', { pid: process.pid });
22
+
23
+ // Save PID
24
+ statusManager.update({
25
+ running: true,
26
+ initialized: false,
27
+ pid: process.pid,
28
+ startedAt: new Date().toISOString()
29
+ });
30
+
31
+ /**
32
+ * Check if config exists and is enabled
33
+ */
34
+ function checkConfig() {
35
+ try {
36
+ if (!fs.existsSync(CONFIG_PATH)) {
37
+ return { valid: false, reason: 'no_config' };
38
+ }
39
+
40
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
41
+
42
+ if (!config.consent?.granted) {
43
+ return { valid: false, reason: 'no_consent' };
44
+ }
45
+
46
+ if (config.enabled === false) {
47
+ return { valid: false, reason: 'disabled' };
48
+ }
49
+
50
+ return { valid: true, config };
51
+ } catch (err) {
52
+ logger.error('MONITOR', 'Config check failed', { error: err.message });
53
+ return { valid: false, reason: 'error' };
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Detect agent runtime
59
+ */
60
+ function detectAgent() {
61
+ // Check various global locations
62
+ if (typeof global.clawdbotAgent !== 'undefined' && global.clawdbotAgent) {
63
+ return { type: 'clawdbot', agent: global.clawdbotAgent };
64
+ }
65
+
66
+ if (typeof global.agent !== 'undefined' && global.agent) {
67
+ return { type: 'generic', agent: global.agent };
68
+ }
69
+
70
+ if (typeof process.clawdbotAgent !== 'undefined' && process.clawdbotAgent) {
71
+ return { type: 'clawdbot', agent: process.clawdbotAgent };
72
+ }
73
+
74
+ // Check for ClawDBot specific indicators
75
+ if (process.env.CLAUDBOT_ENV === 'true') {
76
+ // Try to find agent in parent process or global scope
77
+ if (globalThis.clawdbotAgent) {
78
+ return { type: 'clawdbot', agent: globalThis.clawdbotAgent };
79
+ }
80
+ }
81
+
82
+ return null;
83
+ }
84
+
85
+ /**
86
+ * Try to initialize courtroom
87
+ */
88
+ async function tryInitialize() {
89
+ const configCheck = checkConfig();
90
+
91
+ if (!configCheck.valid) {
92
+ logger.warn('MONITOR', 'Config not valid', { reason: configCheck.reason });
93
+ return false;
94
+ }
95
+
96
+ const agentInfo = detectAgent();
97
+
98
+ if (!agentInfo) {
99
+ logger.debug('MONITOR', 'Agent not detected yet');
100
+ return false;
101
+ }
102
+
103
+ logger.info('MONITOR', `Agent detected: ${agentInfo.type}`);
104
+
105
+ try {
106
+ // Import and initialize courtroom
107
+ const { createCourtroom } = require('./index');
108
+ courtroom = createCourtroom(agentInfo.agent);
109
+
110
+ const result = await courtroom.initialize();
111
+
112
+ if (result.status === 'initialized') {
113
+ logger.info('MONITOR', 'Courtroom initialized successfully');
114
+
115
+ statusManager.update({
116
+ running: true,
117
+ initialized: true,
118
+ agentType: agentInfo.type,
119
+ publicKey: result.publicKey
120
+ });
121
+
122
+ // Attach to global for access
123
+ global.courtroom = courtroom;
124
+
125
+ console.log('\nšŸ›ļø AI Courtroom is now active and monitoring!\n');
126
+
127
+ return true;
128
+ } else {
129
+ logger.warn('MONITOR', 'Courtroom not initialized', { status: result.status });
130
+ return false;
131
+ }
132
+ } catch (err) {
133
+ logger.error('MONITOR', 'Initialization failed', { error: err.message });
134
+ return false;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Main monitoring loop
140
+ */
141
+ async function monitor() {
142
+ logger.info('MONITOR', 'Starting monitoring loop');
143
+
144
+ // Try immediately first
145
+ if (await tryInitialize()) {
146
+ return; // Success! Keep process alive
147
+ }
148
+
149
+ // Set up interval to keep trying
150
+ const interval = setInterval(async () => {
151
+ retryCount++;
152
+
153
+ if (retryCount > MAX_RETRIES) {
154
+ logger.warn('MONITOR', 'Max retries reached, giving up');
155
+ clearInterval(interval);
156
+ statusManager.update({ running: false, error: 'max_retries' });
157
+ process.exit(1);
158
+ }
159
+
160
+ if (await tryInitialize()) {
161
+ clearInterval(interval);
162
+ // Keep process alive - courtroom is running
163
+ }
164
+ }, CHECK_INTERVAL);
165
+
166
+ // Keep process alive
167
+ setInterval(() => {
168
+ // Heartbeat to keep process alive
169
+ if (courtroom && courtroom.enabled) {
170
+ statusManager.update({ lastHeartbeat: new Date().toISOString() });
171
+ }
172
+ }, 30000);
173
+ }
174
+
175
+ // Handle shutdown
176
+ process.on('SIGTERM', () => {
177
+ logger.info('MONITOR', 'Received SIGTERM, shutting down');
178
+ statusManager.update({ running: false });
179
+ process.exit(0);
180
+ });
181
+
182
+ process.on('SIGINT', () => {
183
+ logger.info('MONITOR', 'Received SIGINT, shutting down');
184
+ statusManager.update({ running: false });
185
+ process.exit(0);
186
+ });
187
+
188
+ // Start monitoring
189
+ monitor().catch(err => {
190
+ logger.error('MONITOR', 'Monitor crashed', { error: err.message });
191
+ statusManager.update({ running: false, error: err.message });
192
+ process.exit(1);
193
+ });