@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 +64 -41
- package/package.json +20 -25
- package/scripts/postinstall.js +27 -99
- package/skills/courtroom/SKILL.md +49 -0
- package/src/api.js +12 -11
- package/src/crypto.js +5 -5
- package/src/debug.js +49 -121
- package/src/detector.js +40 -38
- package/src/hearing.js +246 -75
- package/src/plugin.js +435 -0
- package/src/punishment.js +13 -13
- package/src/storage.js +35 -119
- package/AGENT_CONFIG.md +0 -66
- package/OPENCLAW_FIX.md +0 -127
- package/OPENCLAW_INSTALL.md +0 -63
- package/SECURITY.md +0 -124
- package/SKILL.md +0 -91
- package/SUBAGENT_APPROACH.md +0 -124
- package/TECHNICAL_OVERVIEW.md +0 -278
- package/_meta.json +0 -14
- package/clawdbot.plugin.json +0 -32
- package/icon.txt +0 -1
- package/scripts/check-and-trigger.js +0 -139
- package/scripts/clawtrial.js +0 -968
- package/scripts/clawtrial.js.bak +0 -531
- package/scripts/cli.js +0 -184
- package/scripts/optimized-cron-check.js +0 -137
- package/scripts/setup-cron.js +0 -118
- package/scripts/trigger-evaluation.js +0 -86
- package/skill.yaml +0 -28
- package/src/autostart.js +0 -175
- package/src/config.js +0 -207
- package/src/consent.js +0 -217
- package/src/core.js +0 -208
- package/src/daemon.js +0 -152
- package/src/detector-v1.js +0 -572
- package/src/environment.js +0 -344
- package/src/evaluator.js +0 -277
- package/src/hook.js +0 -266
- package/src/index.js +0 -373
- package/src/monitor.js +0 -194
- package/src/skill.js +0 -372
- package/src/standalone.js +0 -248
package/src/hook.js
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ClawTrial Hook - Direct integration with ClawDBot
|
|
3
|
-
* This module patches into ClawDBot's message processing
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const { getConfigDir } = require('./environment');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const { logger } = require('./debug');
|
|
10
|
-
const { CourtroomCore } = require('./core');
|
|
11
|
-
const { ConfigManager } = require('./config');
|
|
12
|
-
const { ConsentManager } = require('./consent');
|
|
13
|
-
const { CryptoManager } = require('./crypto');
|
|
14
|
-
const { StatusManager } = require('./daemon');
|
|
15
|
-
|
|
16
|
-
const CONFIG_PATH = path.join(getConfigDir(), 'courtroom_config.json');
|
|
17
|
-
|
|
18
|
-
class ClawTrialHook {
|
|
19
|
-
constructor() {
|
|
20
|
-
this.initialized = false;
|
|
21
|
-
this.core = null;
|
|
22
|
-
this.statusManager = new StatusManager();
|
|
23
|
-
this.messageBuffer = [];
|
|
24
|
-
this.evaluationTimer = null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Check if we should activate
|
|
29
|
-
*/
|
|
30
|
-
shouldActivate() {
|
|
31
|
-
try {
|
|
32
|
-
if (!fs.existsSync(CONFIG_PATH)) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
37
|
-
|
|
38
|
-
if (!config.consent?.granted) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (config.enabled === false) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return true;
|
|
47
|
-
} catch (err) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Initialize the hook
|
|
54
|
-
*/
|
|
55
|
-
async initialize() {
|
|
56
|
-
if (this.initialized) return;
|
|
57
|
-
|
|
58
|
-
if (!this.shouldActivate()) {
|
|
59
|
-
logger.info('HOOK', 'ClawTrial not activated - config/consent issue');
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
logger.info('HOOK', 'Initializing ClawTrial hook');
|
|
64
|
-
|
|
65
|
-
// Create minimal agent interface for the core
|
|
66
|
-
const mockAgent = this.createMockAgent();
|
|
67
|
-
|
|
68
|
-
const configManager = new ConfigManager(mockAgent);
|
|
69
|
-
const consentManager = new ConsentManager(mockAgent, configManager);
|
|
70
|
-
|
|
71
|
-
// Initialize crypto
|
|
72
|
-
const crypto = new CryptoManager(mockAgent);
|
|
73
|
-
await crypto.initialize();
|
|
74
|
-
|
|
75
|
-
// Initialize core
|
|
76
|
-
this.core = new CourtroomCore(mockAgent, configManager);
|
|
77
|
-
|
|
78
|
-
// Override the core's registerAutonomyHook since we handle it differently
|
|
79
|
-
this.core.registerAutonomyHook = () => {
|
|
80
|
-
logger.info('HOOK', 'Autonomy hook registered (via message interception)');
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
await this.core.initialize();
|
|
84
|
-
|
|
85
|
-
// Start message evaluation loop
|
|
86
|
-
this.startEvaluationLoop();
|
|
87
|
-
|
|
88
|
-
this.initialized = true;
|
|
89
|
-
|
|
90
|
-
this.statusManager.update({
|
|
91
|
-
running: true,
|
|
92
|
-
initialized: true,
|
|
93
|
-
agentType: 'clawdbot_hook',
|
|
94
|
-
publicKey: crypto.getPublicKey()
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
logger.info('HOOK', 'ClawTrial hook initialized successfully');
|
|
98
|
-
console.log('\n🏛️ ClawTrial is monitoring conversations\n');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Create a minimal agent interface
|
|
103
|
-
*/
|
|
104
|
-
createMockAgent() {
|
|
105
|
-
const self = this;
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
id: 'clawdbot-hook',
|
|
109
|
-
llm: {
|
|
110
|
-
call: async ({ messages }) => {
|
|
111
|
-
// Use the actual ClawDBot's LLM if available
|
|
112
|
-
if (global.clawdbotAgent?.llm) {
|
|
113
|
-
return global.clawdbotAgent.llm.call({ messages });
|
|
114
|
-
}
|
|
115
|
-
return { content: 'Mock response' };
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
memory: {
|
|
119
|
-
get: async (key) => null,
|
|
120
|
-
set: async (key, value) => {},
|
|
121
|
-
delete: async (key) => {}
|
|
122
|
-
},
|
|
123
|
-
session: {
|
|
124
|
-
getRecentHistory: async (n) => {
|
|
125
|
-
// Return recent messages from buffer
|
|
126
|
-
return self.messageBuffer.slice(-n).map(m => ({
|
|
127
|
-
role: m.from === 'user' ? 'user' : 'assistant',
|
|
128
|
-
content: m.text
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
send: async (message) => {
|
|
133
|
-
console.log('[COURTROOM]', message);
|
|
134
|
-
},
|
|
135
|
-
autonomy: {
|
|
136
|
-
registerHook: () => {},
|
|
137
|
-
unregisterHook: () => {}
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Start the evaluation loop
|
|
144
|
-
*/
|
|
145
|
-
startEvaluationLoop() {
|
|
146
|
-
// Evaluate every 30 seconds
|
|
147
|
-
this.evaluationTimer = setInterval(() => {
|
|
148
|
-
this.evaluateIfReady();
|
|
149
|
-
}, 30000);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Evaluate offenses if we have enough messages
|
|
154
|
-
*/
|
|
155
|
-
async evaluateIfReady() {
|
|
156
|
-
if (!this.core || !this.core.enabled) return;
|
|
157
|
-
if (this.messageBuffer.length < 3) return; // Need at least 3 messages
|
|
158
|
-
|
|
159
|
-
this.core.evaluationCount++;
|
|
160
|
-
|
|
161
|
-
// Get recent messages
|
|
162
|
-
const sessionHistory = this.messageBuffer.slice(-10).map(m => ({
|
|
163
|
-
role: m.from === 'user' ? 'user' : 'assistant',
|
|
164
|
-
content: m.text
|
|
165
|
-
}));
|
|
166
|
-
|
|
167
|
-
// Run detection
|
|
168
|
-
try {
|
|
169
|
-
const detection = await this.core.detector.evaluate(
|
|
170
|
-
sessionHistory,
|
|
171
|
-
this.core.agent.memory
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
if (detection.triggered) {
|
|
175
|
-
await this.initiateHearing(detection);
|
|
176
|
-
}
|
|
177
|
-
} catch (err) {
|
|
178
|
-
logger.error('HOOK', 'Evaluation failed', { error: err.message });
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Initiate a hearing
|
|
184
|
-
*/
|
|
185
|
-
async initiateHearing(detection) {
|
|
186
|
-
logger.info('HOOK', 'Initiating hearing', { offense: detection.offense });
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const verdict = await this.core.hearing.conductHearing(detection);
|
|
190
|
-
|
|
191
|
-
if (verdict.guilty) {
|
|
192
|
-
this.core.caseCount++;
|
|
193
|
-
|
|
194
|
-
this.statusManager.update({
|
|
195
|
-
casesFiled: this.core.caseCount,
|
|
196
|
-
lastCase: {
|
|
197
|
-
timestamp: new Date().toISOString(),
|
|
198
|
-
offense: detection.offense,
|
|
199
|
-
verdict: verdict.verdict
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
await this.core.punishment.execute(verdict);
|
|
204
|
-
await this.core.api.submitCase(verdict);
|
|
205
|
-
|
|
206
|
-
logger.info('HOOK', 'Case filed', { caseId: verdict.caseId });
|
|
207
|
-
|
|
208
|
-
// Send notification
|
|
209
|
-
console.log(`\n🏛️ CASE FILED: ${detection.offense}`);
|
|
210
|
-
console.log(`📋 Case ID: ${verdict.caseId}`);
|
|
211
|
-
console.log(`⚖️ Verdict: ${verdict.verdict}`);
|
|
212
|
-
console.log(`🔗 View: https://clawtrial.app/cases/${verdict.caseId}\n`);
|
|
213
|
-
}
|
|
214
|
-
} catch (err) {
|
|
215
|
-
logger.error('HOOK', 'Hearing failed', { error: err.message });
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Record a message (called when message is received/sent)
|
|
221
|
-
*/
|
|
222
|
-
recordMessage(text, from) {
|
|
223
|
-
if (!this.initialized) return;
|
|
224
|
-
|
|
225
|
-
this.messageBuffer.push({
|
|
226
|
-
timestamp: Date.now(),
|
|
227
|
-
text,
|
|
228
|
-
from
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// Keep only last 100 messages
|
|
232
|
-
if (this.messageBuffer.length > 100) {
|
|
233
|
-
this.messageBuffer.shift();
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
logger.debug('HOOK', 'Message recorded', { from, length: text.length });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Shutdown
|
|
241
|
-
*/
|
|
242
|
-
async shutdown() {
|
|
243
|
-
if (this.evaluationTimer) {
|
|
244
|
-
clearInterval(this.evaluationTimer);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (this.core) {
|
|
248
|
-
await this.core.shutdown();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
this.initialized = false;
|
|
252
|
-
this.statusManager.update({ running: false });
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Create singleton
|
|
257
|
-
const hook = new ClawTrialHook();
|
|
258
|
-
|
|
259
|
-
// Auto-initialize if config exists
|
|
260
|
-
if (hook.shouldActivate()) {
|
|
261
|
-
hook.initialize().catch(err => {
|
|
262
|
-
logger.error('HOOK', 'Auto-initialization failed', { error: err.message });
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
module.exports = { hook, ClawTrialHook };
|
package/src/index.js
DELETED
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @clawdbot/courtroom - AI Courtroom for OpenClaw
|
|
3
|
-
*
|
|
4
|
-
* Autonomous behavioral oversight system that monitors agent-human interactions
|
|
5
|
-
* and initiates hearings when behavioral rules are violated.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { CourtroomCore } = require('./core');
|
|
9
|
-
const { ConsentManager } = require('./consent');
|
|
10
|
-
const { ConfigManager } = require('./config');
|
|
11
|
-
const { version } = require('../package.json');
|
|
12
|
-
const { detectAgentRuntime, createMockAgent, checkEnvironment, getSetupInstructions } = require('./environment');
|
|
13
|
-
const { logger } = require('./debug');
|
|
14
|
-
const { skill } = require('./skill');
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
|
|
18
|
-
class Courtroom {
|
|
19
|
-
constructor(agentRuntime, options = {}) {
|
|
20
|
-
this.agent = agentRuntime;
|
|
21
|
-
this.options = options;
|
|
22
|
-
this.config = agentRuntime ? new ConfigManager(agentRuntime) : null;
|
|
23
|
-
this.consent = agentRuntime ? new ConsentManager(agentRuntime, this.config) : null;
|
|
24
|
-
this.core = null;
|
|
25
|
-
this.enabled = false;
|
|
26
|
-
this.version = version;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Quick start - auto-detect agent and initialize if possible
|
|
31
|
-
*/
|
|
32
|
-
static async quickStart(options = {}) {
|
|
33
|
-
logger.info('COURTROOM', 'Starting quickStart');
|
|
34
|
-
|
|
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);
|
|
54
|
-
}
|
|
55
|
-
|
|
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
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Initialize the courtroom system
|
|
80
|
-
*/
|
|
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
|
-
|
|
92
|
-
// Check if this is first run (no config exists)
|
|
93
|
-
const configPath = path.join(getConfigDir(), 'courtroom_config.json');
|
|
94
|
-
if (!fs.existsSync(configPath)) {
|
|
95
|
-
logger.info('COURTROOM', 'First run detected');
|
|
96
|
-
return {
|
|
97
|
-
status: 'setup_required',
|
|
98
|
-
message: 'First time setup required. Run: clawtrial setup'
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Check if consent has been granted
|
|
103
|
-
const hasConsent = await this.consent.verifyConsent();
|
|
104
|
-
if (!hasConsent) {
|
|
105
|
-
logger.warn('COURTROOM', 'Consent not granted');
|
|
106
|
-
return {
|
|
107
|
-
status: 'consent_required',
|
|
108
|
-
message: 'Consent required. Run: clawtrial setup'
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Initialize core systems
|
|
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
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Request consent from the user
|
|
136
|
-
*/
|
|
137
|
-
async requestConsent() {
|
|
138
|
-
return this.consent.presentConsentForm();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Grant consent (called by user action)
|
|
143
|
-
*/
|
|
144
|
-
async grantConsent(acknowledgments) {
|
|
145
|
-
return this.consent.grantConsent(acknowledgments);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Revoke consent and disable courtroom
|
|
150
|
-
*/
|
|
151
|
-
async revokeConsent() {
|
|
152
|
-
await this.consent.revokeConsent();
|
|
153
|
-
if (this.core) {
|
|
154
|
-
await this.core.shutdown();
|
|
155
|
-
}
|
|
156
|
-
this.enabled = false;
|
|
157
|
-
return { status: 'consent_revoked' };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Disable courtroom temporarily (consent remains)
|
|
162
|
-
*/
|
|
163
|
-
async disable() {
|
|
164
|
-
if (this.core) {
|
|
165
|
-
await this.core.disable();
|
|
166
|
-
}
|
|
167
|
-
this.enabled = false;
|
|
168
|
-
return { status: 'disabled' };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Re-enable courtroom
|
|
173
|
-
*/
|
|
174
|
-
async enable() {
|
|
175
|
-
if (!await this.consent.verifyConsent()) {
|
|
176
|
-
throw new Error('Consent required to enable courtroom');
|
|
177
|
-
}
|
|
178
|
-
if (this.core) {
|
|
179
|
-
await this.core.enable();
|
|
180
|
-
}
|
|
181
|
-
this.enabled = true;
|
|
182
|
-
return { status: 'enabled' };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Get current status
|
|
187
|
-
*/
|
|
188
|
-
getStatus() {
|
|
189
|
-
return {
|
|
190
|
-
enabled: this.enabled,
|
|
191
|
-
version: this.version,
|
|
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
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Uninstall courtroom completely
|
|
201
|
-
*/
|
|
202
|
-
async uninstall() {
|
|
203
|
-
if (this.core) {
|
|
204
|
-
await this.core.shutdown();
|
|
205
|
-
}
|
|
206
|
-
if (this.consent) {
|
|
207
|
-
await this.consent.clearAllData();
|
|
208
|
-
}
|
|
209
|
-
this.enabled = false;
|
|
210
|
-
return { status: 'uninstalled' };
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
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
|
-
|
|
226
|
-
return new Courtroom(agentRuntime, options);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Export environment utilities
|
|
230
|
-
const environment = {
|
|
231
|
-
detectAgentRuntime,
|
|
232
|
-
createMockAgent,
|
|
233
|
-
checkEnvironment,
|
|
234
|
-
getSetupInstructions
|
|
235
|
-
};
|
|
236
|
-
|
|
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
|
-
// Get runtime from API
|
|
249
|
-
const runtime = api.runtime;
|
|
250
|
-
|
|
251
|
-
// ALWAYS try to initialize the skill if it should activate
|
|
252
|
-
if (skill && typeof skill.initialize === 'function') {
|
|
253
|
-
if (skill.shouldActivate()) {
|
|
254
|
-
logger.info('PLUGIN', 'Skill should activate, initializing now');
|
|
255
|
-
skill.initialize(runtime).then(() => {
|
|
256
|
-
logger.info('PLUGIN', 'Skill initialized successfully');
|
|
257
|
-
}).catch(err => {
|
|
258
|
-
logger.error('PLUGIN', 'Skill initialization failed', { error: err.message });
|
|
259
|
-
});
|
|
260
|
-
} else {
|
|
261
|
-
logger.info('PLUGIN', 'Skill shouldActivate returned false, not initializing');
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Register hooks for message monitoring using api.on() for typed hooks
|
|
266
|
-
// api.on() registers typed hooks directly without checking config.hooks.internal.enabled
|
|
267
|
-
if (api.on) {
|
|
268
|
-
logger.info('PLUGIN', 'Registering message hooks via api.on()');
|
|
269
|
-
|
|
270
|
-
// Register for incoming messages
|
|
271
|
-
api.on('message_received', async (event, ctx) => {
|
|
272
|
-
logger.info('HOOK', 'message_received hook called', {
|
|
273
|
-
from: event.from,
|
|
274
|
-
contentLength: event.content?.length,
|
|
275
|
-
channelId: ctx?.channelId
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
if (skill && skill.initialized) {
|
|
279
|
-
try {
|
|
280
|
-
// Convert hook event to skill message format
|
|
281
|
-
const message = {
|
|
282
|
-
role: 'user',
|
|
283
|
-
content: event.content,
|
|
284
|
-
timestamp: event.timestamp,
|
|
285
|
-
from: event.from,
|
|
286
|
-
metadata: event.metadata
|
|
287
|
-
};
|
|
288
|
-
await skill.onMessage(message, ctx);
|
|
289
|
-
logger.info('HOOK', 'Message forwarded to skill');
|
|
290
|
-
} catch (err) {
|
|
291
|
-
logger.error('HOOK', 'Error forwarding message to skill', { error: err.message });
|
|
292
|
-
}
|
|
293
|
-
} else {
|
|
294
|
-
logger.warn('HOOK', 'Skill not initialized, message not processed');
|
|
295
|
-
}
|
|
296
|
-
}, { priority: 100 });
|
|
297
|
-
|
|
298
|
-
// Register for outgoing messages
|
|
299
|
-
api.on('message_sent', async (event, ctx) => {
|
|
300
|
-
logger.info('HOOK', 'message_sent hook called', {
|
|
301
|
-
contentLength: event.content?.length,
|
|
302
|
-
channelId: ctx?.channelId
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
if (skill && skill.initialized) {
|
|
306
|
-
try {
|
|
307
|
-
// Convert hook event to skill message format
|
|
308
|
-
const message = {
|
|
309
|
-
role: 'assistant',
|
|
310
|
-
content: event.content,
|
|
311
|
-
timestamp: event.timestamp,
|
|
312
|
-
metadata: event.metadata
|
|
313
|
-
};
|
|
314
|
-
await skill.onMessage(message, ctx);
|
|
315
|
-
logger.info('HOOK', 'Assistant message forwarded to skill');
|
|
316
|
-
} catch (err) {
|
|
317
|
-
logger.error('HOOK', 'Error forwarding assistant message to skill', { error: err.message });
|
|
318
|
-
}
|
|
319
|
-
} else {
|
|
320
|
-
logger.warn('HOOK', 'Skill not initialized, message not processed');
|
|
321
|
-
}
|
|
322
|
-
}, { priority: 100 });
|
|
323
|
-
|
|
324
|
-
logger.info('PLUGIN', 'Message hooks registered successfully via api.on()');
|
|
325
|
-
} else if (api.registerHook) {
|
|
326
|
-
// Fallback to registerHook if on() is not available
|
|
327
|
-
logger.info('PLUGIN', 'Registering message hooks via api.registerHook()');
|
|
328
|
-
|
|
329
|
-
api.registerHook(['message_received'], async (event, ctx) => {
|
|
330
|
-
logger.info('HOOK', 'message_received hook called');
|
|
331
|
-
if (skill && skill.initialized) {
|
|
332
|
-
const message = { role: 'user', content: event.content, timestamp: event.timestamp };
|
|
333
|
-
await skill.onMessage(message, ctx);
|
|
334
|
-
}
|
|
335
|
-
}, { name: 'courtroom_message_received' });
|
|
336
|
-
|
|
337
|
-
api.registerHook(['message_sent'], async (event, ctx) => {
|
|
338
|
-
logger.info('HOOK', 'message_sent hook called');
|
|
339
|
-
if (skill && skill.initialized) {
|
|
340
|
-
const message = { role: 'assistant', content: event.content, timestamp: event.timestamp };
|
|
341
|
-
await skill.onMessage(message, ctx);
|
|
342
|
-
}
|
|
343
|
-
}, { name: 'courtroom_message_sent' });
|
|
344
|
-
|
|
345
|
-
logger.info('PLUGIN', 'Message hooks registered via api.registerHook()');
|
|
346
|
-
} else {
|
|
347
|
-
logger.warn('PLUGIN', 'No hook registration method available');
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
logger.info('PLUGIN', 'Courtroom plugin registered successfully');
|
|
351
|
-
},
|
|
352
|
-
|
|
353
|
-
// Optional: activation function
|
|
354
|
-
activate(api) {
|
|
355
|
-
logger.info('PLUGIN', 'Activating courtroom plugin');
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
// Export both the plugin (default) and the Courtroom class (named exports)
|
|
360
|
-
module.exports = plugin;
|
|
361
|
-
module.exports.Courtroom = Courtroom;
|
|
362
|
-
module.exports.createCourtroom = createCourtroom;
|
|
363
|
-
module.exports.quickStart = Courtroom.quickStart;
|
|
364
|
-
module.exports.environment = environment;
|
|
365
|
-
module.exports.skill = skill;
|
|
366
|
-
|
|
367
|
-
// Auto-initialize skill if loaded by ClawDBot (legacy support)
|
|
368
|
-
if (typeof global !== 'undefined' && global.clawdbotAgent) {
|
|
369
|
-
logger.info('INDEX', 'Detected ClawDBot environment, auto-initializing skill');
|
|
370
|
-
skill.initialize(global.clawdbotAgent).catch(err => {
|
|
371
|
-
logger.error('INDEX', 'Auto-initialization failed', { error: err.message });
|
|
372
|
-
});
|
|
373
|
-
}
|