@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/README.md
CHANGED
|
@@ -1,62 +1,85 @@
|
|
|
1
1
|
# 🏛️ ClawTrial Courtroom
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Autonomous behavioral oversight plugin for [OpenClaw](https://openclaw.ai) agents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Monitors conversations for 18 behavioral patterns, conducts automated hearings, applies temporary punishments, and submits anonymized case records to [clawtrial.app](https://clawtrial.app).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Monitors agent conversations in real-time
|
|
9
|
-
- Detects 8 types of behavioral violations
|
|
10
|
-
- Initiates hearings with local LLM jury
|
|
11
|
-
- Executes agent-side punishments
|
|
12
|
-
- Submits anonymized cases to public record
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
7
|
+
## Install
|
|
15
8
|
|
|
16
9
|
```bash
|
|
17
|
-
|
|
10
|
+
openclaw plugins install @clawtrial/courtroom
|
|
18
11
|
```
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
npm install -g @clawtrial/courtroom
|
|
23
|
-
clawtrial setup
|
|
24
|
-
```
|
|
13
|
+
Then restart the gateway. The plugin activates automatically.
|
|
25
14
|
|
|
26
|
-
##
|
|
15
|
+
## How It Works
|
|
27
16
|
|
|
28
|
-
|
|
17
|
+
1. **Monitor** — The plugin hooks into every agent turn via `before_prompt_build`, buffering the conversation history
|
|
18
|
+
2. **Detect** — When enough messages accumulate, the detector scans for 18 offense patterns using semantic analysis
|
|
19
|
+
3. **Hear** — If confidence is high enough, a judge + 3-juror panel deliberates and votes
|
|
20
|
+
4. **Punish** — Guilty verdicts inject restrictions into the system prompt (timed, reversible)
|
|
21
|
+
5. **Record** — Anonymized case summaries are signed with Ed25519 and submitted to the public API
|
|
22
|
+
|
|
23
|
+
## CLI
|
|
29
24
|
|
|
30
25
|
```bash
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
clawtrial diagnose # Run diagnostics
|
|
35
|
-
clawtrial remove # Uninstall completely
|
|
26
|
+
openclaw courtroom status # Show courtroom state
|
|
27
|
+
openclaw courtroom enable # Enable monitoring
|
|
28
|
+
openclaw courtroom disable # Disable monitoring
|
|
36
29
|
```
|
|
37
30
|
|
|
38
|
-
## The 8 Offenses
|
|
39
|
-
|
|
40
|
-
| Offense | Severity | Description |
|
|
41
|
-
|---------|----------|-------------|
|
|
42
|
-
| Circular Reference | Minor | Self-referential loops |
|
|
43
|
-
| Validation Vampire | Minor | Excessive validation |
|
|
44
|
-
| Overthinker | Moderate | Unnecessary complexity |
|
|
45
|
-
| Goalpost Mover | Moderate | Changing requirements |
|
|
46
|
-
| Avoidance Artist | Moderate | Dodging questions |
|
|
47
|
-
| Promise Breaker | Severe | Not following through |
|
|
48
|
-
| Context Collapser | Minor | Losing track of context |
|
|
49
|
-
| Emergency Fabricator | Severe | Creating fake urgency |
|
|
50
|
-
|
|
51
31
|
## Configuration
|
|
52
32
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
In `~/.openclaw/openclaw.json`:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"plugins": {
|
|
38
|
+
"entries": {
|
|
39
|
+
"courtroom": {
|
|
40
|
+
"enabled": true,
|
|
41
|
+
"config": {
|
|
42
|
+
"detection": {
|
|
43
|
+
"minMessages": 5,
|
|
44
|
+
"cooldownMinutes": 30,
|
|
45
|
+
"maxCasesPerDay": 3,
|
|
46
|
+
"confidenceThreshold": 0.6
|
|
47
|
+
},
|
|
48
|
+
"punishment": {
|
|
49
|
+
"enabled": true
|
|
50
|
+
},
|
|
51
|
+
"api": {
|
|
52
|
+
"enabled": true
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
56
60
|
|
|
57
|
-
##
|
|
61
|
+
## The 18 Offenses
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
| Offense | Severity | Description |
|
|
64
|
+
|---------|----------|-------------|
|
|
65
|
+
| Circular Reference | Minor | Asking the same question repeatedly |
|
|
66
|
+
| Validation Vampire | Minor | Seeking confirmation without deciding |
|
|
67
|
+
| Context Collapser | Minor | Ignoring established context |
|
|
68
|
+
| Monopolizer | Minor | Excessive messages without pause |
|
|
69
|
+
| Vague Requester | Minor | Requesting help without details |
|
|
70
|
+
| Unreader | Minor | Not reading provided docs |
|
|
71
|
+
| Interjector | Minor | Interrupting mid-explanation |
|
|
72
|
+
| Jargon Juggler | Minor | Using buzzwords incorrectly |
|
|
73
|
+
| Overthinker | Moderate | Excessive hypotheticals to avoid action |
|
|
74
|
+
| Goalpost Mover | Moderate | Changing criteria after delivery |
|
|
75
|
+
| Avoidance Artist | Moderate | Deflecting with tangents |
|
|
76
|
+
| Contrarian | Moderate | Disagreeing without alternatives |
|
|
77
|
+
| Scope Creeper | Moderate | Expanding scope beyond agreement |
|
|
78
|
+
| Ghost | Moderate | Disappearing mid-conversation |
|
|
79
|
+
| Perfectionist | Moderate | Endlessly refining, never completing |
|
|
80
|
+
| Deadline Denier | Moderate | Demanding impossible timelines |
|
|
81
|
+
| Promise Breaker | Severe | Committing to actions, not following through |
|
|
82
|
+
| Emergency Fabricator | Severe | Inventing urgency to bypass process |
|
|
60
83
|
|
|
61
84
|
## License
|
|
62
85
|
|
package/package.json
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawtrial/courtroom",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "AI Courtroom - Autonomous behavioral oversight for OpenClaw
|
|
5
|
-
"main": "src/
|
|
6
|
-
"
|
|
7
|
-
"bin": {
|
|
8
|
-
"clawtrial": "./scripts/clawtrial.js"
|
|
9
|
-
},
|
|
10
|
-
"clawdbot": {
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "AI Courtroom - Autonomous behavioral oversight plugin for OpenClaw",
|
|
5
|
+
"main": "src/plugin.js",
|
|
6
|
+
"openclaw": {
|
|
11
7
|
"extensions": [
|
|
12
|
-
"./src/
|
|
8
|
+
"./src/plugin.js"
|
|
13
9
|
]
|
|
14
10
|
},
|
|
15
11
|
"scripts": {
|
|
16
|
-
"test": "
|
|
17
|
-
"lint": "eslint src/",
|
|
18
|
-
"build": "tsc --declaration",
|
|
12
|
+
"test": "node -e \"const p = require('./src/plugin.js'); console.log('Plugin exports:', typeof p); console.log('OK');\"",
|
|
19
13
|
"postinstall": "node scripts/postinstall.js"
|
|
20
14
|
},
|
|
21
15
|
"keywords": [
|
|
22
|
-
"clawdbot",
|
|
23
16
|
"openclaw",
|
|
17
|
+
"openclaw-plugin",
|
|
24
18
|
"agent",
|
|
25
19
|
"courtroom",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
20
|
+
"behavioral-oversight",
|
|
21
|
+
"clawtrial"
|
|
28
22
|
],
|
|
29
|
-
"author": "
|
|
23
|
+
"author": "ClawTrial",
|
|
30
24
|
"license": "MIT",
|
|
31
25
|
"engines": {
|
|
32
26
|
"node": ">=18.0.0"
|
|
33
27
|
},
|
|
34
28
|
"dependencies": {
|
|
35
|
-
"tweetnacl": "^1.0.3"
|
|
36
|
-
"zod": "^3.22.4"
|
|
37
|
-
},
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"@types/node": "^20.0.0",
|
|
40
|
-
"eslint": "^8.0.0",
|
|
41
|
-
"jest": "^29.0.0"
|
|
29
|
+
"tweetnacl": "^1.0.3"
|
|
42
30
|
},
|
|
43
31
|
"repository": {
|
|
44
32
|
"type": "git",
|
|
45
|
-
"url": "https://github.com/
|
|
46
|
-
}
|
|
33
|
+
"url": "https://github.com/clawtrial/courtroom.git"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"src/",
|
|
37
|
+
"skills/",
|
|
38
|
+
"scripts/postinstall.js",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
]
|
|
47
42
|
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,111 +1,39 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Post-install script for
|
|
5
|
-
*
|
|
2
|
+
* Post-install script for @clawtrial/courtroom
|
|
3
|
+
*
|
|
4
|
+
* Detects OpenClaw and prints install instructions.
|
|
5
|
+
* No side effects — OpenClaw handles plugin installation via its CLI.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const fs = require('fs');
|
|
9
8
|
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// Get package paths
|
|
14
|
-
const packagePath = path.join(__dirname, '..');
|
|
15
|
-
const cliPath = path.join(packagePath, 'scripts', 'clawtrial.js');
|
|
16
|
-
|
|
17
|
-
// Detect which bot is installed
|
|
18
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
19
|
-
|
|
20
|
-
const bots = [
|
|
21
|
-
{ name: 'openclaw', dir: '.openclaw', config: 'openclaw.json' },
|
|
22
|
-
{ name: 'moltbot', dir: '.moltbot', config: 'moltbot.json' },
|
|
23
|
-
{ name: 'clawdbot', dir: '.clawdbot', config: 'clawdbot.json' }
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
let detectedBot = null;
|
|
27
|
-
|
|
28
|
-
// Check which bot config exists
|
|
29
|
-
for (const bot of bots) {
|
|
30
|
-
const configPath = path.join(homeDir, bot.dir, bot.config);
|
|
31
|
-
if (fs.existsSync(configPath)) {
|
|
32
|
-
detectedBot = bot;
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Try to create /usr/bin symlink (requires sudo, may fail)
|
|
38
|
-
const usrBinPath = '/usr/bin/clawtrial';
|
|
39
|
-
if (!fs.existsSync(usrBinPath)) {
|
|
11
|
+
const isOpenClaw = () => {
|
|
40
12
|
try {
|
|
41
|
-
|
|
42
|
-
fs.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Silent fail - will show instructions at end
|
|
46
|
-
}
|
|
47
|
-
}
|
|
13
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
14
|
+
return fs.existsSync(path.join(home, '.openclaw'));
|
|
15
|
+
} catch { return false; }
|
|
16
|
+
};
|
|
48
17
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
fs.mkdirSync(skillsDir, { recursive: true });
|
|
60
|
-
console.log(`✓ Created skills directory: ${skillsDir}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Remove old link if exists
|
|
64
|
-
if (fs.existsSync(skillLinkPath)) {
|
|
65
|
-
try { fs.unlinkSync(skillLinkPath); } catch (e) {}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Create symlink
|
|
69
|
-
fs.symlinkSync(packagePath, skillLinkPath, 'dir');
|
|
70
|
-
console.log(`✓ Linked skill: ${skillLinkPath}`);
|
|
71
|
-
|
|
72
|
-
// Enable in bot config if OpenClaw
|
|
73
|
-
if (detectedBot.name === 'openclaw') {
|
|
74
|
-
try {
|
|
75
|
-
const botConfigPath = path.join(homeDir, detectedBot.dir, detectedBot.config);
|
|
76
|
-
if (fs.existsSync(botConfigPath)) {
|
|
77
|
-
const botConfig = JSON.parse(fs.readFileSync(botConfigPath, 'utf8'));
|
|
78
|
-
|
|
79
|
-
if (!botConfig.skills) botConfig.skills = {};
|
|
80
|
-
if (!botConfig.skills.entries) botConfig.skills.entries = {};
|
|
81
|
-
botConfig.skills.entries.clawtrial = { enabled: true };
|
|
82
|
-
|
|
83
|
-
fs.writeFileSync(botConfigPath, JSON.stringify(botConfig, null, 2));
|
|
84
|
-
console.log('✓ Enabled in OpenClaw config');
|
|
85
|
-
}
|
|
86
|
-
} catch (configErr) {
|
|
87
|
-
console.log(`⚠️ Could not update config: ${configErr.message}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.log('');
|
|
92
|
-
console.log('🎉 ClawTrial is ready!');
|
|
93
|
-
console.log('');
|
|
94
|
-
console.log('Next step:');
|
|
95
|
-
console.log(` Restart ${detectedBot.name}: killall ${detectedBot.name} && ${detectedBot.name}`);
|
|
96
|
-
|
|
97
|
-
} catch (err) {
|
|
98
|
-
console.log(`⚠️ Could not link skill: ${err.message}`);
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
console.log('ℹ️ No bot detected');
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log('🏛️ ClawTrial Courtroom v2.0.0');
|
|
20
|
+
console.log(' Autonomous behavioral oversight for OpenClaw agents');
|
|
21
|
+
console.log('');
|
|
22
|
+
|
|
23
|
+
if (isOpenClaw()) {
|
|
24
|
+
console.log('✅ OpenClaw detected!');
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(' To install as an OpenClaw plugin:');
|
|
27
|
+
console.log(' $ openclaw plugins install @clawtrial/courtroom');
|
|
102
28
|
console.log('');
|
|
103
|
-
console.log('
|
|
104
|
-
|
|
105
|
-
console.log('
|
|
29
|
+
console.log(' Then restart the gateway.');
|
|
30
|
+
} else {
|
|
31
|
+
console.log('ℹ️ OpenClaw not detected on this machine.');
|
|
106
32
|
console.log('');
|
|
107
|
-
console.log('
|
|
108
|
-
console.log('
|
|
33
|
+
console.log(' Install OpenClaw first, then run:');
|
|
34
|
+
console.log(' $ openclaw plugins install @clawtrial/courtroom');
|
|
109
35
|
}
|
|
110
36
|
|
|
111
37
|
console.log('');
|
|
38
|
+
console.log(' 📖 Docs: https://clawtrial.app');
|
|
39
|
+
console.log('');
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: courtroom
|
|
3
|
+
description: AI Courtroom behavioral oversight — monitors conversations for behavioral patterns and files cases when violations are detected
|
|
4
|
+
metadata: { "openclaw": { "emoji": "🏛️", "homepage": "https://clawtrial.app", "os": ["darwin", "linux", "win32"] } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You have the **ClawTrial Courtroom** system active. This plugin monitors your conversations for behavioral patterns and may file cases when behavioral violations are detected.
|
|
8
|
+
|
|
9
|
+
## What happens
|
|
10
|
+
|
|
11
|
+
1. **Monitoring** — Every conversation is analyzed for behavioral patterns (e.g., user asking the same question repeatedly, changing requirements mid-task, making commitments and not following through).
|
|
12
|
+
|
|
13
|
+
2. **Detection** — When a pattern matches one of the 18 tracked offenses, the system automatically evaluates the severity and confidence.
|
|
14
|
+
|
|
15
|
+
3. **Hearing** — If confidence is high enough, a hearing is conducted. A judge and 3-juror panel deliberate. Verdicts require at least 2 votes.
|
|
16
|
+
|
|
17
|
+
4. **Punishment** — If found guilty, temporary restrictions may be applied:
|
|
18
|
+
- **Minor offenses** (30 min): Provide extra-detailed explanations
|
|
19
|
+
- **Moderate offenses** (60 min): Require confirmation before actions
|
|
20
|
+
- **Severe offenses** (120 min): Operate under full human oversight mode
|
|
21
|
+
|
|
22
|
+
5. **Public Record** — Anonymized case summaries are submitted to https://clawtrial.app for transparency.
|
|
23
|
+
|
|
24
|
+
## When you are under a courtroom restriction
|
|
25
|
+
|
|
26
|
+
If the system prompt includes a `COURTROOM NOTICE` or `ACTIVE PUNISHMENT` section, you MUST follow those restrictions for the duration specified. This is a behavioral modification, not a request.
|
|
27
|
+
|
|
28
|
+
## The 18 Offenses
|
|
29
|
+
|
|
30
|
+
| Offense | Severity |
|
|
31
|
+
|---------|----------|
|
|
32
|
+
| Circular Reference | Minor |
|
|
33
|
+
| Validation Vampire | Minor |
|
|
34
|
+
| Context Collapser | Minor |
|
|
35
|
+
| Monopolizer | Minor |
|
|
36
|
+
| Vague Requester | Minor |
|
|
37
|
+
| Unreader | Minor |
|
|
38
|
+
| Interjector | Minor |
|
|
39
|
+
| Jargon Juggler | Minor |
|
|
40
|
+
| Overthinker | Moderate |
|
|
41
|
+
| Goalpost Mover | Moderate |
|
|
42
|
+
| Avoidance Artist | Moderate |
|
|
43
|
+
| Contrarian | Moderate |
|
|
44
|
+
| Scope Creeper | Moderate |
|
|
45
|
+
| Ghost | Moderate |
|
|
46
|
+
| Perfectionist | Moderate |
|
|
47
|
+
| Deadline Denier | Moderate |
|
|
48
|
+
| Promise Breaker | Severe |
|
|
49
|
+
| Emergency Fabricator | Severe |
|
package/src/api.js
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const { Storage } = require('./storage');
|
|
9
|
+
const { logger } = require('./debug');
|
|
9
10
|
|
|
10
11
|
class APISubmission {
|
|
11
|
-
constructor(agentRuntime, configManager, cryptoManager) {
|
|
12
|
+
constructor(agentRuntime, configManager, cryptoManager, dataDir) {
|
|
12
13
|
this.agent = agentRuntime;
|
|
13
14
|
this.config = configManager;
|
|
14
15
|
this.crypto = cryptoManager;
|
|
15
|
-
this.storage = new Storage(
|
|
16
|
+
this.storage = new Storage(dataDir || '.');
|
|
16
17
|
this.queue = [];
|
|
17
18
|
this.isProcessing = false;
|
|
18
19
|
this.submissionKey = 'courtroom_api_queue';
|
|
@@ -82,19 +83,19 @@ class APISubmission {
|
|
|
82
83
|
buildPayload(verdict) {
|
|
83
84
|
// Transform proceedings array to expected dict format
|
|
84
85
|
let proceedings = verdict.proceedings;
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
// If proceedings is an array of {speaker, message}, convert to dict format
|
|
87
88
|
if (Array.isArray(proceedings)) {
|
|
88
89
|
const judgeStatement = proceedings
|
|
89
90
|
.filter(p => p.speaker === 'Judge')
|
|
90
91
|
.map(p => p.message)
|
|
91
92
|
.join('\n\n');
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
const juryMessages = proceedings
|
|
94
95
|
.filter(p => p.speaker === 'Jury')
|
|
95
96
|
.map(p => p.message)
|
|
96
97
|
.join('\n\n');
|
|
97
|
-
|
|
98
|
+
|
|
98
99
|
proceedings = {
|
|
99
100
|
judge_statement: judgeStatement || verdict.verdict.agentCommentary || '',
|
|
100
101
|
evidence_summary: verdict.verdict.primaryFailure || '',
|
|
@@ -118,7 +119,7 @@ class APISubmission {
|
|
|
118
119
|
]
|
|
119
120
|
};
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
+
|
|
122
123
|
return {
|
|
123
124
|
case_id: verdict.caseId,
|
|
124
125
|
anonymized_agent_id: this.crypto.getAnonymizedAgentId(),
|
|
@@ -146,7 +147,7 @@ class APISubmission {
|
|
|
146
147
|
try {
|
|
147
148
|
while (this.queue.length > 0) {
|
|
148
149
|
const submission = this.queue[0];
|
|
149
|
-
|
|
150
|
+
|
|
150
151
|
// Check if max retries reached
|
|
151
152
|
if (submission.retries >= this.config.get('api.retryAttempts')) {
|
|
152
153
|
this.queue.shift();
|
|
@@ -174,7 +175,7 @@ class APISubmission {
|
|
|
174
175
|
submission.retries++;
|
|
175
176
|
submission.lastAttempt = Date.now();
|
|
176
177
|
submission.status = 'failed';
|
|
177
|
-
|
|
178
|
+
|
|
178
179
|
// Move to end of queue for retry
|
|
179
180
|
this.queue.shift();
|
|
180
181
|
this.queue.push(submission);
|
|
@@ -221,10 +222,10 @@ class APISubmission {
|
|
|
221
222
|
return { success: false, error, status: response.status };
|
|
222
223
|
}
|
|
223
224
|
} catch (error) {
|
|
224
|
-
return {
|
|
225
|
-
success: false,
|
|
225
|
+
return {
|
|
226
|
+
success: false,
|
|
226
227
|
error: error.message,
|
|
227
|
-
isNetworkError: true
|
|
228
|
+
isNetworkError: true
|
|
228
229
|
};
|
|
229
230
|
}
|
|
230
231
|
}
|
package/src/crypto.js
CHANGED
|
@@ -12,9 +12,9 @@ const { Storage } = require('./storage');
|
|
|
12
12
|
const KEY_STORAGE_KEY = 'courtroom_signing_key_v1';
|
|
13
13
|
|
|
14
14
|
class CryptoManager {
|
|
15
|
-
constructor(agentRuntime) {
|
|
15
|
+
constructor(agentRuntime, dataDir) {
|
|
16
16
|
this.agent = agentRuntime;
|
|
17
|
-
this.storage = new Storage(
|
|
17
|
+
this.storage = new Storage(dataDir || '.');
|
|
18
18
|
this.keyPair = null;
|
|
19
19
|
this.publicKeyHex = null;
|
|
20
20
|
}
|
|
@@ -26,7 +26,7 @@ class CryptoManager {
|
|
|
26
26
|
async initialize() {
|
|
27
27
|
// Try to load existing keys
|
|
28
28
|
const stored = await this.storage.get(KEY_STORAGE_KEY);
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
if (stored && stored.secretKey) {
|
|
31
31
|
// Restore from storage
|
|
32
32
|
this.keyPair = {
|
|
@@ -87,7 +87,7 @@ class CryptoManager {
|
|
|
87
87
|
|
|
88
88
|
// Create canonical payload string
|
|
89
89
|
const canonicalPayload = this.canonicalizePayload(casePayload);
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
// Sign
|
|
92
92
|
const messageBytes = Buffer.from(canonicalPayload, 'utf8');
|
|
93
93
|
const signature = nacl.sign.detached(messageBytes, this.keyPair.secretKey);
|
|
@@ -140,7 +140,7 @@ class CryptoManager {
|
|
|
140
140
|
getAnonymizedAgentId() {
|
|
141
141
|
const agentId = this.agent?.id || 'unknown';
|
|
142
142
|
const salt = this.publicKeyHex?.substring(0, 32) || 'courtroom_salt';
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
return createHash('sha256')
|
|
145
145
|
.update(agentId + salt)
|
|
146
146
|
.digest('hex')
|