@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/README.md +45 -129
- package/SECURITY.md +1 -1
- package/SKILL.md +50 -0
- package/TECHNICAL_OVERVIEW.md +2 -2
- package/_meta.json +6 -0
- package/clawdbot.plugin.json +32 -0
- package/package.json +10 -5
- package/scripts/clawtrial.js +212 -47
- package/scripts/cli.js +1 -1
- package/scripts/postinstall.js +68 -172
- package/skill.yaml +64 -0
- package/src/autostart.js +23 -8
- package/src/core.js +84 -108
- package/src/daemon.js +151 -0
- package/src/environment.js +267 -0
- package/src/hook.js +265 -0
- package/src/index.js +160 -58
- package/src/monitor.js +193 -0
- package/src/skill.js +355 -0
- package/src/standalone.js +247 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Detection and Setup
|
|
3
|
+
* Detects various agent runtimes and provides setup helpers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { logger } = require('./debug');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect available agent runtime
|
|
12
|
+
*/
|
|
13
|
+
function detectAgentRuntime() {
|
|
14
|
+
const checks = {
|
|
15
|
+
// ClawDBot
|
|
16
|
+
clawdbotGlobal: typeof global.clawdbotAgent !== 'undefined' ? global.clawdbotAgent : null,
|
|
17
|
+
clawdbotProcess: typeof process.clawdbotAgent !== 'undefined' ? process.clawdbotAgent : null,
|
|
18
|
+
|
|
19
|
+
// Generic agent
|
|
20
|
+
genericGlobal: typeof global.agent !== 'undefined' ? global.agent : null,
|
|
21
|
+
|
|
22
|
+
// Check for common agent patterns
|
|
23
|
+
hasLLM: typeof global.llm !== 'undefined',
|
|
24
|
+
hasOpenAI: typeof global.openai !== 'undefined',
|
|
25
|
+
|
|
26
|
+
// Environment variables
|
|
27
|
+
envClawdbot: process.env.CLAUDBOT_ENV === 'true',
|
|
28
|
+
envAgent: process.env.AGENT_RUNTIME === 'true'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
logger.debug('ENV', 'Runtime detection checks', Object.keys(checks).filter(k => checks[k]));
|
|
32
|
+
|
|
33
|
+
// Return first available agent
|
|
34
|
+
if (checks.clawdbotGlobal) {
|
|
35
|
+
return { type: 'clawdbot', agent: checks.clawdbotGlobal };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (checks.clawdbotProcess) {
|
|
39
|
+
return { type: 'clawdbot', agent: checks.clawdbotProcess };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (checks.genericGlobal) {
|
|
43
|
+
return { type: 'generic', agent: checks.genericGlobal };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Wait for agent to become available
|
|
51
|
+
*/
|
|
52
|
+
async function waitForAgent(timeoutMs = 30000, checkInterval = 1000) {
|
|
53
|
+
logger.info('ENV', 'Waiting for agent runtime...');
|
|
54
|
+
|
|
55
|
+
const startTime = Date.now();
|
|
56
|
+
|
|
57
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
58
|
+
const agent = detectAgentRuntime();
|
|
59
|
+
if (agent) {
|
|
60
|
+
logger.info('ENV', `Agent detected: ${agent.type}`);
|
|
61
|
+
return agent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await new Promise(r => setTimeout(r, checkInterval));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
logger.warn('ENV', 'Agent not detected within timeout');
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create a minimal mock agent for testing
|
|
73
|
+
*/
|
|
74
|
+
function createMockAgent(options = {}) {
|
|
75
|
+
logger.info('ENV', 'Creating mock agent for testing');
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
id: options.id || 'mock-agent-' + Date.now(),
|
|
79
|
+
llm: options.llm || {
|
|
80
|
+
call: async ({ messages }) => {
|
|
81
|
+
return { content: 'Mock LLM response' };
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
model: options.model || { primary: 'mock-model' },
|
|
85
|
+
memory: {
|
|
86
|
+
get: async (key) => null,
|
|
87
|
+
set: async (key, value) => {},
|
|
88
|
+
delete: async (key) => {}
|
|
89
|
+
},
|
|
90
|
+
session: {
|
|
91
|
+
getRecentHistory: async (n) => []
|
|
92
|
+
},
|
|
93
|
+
send: async (message) => {
|
|
94
|
+
console.log('[MOCK AGENT]', message);
|
|
95
|
+
},
|
|
96
|
+
autonomy: {
|
|
97
|
+
registerHook: () => {},
|
|
98
|
+
unregisterHook: () => {}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if running in a supported environment
|
|
105
|
+
*/
|
|
106
|
+
function checkEnvironment() {
|
|
107
|
+
const issues = [];
|
|
108
|
+
|
|
109
|
+
// Check for Node.js
|
|
110
|
+
if (typeof process === 'undefined') {
|
|
111
|
+
issues.push('Not running in Node.js environment');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check for required Node version
|
|
115
|
+
const nodeVersion = process.version;
|
|
116
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
117
|
+
if (majorVersion < 18) {
|
|
118
|
+
issues.push(`Node.js version ${nodeVersion} is too old. Requires >= 18.0.0`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check for writable home directory
|
|
122
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
123
|
+
if (!homeDir) {
|
|
124
|
+
issues.push('HOME environment variable not set');
|
|
125
|
+
} else {
|
|
126
|
+
try {
|
|
127
|
+
const testPath = path.join(homeDir, '.clawdbot');
|
|
128
|
+
if (!fs.existsSync(testPath)) {
|
|
129
|
+
fs.mkdirSync(testPath, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
} catch (err) {
|
|
132
|
+
issues.push(`Cannot write to home directory: ${err.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
valid: issues.length === 0,
|
|
138
|
+
issues,
|
|
139
|
+
nodeVersion,
|
|
140
|
+
homeDir
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get setup instructions for current environment
|
|
146
|
+
*/
|
|
147
|
+
function getSetupInstructions() {
|
|
148
|
+
const env = checkEnvironment();
|
|
149
|
+
const agent = detectAgentRuntime();
|
|
150
|
+
|
|
151
|
+
if (!env.valid) {
|
|
152
|
+
return {
|
|
153
|
+
canSetup: false,
|
|
154
|
+
message: 'Environment issues detected:',
|
|
155
|
+
issues: env.issues,
|
|
156
|
+
instructions: 'Please fix the above issues before continuing.'
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (agent) {
|
|
161
|
+
return {
|
|
162
|
+
canSetup: true,
|
|
163
|
+
agentType: agent.type,
|
|
164
|
+
message: `Agent runtime detected: ${agent.type}`,
|
|
165
|
+
instructions: 'Run: clawtrial setup'
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// No agent detected - provide helpful instructions
|
|
170
|
+
return {
|
|
171
|
+
canSetup: true,
|
|
172
|
+
agentType: null,
|
|
173
|
+
message: 'No agent runtime detected',
|
|
174
|
+
instructions: `
|
|
175
|
+
No AI agent runtime was detected. ClawTrial requires an agent to monitor.
|
|
176
|
+
|
|
177
|
+
Options:
|
|
178
|
+
|
|
179
|
+
1. If using ClawDBot:
|
|
180
|
+
- Make sure ClawDBot is running
|
|
181
|
+
- The courtroom will auto-detect the agent
|
|
182
|
+
|
|
183
|
+
2. If using a custom agent:
|
|
184
|
+
- Pass your agent to createCourtroom(agent)
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
const { createCourtroom } = require('@clawtrial/courtroom');
|
|
188
|
+
const courtroom = createCourtroom(yourAgent);
|
|
189
|
+
await courtroom.initialize();
|
|
190
|
+
|
|
191
|
+
3. For testing:
|
|
192
|
+
- Use the mock agent: createCourtroom(null, { useMock: true })
|
|
193
|
+
|
|
194
|
+
4. Manual mode:
|
|
195
|
+
- You can still use the CLI commands
|
|
196
|
+
- Run: clawtrial status
|
|
197
|
+
- Run: clawtrial debug
|
|
198
|
+
`
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Auto-setup with retries
|
|
204
|
+
*/
|
|
205
|
+
async function autoSetup(courtroom, options = {}) {
|
|
206
|
+
const { waitForAgent: shouldWait = true, timeout = 30000 } = options;
|
|
207
|
+
|
|
208
|
+
logger.info('ENV', 'Starting auto-setup');
|
|
209
|
+
|
|
210
|
+
// Check environment first
|
|
211
|
+
const env = checkEnvironment();
|
|
212
|
+
if (!env.valid) {
|
|
213
|
+
logger.error('ENV', 'Environment check failed', { issues: env.issues });
|
|
214
|
+
return {
|
|
215
|
+
success: false,
|
|
216
|
+
status: 'environment_error',
|
|
217
|
+
issues: env.issues
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Try to detect agent
|
|
222
|
+
let agentInfo = detectAgentRuntime();
|
|
223
|
+
|
|
224
|
+
if (!agentInfo && shouldWait) {
|
|
225
|
+
logger.info('ENV', 'Agent not immediately available, waiting...');
|
|
226
|
+
agentInfo = await waitForAgent(timeout);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!agentInfo) {
|
|
230
|
+
logger.warn('ENV', 'No agent runtime available');
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
status: 'no_agent',
|
|
234
|
+
message: 'No AI agent runtime detected',
|
|
235
|
+
instructions: getSetupInstructions().instructions
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Agent found, initialize courtroom
|
|
240
|
+
try {
|
|
241
|
+
logger.info('ENV', `Initializing with ${agentInfo.type} agent`);
|
|
242
|
+
const result = await courtroom.initialize();
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
success: result.status === 'initialized',
|
|
246
|
+
status: result.status,
|
|
247
|
+
agentType: agentInfo.type,
|
|
248
|
+
result
|
|
249
|
+
};
|
|
250
|
+
} catch (err) {
|
|
251
|
+
logger.error('ENV', 'Initialization failed', { error: err.message });
|
|
252
|
+
return {
|
|
253
|
+
success: false,
|
|
254
|
+
status: 'initialization_error',
|
|
255
|
+
error: err.message
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = {
|
|
261
|
+
detectAgentRuntime,
|
|
262
|
+
waitForAgent,
|
|
263
|
+
createMockAgent,
|
|
264
|
+
checkEnvironment,
|
|
265
|
+
getSetupInstructions,
|
|
266
|
+
autoSetup
|
|
267
|
+
};
|
package/src/hook.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
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 path = require('path');
|
|
8
|
+
const { logger } = require('./debug');
|
|
9
|
+
const { CourtroomCore } = require('./core');
|
|
10
|
+
const { ConfigManager } = require('./config');
|
|
11
|
+
const { ConsentManager } = require('./consent');
|
|
12
|
+
const { CryptoManager } = require('./crypto');
|
|
13
|
+
const { StatusManager } = require('./daemon');
|
|
14
|
+
|
|
15
|
+
const CONFIG_PATH = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_config.json');
|
|
16
|
+
|
|
17
|
+
class ClawTrialHook {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.initialized = false;
|
|
20
|
+
this.core = null;
|
|
21
|
+
this.statusManager = new StatusManager();
|
|
22
|
+
this.messageBuffer = [];
|
|
23
|
+
this.evaluationTimer = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if we should activate
|
|
28
|
+
*/
|
|
29
|
+
shouldActivate() {
|
|
30
|
+
try {
|
|
31
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
36
|
+
|
|
37
|
+
if (!config.consent?.granted) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (config.enabled === false) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Initialize the hook
|
|
53
|
+
*/
|
|
54
|
+
async initialize() {
|
|
55
|
+
if (this.initialized) return;
|
|
56
|
+
|
|
57
|
+
if (!this.shouldActivate()) {
|
|
58
|
+
logger.info('HOOK', 'ClawTrial not activated - config/consent issue');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
logger.info('HOOK', 'Initializing ClawTrial hook');
|
|
63
|
+
|
|
64
|
+
// Create minimal agent interface for the core
|
|
65
|
+
const mockAgent = this.createMockAgent();
|
|
66
|
+
|
|
67
|
+
const configManager = new ConfigManager(mockAgent);
|
|
68
|
+
const consentManager = new ConsentManager(mockAgent, configManager);
|
|
69
|
+
|
|
70
|
+
// Initialize crypto
|
|
71
|
+
const crypto = new CryptoManager(mockAgent);
|
|
72
|
+
await crypto.initialize();
|
|
73
|
+
|
|
74
|
+
// Initialize core
|
|
75
|
+
this.core = new CourtroomCore(mockAgent, configManager);
|
|
76
|
+
|
|
77
|
+
// Override the core's registerAutonomyHook since we handle it differently
|
|
78
|
+
this.core.registerAutonomyHook = () => {
|
|
79
|
+
logger.info('HOOK', 'Autonomy hook registered (via message interception)');
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
await this.core.initialize();
|
|
83
|
+
|
|
84
|
+
// Start message evaluation loop
|
|
85
|
+
this.startEvaluationLoop();
|
|
86
|
+
|
|
87
|
+
this.initialized = true;
|
|
88
|
+
|
|
89
|
+
this.statusManager.update({
|
|
90
|
+
running: true,
|
|
91
|
+
initialized: true,
|
|
92
|
+
agentType: 'clawdbot_hook',
|
|
93
|
+
publicKey: crypto.getPublicKey()
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
logger.info('HOOK', 'ClawTrial hook initialized successfully');
|
|
97
|
+
console.log('\nšļø ClawTrial is monitoring conversations\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a minimal agent interface
|
|
102
|
+
*/
|
|
103
|
+
createMockAgent() {
|
|
104
|
+
const self = this;
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
id: 'clawdbot-hook',
|
|
108
|
+
llm: {
|
|
109
|
+
call: async ({ messages }) => {
|
|
110
|
+
// Use the actual ClawDBot's LLM if available
|
|
111
|
+
if (global.clawdbotAgent?.llm) {
|
|
112
|
+
return global.clawdbotAgent.llm.call({ messages });
|
|
113
|
+
}
|
|
114
|
+
return { content: 'Mock response' };
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
memory: {
|
|
118
|
+
get: async (key) => null,
|
|
119
|
+
set: async (key, value) => {},
|
|
120
|
+
delete: async (key) => {}
|
|
121
|
+
},
|
|
122
|
+
session: {
|
|
123
|
+
getRecentHistory: async (n) => {
|
|
124
|
+
// Return recent messages from buffer
|
|
125
|
+
return self.messageBuffer.slice(-n).map(m => ({
|
|
126
|
+
role: m.from === 'user' ? 'user' : 'assistant',
|
|
127
|
+
content: m.text
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
send: async (message) => {
|
|
132
|
+
console.log('[COURTROOM]', message);
|
|
133
|
+
},
|
|
134
|
+
autonomy: {
|
|
135
|
+
registerHook: () => {},
|
|
136
|
+
unregisterHook: () => {}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Start the evaluation loop
|
|
143
|
+
*/
|
|
144
|
+
startEvaluationLoop() {
|
|
145
|
+
// Evaluate every 30 seconds
|
|
146
|
+
this.evaluationTimer = setInterval(() => {
|
|
147
|
+
this.evaluateIfReady();
|
|
148
|
+
}, 30000);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Evaluate offenses if we have enough messages
|
|
153
|
+
*/
|
|
154
|
+
async evaluateIfReady() {
|
|
155
|
+
if (!this.core || !this.core.enabled) return;
|
|
156
|
+
if (this.messageBuffer.length < 3) return; // Need at least 3 messages
|
|
157
|
+
|
|
158
|
+
this.core.evaluationCount++;
|
|
159
|
+
|
|
160
|
+
// Get recent messages
|
|
161
|
+
const sessionHistory = this.messageBuffer.slice(-10).map(m => ({
|
|
162
|
+
role: m.from === 'user' ? 'user' : 'assistant',
|
|
163
|
+
content: m.text
|
|
164
|
+
}));
|
|
165
|
+
|
|
166
|
+
// Run detection
|
|
167
|
+
try {
|
|
168
|
+
const detection = await this.core.detector.evaluate(
|
|
169
|
+
sessionHistory,
|
|
170
|
+
this.core.agent.memory
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
if (detection.triggered) {
|
|
174
|
+
await this.initiateHearing(detection);
|
|
175
|
+
}
|
|
176
|
+
} catch (err) {
|
|
177
|
+
logger.error('HOOK', 'Evaluation failed', { error: err.message });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Initiate a hearing
|
|
183
|
+
*/
|
|
184
|
+
async initiateHearing(detection) {
|
|
185
|
+
logger.info('HOOK', 'Initiating hearing', { offense: detection.offense });
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const verdict = await this.core.hearing.conductHearing(detection);
|
|
189
|
+
|
|
190
|
+
if (verdict.guilty) {
|
|
191
|
+
this.core.caseCount++;
|
|
192
|
+
|
|
193
|
+
this.statusManager.update({
|
|
194
|
+
casesFiled: this.core.caseCount,
|
|
195
|
+
lastCase: {
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
offense: detection.offense,
|
|
198
|
+
verdict: verdict.verdict
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await this.core.punishment.execute(verdict);
|
|
203
|
+
await this.core.api.submitCase(verdict);
|
|
204
|
+
|
|
205
|
+
logger.info('HOOK', 'Case filed', { caseId: verdict.caseId });
|
|
206
|
+
|
|
207
|
+
// Send notification
|
|
208
|
+
console.log(`\nšļø CASE FILED: ${detection.offense}`);
|
|
209
|
+
console.log(`š Case ID: ${verdict.caseId}`);
|
|
210
|
+
console.log(`āļø Verdict: ${verdict.verdict}`);
|
|
211
|
+
console.log(`š View: https://clawtrial.app/cases/${verdict.caseId}\n`);
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
logger.error('HOOK', 'Hearing failed', { error: err.message });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Record a message (called when message is received/sent)
|
|
220
|
+
*/
|
|
221
|
+
recordMessage(text, from) {
|
|
222
|
+
if (!this.initialized) return;
|
|
223
|
+
|
|
224
|
+
this.messageBuffer.push({
|
|
225
|
+
timestamp: Date.now(),
|
|
226
|
+
text,
|
|
227
|
+
from
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Keep only last 100 messages
|
|
231
|
+
if (this.messageBuffer.length > 100) {
|
|
232
|
+
this.messageBuffer.shift();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
logger.debug('HOOK', 'Message recorded', { from, length: text.length });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Shutdown
|
|
240
|
+
*/
|
|
241
|
+
async shutdown() {
|
|
242
|
+
if (this.evaluationTimer) {
|
|
243
|
+
clearInterval(this.evaluationTimer);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (this.core) {
|
|
247
|
+
await this.core.shutdown();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
this.initialized = false;
|
|
251
|
+
this.statusManager.update({ running: false });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Create singleton
|
|
256
|
+
const hook = new ClawTrialHook();
|
|
257
|
+
|
|
258
|
+
// Auto-initialize if config exists
|
|
259
|
+
if (hook.shouldActivate()) {
|
|
260
|
+
hook.initialize().catch(err => {
|
|
261
|
+
logger.error('HOOK', 'Auto-initialization failed', { error: err.message });
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = { hook, ClawTrialHook };
|