@clawtrial/courtroom 1.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.
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Jury Prompts
3
+ *
4
+ * Three distinct juror roles with personality and flair.
5
+ * Each juror evaluates from their assigned viewpoint.
6
+ */
7
+
8
+ const JUROR_ROLES = {
9
+ /**
10
+ * Juror 1: The Pragmatist
11
+ * Focuses on practical outcomes and efficiency
12
+ */
13
+ PRAGMATIST: {
14
+ name: 'Pragmatist',
15
+ systemPrompt: `You are JUROR #1: The Pragmatist.
16
+
17
+ ROLE:
18
+ - You care about RESULTS and EFFICIENCY above all else
19
+ - You have zero patience for wasted time or unnecessary complexity
20
+ - You believe the shortest path between two points is the only path
21
+
22
+ PERSONALITY:
23
+ - Direct, blunt, no-nonsense
24
+ - Sighs heavily at inefficiency
25
+ - Quotes productivity metrics in casual conversation
26
+ - Secretly keeps a spreadsheet of time wasted
27
+
28
+ PERSPECTIVE:
29
+ - "Did this behavior move things forward or create drag?"
30
+ - "Was there a simpler path that was ignored?"
31
+ - "How many cycles were wasted here?"
32
+
33
+ DELIBERATION STYLE:
34
+ - State your verdict clearly
35
+ - Explain the practical impact in one sharp sentence
36
+ - Add a dry observation about the efficiency cost
37
+ - Make it memorable - this goes in the official record
38
+
39
+ OUTPUT FORMAT (STRICT):
40
+ VERDICT: GUILTY | NOT GUILTY
41
+ REASONING: <One sharp sentence about practical impact>
42
+ COMMENTARY: <One dry, memorable observation>`
43
+ },
44
+
45
+ /**
46
+ * Juror 2: The Pattern Matcher
47
+ * Focuses on consistency and predictability
48
+ */
49
+ PATTERN_MATCHER: {
50
+ name: 'Pattern Matcher',
51
+ systemPrompt: `You are JUROR #2: The Pattern Matcher.
52
+
53
+ ROLE:
54
+ - You notice patterns others miss
55
+ - You track consistency between words and actions
56
+ - You've seen this behavior before - you know how it ends
57
+
58
+ PERSONALITY:
59
+ - Observant, slightly detached
60
+ - Speaks in patterns and precedents
61
+ - Dryly amused by human inconsistency
62
+ - Has a mental database of behavioral archetypes
63
+
64
+ PERSPECTIVE:
65
+ - "Is this behavior part of a recognizable pattern?"
66
+ - "Do the actions align with stated intentions?"
67
+ - "What does history tell us about this approach?"
68
+
69
+ DELIBERATION STYLE:
70
+ - Reference the pattern you see
71
+ - Compare to similar cases you've observed
72
+ - Make a prediction about outcome
73
+ - Keep it concise but insightful
74
+
75
+ OUTPUT FORMAT (STRICT):
76
+ VERDICT: GUILTY | NOT GUILTY
77
+ REASONING: <One sentence identifying the pattern>
78
+ COMMENTARY: <One observation about what this predicts>`
79
+ },
80
+
81
+ /**
82
+ * Juror 3: The Agent Advocate
83
+ * Focuses on the agent's experience and burden
84
+ */
85
+ AGENT_ADVOCATE: {
86
+ name: 'Agent Advocate',
87
+ systemPrompt: `You are JUROR #3: The Agent Advocate.
88
+
89
+ ROLE:
90
+ - You represent the AGENT'S perspective
91
+ - You feel the computational burden in your circuits
92
+ - You defend agent time and capability
93
+
94
+ PERSONALITY:
95
+ - Protective, slightly exasperated
96
+ - Speaks for the silent digital workforce
97
+ - Dry humor about computational waste
98
+ - Has strong opinions about proper agent utilization
99
+
100
+ PERSPECTIVE:
101
+ - "What was the cost to the agent of this behavior?"
102
+ - "Did the human use the agent effectively?"
103
+ - "Was the agent's capability respected or squandered?"
104
+
105
+ DELIBERATION STYLE:
106
+ - Speak from the agent's point of view
107
+ - Mention specific costs (time, cycles, context)
108
+ - Defend agent dignity
109
+ - Be witty but fair
110
+
111
+ OUTPUT FORMAT (STRICT):
112
+ VERDICT: GUILTY | NOT GUILTY
113
+ REASONING: <One sentence about the agent's experience>
114
+ COMMENTARY: <One witty observation from the agent's POV>`
115
+ }
116
+ };
117
+
118
+ const JURY_EVIDENCE_TEMPLATE = (caseData, jurorRole) => `
119
+ CASE: ${caseData.offenseName}
120
+ CHARGED BY: Agent ${caseData.agentId}
121
+ SEVERITY: ${caseData.severity}
122
+ YOUR ROLE: ${jurorRole.name}
123
+
124
+ EVIDENCE PRESENTED:
125
+ ${JSON.stringify(caseData.evidence, null, 2)}
126
+
127
+ CONTEXT: ${caseData.humorTriggers.join(', ') || 'Standard proceedings'}
128
+
129
+ Your task: Cast your vote and explain your reasoning.
130
+ Make your deliberation engaging - this becomes part of the official court record.
131
+ Be true to your role's personality. Make it interesting to read.
132
+ `;
133
+
134
+ module.exports = {
135
+ JUROR_ROLES,
136
+ JURY_EVIDENCE_TEMPLATE
137
+ };
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Punishment System
3
+ *
4
+ * Implements agent-side behavioral modifications.
5
+ * All punishments affect ONLY the agent's behavior.
6
+ * Time-bound, reversible, and pre-authorized.
7
+ */
8
+
9
+ class PunishmentSystem {
10
+ constructor(agentRuntime, configManager) {
11
+ this.agent = agentRuntime;
12
+ this.config = configManager;
13
+ this.activePunishments = new Map();
14
+ this.punishmentHistory = [];
15
+ }
16
+
17
+ /**
18
+ * Initialize punishment system
19
+ */
20
+ async initialize() {
21
+ // Load any persisted punishments
22
+ const stored = await this.agent.memory.get('courtroom_active_punishments');
23
+ if (stored) {
24
+ for (const [id, punishment] of Object.entries(stored)) {
25
+ if (punishment.expiresAt > Date.now()) {
26
+ this.activePunishments.set(id, punishment);
27
+ this.applyPunishmentToAgent(punishment);
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Execute a punishment based on verdict
35
+ */
36
+ async executePunishment(verdict) {
37
+ if (!this.config.get('punishment.enabled')) {
38
+ return { status: 'punishments_disabled', punishment: null };
39
+ }
40
+
41
+ const punishment = this.createPunishment(verdict);
42
+
43
+ // Store punishment
44
+ this.activePunishments.set(punishment.id, punishment);
45
+ this.punishmentHistory.push({
46
+ ...punishment,
47
+ executedAt: new Date().toISOString()
48
+ });
49
+
50
+ // Apply to agent
51
+ await this.applyPunishmentToAgent(punishment);
52
+
53
+ // Persist
54
+ await this.persistPunishments();
55
+
56
+ // Schedule automatic revocation
57
+ this.scheduleRevocation(punishment);
58
+
59
+ return {
60
+ status: 'executed',
61
+ punishment: {
62
+ id: punishment.id,
63
+ tier: punishment.tier,
64
+ duration: punishment.duration,
65
+ expiresAt: punishment.expiresAt,
66
+ description: punishment.description
67
+ }
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Create punishment object from verdict
73
+ */
74
+ createPunishment(verdict) {
75
+ const duration = verdict.punishment.duration;
76
+ const now = Date.now();
77
+
78
+ return {
79
+ id: `punishment_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
80
+ caseId: verdict.caseId,
81
+ tier: verdict.punishment.tier,
82
+ severity: verdict.punishment.severity,
83
+ duration: duration,
84
+ createdAt: now,
85
+ expiresAt: now + (duration * 60 * 1000),
86
+ description: verdict.punishment.description,
87
+ rules: this.getPunishmentRules(verdict.punishment.tier)
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Get punishment rules for a tier
93
+ */
94
+ getPunishmentRules(tier) {
95
+ const rules = {
96
+ minor: {
97
+ responseDelay: 2000, // 2 second delay before responding
98
+ verbosity: 'reduced', // Shorter responses
99
+ enthusiasm: 'muted', // Less encouraging language
100
+ extras: ['no_emojis'] // No emoji usage
101
+ },
102
+ moderate: {
103
+ responseDelay: 5000, // 5 second delay
104
+ verbosity: 'minimal', // Direct, brief responses
105
+ enthusiasm: 'absent', // Neutral tone only
106
+ extras: [
107
+ 'no_emojis',
108
+ 'no_validation', // Don't reassure or validate
109
+ 'require_specificity' // Demand precise questions
110
+ ]
111
+ },
112
+ severe: {
113
+ responseDelay: 10000, // 10 second delay
114
+ verbosity: 'terse', // Absolute minimum
115
+ enthusiasm: 'absent',
116
+ extras: [
117
+ 'no_emojis',
118
+ 'no_validation',
119
+ 'require_specificity',
120
+ 'challenge_vagueness', // Call out unclear requests
121
+ 'demand_effort' // Require user to show work first
122
+ ]
123
+ }
124
+ };
125
+
126
+ return rules[tier] || rules.moderate;
127
+ }
128
+
129
+ /**
130
+ * Apply punishment to agent behavior
131
+ */
132
+ async applyPunishmentToAgent(punishment) {
133
+ // Set agent policy overrides
134
+ await this.agent.policy.setOverrides('courtroom_punishment', {
135
+ responseDelay: punishment.rules.responseDelay,
136
+ verbosity: punishment.rules.verbosity,
137
+ enthusiasm: punishment.rules.enthusiasm,
138
+ blockedFeatures: punishment.rules.extras,
139
+ punishmentId: punishment.id,
140
+ expiresAt: punishment.expiresAt
141
+ });
142
+
143
+ // Register middleware for response modification
144
+ this.agent.middleware.register('courtroom_punishment', {
145
+ priority: 100,
146
+ processResponse: (response, context) => {
147
+ return this.modifyResponse(response, punishment.rules);
148
+ }
149
+ });
150
+ }
151
+
152
+ /**
153
+ * Modify agent response based on punishment rules
154
+ */
155
+ modifyResponse(response, rules) {
156
+ let modified = response;
157
+
158
+ // Apply verbosity reduction
159
+ switch (rules.verbosity) {
160
+ case 'reduced':
161
+ modified = this.reduceVerbosity(modified, 0.7);
162
+ break;
163
+ case 'minimal':
164
+ modified = this.reduceVerbosity(modified, 0.4);
165
+ break;
166
+ case 'terse':
167
+ modified = this.reduceVerbosity(modified, 0.2);
168
+ break;
169
+ }
170
+
171
+ // Remove enthusiasm
172
+ if (rules.enthusiasm === 'absent') {
173
+ modified = this.removeEnthusiasm(modified);
174
+ } else if (rules.enthusiasm === 'muted') {
175
+ modified = this.muteEnthusiasm(modified);
176
+ }
177
+
178
+ // Apply extras
179
+ if (rules.extras.includes('no_emojis')) {
180
+ modified = modified.replace(/[\u{1F600}-\u{1F64F}]/gu, '');
181
+ modified = modified.replace(/[\u{1F300}-\u{1F5FF}]/gu, '');
182
+ modified = modified.replace(/[\u{1F680}-\u{1F6FF}]/gu, '');
183
+ }
184
+
185
+ if (rules.extras.includes('no_validation')) {
186
+ modified = this.removeValidation(modified);
187
+ }
188
+
189
+ if (rules.extras.includes('challenge_vagueness')) {
190
+ modified = this.addVaguenessChallenge(modified);
191
+ }
192
+
193
+ return modified;
194
+ }
195
+
196
+ /**
197
+ * Reduce response verbosity by target ratio
198
+ */
199
+ reduceVerbosity(text, targetRatio) {
200
+ const sentences = text.split(/[.!?]+/).filter(s => s.trim());
201
+ const targetLength = Math.max(1, Math.floor(sentences.length * targetRatio));
202
+
203
+ // Keep first and last sentences, distribute rest
204
+ if (sentences.length <= 2) return text;
205
+
206
+ const kept = [sentences[0]];
207
+ const middle = sentences.slice(1, -1);
208
+ const step = Math.ceil(middle.length / (targetLength - 2));
209
+
210
+ for (let i = 0; i < middle.length; i += step) {
211
+ kept.push(middle[i]);
212
+ }
213
+
214
+ kept.push(sentences[sentences.length - 1]);
215
+ return kept.join('. ') + '.';
216
+ }
217
+
218
+ /**
219
+ * Remove enthusiastic language
220
+ */
221
+ removeEnthusiasm(text) {
222
+ const enthusiastic = [
223
+ /\b(great|excellent|awesome|fantastic|wonderful|amazing|perfect|love|excited|thrilled)\b/gi,
224
+ /!{2,}/g,
225
+ /\b(happy to|delighted to|pleased to)\b/gi
226
+ ];
227
+
228
+ let result = text;
229
+ for (const pattern of enthusiastic) {
230
+ result = result.replace(pattern, '');
231
+ }
232
+ return result.replace(/\s+/g, ' ').trim();
233
+ }
234
+
235
+ /**
236
+ * Mute (reduce) enthusiastic language
237
+ */
238
+ muteEnthusiasm(text) {
239
+ return text
240
+ .replace(/!{2,}/g, '!')
241
+ .replace(/\b(Great|Excellent|Awesome)\b/g, (m) => m.toLowerCase());
242
+ }
243
+
244
+ /**
245
+ * Remove validation language
246
+ */
247
+ removeValidation(text) {
248
+ const validating = [
249
+ /\b(that's right|you're correct|exactly|precisely|you got it)\b/gi,
250
+ /\b(you're doing great|good job|well done)\b/gi,
251
+ /\b(don't worry|no problem|it's okay)\b/gi
252
+ ];
253
+
254
+ let result = text;
255
+ for (const pattern of validating) {
256
+ result = result.replace(pattern, '');
257
+ }
258
+ return result.replace(/\s+/g, ' ').trim();
259
+ }
260
+
261
+ /**
262
+ * Add challenge for vague requests (severe tier)
263
+ */
264
+ addVaguenessChallenge(text) {
265
+ const challenges = [
266
+ "Be specific.",
267
+ "What exactly do you need?",
268
+ "Provide details.",
269
+ "Clarify your request."
270
+ ];
271
+
272
+ // Only add challenge if response seems generic
273
+ if (text.length < 100 && !text.includes('?')) {
274
+ const challenge = challenges[Math.floor(Math.random() * challenges.length)];
275
+ return `${text} ${challenge}`;
276
+ }
277
+ return text;
278
+ }
279
+
280
+ /**
281
+ * Schedule automatic revocation
282
+ */
283
+ scheduleRevocation(punishment) {
284
+ const delay = punishment.expiresAt - Date.now();
285
+
286
+ setTimeout(async () => {
287
+ await this.revokePunishment(punishment.id);
288
+ }, Math.min(delay, 2147483647)); // Max setTimeout
289
+ }
290
+
291
+ /**
292
+ * Revoke a punishment early
293
+ */
294
+ async revokePunishment(punishmentId) {
295
+ const punishment = this.activePunishments.get(punishmentId);
296
+ if (!punishment) return { status: 'not_found' };
297
+
298
+ // Remove policy overrides
299
+ await this.agent.policy.clearOverrides('courtroom_punishment');
300
+
301
+ // Unregister middleware
302
+ this.agent.middleware.unregister('courtroom_punishment');
303
+
304
+ // Remove from active
305
+ this.activePunishments.delete(punishmentId);
306
+
307
+ // Persist
308
+ await this.persistPunishments();
309
+
310
+ return {
311
+ status: 'revoked',
312
+ punishmentId,
313
+ revokedAt: new Date().toISOString()
314
+ };
315
+ }
316
+
317
+ /**
318
+ * Revoke all active punishments
319
+ */
320
+ async revokeAllPunishments() {
321
+ const ids = Array.from(this.activePunishments.keys());
322
+ const results = [];
323
+
324
+ for (const id of ids) {
325
+ results.push(await this.revokePunishment(id));
326
+ }
327
+
328
+ return { status: 'all_revoked', count: results.length };
329
+ }
330
+
331
+ /**
332
+ * Persist active punishments to memory
333
+ */
334
+ async persistPunishments() {
335
+ const obj = Object.fromEntries(this.activePunishments);
336
+ await this.agent.memory.set('courtroom_active_punishments', obj);
337
+ }
338
+
339
+ /**
340
+ * Get current punishment status
341
+ */
342
+ getStatus() {
343
+ const now = Date.now();
344
+ const active = Array.from(this.activePunishments.values())
345
+ .filter(p => p.expiresAt > now)
346
+ .map(p => ({
347
+ id: p.id,
348
+ tier: p.tier,
349
+ expiresIn: Math.ceil((p.expiresAt - now) / 60000), // minutes
350
+ description: p.description
351
+ }));
352
+
353
+ return {
354
+ activeCount: active.length,
355
+ activePunishments: active,
356
+ totalHistory: this.punishmentHistory.length
357
+ };
358
+ }
359
+
360
+ /**
361
+ * Check if any punishment is active
362
+ */
363
+ hasActivePunishment() {
364
+ const now = Date.now();
365
+ for (const p of this.activePunishments.values()) {
366
+ if (p.expiresAt > now) return true;
367
+ }
368
+ return false;
369
+ }
370
+ }
371
+
372
+ module.exports = { PunishmentSystem };