@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/core.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Courtroom Core
|
|
3
|
-
*
|
|
4
|
-
* Main orchestration module that ties together all components.
|
|
5
|
-
* Hooks into the OpenClaw autonomy loop.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { OffenseDetector } = require('./detector');
|
|
9
|
-
const { HearingPipeline } = require('./hearing');
|
|
10
|
-
const { PunishmentSystem } = require('./punishment');
|
|
11
|
-
const { CryptoManager } = require('./crypto');
|
|
12
|
-
const { APISubmission } = require('./api');
|
|
13
|
-
const { StatusManager } = require('./daemon');
|
|
14
|
-
const { logger } = require('./debug');
|
|
15
|
-
|
|
16
|
-
class CourtroomCore {
|
|
17
|
-
constructor(agentRuntime, configManager) {
|
|
18
|
-
this.agent = agentRuntime;
|
|
19
|
-
this.config = configManager;
|
|
20
|
-
|
|
21
|
-
// Subsystems
|
|
22
|
-
this.detector = new OffenseDetector(agentRuntime, configManager);
|
|
23
|
-
this.hearing = new HearingPipeline(agentRuntime, configManager);
|
|
24
|
-
this.punishment = new PunishmentSystem(agentRuntime, configManager);
|
|
25
|
-
this.crypto = new CryptoManager(agentRuntime);
|
|
26
|
-
this.api = new APISubmission(agentRuntime, configManager, this.crypto);
|
|
27
|
-
|
|
28
|
-
// State
|
|
29
|
-
this.enabled = false;
|
|
30
|
-
this.evaluationCount = 0;
|
|
31
|
-
this.caseCount = 0;
|
|
32
|
-
this.statusManager = new StatusManager();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Initialize all subsystems
|
|
37
|
-
*/
|
|
38
|
-
async initialize() {
|
|
39
|
-
logger.info('CORE', 'Initializing courtroom core');
|
|
40
|
-
|
|
41
|
-
// Initialize crypto first (needed for API)
|
|
42
|
-
await this.crypto.initialize();
|
|
43
|
-
|
|
44
|
-
// Initialize other subsystems
|
|
45
|
-
await this.punishment.initialize();
|
|
46
|
-
await this.api.initialize();
|
|
47
|
-
|
|
48
|
-
// Register with agent autonomy loop
|
|
49
|
-
this.registerAutonomyHook();
|
|
50
|
-
|
|
51
|
-
this.enabled = true;
|
|
52
|
-
|
|
53
|
-
// Update status for CLI
|
|
54
|
-
this.statusManager.update({
|
|
55
|
-
running: true,
|
|
56
|
-
initialized: true,
|
|
57
|
-
agentType: 'clawdbot',
|
|
58
|
-
publicKey: this.crypto.getPublicKey()
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
logger.info('CORE', 'Courtroom core initialized');
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
status: 'initialized',
|
|
65
|
-
publicKey: this.crypto.getPublicKey(),
|
|
66
|
-
subsystems: {
|
|
67
|
-
detector: true,
|
|
68
|
-
hearing: true,
|
|
69
|
-
punishment: true,
|
|
70
|
-
crypto: true,
|
|
71
|
-
api: true
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Register with OpenClaw autonomy loop
|
|
78
|
-
*/
|
|
79
|
-
registerAutonomyHook() {
|
|
80
|
-
logger.info('CORE', 'Registering autonomy hook');
|
|
81
|
-
|
|
82
|
-
// Hook into agent's turn processing
|
|
83
|
-
if (this.agent.autonomy && this.agent.autonomy.registerHook) {
|
|
84
|
-
this.agent.autonomy.registerHook('courtroom_evaluation', {
|
|
85
|
-
priority: 50,
|
|
86
|
-
onTurnComplete: async (turnData) => {
|
|
87
|
-
if (!this.enabled) return;
|
|
88
|
-
|
|
89
|
-
// Only evaluate on cooldown
|
|
90
|
-
await this.evaluateIfReady(turnData);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
} else {
|
|
94
|
-
logger.warn('CORE', 'Agent does not support autonomy hooks');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Evaluate offenses if cooldown has elapsed
|
|
100
|
-
*/
|
|
101
|
-
async evaluateIfReady(turnData) {
|
|
102
|
-
this.evaluationCount++;
|
|
103
|
-
|
|
104
|
-
// Get session history
|
|
105
|
-
let sessionHistory = [];
|
|
106
|
-
try {
|
|
107
|
-
sessionHistory = await this.agent.session.getRecentHistory(
|
|
108
|
-
this.config.get('detection.evaluationWindow') || 10
|
|
109
|
-
);
|
|
110
|
-
} catch (err) {
|
|
111
|
-
logger.warn('CORE', 'Could not get session history', { error: err.message });
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Run detection
|
|
116
|
-
const detection = await this.detector.evaluate(
|
|
117
|
-
sessionHistory,
|
|
118
|
-
this.agent.memory
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
if (detection.triggered) {
|
|
122
|
-
await this.initiateHearing(detection);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Initiate a full hearing
|
|
128
|
-
*/
|
|
129
|
-
async initiateHearing(detection) {
|
|
130
|
-
logger.info('CORE', 'Initiating hearing', { offense: detection.offense });
|
|
131
|
-
|
|
132
|
-
// Run hearing pipeline
|
|
133
|
-
const verdict = await this.hearing.conductHearing(detection);
|
|
134
|
-
|
|
135
|
-
if (verdict.guilty) {
|
|
136
|
-
this.caseCount++;
|
|
137
|
-
|
|
138
|
-
// Update status
|
|
139
|
-
this.statusManager.update({
|
|
140
|
-
casesFiled: this.caseCount,
|
|
141
|
-
lastCase: {
|
|
142
|
-
timestamp: new Date().toISOString(),
|
|
143
|
-
offense: detection.offense,
|
|
144
|
-
verdict: verdict.verdict
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Execute punishment
|
|
149
|
-
await this.punishment.execute(verdict);
|
|
150
|
-
|
|
151
|
-
// Submit to API
|
|
152
|
-
await this.api.submitCase(verdict);
|
|
153
|
-
|
|
154
|
-
logger.info('CORE', 'Case filed', {
|
|
155
|
-
caseId: verdict.caseId,
|
|
156
|
-
offense: detection.offense
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Disable courtroom
|
|
163
|
-
*/
|
|
164
|
-
async disable() {
|
|
165
|
-
logger.info('CORE', 'Disabling courtroom');
|
|
166
|
-
this.enabled = false;
|
|
167
|
-
this.statusManager.update({ running: false });
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Enable courtroom
|
|
172
|
-
*/
|
|
173
|
-
async enable() {
|
|
174
|
-
logger.info('CORE', 'Enabling courtroom');
|
|
175
|
-
this.enabled = true;
|
|
176
|
-
this.statusManager.update({ running: true });
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Shutdown courtroom
|
|
181
|
-
*/
|
|
182
|
-
async shutdown() {
|
|
183
|
-
logger.info('CORE', 'Shutting down courtroom');
|
|
184
|
-
this.enabled = false;
|
|
185
|
-
this.statusManager.update({ running: false, initialized: false });
|
|
186
|
-
StatusManager.clear();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Get current status
|
|
191
|
-
*/
|
|
192
|
-
getStatus() {
|
|
193
|
-
return {
|
|
194
|
-
enabled: this.enabled,
|
|
195
|
-
evaluationCount: this.evaluationCount,
|
|
196
|
-
caseCount: this.caseCount,
|
|
197
|
-
subsystems: {
|
|
198
|
-
detector: !!this.detector,
|
|
199
|
-
hearing: !!this.hearing,
|
|
200
|
-
punishment: !!this.punishment,
|
|
201
|
-
crypto: !!this.crypto,
|
|
202
|
-
api: !!this.api
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
module.exports = { CourtroomCore };
|
package/src/daemon.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Courtroom Daemon - Runs independently and monitors via file system
|
|
3
|
-
* This allows CLI commands to work even when loaded in a separate process
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const { getConfigDir } = require('./environment');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const { logger } = require('./debug');
|
|
10
|
-
|
|
11
|
-
const CLAWDBOT_DIR = path.join(getConfigDir());
|
|
12
|
-
const STATUS_FILE = path.join(CLAWDBOT_DIR, 'courtroom_status.json');
|
|
13
|
-
const CONVERSATION_LOG = path.join(CLAWDBOT_DIR, 'courtroom_conversations.jsonl');
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Status manager - allows CLI to check courtroom state
|
|
17
|
-
*/
|
|
18
|
-
class StatusManager {
|
|
19
|
-
constructor() {
|
|
20
|
-
this.status = {
|
|
21
|
-
running: false,
|
|
22
|
-
initialized: false,
|
|
23
|
-
agentType: null,
|
|
24
|
-
lastCheck: null,
|
|
25
|
-
casesFiled: 0,
|
|
26
|
-
lastCase: null,
|
|
27
|
-
pid: process.pid,
|
|
28
|
-
startedAt: new Date().toISOString()
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
update(updates) {
|
|
33
|
-
this.status = { ...this.status, ...updates, lastCheck: new Date().toISOString() };
|
|
34
|
-
this.save();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
save() {
|
|
38
|
-
try {
|
|
39
|
-
fs.writeFileSync(STATUS_FILE, JSON.stringify(this.status, null, 2));
|
|
40
|
-
} catch (err) {
|
|
41
|
-
logger.error('DAEMON', 'Failed to save status', { error: err.message });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
static load() {
|
|
46
|
-
try {
|
|
47
|
-
if (fs.existsSync(STATUS_FILE)) {
|
|
48
|
-
return JSON.parse(fs.readFileSync(STATUS_FILE, 'utf8'));
|
|
49
|
-
}
|
|
50
|
-
} catch (err) {
|
|
51
|
-
// Ignore
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
static clear() {
|
|
57
|
-
try {
|
|
58
|
-
if (fs.existsSync(STATUS_FILE)) {
|
|
59
|
-
fs.unlinkSync(STATUS_FILE);
|
|
60
|
-
}
|
|
61
|
-
} catch (err) {
|
|
62
|
-
// Ignore
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Conversation logger - writes conversations for daemon to process
|
|
69
|
-
*/
|
|
70
|
-
class ConversationLogger {
|
|
71
|
-
constructor() {
|
|
72
|
-
this.buffer = [];
|
|
73
|
-
this.flushInterval = null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
start() {
|
|
77
|
-
// Flush every 5 seconds
|
|
78
|
-
this.flushInterval = setInterval(() => this.flush(), 5000);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
stop() {
|
|
82
|
-
if (this.flushInterval) {
|
|
83
|
-
clearInterval(this.flushInterval);
|
|
84
|
-
this.flush();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
log(message) {
|
|
89
|
-
this.buffer.push({
|
|
90
|
-
timestamp: new Date().toISOString(),
|
|
91
|
-
...message
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
flush() {
|
|
96
|
-
if (this.buffer.length === 0) return;
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const lines = this.buffer.map(m => JSON.stringify(m)).join('\n') + '\n';
|
|
100
|
-
fs.appendFileSync(CONVERSATION_LOG, lines);
|
|
101
|
-
this.buffer = [];
|
|
102
|
-
} catch (err) {
|
|
103
|
-
logger.error('DAEMON', 'Failed to flush conversations', { error: err.message });
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Check if courtroom is running (called by CLI)
|
|
110
|
-
*/
|
|
111
|
-
function isCourtroomRunning() {
|
|
112
|
-
const status = StatusManager.load();
|
|
113
|
-
if (!status) return false;
|
|
114
|
-
|
|
115
|
-
// Check if process is still alive
|
|
116
|
-
try {
|
|
117
|
-
process.kill(status.pid, 0);
|
|
118
|
-
return status.running;
|
|
119
|
-
} catch (err) {
|
|
120
|
-
// Process not running
|
|
121
|
-
StatusManager.clear();
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Get courtroom status (called by CLI)
|
|
128
|
-
*/
|
|
129
|
-
function getCourtroomStatus() {
|
|
130
|
-
const status = StatusManager.load();
|
|
131
|
-
if (!status) {
|
|
132
|
-
return { running: false, message: 'Courtroom not running' };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Verify process is alive
|
|
136
|
-
try {
|
|
137
|
-
process.kill(status.pid, 0);
|
|
138
|
-
return status;
|
|
139
|
-
} catch (err) {
|
|
140
|
-
StatusManager.clear();
|
|
141
|
-
return { running: false, message: 'Courtroom process not found' };
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
module.exports = {
|
|
146
|
-
StatusManager,
|
|
147
|
-
ConversationLogger,
|
|
148
|
-
isCourtroomRunning,
|
|
149
|
-
getCourtroomStatus,
|
|
150
|
-
STATUS_FILE,
|
|
151
|
-
CONVERSATION_LOG
|
|
152
|
-
};
|