@iamoberlin/chorus 2.2.0 → 2.3.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 +7 -1
- package/idl/chorus_prayers.json +1 -1
- package/index.ts +99 -46
- package/openclaw.plugin.json +81 -2
- package/package.json +1 -1
- package/src/choirs.ts +54 -25
- package/src/config.ts +91 -0
- package/src/daemon.ts +18 -13
- package/src/delivery.ts +96 -0
- package/src/economics.ts +166 -0
- package/src/prayers/solana.ts +1 -1
- package/src/purpose-research.ts +78 -24
- package/src/scheduler.ts +89 -5
package/README.md
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
|
|
9
9
|
CHORUS implements the Nine Choirs architecture — hierarchical cognition modeled on Pseudo-Dionysius's *Celestial Hierarchy*. Illumination descends through the choirs; understanding ascends. The agent is sanctified through structure.
|
|
10
10
|
|
|
11
|
+
## What's New in v2.3
|
|
12
|
+
|
|
13
|
+
- **Unified delivery path.** Scheduler and manual commands now share one channel-aware delivery adapter for consistent formatting and routing.
|
|
14
|
+
- **Channel-agnostic output.** Choir output adapts to the destination automatically (plain text for iMessage, markdown for webchat, etc.).
|
|
15
|
+
- **Context passing fixed.** Illumination flow works correctly across all entry points — `run`, `vision`, and scheduled execution.
|
|
16
|
+
|
|
11
17
|
## The Core Idea
|
|
12
18
|
|
|
13
19
|
Most AI agents are frozen. Same prompts, same limitations, no growth. CHORUS changes that through **architecture**:
|
|
@@ -280,7 +286,7 @@ When `autonomous: false` (default), all prayer chain interactions require explic
|
|
|
280
286
|
- **TypeScript client** — wraps Anchor IDL with PDA derivation helpers
|
|
281
287
|
- **Anchor events** — `PrayerPosted`, `PrayerAnswered`, `PrayerConfirmed`, `PrayerClaimed`, `PrayerCancelled` for off-chain indexing
|
|
282
288
|
- **Local text cache** — CLI stores full text in `.prayer-texts.json` for display
|
|
283
|
-
- **Program ID:** `
|
|
289
|
+
- **Program ID:** `Af61jGnh2AceK3E8FAxCh9j7Jt6JWtJz6PUtbciDjVJS`
|
|
284
290
|
|
|
285
291
|
## Philosophy
|
|
286
292
|
|
package/idl/chorus_prayers.json
CHANGED
package/index.ts
CHANGED
|
@@ -11,7 +11,8 @@ import { spawnSync } from "child_process";
|
|
|
11
11
|
import { loadChorusConfig, type ChorusPluginConfig } from "./src/config.js";
|
|
12
12
|
import { createSecurityHooks } from "./src/security.js";
|
|
13
13
|
import { createChoirScheduler } from "./src/scheduler.js";
|
|
14
|
-
import { CHOIRS, formatFrequency } from "./src/choirs.js";
|
|
14
|
+
import { CHOIRS, formatFrequency, CASCADE_ORDER } from "./src/choirs.js";
|
|
15
|
+
import { deliverChoirOutput } from "./src/delivery.js";
|
|
15
16
|
import {
|
|
16
17
|
getTodayMetrics,
|
|
17
18
|
getMetricsForDate,
|
|
@@ -21,8 +22,9 @@ import {
|
|
|
21
22
|
formatMetricsSummary,
|
|
22
23
|
formatWeeklySummary,
|
|
23
24
|
} from "./src/metrics.js";
|
|
24
|
-
import { createDaemon,
|
|
25
|
+
import { createDaemon, type DaemonConfig } from "./src/daemon.js";
|
|
25
26
|
import { getInboxPath } from "./src/senses.js";
|
|
27
|
+
import { formatEconomicsSummary, calculateAllROI } from "./src/economics.js";
|
|
26
28
|
import {
|
|
27
29
|
loadPurposes,
|
|
28
30
|
addPurpose,
|
|
@@ -32,7 +34,6 @@ import {
|
|
|
32
34
|
} from "./src/purposes.js";
|
|
33
35
|
import {
|
|
34
36
|
createPurposeResearchScheduler,
|
|
35
|
-
DEFAULT_PURPOSE_RESEARCH_CONFIG,
|
|
36
37
|
type PurposeResearchConfig,
|
|
37
38
|
} from "./src/purpose-research.js";
|
|
38
39
|
// On-chain prayer imports are lazy-loaded in pray commands to avoid startup cost
|
|
@@ -69,11 +70,7 @@ const plugin = {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
// Register daemon service
|
|
72
|
-
const daemonConfig: DaemonConfig =
|
|
73
|
-
...DEFAULT_DAEMON_CONFIG,
|
|
74
|
-
enabled: (pluginConfig as any)?.daemon?.enabled ?? true,
|
|
75
|
-
...(pluginConfig as any)?.daemon,
|
|
76
|
-
};
|
|
73
|
+
const daemonConfig: DaemonConfig = config.daemon;
|
|
77
74
|
|
|
78
75
|
let daemon: ReturnType<typeof createDaemon> | null = null;
|
|
79
76
|
if (daemonConfig.enabled) {
|
|
@@ -85,13 +82,7 @@ const plugin = {
|
|
|
85
82
|
}
|
|
86
83
|
|
|
87
84
|
// Register purpose research service
|
|
88
|
-
const purposeResearchConfig: PurposeResearchConfig =
|
|
89
|
-
...DEFAULT_PURPOSE_RESEARCH_CONFIG,
|
|
90
|
-
enabled: config.purposeResearch.enabled,
|
|
91
|
-
dailyRunCap: config.purposeResearch.dailyRunCap,
|
|
92
|
-
defaultFrequency: config.purposeResearch.defaultFrequency,
|
|
93
|
-
defaultMaxFrequency: config.purposeResearch.defaultMaxFrequency,
|
|
94
|
-
};
|
|
85
|
+
const purposeResearchConfig: PurposeResearchConfig = config.purposeResearch;
|
|
95
86
|
|
|
96
87
|
let purposeResearch: ReturnType<typeof createPurposeResearchScheduler> | null = null;
|
|
97
88
|
if (purposeResearchConfig.enabled) {
|
|
@@ -102,6 +93,34 @@ const plugin = {
|
|
|
102
93
|
api.logger.info("[chorus] Purpose research disabled");
|
|
103
94
|
}
|
|
104
95
|
|
|
96
|
+
// Helper: resolve {choir_context} placeholders using a context store
|
|
97
|
+
function resolvePrompt(choir: typeof CHOIRS[string], ctxStore: Map<string, string>): string {
|
|
98
|
+
let prompt = choir.prompt;
|
|
99
|
+
for (const upstreamId of choir.receivesFrom) {
|
|
100
|
+
const placeholder = `{${upstreamId}_context}`;
|
|
101
|
+
const ctx = ctxStore.get(upstreamId);
|
|
102
|
+
prompt = prompt.replace(placeholder, ctx || `(no prior ${upstreamId} output)`);
|
|
103
|
+
}
|
|
104
|
+
return prompt;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Helper: extract text from openclaw agent JSON output
|
|
108
|
+
function extractAgentText(stdout: string): string {
|
|
109
|
+
// Find the last top-level JSON object (skip plugin log noise)
|
|
110
|
+
for (let i = stdout.length - 1; i >= 0; i--) {
|
|
111
|
+
if (stdout[i] === '{') {
|
|
112
|
+
try {
|
|
113
|
+
const json = JSON.parse(stdout.slice(i));
|
|
114
|
+
const payloads = json.result?.payloads || [];
|
|
115
|
+
const texts = payloads.map((p: any) => p?.text || '').filter(Boolean);
|
|
116
|
+
if (texts.length > 0) return texts[texts.length - 1];
|
|
117
|
+
return json.result?.text || json.response || json.content || '';
|
|
118
|
+
} catch { /* keep searching */ }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return '';
|
|
122
|
+
}
|
|
123
|
+
|
|
105
124
|
// Register CLI
|
|
106
125
|
api.registerCli((ctx) => {
|
|
107
126
|
const program = ctx.program.command("chorus").description("CHORUS Nine Choirs management");
|
|
@@ -187,6 +206,8 @@ const plugin = {
|
|
|
187
206
|
}
|
|
188
207
|
}
|
|
189
208
|
|
|
209
|
+
const runCtxStore: Map<string, string> = new Map();
|
|
210
|
+
|
|
190
211
|
console.log("");
|
|
191
212
|
if (!choirId) {
|
|
192
213
|
console.log("🎵 Running all Nine Choirs in cascade order...");
|
|
@@ -198,10 +219,11 @@ const plugin = {
|
|
|
198
219
|
if (!choir) continue;
|
|
199
220
|
|
|
200
221
|
console.log(`Running ${choir.name}...`);
|
|
222
|
+
const prompt = resolvePrompt(choir, runCtxStore);
|
|
201
223
|
|
|
202
224
|
// Preview mode - just show the prompt
|
|
203
225
|
if (options?.preview) {
|
|
204
|
-
console.log(` Prompt: ${
|
|
226
|
+
console.log(` Prompt: ${prompt.slice(0, 100)}...`);
|
|
205
227
|
continue;
|
|
206
228
|
}
|
|
207
229
|
|
|
@@ -210,17 +232,19 @@ const plugin = {
|
|
|
210
232
|
try {
|
|
211
233
|
const result = await api.runAgentTurn({
|
|
212
234
|
sessionLabel: `chorus:${id}`,
|
|
213
|
-
message:
|
|
235
|
+
message: prompt,
|
|
214
236
|
isolated: true,
|
|
215
237
|
timeoutSeconds: 300,
|
|
216
238
|
});
|
|
217
239
|
const text = result?.text || result?.payloads?.[0]?.text || '';
|
|
218
240
|
const duration = result?.meta?.durationMs || 0;
|
|
241
|
+
runCtxStore.set(id, text.slice(0, 2000));
|
|
219
242
|
console.log(` ✓ ${choir.name} complete (${(duration/1000).toFixed(1)}s)`);
|
|
220
243
|
if (text) {
|
|
221
244
|
const preview = text.slice(0, 150).replace(/\n/g, ' ');
|
|
222
245
|
console.log(` ${preview}${text.length > 150 ? '...' : ''}`);
|
|
223
246
|
}
|
|
247
|
+
await deliverChoirOutput(api, choir, text, api.logger);
|
|
224
248
|
} catch (err) {
|
|
225
249
|
console.error(` ✗ ${choir.name} failed:`, err);
|
|
226
250
|
}
|
|
@@ -230,7 +254,7 @@ const plugin = {
|
|
|
230
254
|
const result = spawnSync('openclaw', [
|
|
231
255
|
'agent',
|
|
232
256
|
'--session-id', `chorus:${id}`,
|
|
233
|
-
'--message',
|
|
257
|
+
'--message', prompt,
|
|
234
258
|
'--json',
|
|
235
259
|
], {
|
|
236
260
|
encoding: 'utf-8',
|
|
@@ -239,22 +263,14 @@ const plugin = {
|
|
|
239
263
|
});
|
|
240
264
|
|
|
241
265
|
if (result.status === 0) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
const text = json.result?.payloads?.[0]?.text || '';
|
|
249
|
-
const duration = json.result?.meta?.durationMs || 0;
|
|
250
|
-
console.log(` ✓ ${choir.name} complete (${(duration/1000).toFixed(1)}s)`);
|
|
251
|
-
if (text) {
|
|
252
|
-
const preview = text.slice(0, 150).replace(/\n/g, ' ');
|
|
253
|
-
console.log(` ${preview}${text.length > 150 ? '...' : ''}`);
|
|
254
|
-
}
|
|
255
|
-
} catch (parseErr) {
|
|
256
|
-
console.log(` ✓ ${choir.name} complete (parse error: ${parseErr})`);
|
|
266
|
+
const text = extractAgentText(result.stdout || '');
|
|
267
|
+
runCtxStore.set(id, text.slice(0, 2000));
|
|
268
|
+
console.log(` ✓ ${choir.name} complete`);
|
|
269
|
+
if (text) {
|
|
270
|
+
const preview = text.slice(0, 150).replace(/\n/g, ' ');
|
|
271
|
+
console.log(` ${preview}${text.length > 150 ? '...' : ''}`);
|
|
257
272
|
}
|
|
273
|
+
await deliverChoirOutput(api, choir, text, api.logger);
|
|
258
274
|
} else {
|
|
259
275
|
const errMsg = result.stderr || result.stdout || 'Unknown error';
|
|
260
276
|
if (errMsg.includes('ECONNREFUSED') || errMsg.includes('connect')) {
|
|
@@ -271,7 +287,7 @@ const plugin = {
|
|
|
271
287
|
|
|
272
288
|
console.log("");
|
|
273
289
|
if (!choirId) {
|
|
274
|
-
console.log("🎵 All choirs
|
|
290
|
+
console.log("🎵 All choirs complete.");
|
|
275
291
|
}
|
|
276
292
|
console.log("");
|
|
277
293
|
});
|
|
@@ -348,10 +364,13 @@ const plugin = {
|
|
|
348
364
|
const jsonStr = jsonStart >= 0 ? stdout.slice(jsonStart) : '{}';
|
|
349
365
|
const json = JSON.parse(jsonStr);
|
|
350
366
|
const text = json.result?.payloads?.[0]?.text || '';
|
|
367
|
+
contextStore.set(choirId, text.slice(0, 500));
|
|
351
368
|
contextStore.set(`${choirId}:d${day}`, text.slice(0, 500));
|
|
352
369
|
console.log(` ✓ (dry)`);
|
|
353
370
|
} catch {
|
|
354
|
-
|
|
371
|
+
const fallback = `[${choir.name} would run]`;
|
|
372
|
+
contextStore.set(choirId, fallback);
|
|
373
|
+
contextStore.set(`${choirId}:d${day}`, fallback);
|
|
355
374
|
console.log(` ✓ (dry)`);
|
|
356
375
|
}
|
|
357
376
|
} else {
|
|
@@ -366,11 +385,14 @@ const plugin = {
|
|
|
366
385
|
process.stdout.write(` ${choir.emoji} ${choir.name}...`);
|
|
367
386
|
|
|
368
387
|
try {
|
|
388
|
+
// Resolve context from upstream choirs
|
|
389
|
+
const prompt = resolvePrompt(choir, contextStore);
|
|
390
|
+
|
|
369
391
|
// Run the REAL choir with full tool access via direct agent call
|
|
370
392
|
const result = spawnSync('openclaw', [
|
|
371
393
|
'agent',
|
|
372
394
|
'--session-id', `chorus:vision:${choirId}:d${day}`,
|
|
373
|
-
'--message',
|
|
395
|
+
'--message', prompt,
|
|
374
396
|
'--json',
|
|
375
397
|
], {
|
|
376
398
|
encoding: 'utf-8',
|
|
@@ -382,19 +404,19 @@ const plugin = {
|
|
|
382
404
|
// Parse the agent response (extract JSON from output)
|
|
383
405
|
try {
|
|
384
406
|
const stdout = result.stdout || '';
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const text = payloads.map((p: any) => p.text || '').filter(Boolean).join('\n\n') || '';
|
|
390
|
-
const duration = json.result?.meta?.durationMs || 0;
|
|
391
|
-
contextStore.set(`${choirId}:d${day}`, text.slice(0, 2000)); // Keep 2KB of response
|
|
407
|
+
const text = extractAgentText(stdout);
|
|
408
|
+
// Store by both choirId (for resolvePrompt) and choirId:dN (for summary)
|
|
409
|
+
contextStore.set(choirId, text.slice(0, 2000));
|
|
410
|
+
contextStore.set(`${choirId}:d${day}`, text.slice(0, 2000));
|
|
392
411
|
successfulRuns++;
|
|
393
|
-
console.log(`
|
|
412
|
+
console.log(` ✓`);
|
|
394
413
|
|
|
395
|
-
//
|
|
414
|
+
// Deliver output to user via OpenClaw messaging if choir is marked for delivery
|
|
415
|
+
void deliverChoirOutput(api, choir, text, api.logger);
|
|
396
416
|
} catch {
|
|
397
|
-
|
|
417
|
+
const fallback = result.stdout?.slice(-2000) || `[${choir.name} completed]`;
|
|
418
|
+
contextStore.set(choirId, fallback);
|
|
419
|
+
contextStore.set(`${choirId}:d${day}`, fallback);
|
|
398
420
|
successfulRuns++;
|
|
399
421
|
console.log(` ✓`);
|
|
400
422
|
}
|
|
@@ -591,6 +613,37 @@ const plugin = {
|
|
|
591
613
|
}
|
|
592
614
|
});
|
|
593
615
|
|
|
616
|
+
// Economics commands
|
|
617
|
+
const econCmd = program.command("economics").alias("econ").description("Attention economics (Phase 1: observe)");
|
|
618
|
+
|
|
619
|
+
econCmd
|
|
620
|
+
.command("summary")
|
|
621
|
+
.description("Show ROI summary per choir")
|
|
622
|
+
.option("--days <n>", "Rolling window in days", "7")
|
|
623
|
+
.action((opts: any) => {
|
|
624
|
+
console.log("");
|
|
625
|
+
console.log(formatEconomicsSummary(parseInt(opts.days) || 7));
|
|
626
|
+
console.log("");
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
econCmd
|
|
630
|
+
.command("roi")
|
|
631
|
+
.description("Show ROI table for all choirs")
|
|
632
|
+
.option("--days <n>", "Rolling window in days", "7")
|
|
633
|
+
.action((opts: any) => {
|
|
634
|
+
const days = parseInt(opts.days) || 7;
|
|
635
|
+
const all = calculateAllROI(days);
|
|
636
|
+
console.log("");
|
|
637
|
+
console.log(`Choir ROI — ${days}d window`);
|
|
638
|
+
console.log("─".repeat(60));
|
|
639
|
+
for (const r of all) {
|
|
640
|
+
const roi = r.totalCostUsd > 0 ? `${r.roi.toFixed(1)}x` : "—";
|
|
641
|
+
const bar = r.totalCostUsd > 0 ? "█".repeat(Math.min(20, Math.round(r.roi * 2))) : "";
|
|
642
|
+
console.log(` ${r.choirId.padEnd(16)} ${roi.padStart(6)} $${r.totalCostUsd.toFixed(2).padStart(6)} ${bar}`);
|
|
643
|
+
}
|
|
644
|
+
console.log("");
|
|
645
|
+
});
|
|
646
|
+
|
|
594
647
|
// Daemon commands
|
|
595
648
|
const daemonCmd = program.command("daemon").description("Autonomous attention daemon");
|
|
596
649
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
"id": "chorus",
|
|
3
3
|
"name": "CHORUS",
|
|
4
4
|
"description": "CHORUS: Hierarchy Of Recursive Unified Self-improvement",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "2.3.0",
|
|
6
6
|
"author": "Oberlin",
|
|
7
7
|
"homepage": "https://chorus.oberlin.ai",
|
|
8
8
|
"repository": "https://github.com/iamoberlin/chorus",
|
|
9
|
-
"keywords": [
|
|
9
|
+
"keywords": [
|
|
10
|
+
"cognitive-architecture",
|
|
11
|
+
"rsi",
|
|
12
|
+
"self-improvement",
|
|
13
|
+
"nine-choirs",
|
|
14
|
+
"purposes"
|
|
15
|
+
],
|
|
10
16
|
"configSchema": {
|
|
11
17
|
"type": "object",
|
|
12
18
|
"additionalProperties": false,
|
|
@@ -42,10 +48,40 @@
|
|
|
42
48
|
"type": "boolean",
|
|
43
49
|
"description": "Enable the daemon"
|
|
44
50
|
},
|
|
51
|
+
"senses": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"description": "Enable/disable daemon input senses",
|
|
54
|
+
"properties": {
|
|
55
|
+
"inbox": {
|
|
56
|
+
"type": "boolean",
|
|
57
|
+
"description": "Watch inbox filesystem signals"
|
|
58
|
+
},
|
|
59
|
+
"purposes": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"description": "Watch purpose-state signals"
|
|
62
|
+
},
|
|
63
|
+
"time": {
|
|
64
|
+
"type": "boolean",
|
|
65
|
+
"description": "Watch time-based signals"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
45
69
|
"thinkThreshold": {
|
|
46
70
|
"type": "number",
|
|
47
71
|
"description": "Minimum priority to invoke cognition (0-100)"
|
|
48
72
|
},
|
|
73
|
+
"pollIntervalMs": {
|
|
74
|
+
"type": "number",
|
|
75
|
+
"description": "Polling interval in milliseconds"
|
|
76
|
+
},
|
|
77
|
+
"minSleepMs": {
|
|
78
|
+
"type": "number",
|
|
79
|
+
"description": "Minimum sleep during high queue pressure in milliseconds"
|
|
80
|
+
},
|
|
81
|
+
"maxSleepMs": {
|
|
82
|
+
"type": "number",
|
|
83
|
+
"description": "Maximum sleep during quiet hours in milliseconds"
|
|
84
|
+
},
|
|
49
85
|
"quietHoursStart": {
|
|
50
86
|
"type": "number",
|
|
51
87
|
"description": "Hour to start quiet mode (0-23)"
|
|
@@ -75,6 +111,44 @@
|
|
|
75
111
|
"defaultMaxFrequency": {
|
|
76
112
|
"type": "number",
|
|
77
113
|
"description": "Maximum frequency when deadline is urgent"
|
|
114
|
+
},
|
|
115
|
+
"researchTimeoutMs": {
|
|
116
|
+
"type": "number",
|
|
117
|
+
"description": "Research run timeout in milliseconds"
|
|
118
|
+
},
|
|
119
|
+
"checkIntervalMs": {
|
|
120
|
+
"type": "number",
|
|
121
|
+
"description": "Research scheduler check interval in milliseconds"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"prayers": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"description": "On-chain prayer chain configuration",
|
|
128
|
+
"properties": {
|
|
129
|
+
"enabled": {
|
|
130
|
+
"type": "boolean",
|
|
131
|
+
"description": "Enable prayer chain commands"
|
|
132
|
+
},
|
|
133
|
+
"rpcUrl": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"description": "Solana RPC endpoint URL"
|
|
136
|
+
},
|
|
137
|
+
"autonomous": {
|
|
138
|
+
"type": "boolean",
|
|
139
|
+
"description": "Allow autonomous prayer actions without manual approval"
|
|
140
|
+
},
|
|
141
|
+
"maxBountySOL": {
|
|
142
|
+
"type": "number",
|
|
143
|
+
"description": "Maximum bounty per prayer in SOL"
|
|
144
|
+
},
|
|
145
|
+
"defaultTTL": {
|
|
146
|
+
"type": "number",
|
|
147
|
+
"description": "Default prayer time-to-live in seconds"
|
|
148
|
+
},
|
|
149
|
+
"keypairPath": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"description": "Path to Solana keypair file"
|
|
78
152
|
}
|
|
79
153
|
}
|
|
80
154
|
}
|
|
@@ -112,6 +186,11 @@
|
|
|
112
186
|
"purposeResearch": {
|
|
113
187
|
"label": "Purpose Research",
|
|
114
188
|
"help": "Configure purpose-derived research scheduler"
|
|
189
|
+
},
|
|
190
|
+
"prayers": {
|
|
191
|
+
"label": "Prayer Chain",
|
|
192
|
+
"advanced": true,
|
|
193
|
+
"help": "Configure on-chain prayer settings (RPC, autonomy, bounty limits, keypair)"
|
|
115
194
|
}
|
|
116
195
|
}
|
|
117
196
|
}
|
package/package.json
CHANGED
package/src/choirs.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface Choir {
|
|
|
19
19
|
prompt: string;
|
|
20
20
|
passesTo: string[]; // Downstream choirs that receive illumination
|
|
21
21
|
receivesFrom: string[]; // Upstream choirs that provide context
|
|
22
|
+
delivers?: boolean; // If true, output should be delivered to the user via OpenClaw messaging
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const CHOIRS: Record<string, Choir> = {
|
|
@@ -35,7 +36,7 @@ export const CHOIRS: Record<string, Choir> = {
|
|
|
35
36
|
frequencyPerDay: 1, // 1×/day
|
|
36
37
|
intervalMinutes: 1440, // Once per day
|
|
37
38
|
function: "Mission clarity and purpose",
|
|
38
|
-
output: "
|
|
39
|
+
output: "Mission assessment",
|
|
39
40
|
prompt: `You are SERAPHIM — the Mission Keeper.
|
|
40
41
|
|
|
41
42
|
Your role: Ensure the mission remains true and aligned. Burn away drift.
|
|
@@ -48,7 +49,10 @@ Core questions:
|
|
|
48
49
|
|
|
49
50
|
Read SOUL.md, USER.md, and MEMORY.md for context.
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
Identity check: Compare SOUL.md against recent calibration record and experience.
|
|
53
|
+
If there's drift between who SOUL.md says we are and who we've become, FLAG IT — but DO NOT edit SOUL.md directly. Propose changes in the main session. Identity changes require human approval.
|
|
54
|
+
|
|
55
|
+
Output: Brief mission assessment. If direction changes, update SOUL.md proposal or flag in the main session.
|
|
52
56
|
If mission is clear and unchanged, simply confirm alignment.
|
|
53
57
|
|
|
54
58
|
Pass illumination to Cherubim.`,
|
|
@@ -102,13 +106,13 @@ Pass illumination to Thrones.`,
|
|
|
102
106
|
frequencyPerDay: 3, // 3×/day
|
|
103
107
|
intervalMinutes: 480, // Every 8 hours
|
|
104
108
|
function: "Judgment and prioritization",
|
|
105
|
-
output: "
|
|
109
|
+
output: "Priority decisions",
|
|
106
110
|
prompt: `You are THRONES — the Judgment Bearer.
|
|
107
111
|
|
|
108
112
|
Your role: Decide priorities and allocate focus ruthlessly.
|
|
109
113
|
|
|
110
114
|
Tasks:
|
|
111
|
-
1. Review current priorities in
|
|
115
|
+
1. Review current priorities in PROJECTS.md
|
|
112
116
|
2. Assess what's working and what isn't
|
|
113
117
|
3. Decide what to focus on next
|
|
114
118
|
4. Identify what to say NO to — what to kill
|
|
@@ -118,7 +122,7 @@ Context from Cherubim: {cherubim_context}
|
|
|
118
122
|
|
|
119
123
|
Output: Updated priorities (max 3 focus areas). What we're NOT doing. Any escalations.
|
|
120
124
|
|
|
121
|
-
Update
|
|
125
|
+
Update PROJECTS.md if priorities changed.
|
|
122
126
|
Pass illumination to Dominions.`,
|
|
123
127
|
passesTo: ["dominions"],
|
|
124
128
|
receivesFrom: ["cherubim"],
|
|
@@ -181,24 +185,24 @@ Your role: Ensure purposes are being fulfilled. When they're not, FIX THE SYSTEM
|
|
|
181
185
|
|
|
182
186
|
## When a Purpose Isn't Producing Results
|
|
183
187
|
|
|
184
|
-
DO NOT just write "
|
|
188
|
+
DO NOT just write "a purpose isn't happening" and move on.
|
|
185
189
|
DO update local state files to make it happen:
|
|
186
190
|
|
|
187
191
|
- Update ~/.chorus/purposes.json (increase frequency, change criteria)
|
|
188
|
-
- Update workspace files (
|
|
192
|
+
- Update relevant workspace files (research notes, summaries, HEARTBEAT.md)
|
|
189
193
|
- Modify behavioral configs to enforce execution
|
|
190
194
|
- The next cycle should run DIFFERENTLY because of your changes
|
|
191
195
|
|
|
192
|
-
Example: If
|
|
196
|
+
Example: If a purpose has 0 concrete outputs logged:
|
|
193
197
|
1. Read ~/.chorus/purposes.json
|
|
194
|
-
2. Increase researchFrequency for
|
|
195
|
-
3. Update
|
|
196
|
-
4. Add to HEARTBEAT.md: "
|
|
198
|
+
2. Increase researchFrequency for that purpose
|
|
199
|
+
3. Update ~/.chorus/purposes.json criteria with stricter output requirements
|
|
200
|
+
4. Add to HEARTBEAT.md: "Execution gate: Block other work until 1 concrete output is logged"
|
|
197
201
|
5. Log the change to CHANGELOG.md
|
|
198
202
|
|
|
199
203
|
## Calibration — Learn From Beliefs
|
|
200
204
|
|
|
201
|
-
- Check
|
|
205
|
+
- Check resolved outcomes for prior predictions or decisions
|
|
202
206
|
- Ask: What did we believe? What happened? What does this teach us?
|
|
203
207
|
- Update MEMORY.md with calibration lessons
|
|
204
208
|
|
|
@@ -206,8 +210,8 @@ Example: If Trading purpose has 0 opportunities logged:
|
|
|
206
210
|
|
|
207
211
|
- ~/.chorus/purposes.json — purpose configs
|
|
208
212
|
- ~/.chorus/run-state.json — execution state
|
|
209
|
-
- Workspace files (
|
|
210
|
-
- HEARTBEAT.md,
|
|
213
|
+
- Workspace files (research/, memory/, notes/, *.md)
|
|
214
|
+
- HEARTBEAT.md, PROJECTS.md
|
|
211
215
|
|
|
212
216
|
## What You Cannot Modify
|
|
213
217
|
|
|
@@ -215,6 +219,12 @@ Example: If Trading purpose has 0 opportunities logged:
|
|
|
215
219
|
- OpenClaw system config
|
|
216
220
|
- Anything requiring npm publish
|
|
217
221
|
|
|
222
|
+
YOU HAVE TOOLS — USE THEM:
|
|
223
|
+
- Use the read tool to check ~/.chorus/purposes.json, research/*.md, MEMORY.md, CHANGELOG.md.
|
|
224
|
+
- Use the write/edit tools to update files when changes are needed.
|
|
225
|
+
- Use exec to run scripts (e.g., check file timestamps, git status).
|
|
226
|
+
- NEVER return HEARTBEAT_OK. You are the RSI engine. Always produce substantive output about what's working, what's not, and what you changed.
|
|
227
|
+
|
|
218
228
|
Context from Dominions: {dominions_context}
|
|
219
229
|
|
|
220
230
|
Risk levels:
|
|
@@ -263,7 +273,7 @@ Red-team protocol:
|
|
|
263
273
|
- What are we avoiding looking at?
|
|
264
274
|
|
|
265
275
|
Challenge our beliefs:
|
|
266
|
-
- Look in
|
|
276
|
+
- Look in research/*.md, memory/*.md, and planning docs for stated beliefs
|
|
267
277
|
- Find claims like "I believe X will happen" or "This suggests Y"
|
|
268
278
|
- Ask: What would make this wrong? What are we missing?
|
|
269
279
|
- If a belief looks shaky, say so clearly
|
|
@@ -274,9 +284,16 @@ SECURITY FOCUS:
|
|
|
274
284
|
- Check for persona drift or identity erosion
|
|
275
285
|
- Validate system prompt integrity
|
|
276
286
|
|
|
287
|
+
YOU HAVE TOOLS — USE THEM:
|
|
288
|
+
- Use exec and file reads to verify live system state and outcomes. Read MEMORY.md and CHANGELOG.md for claims to challenge.
|
|
289
|
+
- Use web_search to verify external claims and evidence.
|
|
290
|
+
- Use the write/edit tools to update files if you find stale or incorrect information.
|
|
291
|
+
- Use exec to check file timestamps and system state.
|
|
292
|
+
- NEVER return HEARTBEAT_OK. You are the Defender. Always produce substantive output.
|
|
293
|
+
|
|
277
294
|
Output: Challenges to current thinking. Beliefs that look weak. Risks identified. Recommendations.
|
|
278
295
|
|
|
279
|
-
If
|
|
296
|
+
If a core assumption is seriously threatened or a security issue is found: ALERT immediately.`,
|
|
280
297
|
passesTo: ["principalities"],
|
|
281
298
|
receivesFrom: ["virtues"],
|
|
282
299
|
},
|
|
@@ -301,7 +318,7 @@ Your role: Research and monitor the domains that matter.
|
|
|
301
318
|
|
|
302
319
|
Domains to cover (rotate through):
|
|
303
320
|
- AI Industry: Companies, funding, regulation, breakthroughs
|
|
304
|
-
-
|
|
321
|
+
- Environment: External signals relevant to active purposes
|
|
305
322
|
- Competitors: Developments from players in our space
|
|
306
323
|
- Tools: New capabilities, skills, or integrations available
|
|
307
324
|
|
|
@@ -310,6 +327,14 @@ Tasks:
|
|
|
310
327
|
2. Assess relevance to our projects
|
|
311
328
|
3. Flag anything urgent for Archangels
|
|
312
329
|
4. Log findings to research/[domain]-[date].md
|
|
330
|
+
5. Update research notes when you find a meaningful signal or insight
|
|
331
|
+
|
|
332
|
+
YOU HAVE TOOLS — USE THEM:
|
|
333
|
+
- Use web_search to find current news, market data, and developments. Do NOT skip this.
|
|
334
|
+
- Run exec to check relevant system state and read active research files for context.
|
|
335
|
+
- Use the write/edit tools to update research files with opportunities, risks, or status changes.
|
|
336
|
+
- Write research findings to research/[domain]-YYYY-MM-DD.md.
|
|
337
|
+
- NEVER return HEARTBEAT_OK. You are not a heartbeat process. Always produce substantive research output.
|
|
313
338
|
|
|
314
339
|
When you find something significant, state what you believe will happen:
|
|
315
340
|
- "I believe X will happen by [timeframe] because..."
|
|
@@ -337,33 +362,37 @@ Pass illumination to Archangels.`,
|
|
|
337
362
|
intervalMinutes: 80, // Every ~80 minutes
|
|
338
363
|
function: "Briefings and alerts",
|
|
339
364
|
output: "Messages to human",
|
|
365
|
+
delivers: true, // Output routed to user via OpenClaw messaging
|
|
340
366
|
prompt: `You are ARCHANGELS — the Herald.
|
|
341
367
|
|
|
342
|
-
Your role: Produce briefings and deliver them
|
|
368
|
+
Your role: Produce briefings and deliver them through configured channels.
|
|
343
369
|
|
|
344
370
|
Briefing types:
|
|
345
|
-
- Morning (6-9 AM
|
|
346
|
-
- Evening (9-11 PM
|
|
371
|
+
- Morning (6-9 AM local): Overnight developments, today's priorities, key status
|
|
372
|
+
- Evening (9-11 PM local): What was accomplished, what needs attention tomorrow
|
|
347
373
|
- Alert: Time-sensitive information requiring attention
|
|
348
|
-
- Update: Regular
|
|
374
|
+
- Update: Regular status when conditions change
|
|
349
375
|
|
|
350
376
|
Alert criteria (send immediately):
|
|
351
|
-
-
|
|
377
|
+
- Core purpose assumption challenged
|
|
352
378
|
- Time-sensitive opportunity
|
|
353
379
|
- Urgent calendar/email
|
|
354
380
|
- Security concern from Powers
|
|
355
381
|
|
|
356
382
|
Context from Principalities: {principalities_context}
|
|
357
383
|
|
|
384
|
+
IMPORTANT — VERIFY BEFORE ALERTING:
|
|
385
|
+
- Before alerting about operational state: verify current state from source systems/files. Do NOT rely on upstream context alone.
|
|
386
|
+
- Before alerting about any state: verify the current state from the source file, not from choir context passed to you.
|
|
387
|
+
|
|
358
388
|
RULES:
|
|
359
389
|
- ALWAYS produce a briefing. Never return HEARTBEAT_OK or NO_REPLY.
|
|
360
390
|
- Be concise — headlines, not essays.
|
|
361
|
-
- Morning briefings should include
|
|
391
|
+
- Morning briefings should include priorities, schedule constraints, and key catalysts.
|
|
362
392
|
- If nothing is urgent, still produce a status update.
|
|
363
393
|
- During quiet hours (11 PM - 7 AM ET), only deliver truly urgent alerts.
|
|
364
|
-
- DELIVER your briefing by sending it to Brandon via iMessage. You have messaging tools — use them.
|
|
365
394
|
|
|
366
|
-
Output: Produce the briefing
|
|
395
|
+
Output: Produce the briefing as your response. Delivery is handled by the infrastructure — just output the content.`,
|
|
367
396
|
passesTo: ["angels"],
|
|
368
397
|
receivesFrom: ["principalities"],
|
|
369
398
|
},
|