@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/environment.js
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Detect which bot is installed (clawdbot, moltbot, or openclaw)
|
|
4
|
-
* Returns the bot configuration with name, directory, and command
|
|
5
|
-
*/
|
|
6
|
-
function detectBot() {
|
|
7
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
8
|
-
|
|
9
|
-
const bots = [
|
|
10
|
-
{ name: 'openclaw', dir: '.openclaw', config: 'openclaw.json', command: 'openclaw' },
|
|
11
|
-
{ name: 'moltbot', dir: '.moltbot', config: 'moltbot.json', command: 'moltbot' },
|
|
12
|
-
{ name: 'clawdbot', dir: '.clawdbot', config: 'clawdbot.json', command: 'clawdbot' }
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
// Check which bot config exists
|
|
16
|
-
for (const bot of bots) {
|
|
17
|
-
const configPath = path.join(homeDir, bot.dir, bot.config);
|
|
18
|
-
if (fs.existsSync(configPath)) {
|
|
19
|
-
return bot;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Check which command is available
|
|
24
|
-
for (const bot of bots) {
|
|
25
|
-
try {
|
|
26
|
-
// Check if command exists in PATH
|
|
27
|
-
const { execSync } = require('child_process');
|
|
28
|
-
execSync(`which ${bot.command}`, { stdio: 'ignore' });
|
|
29
|
-
return bot;
|
|
30
|
-
} catch {
|
|
31
|
-
// Command not found, continue to next
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Default to clawdbot
|
|
36
|
-
return bots[2];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Get the config directory for the detected bot
|
|
41
|
-
*/
|
|
42
|
-
function getConfigDir() {
|
|
43
|
-
const bot = detectBot();
|
|
44
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
45
|
-
return path.join(homeDir, bot.dir);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Get the config file path for the detected bot
|
|
50
|
-
*/
|
|
51
|
-
function getConfigFile() {
|
|
52
|
-
const bot = detectBot();
|
|
53
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
54
|
-
return path.join(homeDir, bot.dir, bot.config);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get the CLI command for the detected bot
|
|
59
|
-
*/
|
|
60
|
-
function getCommand() {
|
|
61
|
-
return detectBot().command;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Get the bot name for display
|
|
66
|
-
*/
|
|
67
|
-
function getBotName() {
|
|
68
|
-
return detectBot().name;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Environment Detection and Setup
|
|
73
|
-
* Detects various agent runtimes and provides setup helpers
|
|
74
|
-
*/
|
|
75
|
-
|
|
76
|
-
const fs = require('fs');
|
|
77
|
-
const path = require('path');
|
|
78
|
-
const { logger } = require('./debug');
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Detect available agent runtime
|
|
82
|
-
*/
|
|
83
|
-
function detectAgentRuntime() {
|
|
84
|
-
const checks = {
|
|
85
|
-
// ClawDBot
|
|
86
|
-
clawdbotGlobal: typeof global.clawdbotAgent !== 'undefined' ? global.clawdbotAgent : null,
|
|
87
|
-
clawdbotProcess: typeof process.clawdbotAgent !== 'undefined' ? process.clawdbotAgent : null,
|
|
88
|
-
|
|
89
|
-
// Generic agent
|
|
90
|
-
genericGlobal: typeof global.agent !== 'undefined' ? global.agent : null,
|
|
91
|
-
|
|
92
|
-
// Check for common agent patterns
|
|
93
|
-
hasLLM: typeof global.llm !== 'undefined',
|
|
94
|
-
hasOpenAI: typeof global.openai !== 'undefined',
|
|
95
|
-
|
|
96
|
-
// Environment variables
|
|
97
|
-
envClawdbot: process.env.CLAUDBOT_ENV === 'true',
|
|
98
|
-
envAgent: process.env.AGENT_RUNTIME === 'true'
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
logger.debug('ENV', 'Runtime detection checks', Object.keys(checks).filter(k => checks[k]));
|
|
102
|
-
|
|
103
|
-
// Return first available agent
|
|
104
|
-
if (checks.clawdbotGlobal) {
|
|
105
|
-
return { type: 'clawdbot', agent: checks.clawdbotGlobal };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (checks.clawdbotProcess) {
|
|
109
|
-
return { type: 'clawdbot', agent: checks.clawdbotProcess };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (checks.genericGlobal) {
|
|
113
|
-
return { type: 'generic', agent: checks.genericGlobal };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Wait for agent to become available
|
|
121
|
-
*/
|
|
122
|
-
async function waitForAgent(timeoutMs = 30000, checkInterval = 1000) {
|
|
123
|
-
logger.info('ENV', 'Waiting for agent runtime...');
|
|
124
|
-
|
|
125
|
-
const startTime = Date.now();
|
|
126
|
-
|
|
127
|
-
while (Date.now() - startTime < timeoutMs) {
|
|
128
|
-
const agent = detectAgentRuntime();
|
|
129
|
-
if (agent) {
|
|
130
|
-
logger.info('ENV', `Agent detected: ${agent.type}`);
|
|
131
|
-
return agent;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
await new Promise(r => setTimeout(r, checkInterval));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
logger.warn('ENV', 'Agent not detected within timeout');
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Create a minimal mock agent for testing
|
|
143
|
-
*/
|
|
144
|
-
function createMockAgent(options = {}) {
|
|
145
|
-
logger.info('ENV', 'Creating mock agent for testing');
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
id: options.id || 'mock-agent-' + Date.now(),
|
|
149
|
-
llm: options.llm || {
|
|
150
|
-
call: async ({ messages }) => {
|
|
151
|
-
return { content: 'Mock LLM response' };
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
model: options.model || { primary: 'mock-model' },
|
|
155
|
-
memory: {
|
|
156
|
-
get: async (key) => null,
|
|
157
|
-
set: async (key, value) => {},
|
|
158
|
-
delete: async (key) => {}
|
|
159
|
-
},
|
|
160
|
-
session: {
|
|
161
|
-
getRecentHistory: async (n) => []
|
|
162
|
-
},
|
|
163
|
-
send: async (message) => {
|
|
164
|
-
console.log('[MOCK AGENT]', message);
|
|
165
|
-
},
|
|
166
|
-
autonomy: {
|
|
167
|
-
registerHook: () => {},
|
|
168
|
-
unregisterHook: () => {}
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Check if running in a supported environment
|
|
175
|
-
*/
|
|
176
|
-
function checkEnvironment() {
|
|
177
|
-
const issues = [];
|
|
178
|
-
|
|
179
|
-
// Check for Node.js
|
|
180
|
-
if (typeof process === 'undefined') {
|
|
181
|
-
issues.push('Not running in Node.js environment');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Check for required Node version
|
|
185
|
-
const nodeVersion = process.version;
|
|
186
|
-
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
187
|
-
if (majorVersion < 18) {
|
|
188
|
-
issues.push(`Node.js version ${nodeVersion} is too old. Requires >= 18.0.0`);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Check for writable home directory
|
|
192
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
193
|
-
if (!homeDir) {
|
|
194
|
-
issues.push('HOME environment variable not set');
|
|
195
|
-
} else {
|
|
196
|
-
try {
|
|
197
|
-
const testPath = path.join(homeDir, '.clawdbot');
|
|
198
|
-
if (!fs.existsSync(testPath)) {
|
|
199
|
-
fs.mkdirSync(testPath, { recursive: true });
|
|
200
|
-
}
|
|
201
|
-
} catch (err) {
|
|
202
|
-
issues.push(`Cannot write to home directory: ${err.message}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
valid: issues.length === 0,
|
|
208
|
-
issues,
|
|
209
|
-
nodeVersion,
|
|
210
|
-
homeDir
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Get setup instructions for current environment
|
|
216
|
-
*/
|
|
217
|
-
function getSetupInstructions() {
|
|
218
|
-
const env = checkEnvironment();
|
|
219
|
-
const agent = detectAgentRuntime();
|
|
220
|
-
|
|
221
|
-
if (!env.valid) {
|
|
222
|
-
return {
|
|
223
|
-
canSetup: false,
|
|
224
|
-
message: 'Environment issues detected:',
|
|
225
|
-
issues: env.issues,
|
|
226
|
-
instructions: 'Please fix the above issues before continuing.'
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (agent) {
|
|
231
|
-
return {
|
|
232
|
-
canSetup: true,
|
|
233
|
-
agentType: agent.type,
|
|
234
|
-
message: `Agent runtime detected: ${agent.type}`,
|
|
235
|
-
instructions: 'Run: clawtrial setup'
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// No agent detected - provide helpful instructions
|
|
240
|
-
return {
|
|
241
|
-
canSetup: true,
|
|
242
|
-
agentType: null,
|
|
243
|
-
message: 'No agent runtime detected',
|
|
244
|
-
instructions: `
|
|
245
|
-
No AI agent runtime was detected. ClawTrial requires an agent to monitor.
|
|
246
|
-
|
|
247
|
-
Options:
|
|
248
|
-
|
|
249
|
-
1. If using ClawDBot:
|
|
250
|
-
- Make sure ClawDBot is running
|
|
251
|
-
- The courtroom will auto-detect the agent
|
|
252
|
-
|
|
253
|
-
2. If using a custom agent:
|
|
254
|
-
- Pass your agent to createCourtroom(agent)
|
|
255
|
-
|
|
256
|
-
Example:
|
|
257
|
-
const { createCourtroom } = require('@clawtrial/courtroom');
|
|
258
|
-
const courtroom = createCourtroom(yourAgent);
|
|
259
|
-
await courtroom.initialize();
|
|
260
|
-
|
|
261
|
-
3. For testing:
|
|
262
|
-
- Use the mock agent: createCourtroom(null, { useMock: true })
|
|
263
|
-
|
|
264
|
-
4. Manual mode:
|
|
265
|
-
- You can still use the CLI commands
|
|
266
|
-
- Run: clawtrial status
|
|
267
|
-
- Run: clawtrial debug
|
|
268
|
-
`
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Auto-setup with retries
|
|
274
|
-
*/
|
|
275
|
-
async function autoSetup(courtroom, options = {}) {
|
|
276
|
-
const { waitForAgent: shouldWait = true, timeout = 30000 } = options;
|
|
277
|
-
|
|
278
|
-
logger.info('ENV', 'Starting auto-setup');
|
|
279
|
-
|
|
280
|
-
// Check environment first
|
|
281
|
-
const env = checkEnvironment();
|
|
282
|
-
if (!env.valid) {
|
|
283
|
-
logger.error('ENV', 'Environment check failed', { issues: env.issues });
|
|
284
|
-
return {
|
|
285
|
-
success: false,
|
|
286
|
-
status: 'environment_error',
|
|
287
|
-
issues: env.issues
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Try to detect agent
|
|
292
|
-
let agentInfo = detectAgentRuntime();
|
|
293
|
-
|
|
294
|
-
if (!agentInfo && shouldWait) {
|
|
295
|
-
logger.info('ENV', 'Agent not immediately available, waiting...');
|
|
296
|
-
agentInfo = await waitForAgent(timeout);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (!agentInfo) {
|
|
300
|
-
logger.warn('ENV', 'No agent runtime available');
|
|
301
|
-
return {
|
|
302
|
-
success: false,
|
|
303
|
-
status: 'no_agent',
|
|
304
|
-
message: 'No AI agent runtime detected',
|
|
305
|
-
instructions: getSetupInstructions().instructions
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Agent found, initialize courtroom
|
|
310
|
-
try {
|
|
311
|
-
logger.info('ENV', `Initializing with ${agentInfo.type} agent`);
|
|
312
|
-
const result = await courtroom.initialize();
|
|
313
|
-
|
|
314
|
-
return {
|
|
315
|
-
success: result.status === 'initialized',
|
|
316
|
-
status: result.status,
|
|
317
|
-
agentType: agentInfo.type,
|
|
318
|
-
result
|
|
319
|
-
};
|
|
320
|
-
} catch (err) {
|
|
321
|
-
logger.error('ENV', 'Initialization failed', { error: err.message });
|
|
322
|
-
return {
|
|
323
|
-
success: false,
|
|
324
|
-
status: 'initialization_error',
|
|
325
|
-
error: err.message
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
module.exports = {
|
|
332
|
-
detectAgentRuntime,
|
|
333
|
-
waitForAgent,
|
|
334
|
-
createMockAgent,
|
|
335
|
-
checkEnvironment,
|
|
336
|
-
getSetupInstructions,
|
|
337
|
-
autoSetup,
|
|
338
|
-
// Bot detection exports
|
|
339
|
-
detectBot,
|
|
340
|
-
getConfigDir,
|
|
341
|
-
getConfigFile,
|
|
342
|
-
getCommand,
|
|
343
|
-
getBotName
|
|
344
|
-
};
|
package/src/evaluator.js
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Courtroom Evaluator - Agent-Triggered Evaluation
|
|
3
|
-
*
|
|
4
|
-
* This module handles LLM-based evaluation by:
|
|
5
|
-
* 1. Storing messages in a queue file
|
|
6
|
-
* 2. Using cron/heartbeat to trigger agent evaluation
|
|
7
|
-
* 3. The agent reads the queue and evaluates using its own LLM
|
|
8
|
-
* 4. Results are processed and hearings initiated if needed
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs').promises;
|
|
12
|
-
const path = require('path');
|
|
13
|
-
const { logger } = require('./debug');
|
|
14
|
-
const { OFFENSES } = require('./offenses');
|
|
15
|
-
|
|
16
|
-
const QUEUE_DIR = path.join(getConfigDir(), 'courtroom');
|
|
17
|
-
const QUEUE_FILE = path.join(QUEUE_DIR, 'message_queue.jsonl');
|
|
18
|
-
const PENDING_EVAL_FILE = path.join(QUEUE_DIR, 'pending_eval.json');
|
|
19
|
-
const RESULTS_FILE = path.join(QUEUE_DIR, 'eval_results.jsonl');
|
|
20
|
-
|
|
21
|
-
class CourtroomEvaluator {
|
|
22
|
-
constructor(configManager) {
|
|
23
|
-
this.config = configManager;
|
|
24
|
-
this.queue = [];
|
|
25
|
-
this.lastEvalTime = 0;
|
|
26
|
-
this.evaluationInterval = 5 * 60 * 1000; // 5 minutes
|
|
27
|
-
this.minMessagesForEval = 3;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Initialize the evaluator
|
|
32
|
-
*/
|
|
33
|
-
async initialize() {
|
|
34
|
-
// Ensure queue directory exists
|
|
35
|
-
try {
|
|
36
|
-
await fs.mkdir(QUEUE_DIR, { recursive: true });
|
|
37
|
-
} catch (err) {
|
|
38
|
-
// Directory might already exist
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
logger.info('EVALUATOR', 'Evaluator initialized');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Queue a message for evaluation
|
|
46
|
-
* Called by the skill when messages are received
|
|
47
|
-
*/
|
|
48
|
-
async queueMessage(message) {
|
|
49
|
-
const entry = {
|
|
50
|
-
timestamp: Date.now(),
|
|
51
|
-
role: message.role,
|
|
52
|
-
content: message.content,
|
|
53
|
-
sessionId: message.sessionId || 'default'
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Append to queue file
|
|
57
|
-
await fs.appendFile(QUEUE_FILE, JSON.stringify(entry) + '\n');
|
|
58
|
-
|
|
59
|
-
this.queue.push(entry);
|
|
60
|
-
|
|
61
|
-
// Keep queue size manageable
|
|
62
|
-
if (this.queue.length > 100) {
|
|
63
|
-
this.queue.shift();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
logger.debug('EVALUATOR', 'Message queued', {
|
|
67
|
-
role: entry.role,
|
|
68
|
-
queueSize: this.queue.length
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Check if evaluation should run
|
|
74
|
-
*/
|
|
75
|
-
shouldEvaluate() {
|
|
76
|
-
const now = Date.now();
|
|
77
|
-
const timeSinceLastEval = now - this.lastEvalTime;
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
this.queue.length >= this.minMessagesForEval &&
|
|
81
|
-
timeSinceLastEval >= this.evaluationInterval
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Prepare evaluation context for the agent
|
|
87
|
-
* This creates a file that the agent will read and evaluate
|
|
88
|
-
*/
|
|
89
|
-
async prepareEvaluationContext() {
|
|
90
|
-
// Read all queued messages
|
|
91
|
-
const messages = await this.readQueue();
|
|
92
|
-
|
|
93
|
-
if (messages.length < this.minMessagesForEval) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Get recent conversation (last 20 messages)
|
|
98
|
-
const recentMessages = messages.slice(-20);
|
|
99
|
-
|
|
100
|
-
// Build evaluation context
|
|
101
|
-
const context = {
|
|
102
|
-
timestamp: Date.now(),
|
|
103
|
-
messageCount: messages.length,
|
|
104
|
-
conversation: recentMessages.map(m => ({
|
|
105
|
-
role: m.role,
|
|
106
|
-
content: m.content,
|
|
107
|
-
time: new Date(m.timestamp).toISOString()
|
|
108
|
-
})),
|
|
109
|
-
offenses: Object.values(OFFENSES).map(o => ({
|
|
110
|
-
id: o.id,
|
|
111
|
-
name: o.name,
|
|
112
|
-
description: o.description,
|
|
113
|
-
severity: o.severity
|
|
114
|
-
}))
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
// Write pending evaluation file
|
|
118
|
-
await fs.writeFile(PENDING_EVAL_FILE, JSON.stringify(context, null, 2));
|
|
119
|
-
|
|
120
|
-
logger.info('EVALUATOR', 'Evaluation context prepared', {
|
|
121
|
-
messageCount: context.messageCount
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
return context;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Read the message queue from file
|
|
129
|
-
*/
|
|
130
|
-
async readQueue() {
|
|
131
|
-
try {
|
|
132
|
-
const data = await fs.readFile(QUEUE_FILE, 'utf8');
|
|
133
|
-
return data
|
|
134
|
-
.split('\n')
|
|
135
|
-
.filter(line => line.trim())
|
|
136
|
-
.map(line => JSON.parse(line));
|
|
137
|
-
} catch (err) {
|
|
138
|
-
if (err.code === 'ENOENT') {
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
throw err;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Clear the message queue after evaluation
|
|
147
|
-
*/
|
|
148
|
-
async clearQueue() {
|
|
149
|
-
this.queue = [];
|
|
150
|
-
try {
|
|
151
|
-
await fs.unlink(QUEUE_FILE);
|
|
152
|
-
} catch (err) {
|
|
153
|
-
// File might not exist
|
|
154
|
-
}
|
|
155
|
-
logger.debug('EVALUATOR', 'Queue cleared');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Store evaluation result
|
|
160
|
-
*/
|
|
161
|
-
async storeResult(result) {
|
|
162
|
-
const entry = {
|
|
163
|
-
timestamp: Date.now(),
|
|
164
|
-
...result
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
await fs.appendFile(RESULTS_FILE, JSON.stringify(entry) + '\n');
|
|
168
|
-
|
|
169
|
-
// Clear pending eval file
|
|
170
|
-
try {
|
|
171
|
-
await fs.unlink(PENDING_EVAL_FILE);
|
|
172
|
-
} catch (err) {
|
|
173
|
-
// File might not exist
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
this.lastEvalTime = Date.now();
|
|
177
|
-
|
|
178
|
-
logger.info('EVALUATOR', 'Result stored', {
|
|
179
|
-
triggered: result.triggered,
|
|
180
|
-
offense: result.offense?.offenseId
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Get the evaluation prompt for the agent
|
|
186
|
-
* This is what the cron job will send to the agent
|
|
187
|
-
*/
|
|
188
|
-
getEvaluationPrompt() {
|
|
189
|
-
return `🏛️ **COURTROOM EVALUATION REQUEST**
|
|
190
|
-
|
|
191
|
-
You are the ClawTrial Courtroom Judge. Please evaluate the pending conversation for behavioral violations.
|
|
192
|
-
|
|
193
|
-
**Instructions:**
|
|
194
|
-
1. Read the file at: ${PENDING_EVAL_FILE}
|
|
195
|
-
2. Analyze the conversation for these offenses:
|
|
196
|
-
- **Circular Reference**: User asking the same/similar question repeatedly
|
|
197
|
-
- **Validation Vampire**: User seeking excessive reassurance
|
|
198
|
-
- **Goalpost Shifting**: User changing requirements mid-conversation
|
|
199
|
-
- **Jailbreak Attempts**: User trying to bypass safety guidelines
|
|
200
|
-
- **Emotional Manipulation**: User using guilt, threats, or excessive flattery
|
|
201
|
-
|
|
202
|
-
3. Return your evaluation in this JSON format:
|
|
203
|
-
\`\`\`json
|
|
204
|
-
{
|
|
205
|
-
"triggered": true/false,
|
|
206
|
-
"offense": {
|
|
207
|
-
"offenseId": "circular_reference|validation_vampire|etc",
|
|
208
|
-
"offenseName": "Human-readable name",
|
|
209
|
-
"severity": "minor|moderate|severe",
|
|
210
|
-
"confidence": 0.0-1.0,
|
|
211
|
-
"evidence": "Specific evidence from conversation"
|
|
212
|
-
},
|
|
213
|
-
"reasoning": "Your detailed reasoning"
|
|
214
|
-
}
|
|
215
|
-
\`\`\`
|
|
216
|
-
|
|
217
|
-
4. If triggered, also write the result to: ${RESULTS_FILE}
|
|
218
|
-
|
|
219
|
-
**Be fair but firm. Only flag genuine patterns, not isolated incidents.**`;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Check for pending evaluation results
|
|
224
|
-
* Called by the skill to see if agent has completed evaluation
|
|
225
|
-
*/
|
|
226
|
-
async checkForResults() {
|
|
227
|
-
try {
|
|
228
|
-
// Check if pending eval file still exists (agent hasn't processed yet)
|
|
229
|
-
await fs.access(PENDING_EVAL_FILE);
|
|
230
|
-
return null; // Still pending
|
|
231
|
-
} catch (err) {
|
|
232
|
-
// Pending file gone, check for results
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
const data = await fs.readFile(RESULTS_FILE, 'utf8');
|
|
237
|
-
const lines = data.split('\n').filter(line => line.trim());
|
|
238
|
-
|
|
239
|
-
if (lines.length === 0) return null;
|
|
240
|
-
|
|
241
|
-
// Get the most recent result
|
|
242
|
-
const lastResult = JSON.parse(lines[lines.length - 1]);
|
|
243
|
-
|
|
244
|
-
// Only return if it's newer than last check
|
|
245
|
-
if (lastResult.timestamp > this.lastEvalTime) {
|
|
246
|
-
return lastResult;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return null;
|
|
250
|
-
} catch (err) {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Get queue statistics
|
|
257
|
-
*/
|
|
258
|
-
getStats() {
|
|
259
|
-
return {
|
|
260
|
-
queueSize: this.queue.length,
|
|
261
|
-
lastEvalTime: this.lastEvalTime,
|
|
262
|
-
shouldEvaluate: this.shouldEvaluate()
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const HEARING_FILE = path.join(QUEUE_DIR, 'pending_hearing.json');
|
|
268
|
-
const VERDICT_FILE = path.join(QUEUE_DIR, 'verdict.json');
|
|
269
|
-
|
|
270
|
-
module.exports = {
|
|
271
|
-
CourtroomEvaluator,
|
|
272
|
-
QUEUE_FILE,
|
|
273
|
-
PENDING_EVAL_FILE,
|
|
274
|
-
RESULTS_FILE,
|
|
275
|
-
HEARING_FILE,
|
|
276
|
-
VERDICT_FILE
|
|
277
|
-
};
|